【问题标题】:Google Cloud Build deploy to GKE Private ClusterGoogle Cloud Build 部署到 GKE 私有集群
【发布时间】:2018-08-21 08:42:04
【问题描述】:

我正在运行带有“私有集群”选项的 Google Kubernetes Engine。 我还定义了“授权主网络”以能够远程访问环境 - 这很好用。 现在我想使用 Google Cloud Build 设置某种 CI/CD 管道 - 成功构建新的 docker 镜像后,这个新镜像应该会自动部署到 GKE。 当我第一次启动新管道时,部署到 GKE 失败 - 错误消息类似于:“无法连接到服务器:dial tcp xxx.xxx.xxx.xxx:443: i/o timeout”。 由于我怀疑“授权主网络”选项是连接超时的根本原因,因此我已将 0.0.0.0/0 添加到允许的网络并再次启动 Cloud Build 作业 - 这次一切顺利,之后创建了 docker 映像,并将其部署到 GKE。很好。

剩下的唯一问题是我真的不想让整个互联网都能够访问我的 Kubernetes 主服务器 - 这是个坏主意,不是吗?

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

【问题讨论】:

  • 这里的问题是 Cloud Build API 需要与您的集群通信,这就是当您将授权网络更改为 0.0.0.0 时它工作的原因。您必须将 Google API 使用的一系列 IP 添加到您的主授权网络,这似乎不是一个好主意。相反,构建是否会触发本地计算机的某些内容,进而触发本地计算机对 K8s 主节点的调用以更新映像?
  • 我真的尽量不让我的本地机器参与构建过程,因为我不喜欢我或我的本地机器是其中的重要组成部分。我认为问题的根本原因很明显——我认为可能有一种“谷歌内部”方式来建立与我的集群的连接。我还尝试缩小用于云构建器的 IP 范围列表,但尝试这样做仍然给我留下了很长的列表...
  • 您绝对可以采用内部方式(您可以在同一 VPC 网络上配置 GCE VM 以使用 k8s 内部端点而不是外部端点),但云构建器不会有一个 IP可以被认为是内部的,而无需将集群开放到比您想要的更多的 IP

标签: kubernetes google-kubernetes-engine google-cloud-build


【解决方案1】:

目前无法将 Cloud Build 机器添加到 VPC。同样,Cloud Build 不会公布构建机器的 IP 范围。因此,如果不在该 VPC 内的 GCE 上创建“ssh 堡垒实例”或“代理实例”,您今天就无法做到这一点。

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

【讨论】:

  • 好的——谢谢你的解释。然后我会弄清楚如何在我的 VPC 中设置 ssh/proxy。希望 Cloud Build 能够很快学会处理私有 GKE 集群。
  • 只是为了让你知道——与此同时,我已经实现了一些人们可能称之为解决方法的东西:基本上,我在 kubernetes 部署步骤之前和之后修改了允许的主网络。这样我仍然必须允许从 0.0.0.0/0 访问,但是 - 只允许几秒钟。由于这“只”涉及我们的开发环境,我可以在这里接受这种安全权衡。
  • 您可以在 k8s 部署之前再添加一个部署步骤,而不是 0.0.0.0/0,这基本上是查找正在运行的管道的公共 ip... 就像使用 'curl' 容器到 ' curl icanhazip.com',然后使用它而不是 0.0.0.0..
  • 是否有合适的解决方案(由 GCP 团队提供)?
  • 我在 gcp 中找到了这个:cloud.google.com/solutions/…
【解决方案2】:

我们最终做了以下事情:

1) 从 cloudbuild.yaml 中删除部署步骤

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

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

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

【讨论】:

  • 这是执行 Kubernetes 集群部署的绝佳方式,我也将它用于几个客户端。除了解决私有控制平面的可达性问题之外,它还是 Kubernetes 和构建之间的一个很好的分离。使用 keel (keel.io),您不仅可以获得自动部署,还可以在 web ui 中手动批准它们。
  • 这是一个全新的话题,不是吗——退后一步,用不同的范例重新设计 CI/CD。如果是这样,FluxCDArgoCD 可以做同样的事情——事实上,他们可以返回到您的 Github 并更新您的 Github 存储库中的图像标签,以便您的 Github 与您新上传的 Docker 图像保持同步。
【解决方案3】:

更新答案(2021 年 2 月 22 日)

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

原答案

还可以在 Cloud Build 步骤中创建 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

此步骤在cloudbuild network 中启动一个名为kubectl-proxy背景 Docker 容器,所有其他 Cloud Build 步骤都使用该容器。 Docker 容器使用 Cloud Build 服务帐户身份建立 IAP tunnel。隧道连接到 GCE 实例,其中预装了 SOCKS 或 HTTPS 代理(留给读者的练习)。

在后续步骤中,您可以简单地访问集群

- id: setup-k8s
  name: gcr.io/cloud-builders/kubectl
  entrypoint: sh
  args:
  - -c
  - HTTPS_PROXY=socks5://kubectl-proxy:8080 kubectl apply -f config.yml

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

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

【讨论】:

  • 这看起来很棒!我花了一段时间才了解网络流程。说明每一跳和相应的通信协议(例如,“https”)的图表会有很大帮助。
  • 不幸的是,虽然这种方法有效,但 IAP 隧道似乎受到速率限制。如果有很多资源通过kubectl 部署,那么隧道会在一段时间后超时。我不得不使用另一个技巧,即通过 Terraform 将 Cloud Build IP 列入白名单,然后直接应用,这每次都有效。我更新了答案。
  • 所以您将 Cloud Build 的(公共)IP 列入 master authorized networks CIDR 的白名单,对吧?这只适用于 K8s 主节点具有 external IP 的情况,对吗?如果主节点有一个internal IP 怎么办?您的 IAP 隧道方法(尽管有速率限制)在后一种情况下提供 TCP 级别的连接。这是我见过的所有其他解决方案的关键区别。
  • 确实如此,尽管我为我们的集群找到了一个很好的平衡点:将集群节点设为私有,同时让主节点保持公开。这样一来,master 授权网络只允许从少数几个 IP 访问,即使从集群外未列入白名单的 GCP 节点也无法访问 master。
  • @dinvlad - 非常感谢您帮助我。我已经添加了建立与 kubectl bastion 连接的云构建步骤的端口,但是在下一步中,当我尝试执行 kubectl 命令时它失败了,gist.github.com/rajathithan/4faf04f94c40772a997e7c774a6fdb9e
【解决方案4】:

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

此外,即使我们不将它用于 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. 我家中的笔记本电脑使用我的家庭互联网公共 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

【讨论】:

  • 我认为这里的问题是,如何在云构建步骤中以分离模式启动第一个终端?
  • 回答你的问题^^^,@dinvlad 的第一个启动 background Docker 容器的代码 sn-p 就可以解决问题。他的回答有一些没有详细说明的好点子——这在 Docker 和网络中都是一个技巧。
  • 是的 - 如果它过于简洁,我们深表歉意。感谢您的详细说明!
【解决方案5】:

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

Quickstart

【讨论】:

    【解决方案6】:

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-04-14
      • 1970-01-01
      • 2021-05-20
      • 2020-01-30
      • 2019-12-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多