GKE 地址管理:为 GKE Pod CIDR 地址块配置 NAT

本教程属于面向网络架构师的系列教程。该系列介绍了 IPv4 地址紧缺的组织可用的 Google Kubernetes Engine (GKE) 地址管理方案。

该系列包含以下部分:

本教程介绍如何配置一个解决方案,在该解决方案中您将为 Pod CIDR 地址块分配已在本地网络中使用的 RFC 1918 地址块。然后,您将使用 ip-masq-agent 功能(通过 NAT)转换 CIDR 地址块。此方法可有效地隐藏节点 IP 地址后面的 Pod IPv4 地址。然后,您将使用 Terraform 自动构建基础架构,并使用 Cloud SDK 检查下图中显示的组件。

转换 GKE 集群中的 Pod CIDR 地址块(如需支持屏幕阅读功能的 PDF 版本,请点击图片。)
图 1:转换 GKE 集群中的 Pod CIDR 地址块(如需支持屏幕阅读功能的 PDF 版本,请点击图片。)

请使用 Terraform 设置以下组件:

  • 具有 VPC 原生 GKE 集群的 Cloud 项目,该集群用于托管 Hello World 应用。该应用通过内部负载平衡器公开。
  • GKE 集群的子网。
  • 模拟本地 CIDR 地址块的子网。

如需更深入地了解整体解决方案和各个组件,请参阅 GKE Pod CIDR 地址块的 NAT

本教程假定您熟悉以下内容:

  • Linux 系统管理员命令
  • GKE
  • Compute Engine
  • NAT
  • Terraform

目标

使用 Terraform 部署以下内容:

  • 具有 VPC 原生 GKE 集群的项目
  • GKE 集群的子网
  • 模拟本地 CIDR 地址块的子网
  • Hello World 应用
  • 用于公开 Hello World 应用的内部负载平衡器

使用 Cloud SDK 执行以下操作:

  • 检查每个解决方案组件。
  • 验证 Hello World 应用。
  • 验证模拟的本地机器中 Pod CIDR 地址块的转换。

费用

本教程使用 Google Cloud 的以下收费组件:

您可使用价格计算器根据您的预计使用量来估算费用。 Google Cloud 新用户可能有资格申请免费试用

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

准备工作

在本部分中,您需要准备 Cloud Shell,设置环境变量并部署辅助基础架构。

准备 Cloud Shell

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

    转到 Cloud Shell

    您可以使用 HashiCorp 的 Terraform 和 Cloud SDK 从 Cloud Shell 终端完成本教程的大部分内容。

  2. 在 Cloud Shell 中,克隆 GitHub 代码库并更改为本地工作目录:

    git clone https://github.com/GoogleCloudPlatform/terraform-gke-nat-connectivity.git kam
    cd kam/podnat
    

    该代码库包含完成本教程所需的所有文件。如需查看每个文件的完整说明,请参阅代码库中的 README.md 文件。

  3. 将所有 Shell 脚本设置为可执行脚本:

    sudo chmod 755 *.sh
    
  4. 安装 Terraform

  5. 初始化 Terraform:

    terraform init
    

    输出类似于以下内容:

    ...
    Initializing provider plugins...
    The following providers do not have any version constraints in configuration, so the latest version was installed.
    
    To prevent automatic upgrades to new major versions that may contain breaking changes, it is recommended to add version = "..." constraints to the corresponding provider blocks in configuration, with the constraint strings suggested below.
    
    ∗ provider.google: version = "~> 2.5"
    
    Terraform has been successfully initialized!
    
    You may now begin working with Terraform. Try running "terraform plan" to see any changes that are required for your infrastructure. All Terraform commands should now work.
    
    If you ever set or change modules or backend configuration for Terraform, rerun this command to reinitialize your working directory. If you forget, other commands will detect it and remind you to do so if necessary.
    ...
    

设置环境变量

  1. 设置并验证 TF_VAR_org_id 变量,将 your-organization-name 替换为您希望在本教程中使用的 Google Cloud 组织名称:

    export TF_VAR_org_id=$(gcloud organizations list | \
        awk '/your-organization-name/ {print $2}')
    
  2. 验证是否已正确设置环境变量:

    echo $TF_VAR_org_id
    

    命令输出会以数字形式列出您的组织 ID,如下所示:

    ...
    123123123123
    ...
    
  3. 设置其余的环境变量:

    source set_variables.sh
    

    验证是否已正确设置环境变量:

    env | grep TF_
    

    输出类似于以下内容:

    ...
    TF_VAR_zone=us-west1-b
    TF_VAR_cluster_password=ThanksForAllTheFish
    TF_VAR_node_cidr=10.32.1.0/24
    TF_VAR_region=us-west1
    TF_VAR_billing_account=QQQQQQ-XAAAAA-E87690
    TF_VAR_cluster_cidr=192.168.1.0/24
    TF_VAR_org_id=406999999999
    TF_VAR_ilb_ip=10.32.1.49
    TF_VAR_isolated_vpc_pid=ivpc-pid--999999999
    TF_VAR_gcp_user=user@example
    TF_VAR_on_prem_cidr=10.32.2.0/24
    TF_VAR_cluster_username=dolphins
    TF_VAR_pod_cidr=172.16.0.0/16
    ...
    
  4. 创建环境变量文件:

    env | grep TF_ | sed 's/^/export /' > TF_ENV_VARS
    

    此命令链会将您创建的环境变量重定向到名为 TF_ENV_VARS 的文件中。每个变量前面都会附加 export 命令。如果 Cloud Shell 会话终止,您可以使用此文件来重置环境变量。这些变量可供 Terraform 脚本、Cloud Shell 脚本和 gcloud 命令行工具使用。

    如果以后需要重新初始化这些变量,则可以从该文件所在的目录运行以下命令:

    source TF_ENV_VARS
    

部署辅助基础架构

  • 在 Cloud Shell 中,部署 Terraform 支持的基础架构:

    terraform apply
    

    在进行任何更改之前,Terraform 会先提示您进行确认。回答 yes 即可应用任一配置。

    terraform apply 命令指示 Terraform 部署此解决方案的所有组件。要详细了解如何通过声明定义基础结构,您可以浏览 Terraform 清单,即扩展名为 .tf 的文件。

检查辅助基础架构

您现在可以使用 gcloud 命令行工具来查看和验证 Terraform 所创建的基础架构。在验证时,您需要运行一条命令,以查看资源是否响应以及是否已正确创建。

验证项目

  1. 在 Cloud Shell 中,列出项目:

    gcloud projects list | grep ivpc-pid
    

    输出类似于以下内容:

    ...
    isolated-vpc-pid       isolated-vpc-pname        777999333962
    ...
    
  2. 列出 API 状态:

    gcloud services list --project=$TF_VAR_isolated_vpc_pid \
        | grep -E "compute|container"
    

    输出类似于以下内容:

    ...
    compute.googleapis.com            Compute Engine API
    container.googleapis.com          Google Kubernetes Engine API
    ...
    

验证网络和子网

  • 在 Cloud Shell 中,验证网络和子网:

    gcloud compute networks describe ivpc \
        --project=$TF_VAR_isolated_vpc_pid
    gcloud compute networks subnets describe node-cidr \
        --project=$TF_VAR_isolated_vpc_pid \
        --region=$TF_VAR_region
    gcloud compute networks subnets describe simulated-on-prem \
      --project=$TF_VAR_isolated_vpc_pid \
      --region=$TF_VAR_region
    

    输出类似于以下内容:

    ...
    kind: compute#network
    name: ivpc
    routingConfig:
      routingMode: GLOBAL
    ...
    subnetworks:
    ‐ https://www.googleapis.com/compute/v1/projects/ivpc-pid--695116665/regions/us-west1/subnetworks/node-cidr
    x_gcloud_bgp_routing_mode: GLOBAL
    ...
    gatewayAddress: 10.32.1.1
    ...
    ipCidrRange: 10.32.1.0/24
    kind: compute#subnetwork
    name: node-cidr
    ...
    secondaryIpRanges:
    ‐ ipCidrRange: 172.16.0.0/16
      rangeName: pod-cidr
    ...
    subnetworks:
    ‐ https://www.googleapis.com/compute/v1/projects/ivpc-pid--695116665/regions/us-west1/subnetworks/simulated-on-prem
    x_gcloud_bgp_routing_mode: GLOBAL
    ...
    gatewayAddress: 10.32.2.1
    ...
    ipCidrRange: 10.32.2.0/24
    kind: compute#subnetwork
    name: simulated-on-prem
    ...
    

验证防火墙规则

  • 在 Cloud Shell 中,验证隔离的 VPC 中的防火墙规则:

    gcloud compute firewall-rules list --project=$TF_VAR_isolated_vpc_pid
    

    输出类似于以下内容:

    ...
    NAME                  NETWORK           DIRECTION  PRIORITY  ALLOW  DENY  DISABLED
    allow-rfc1918-in-fwr  isolated-vpc-net  INGRESS    1000      all          False
    allow-ssh-in-fwr      isolated-vpc-net  INGRESS    1000      22           False
    ...
    

验证虚拟机

  • 在 Cloud Shell 中,验证虚拟机:

    gcloud compute instances list --project=$TF_VAR_isolated_vpc_pid
    

    输出类似于以下内容:

    ...
    NAME                                     ZONE        MACHINE_TYPE   PREEMPTIBLE  INTERNAL_IP  EXTERNAL_IP     STATUS
    gke-cluster1-default-pool-fc9ba891-4xhj  us-west1-b  n1-standard-1               10.32.1.4    34.83.33.188    RUNNING
    gke-cluster1-default-pool-fc9ba891-d0bd  us-west1-b  n1-standard-1               10.32.1.3    34.83.48.81     RUNNING
    gke-cluster1-default-pool-fc9ba891-xspg  us-west1-b  n1-standard-1               10.32.1.2    35.247.62.159   RUNNING
    simulated-on-prem-host                   us-west1-b  n1-standard-1               10.32.2.2    35.227.173.106  RUNNING
    ...
    

验证 GKE 集群及其资源

  1. 在 Cloud Shell 中,获取集群凭据:

    gcloud container clusters get-credentials cluster1 \
        --project=$TF_VAR_isolated_vpc_pid \
        --zone $TF_VAR_zone
    

    输出类似于以下内容:

    ...
    Fetching cluster endpoint and auth data.
    kubeconfig entry generated for cluster1.
    ...
    
  2. 验证集群:

    gcloud container clusters list \
        --project=$TF_VAR_isolated_vpc_pid \
        --zone=$TF_VAR_zone
    

    输出类似于以下内容:

    ...
    NAME     LOCATION    MASTER_VERSION MASTER_IP   MACHINE_TYPE   NODE_VERSION  NUM_NODES  STATUS
    cluster1 us-west1-b  1.11.8-gke.6   192.0.2.58  n1-standard-1  1.11.8-gke.6  3          RUNNING
    ...
    
  3. 验证 Hello World 应用:

    kubectl get deployment my-app
    

    输出类似于以下内容:

    ...
    NAME     DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
    my-app   3         3         3            3           118m
    ...
    
  4. 验证内部负载平衡器服务:

    kubectl get service hello-server
    

    输出类似于以下内容:

    ...
    NAME           TYPE           CLUSTER-IP    EXTERNAL-IP   PORT(S)          AGE
    hello-server   LoadBalancer   10.32.11.49   <pending>     8080:30635/TCP   3m
    ...
    

验证解决方案

如需验证解决方案,您必须验证以下内容:

  • ip-masq-agent 功能
  • 是否可以从外部访问 Hello World 应用
  • Pod CIDR NAT

验证 ip-masq-agent 功能

  1. 在 Cloud Shell 中,获取节点名称:

    export NODE_NAME=$(kubectl get nodes | awk '/gke/ {print $1}' | head -n 1)
    
  2. 使用 SSH 连接到集群节点:

    gcloud compute ssh $NODE_NAME \
        --project=$TF_VAR_isolated_vpc_pid \
        --zone=$TF_VAR_zone
    
  3. 验证 ip-masq-agent 配置:

    sudo iptables -t nat -L
    

    输出类似于以下内容:

    ...
    Chain IP-MASQ (2 references)
    target      prot opt source               destination
    RETURN      all  --  anywhere             169.254.0.0/16       /* ip-masq-agent: local traffic is not subject to MASQUERADE */
    RETURN      all  --  anywhere             10.32.1.0/24         /* ip-masq-agent: local traffic is not subject to MASQUERADE */
    RETURN      all  --  anywhere             172.16.0.0/16        /* ip-masq-agent: local traffic is not subject to MASQUERADE */
    RETURN      all  --  anywhere             192.168.1.0/24       /* ip-masq-agent: local traffic is not subject to MASQUERADE */
    MASQUERADE  all  --  anywhere             anywhere             /* ip-masq-agent: outbound traffic is subject to MASQUERADE (must be last in chain) */
    ...
    
  4. 退出 SSH 会话。

    exit
    

验证是否可从外部访问 Hello World 应用

  1. 使用 SSH 连接到模拟的本地虚拟机:

    gcloud compute ssh simulated-on-prem-host \
        --project=$TF_VAR_isolated_vpc_pid \
        --zone=$TF_VAR_zone
    
  2. 验证 Hello World 应用:

    curl http://10.32.1.49:8080
    

    输出类似于以下内容:

    ...
    Hello, world!
    Version: 1.0.0
    Hostname: my-app-77748bfbd8-nqwl2
    ...
    

验证 Pod CIDR NAT

  1. 在模拟的本地虚拟机上,运行 tcpdump

    sudo tcpdump -n icmp
    

    输出类似于以下内容:

    ...
    tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
    listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
    ...
    

    此命令会启动 tcpdump 实用程序来捕获遍历隔离的 VPC 网关的数据包。该实用程序仅捕获包含 GKE 服务所创建的内部负载平衡器 IP 地址的数据包。

  2. 在 Cloud Console 中,打开新的 Cloud Shell 终端。

    转到 Cloud Shell

  3. 在该新终端中,通过更改为 Git 代码库目录并输入以下命令来设置环境变量:

    source TF_ENV_VARS
    
  4. 获取集群凭据:

    gcloud container clusters get-credentials cluster1 \
        --project=$TF_VAR_isolated_vpc_pid \
        --zone $TF_VAR_zone
    
  5. 获取 Pod 名称:

    export POD_NAME=$(kubectl get pods | awk '/my-app/ {print $1}' | head -n 1)
    
  6. 连接到 Pod shell:

    kubectl exec -it $POD_NAME -- /bin/sh
    
  7. 在原始 Cloud Shell 终端中,对模拟的本地虚拟机执行 ping 操作:

    ping 10.32.2.2
    

    在原始终端中,输出类似于以下内容:

    ...
    
    05:43:40.669371 IP 10.32.1.3 > 10.32.2.2: ICMP echo request, id 3328, seq 0, length 64
    05:43:40.669460 IP 10.32.2.2 > 10.32.1.3: ICMP echo reply, id 3328, seq 0, length 64
    ...
    

    请注意,源 IP 地址来自节点 10.32.1.0/24 CIDR 地址块。 节点地址后面的 Pod 10.32.2.0/24 CIDR 地址块已转换。按 Control+C 可停止 ping 操作,然后退出所有终端会话。

清除数据

销毁基础架构

  1. 在第一个 Cloud Shell 终端中,输入 exit,以便从 SSH 会话退出到隔离的 VPC 网关。
  2. 销毁本教程中的所有组件:

    terraform destroy
    

    在进行该更改之前,Terraform 会先提示您进行确认。回答 yes 即可销毁此配置。

    您可能会看到以下 Terraform 错误:

    ...
    ∗ google_compute_network.ivpc (destroy): 1 error(s) occurred:
    ∗ google_compute_network.ivpc: Error waiting for Deleting Network: The network resource 'projects/ivpc-pid--1058675427/global/networks/isolated-vpc-net' is already being used by 'projects/ivpc-pid--1058675427/global/firewalls/k8s-05693142c93de80e-node-hc'
    ...
    

    如果该命令在销毁 GKE 防火墙规则之前试图销毁隔离的 VPC 网络,则会出现此错误。运行以下脚本,从隔离的 VPC 中移除非默认的防火墙规则:

    ./k8-fwr.sh
    

    输出结果显示将要移除的防火墙规则。

  3. 查看规则,并在出现提示时输入 yes

  4. 在第一个 Cloud Shell 终端上,重新发出以下命令:

    terraform destroy
    

    在进行该更改之前,Terraform 会先提示您进行确认。回答 yes 即可销毁此配置。

  5. 从原始 Cloud Shell 终端发出以下命令:

    cd ../..
    rm -rf kam
    

后续步骤