Google Cloud Build 部署到 GKE 私有集群

Google Cloud Build deploy to GKE Private Cluster

我是 运行 具有 "private-cluster" 选项的 Google Kubernetes 引擎。 我还定义了 "authorized Master Network" 以能够远程访问环境 - 这很好用。 现在我想使用 Google Cloud Build 设置某种 CI/CD 管道 - 成功构建新的 docker 镜像后,这个新镜像应该会自动部署到 GKE。 当我第一次启动新管道时,部署到 GKE 失败了——错误消息类似于:"Unable to connect to the server: dial tcp xxx.xxx.xxx.xxx:443: i/o timeout"。 由于我怀疑 "authorized master networks" 选项是连接超时的根本原因,我已将 0.0.0.0/0 添加到允许的网络并再次启动 Cloud Build 作业 - 这次一切顺利,之后docker 映像已创建,已部署到 GKE。好

剩下的唯一问题是我真的不想让整个 Internet 都能够访问我的 Kubernetes master - 这是个坏主意,不是吗?

是否有更优雅的解决方案来通过使用允许的主网络来缩小访问范围并能够通过云构建进行部署?

目前无法将 Cloud Build 机器添加到 VPC。同样,Cloud Build 不会公布构建机器的 IP 范围。因此,如果不在该 VPC 内的 GCE 上创建 "ssh bastion instance" 或 "proxy instance",您今天就无法执行此操作。

我怀疑这种情况很快就会改变。 GCB 在 GKE 私有集群之前就已经存在,私有集群仍然是测试版功能。

我们最终做了以下事情:

1) 从 cloudbuild.yaml

中移除部署步骤

2) 在私有集群中安装 Keel,并在 cloud builder / registry 项目中赋予它 pub/sub 编辑权限

Keel 会监控镜像的变化,并根据您的设置自动部署它们。

效果很好,因为现在我们无需添加虚拟机或执行任何类型的 bastion/ssh 主机即可推送 sha 哈希图像更新。

更新答案 (02/22/2021)

不幸的是,虽然以下方法有效,但 IAP 隧道似乎受到速率限制。如果通过 kubectl 部署了大量资源,则隧道会在一段时间后超时。我不得不使用另一个技巧,即通过 Terraform 将 Cloud Build IP 地址动态列入白名单,然后直接申请,每次都有效。

原回答

也可以在云构建步骤中创建 IAP 隧道:

- id: kubectl-proxy
  name: gcr.io/cloud-builders/docker
  entrypoint: sh
  args:
  - -c
  - docker run -d --net cloudbuild --name kubectl-proxy
      gcr.io/cloud-builders/gcloud compute start-iap-tunnel
      bastion-instance 8080 --local-host-port 0.0.0.0:8080 --zone us-east1-b &&
    sleep 5

此步骤使用 Cloud Build 服务帐户身份在 cloudbuild network, which is used by all of the other Cloud Build steps. The Docker container establishes an IAP tunnel 中启动名为 kubectl-proxybackground Docker 容器。隧道连接到预装了 SOCKS 或 HTTPS 代理的 GCE 实例(留给 reader 的练习)。

在后续步骤中,您可以像

一样简单地访问集群
- id: setup-k8s
  name: gcr.io/cloud-builders/kubectl
  entrypoint: sh
  args:
  - -c
  - HTTPS_PROXY=socks5://kubectl-proxy:8080 kubectl apply -f config.yml

与上面建议的其他方法相比,此方法的主要优点:

  • 无需具有 public IP 的“堡垒”主机 - kubectl-proxy 主机可以完全私有,从而维护隐私集群
  • 隧道连接依赖于 Cloud Build 可用的默认 Google 凭据,因此不需要 store/pass 任何长期凭据,例如 SSH 密钥

更新: 我想这不会与生产强度一起工作,原因与上面@dinvlad 的更新相同,即 IAP 中的速率限制。我将把我原来的 post 留在这里,因为它确实解决了网络连接问题,并说明了底层网络机制。

此外,即使我们不将它用于 Cloud Build,我的方法也提供了一种从我的笔记本电脑到 K8s 私有主节点的隧道。因此,我可以在我的笔记本电脑上编辑 K8s yaml 文件(例如,使用 VS Code),并立即从我的笔记本电脑上执行 kubectl,而不必将代码发送到堡垒主机并在里面执行 kubectl堡垒主机。我发现这大大提高了开发时间的生产力。

原回答

================

我想我可能会对上面@dinvlad 提供的出色解决方案有所改进。

我认为无需安装 HTTP 代理服务器即可简化解决方案。还需要一个堡垒主机。

我提供以下概念证明(没有 HTTP 代理服务器)。此 PoC 说明了底层网络机制,而不涉及 Google Cloud Build (GCB) 的干扰。 (以后有空的时候,我会在 Google Cloud Build 上测试完整的实现。)

假设:

  1. 我有一个 GKE 集群,其主节点是私有的,例如,具有 IP 地址 10.x.x.x。
  2. 我有一个名为 my-bastion 的堡垒计算实例。它只有私有 IP 而没有外部 IP。私有 IP 在 GKE 集群的 master authorized networks CIDR 内。因此,在 my-bastion 中,kubectl 对私有 GKE 主节点起作用。因为my-bastion没有外部IP,我家的笔记本电脑通过IAP连接到它。
  3. 我家里的笔记本电脑,使用家庭互联网 public IP 地址,无法轻松连接到上面的私有 GKE 主节点。

我的目标是在我的笔记本电脑上针对该私有 GKE 集群执行 kubectl。从网络架构的角度来看,我家笔记本电脑的位置就像Google Cloud Build服务器。

理论: 知道 gcloud compute ssh(和关联的 IAP)是 SSH 的包装器,SSH 动态端口转发应该可以为我们实现这个目标。

实践:

## On laptop:
LAPTOP~$ kubectl get ns
^C            <<<=== Without setting anything up, this hangs (no connectivity to GKE).

## Set up SSH Dynamic Port Forwarding (SOCKS proxy) from laptop's port 8443 to my-bastion.
LAPTOP~$ gcloud compute ssh my-bastion --ssh-flag="-ND 8443" --tunnel-through-iap

在我笔记本电脑的另一个终端中:

## Without using the SOCKS proxy, this returns my laptop's home public IP:
LAPTOP~$ curl https://checkip.amazonaws.com
199.xxx.xxx.xxx

## Using the proxy, the same curl command above now returns a different IP address, 
## i.e., the IP of my-bastion. 
## Note: Although my-bastion doesn't have an external IP, I have a GCP Cloud NAT 
## for its subnet (for purpose unrelated to GKE or tunneling).
## Anyway, this NAT is handy as a demonstration for our curl command here.
LAPTOP~$ HTTPS_PROXY=socks5://127.0.0.1:8443 curl -v --insecure https://checkip.amazonaws.com
* Uses proxy env variable HTTPS_PROXY == 'socks5://127.0.0.1:8443'  <<<=== Confirming it's using the proxy
...
* SOCKS5 communication to checkip.amazonaws.com:443
...
* TLSv1.2 (IN), TLS handshake, Finished (20):             <<<==== successful SSL handshake
...
> GET / HTTP/1.1
> Host: checkip.amazonaws.com
> User-Agent: curl/7.68.0
> Accept: */*
...
< Connection: keep-alive
<
34.xxx.xxx.xxx            <<<=== Returns the GCP Cloud NAT'ed IP address for my-bastion 

最后,kubectl的关键时刻:

## On laptop:
LAPTOP~$ HTTPS_PROXY=socks5://127.0.0.1:8443 kubectl --insecure-skip-tls-verify=true get ns
NAME              STATUS   AGE
default           Active   3d10h
kube-system       Active   3d10h

现在可以创建连接到您的私有 VPC 并且可以从 Cloud Build 访问的 VM 池。

Quickstart

我按照此 google 文档让 cloudbuild 与我的私有 GKE 集群一起工作: https://cloud.google.com/architecture/accessing-private-gke-clusters-with-cloud-build-private-pools

这让我可以使用 cloudbuild 和 terraform 管理 GKE 集群,并启用对控制平面的授权网络访问。我考虑过维护一个荒谬的白名单,但这最终会破坏使用授权网络访问控制的目的。

我会注意到 cloudbuild 私有池通常比非私有池慢。这是由于专用池的无服务器性质。到目前为止,我没有遇到其他人提到的速率限制。

我们的解决方法是在 CI/CD 中添加步骤——将 cloudbuild 的 IP 列入白名单,通过授权主网络

注意:需要 Cloud Build 服务帐户 的额外权限

Kubernetes Engine Cluster Admin

在 cloudbuild.yaml 上,在 deployment/s 之前添加白名单步骤。

此步骤获取 Cloud Build 的 IP,然后更新容器集群设置;

# Authorize Cloud Build to Access the Private Cluster (Enable Control Plane Authorized Networks)
- name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
  id: 'Authorize Cloud Build'
  entrypoint: 'bash'
  args:
    - -c
    - |
      apt-get install dnsutils -y &&
      cloudbuild_external_ip=$(dig @resolver4.opendns.com myip.opendns.com +short) &&
      gcloud container clusters update my-private-cluster --zone=$_ZONE --enable-master-authorized-networks --master-authorized-networks $cloudbuild_external_ip/32 &&
      echo $cloudbuild_external_ip

由于云构建已列入白名单,部署将继续进行,不会出现 i/o 超时错误。

这消除了设置 VPN/专用工作人员池的复杂性。

部署后禁用控制平面授权网络。

# Disable Control Plane Authorized Networks after Deployment
- name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
  id: 'Disable Authorized Networks'
  entrypoint: 'gcloud'
  args:
    - 'container'
    - 'clusters'
    - 'update'
    - 'my-private-cluster'
    - '--zone=$_ZONE'
    - '--no-enable-master-authorized-networks'

即使在 cross-project / cross-environment 部署中,这种方法也很有效。