使用网络代理创建 GKE 专用集群以进行主实例访问

使用专用集群主端点创建 GKE 专用集群时,您无法通过公共互联网访问集群的主实例,但您必须访问该主实例以对其进行管理。

默认情况下,集群可以通过其专用端点访问主实例,并且已获授权的网络可以在 VPC 中进行定义。

但是,如需从本地或其他 VPC 网络访问主实例,则需要执行额外步骤。这是因为托管主实例的 VPC 网络由 Google 拥有,无法通过借助其他 VPC 网络对等互连连接、Cloud VPN 或 Cloud Interconnect 连接的资源进行访问。

要从本地或从通过 Cloud VPN 或 Cloud Interconnect 连接的其他 VPC 网络访问主实例,请从您的 VPC 网络到 Google 自有 VPC 网络之间启用路由导出

如需从其他 VPC 网络或通过其他 VPC 网络对等互连(例如采用中心辐射型设计)连接的本地访问主实例,请创建在已获授权的 IP 地址空间中托管的代理,这是因为 VPC 网络对等互连不具有传递性。

本教程介绍了如何在您的 GKE 专用集群中配置代理。

目标

  • 创建没有外部访问权限的 GKE 专用集群。
  • 创建并部署 Docker 映像以运行代理。
  • 创建 Kubernetes 服务以访问代理。
  • 测试对代理的访问权限。

费用

本教程使用 Google Cloud Platform 的收费组件,包括:

您可以使用价格计算器根据预计使用情况来估算费用。

完成本教程后,您可以删除所创建的资源以避免继续计费。如需了解详情,请参阅清理

准备工作

  1. 登录您的 Google 帐号。

    如果您还没有 Google 帐号,请注册一个新帐号

  2. 在 Cloud Console 的项目选择器页面上,选择或创建 Cloud 项目。

    转到项目选择器页面

  3. 确保您的 Google Cloud 项目已启用结算功能。 了解如何确认您的项目已启用结算功能

  4. 启用 Compute Engine and Google Kubernetes Engine API。

    启用 API

设置环境

在本教程中,您将使用 Cloud Shell 输入命令。Cloud Shell 让您能够使用 Cloud Console 中的命令行,并包含在 Google Cloud 中进行开发所需的 Cloud SDK 和其他工具。Cloud Shell 显示为 Cloud Console 底部的一个窗口。初始化可能需要几分钟,但窗口会立即显示。

如需使用 Cloud Shell 设置您的环境,请执行以下操作:

  1. 在 Cloud Console 中,打开 Cloud Shell。

    打开 Cloud Shell

  2. 确保正在使用您创建或选择的项目。将 [YOUR_PROJECT_ID] 替换为您的 Google Cloud 项目。

    gcloud config set project [YOUR_PROJECT_ID]
    export PROJECT_ID=`gcloud config list --format="value(core.project)"`
    
  3. 设置默认计算区域。在本教程中,计算区域为 us-central1-c。如果要部署到生产环境,请部署到您选择的区域

    gcloud config set compute/region us-central1
    gcloud config set compute/zone us-central1-c
    export REGION=us-central1
    export ZONE=us-central1-c
    
    

创建 VPC 网络和客户端虚拟机

创建用于托管资源的 VPC 网络和子网。

  1. 创建 VPC 网络:

    gcloud compute networks create k8s-proxy --subnet-mode=custom
    
  2. 在新创建的 VPC 网络中创建自定义子网:

    gcloud compute networks subnets create subnet-cluster \
        --network=k8s-proxy --range=10.50.0.0/16
    
  3. 创建将用于在 Kubernetes 集群中部署资源的客户端虚拟机:

    gcloud compute instances create --subnet=subnet-cluster \
        --scopes cloud-platform proxy-temp
    
  4. 将新创建的实例的内部 IP 地址保存在环境变量中:

    export CLIENT_IP=`gcloud compute instances describe proxy-temp \
        --format="value(networkInterfaces[0].networkIP)"`
    
  5. 创建防火墙规则以允许通过 SSH 访问 VPC 网络:

    gcloud compute firewall-rules create k8s-proxy-ssh --network k8s-proxy \
        --allow tcp:22
    

创建专用集群

现在创建一个用于本教程的专用集群。

如果您已经拥有要使用的集群,则可以跳过创建集群的步骤,但必须在客户端计算机上配置一些初始访问形式。

  • 在 Cloud Shell 中,创建一个集群:

    gcloud container clusters create frobnitz  \
        --master-ipv4-cidr=172.16.0.64/28 \
        --network k8s-proxy \
        --subnetwork=subnet-cluster \
        --enable-ip-alias \
        --enable-private-nodes \
        --enable-private-endpoint \
        --master-authorized-networks $CLIENT_IP/32 \
        --enable-master-authorized-networks
    

    该命令会创建一个名为 frobnitz 的 GKE 专用集群,并将主实例授权网络设置为仅允许客户端机器访问。

创建 Docker 映像

按照以下步骤构建一个名为 k8s-api-proxy, 的 Kubernetes API 代理映像,该映像充当 Kubernetes API 服务器的转发代理。

  1. 在 Cloud Shell 中,创建一个目录并切换到该目录:

    mkdir k8s-api-proxy && cd k8s-api-proxy
  2. 创建 Dockerfile。以下配置会在 Alpine 中创建容器,它是一个具有 Privoxy 代理的轻量级分发容器。Dockerfile 还会安装 curljq 以进行容器初始化,添加必要的配置文件,在内部向 GKE 公开端口 8118,并添加启动脚本。

    FROM alpine
    RUN apk add -U curl privoxy jq && \ mv /etc/privoxy/templates /etc/privoxy-templates && \ rm -rf /var/cache/apk/* /etc/privoxy/* && \ mv /etc/privoxy-templates /etc/privoxy/templates ADD --chown=privoxy:privoxy config \ /etc/privoxy/ ADD --chown=privoxy:privoxy k8s-only.action \ /etc/privoxy/ ADD --chown=privoxy:privoxy k8s-rewrite-internal.filter \ /etc/privoxy/ ADD k8s-api-proxy.sh /
    EXPOSE 8118/tcp
    ENTRYPOINT ["./k8s-api-proxy.sh"]
  3. k8s-api-proxy 目录中,创建 config 文件并向其中添加以下内容。

    #config directory
    confdir /etc/privoxy
    # Allow Kubernetes API access only
    actionsfile /etc/privoxy/k8s-only.action
    # Rewrite https://CLUSTER_IP to https://kubernetes.default
    filterfile /etc/privoxy/k8s-rewrite-internal.filter
    # Don't show the pod name in errors
    hostname k8s-privoxy
    # Bind to all interfaces, port :8118
    listen-address  :8118
    # User cannot click-through a block
    enforce-blocks 1
    # Allow more than one outbound connection
    tolerate-pipelining 1
    
  4. 在同一目录中,创建 k8s-only.action 文件并向其中添加以下内容。请注意,k8s-api-proxy.sh 运行时将替换 CLUSTER_IP

    # Block everything...
    {+block{Not Kubernetes}}
    /
    # ... except the internal k8s endpoint, which you rewrite (see # k8s-rewrite-internal.filter). {+client-header-filter{k8s-rewrite-internal} -block{Kubernetes}} CLUSTER_IP/
  5. 创建 k8s-rewrite-internal.filter 文件并向其中添加以下内容。请注意,k8s-api-proxy.sh 运行时将替换 CLUSTER_IP

    CLIENT-HEADER-FILTER: k8s-rewrite-internal\
     Rewrite https://CLUSTER_IP/ to https://kubernetes.default/
    s@(CONNECT) CLUSTER_IP:443\
     (HTTP/\d\.\d)@$1 kubernetes.default:443 $2@ig
    
  6. 创建 k8s-api-proxy.sh 文件并向其中添加以下内容。

    #!/bin/sh
    
    set -o errexit
    set -o pipefail
    set -o nounset
    
    # Get the internal cluster IP
    export TOKEN=$(cat /run/secrets/kubernetes.io/serviceaccount/token)
    INTERNAL_IP=$(curl -H "Authorization: Bearer $TOKEN" -k -SsL https://kubernetes.default/api |
    jq -r '.serverAddressByClientCIDRs[0].serverAddress')
    
    # Replace CLUSTER_IP in the rewrite filter and action file
    sed -i "s/CLUSTER_IP/${INTERNAL_IP}/g"\
     /etc/privoxy/k8s-rewrite-internal.filter
    sed -i "s/CLUSTER_IP/${INTERNAL_IP}/g"\
     /etc/privoxy/k8s-only.action
    
    # Start Privoxy un-daemonized
    privoxy --no-daemon /etc/privoxy/config
    
  7. k8s-api-proxy.sh 设为可执行文件:

    chmod +x k8s-api-proxy.sh
  8. 构建容器并将其推送到项目中。

    docker build -t gcr.io/$PROJECT_ID/k8s-api-proxy:0.1 .
    docker push gcr.io/$PROJECT_ID/k8s-api-proxy:0.1
    

部署映像和服务

  1. 在 Cloud Shell 中,登录您之前创建的客户端虚拟机:

    gcloud compute ssh proxy-temp
    
  2. 安装 kubectl 工具。

    sudo apt-get install kubectl
    
  3. 将项目 ID 保存为环境变量:

    export PROJECT_ID=`gcloud config list --format="value(core.project)"`
    
  4. 获取集群凭据:

    gcloud container clusters get-credentials frobnitz \
    --zone us-central1-c --internal-ip
    
  5. 创建一个 Kubernetes 部署,以公开您刚刚创建的容器:

    kubectl run k8s-api-proxy \
        --image=gcr.io/$PROJECT_ID/k8s-api-proxy:0.1 \
        --port=8118
    
  6. 为内部负载平衡器创建 ilb.yaml 文件,并将以下内容复制到其中:

    apiVersion: v1
    kind: Service
    metadata:
      labels:
        run: k8s-api-proxy
      name: k8s-api-proxy
      namespace: default
      annotations:
        cloud.google.com/load-balancer-type: "Internal"
    spec:
      ports:
      - port: 8118
        protocol: TCP
        targetPort: 8118
      selector:
        run: k8s-api-proxy
      type: LoadBalancer
    
  7. 部署内部负载平衡器:

    kubectl create -f ilb.yaml
  8. 检查服务并等待为其分配 IP 地址:

    kubectl get service/k8s-api-proxy

    输出将如下所示。当您看到分配了一个外部 IP 时,代理已准备就绪。

    NAME            TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE
    k8s-api-proxy   LoadBalancer   10.24.13.129   10.24.24.3    8118:30282/TCP   2m
    

    此步骤中的外部 IP 地址即是您的代理地址。

  9. 将 ILB 的 IP 地址保存为环境变量:

    export LB_IP=`kubectl get  service/k8s-api-proxy \
    -o jsonpath='{.status.loadBalancer.ingress[].ip}'`
    
  10. 将集群的主 IP 地址保存到环境变量中:

    export MASTER_IP=`gcloud container clusters describe frobnitz \
    --zone=us-central1-c \
    --format="get(privateClusterConfig.privateEndpoint)"`
    
  11. 通过代理访问 Kubernetes API,以验证该代理是否可用:

    curl -k -x $LB_IP:8118 https://$MASTER_IP/version
    
    输出将如下所示(您的输出可能会有所不同):
    {
      "major": "1",
      "minor": "15+",
      "gitVersion": "v1.15.11-gke.5",
      "gitCommit": "a5bf731ea129336a3cf32c3375317b3a626919d7",
      "gitTreeState": "clean",
      "buildDate": "2020-03-31T02:49:49Z",
      "goVersion": "go1.12.17b4",
      "compiler": "gc",
      "platform": "linux/amd64"
    }
    
  12. https_proxy 环境变量设置为 HTTP(S) 代理,以便 kubectl 命令可以从任何位置到达内部负载平衡器:

    export https_proxy=$LB_IP:8118
  13. 通过运行 kubectl 命令测试您的代理和 https_proxy 变量:

    kubectl get pods

    您将获得如下所示的输出,这表示您已通过代理成功连接到 Kubernetes API:

    NAME                             READY   STATUS    RESTARTS   AGE
    k8s-api-proxy-766c69dd45-mfqf4   1/1     Running   0          6m15s
    
  14. 退出客户端虚拟机:

    exit

清理

为避免因本教程中使用的资源而导致您的 Google Cloud Platform 帐号产生费用,请执行以下操作:

删除项目

  1. 在 Cloud Console 中,转到管理资源页面。

    转到“管理资源”页面

  2. 在项目列表中,选择要删除的项目,然后点击删除
  3. 在对话框中输入项目 ID,然后点击关闭以删除项目。

后续步骤