为 GKE pod 设置内部 HTTP(S) 负载平衡

本文档介绍如何为在 Google Kubernetes Engine (GKE) pod 中运行的服务配置内部 HTTP(S) 负载平衡。

准备工作

本指南中的说明要求您使用 bash 或类似 bash 的 shell。例如,您可以使用 Cloud Shell

在按照本指南中的说明进行操作之前,请查看以下文档:

使用基于 GKE 的服务配置内部 HTTP(S) 负载平衡

此部分显示了在 GKE pod 上运行的服务所需的配置。客户端虚拟机连接到您在转发规则中配置的 IP 地址和端口。当您的客户端应用向此 IP 地址和端口发送流量时,它们的请求会根据您的内部 HTTP(S) 负载平衡器的网址映射转发到您的后端 GKE pod。

此页面上的示例明确设置内部转发规则的预留内部 IP 地址,而不是允许分配临时内部 IP 地址。这是转发规则的推荐最佳做法。

对于转发规则的 IP 地址,请使用 backend-subnet。如果您尝试使用代理专用子网,则无法创建转发规则。

配置后端:GKE 集群

本部分创建一个演示 GKE 集群和一个用于运行简单容器的部署,该容器运行提供节点主机名的简单网络服务器。此外,本部分还创建一个自动创建 NEG 的 ClusterIP 服务。

控制台

要使用 Cloud Console 创建集群,请执行以下步骤:

  1. 转到 Cloud Console 中的 Google Kubernetes Engine 菜单。

    访问 Google Kubernetes Engine 菜单

  2. 点击创建集群

  3. 名称字段中,输入 l7-ilb-cluster

  4. 对于位置类型,选择 Zonal

  5. 对于地区,请选择 us-west1-b

  6. 选择默认的主版本

  7. 展开可用性、网络、安全性及其他功能

    1. 确保已勾选启用 VPC 原生(使用别名 IP)框。
    2. 对于网络,请选择 lb-network
    3. 对于节点子网,请选择 backend-subnet
    4. 通过勾选 Enable HTTP load balancing 旁边的框,确保启用负载平衡
  8. 对于其余字段,保留默认值或根据您的需求对其进行调整。

  9. 点击创建

在使用 Cloud Console 创建集群时,必须将系统生成的网络标记添加到代理防火墙过滤条件:

  1. 查找 GKE 添加到集群中的节点的网络标记。该标记通过集群哈希生成。

    1. 转到“虚拟机实例”页面。

      转到“虚拟机实例”页面

    2. 下拉菜单中,选择网络标记

    3. 复制 GKE 节点的网络标记。该标记应如下所示:

      gke-l7-ilb-cluster-id-node
      
  2. 修改 fw-allow-proxies 防火墙规则并添加标记。

    1. 转到 Google Cloud Console 中的“防火墙规则”页面。
      转到“防火墙规则”页面
    2. 点击 fw-allow-proxies 防火墙规则,然后点击修改
    3. 目标标记字段中,添加您在上一步中复制的网络标记。
    4. 点击保存

gcloud

  1. 使用 gcloud container clusters create 命令创建 GKE 集群。

    gcloud container clusters create l7-ilb-cluster \
        --zone=us-west1-b \
        --network=lb-network \
        --subnetwork=backend-subnet \
        --enable-ip-alias \
        --tags=allow-ssh,load-balanced-backend
    

API

使用 projects.zones.clusters.create 方法创建 GKE 集群,将 [project-id] 替换为您的项目 ID

POST https://container.googleapis.com/v1/projects/[project-id]/zones/us-west1-b/clusters
{
  "cluster": {
    "name": "l7-ilb-cluster",
    "network": "projects/[project-id]/global/networks/lb-network",
    "subnetwork": "projects/[project-id]/regions/us-west1/subnetworks/backend-subnet",
    "initialClusterVersion": "",
    "location": "us-west1-b",
    "nodePools": [{
      "name": "l7-ilb-node-pool",
      "initialNodeCount": 3,
      "config": {
        "tags":["allow-ssh","load-balanced-backend"]
      }
    }],
    "defaultMaxPodsConstraint": {
      "maxPodsPerNode": "110"
    },
    "ipAllocationPolicy": {
      "useIpAliases": true
    }
  }
}

获取操作集群的凭据

使用 gcloud container clusters get-credentials 命令。

gcloud container clusters get-credentials l7-ilb-cluster \
    --zone=us-west1-b

使用提供其主机名的测试容器定义部署

使用部署和服务规范创建 hostname.yaml

cat << EOF > hostname.yaml
apiVersion: v1
kind: Service
metadata:
  name: hostname
  annotations:
    cloud.google.com/neg: '{"exposed_ports":{"80":{}}}'
spec:
  ports:
  - port: 80
    name: host1
    protocol: TCP
    targetPort: 8000
  selector:
    run: hostname
  type: ClusterIP

---

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    run: hostname
  name: hostname
spec:
  replicas: 3
  selector:
    matchLabels:
      run: hostname
  template:
    metadata:
      labels:
        run: hostname
    spec:
      containers:
      - image: gcr.io/kubernetes-e2e-test-images/serve-hostname-amd64:1.1
        name: host1
        command:
        - /bin/sh
        - -c
        - /serve_hostname -http=true -udp=false -port=8000
        ports:
        - protocol: TCP
          containerPort: 8000
EOF

应用配置

  1. 如果您没有使用服务帐号授予对 Google Cloud 的访问权限,请运行 gcloud auth application-default login 命令。

    gcloud auth application-default login
    
  2. 应用配置。

    kubectl apply -f hostname.yaml
    

验证部署和 GKE 配置

确保已创建新服务主机名且正在运行应用 pod。

kubectl get svc

示例输出如下所示:

NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
hostname     ClusterIP   10.71.9.71           80/TCP    41m
[..skip..]

请注意,您的集群 IP 地址可能会有所不同,因为 GKE 会自动将集群 IP 范围创建为 backend-subnet 的次要 IP 范围。

kubectl get pods

示例输出如下所示:

NAME                        READY     STATUS    RESTARTS   AGE
hostname-6db459dcb9-896kh   1/1       Running   0          33m
hostname-6db459dcb9-k6ddk   1/1       Running   0          50m
hostname-6db459dcb9-x72kb   1/1       Running   0          33m
[..skip..]

输出应显示新的服务主机名。主机名应用的 pod 的 STATUS 应该为 Running

注意,您的 pod 名称与示例名称不同。

获取 NEG 的名称

使用 gcloud compute network-endpoint-groups list 命令查找 NEG 的名称,并按集群的地区和部署的名称进行过滤。将 NEG 名称分配给名为 DEPLOYMENT_NAME 的变量:

export DEPLOYMENT_NAME=$(gcloud compute network-endpoint-groups list \
       --filter="us-west1-b AND hostname" \
       --format="get(name)")
echo $DEPLOYMENT_NAME

检查 NEG 配置

使用 gcloud compute network-endpoint-groups list-network-endpointsgcloud compute network-endpoint-groups describe 命令检查 NEG 中的详细信息并列出其中的端点。

gcloud compute network-endpoint-groups describe $DEPLOYMENT_NAME \
    --zone=us-west1-b
gcloud compute network-endpoint-groups list-network-endpoints $DEPLOYMENT_NAME \
    --zone=us-west1-b

为 GKE 配置负载平衡器

下面的示例介绍了如何创建以下内部 HTTP(S) 负载平衡器资源:

  • HTTP 运行状况检查
  • 将 NEG 作为后端的后端服务
  • 网址映射
    • 如果为目标 HTTP(S) 代理定义了区域,请务必参阅区域网址映射。区域网址映射根据您为传入网址的主机和路径定义的规则将请求路由到区域后端服务。区域网址映射只能由同一区域中的区域目标代理规则引用。
  • SSL 证书(适用于 HTTPS)
  • 目标代理
  • 转发规则

对于转发规则的 IP 地址,请使用 backend-subnet。如果您尝试使用代理专用子网,则无法创建转发规则。

控制台

选择负载平衡器类型

  1. 转到 Google Cloud Console 中的“负载平衡”页面。
    转到“负载平衡”页面
  2. HTTP(S) 负载平衡下,点击开始配置
  3. 选择“仅在我的虚拟机之间”。此设置表示负载平衡器是内部的。
  4. 点击继续

准备负载平衡器

  1. 对于负载平衡器的名称,请输入 l7-ilb-gke-map
  2. 对于区域,请选择 us-west1
  3. 对于网络,请选择 lb-network
  4. 不关闭窗口继续操作。

预留代理专用子网

对于内部 HTTP(S) 负载平衡,预留代理子网:

  1. 点击 Reserve a Subnet(预留子网)。
  2. 名称输入 proxy-only-subnet
  3. 对于 IP 地址范围,请输入 10.129.0.0/23
  4. 点击添加

配置后端服务

  1. 点击后端配置
  2. 创建或选择后端服务菜单中,选择创建后端服务
  3. 将后端服务的名称设置为 l7-ilb-gke-backend-service
  4. 后端类型下,选择网络端点组
  5. 后端部分的新建后端卡片中执行以下操作:
    1. 网络端点组设置为由 GKE 创建的 NEG。如需了解如何确定其名称,请参阅“获取 NEG 的名称”
    2. 输入每个端点的最大速率 5 RPS。必要时 Google Cloud 将超过此上限。
    3. 点击完成
  6. 运行状况检查部分,使用以下参数选择“创建运行状况检查”
    1. 名称l7-ilb-gke-basic-check
    2. 协议:HTTP
    3. 端口指定:正在服务的端口
    4. 点击保存并继续
  7. 点击创建

配置网址映射

  1. 点击主机和路径规则。确保 l7-ilb-gke-backend-service 是任何不匹配的主机和任何不匹配的路径的唯一后端服务。

配置前端

对于 HTTP

  1. 点击前端配置
  2. 点击添加前端 IP 和端口
  3. 名称设置为 l7-ilb-gke-forwarding-rule
  4. 协议设置为“HTTP”
  5. 子网设置为“backend-subnet”
  6. 内部 IP 下,选择“预留静态内部 IP 地址”
  7. 在显示的面板中提供以下详细信息:
    1. 名称:l7-ilb-gke-ip
    2. 静态 IP 地址部分,选择“让我选择”
    3. 自定义 IP 地址部分,输入 10.1.2.199
    4. 点击保留
  8. 端口设置为 80
  9. 点击完成

对于 HTTPS

如果您在客户端和负载平衡器之间使用 HTTPS,则需要一个或多个 SSL 证书资源来配置代理。请参阅 SSL 证书,了解如何创建 SSL 证书资源。 内部 HTTP(S) 负载平衡器目前不支持由 Google 管理的证书。

  1. 点击前端配置
  2. 点击添加前端 IP 和端口
  3. 名称字段中,输入 l7-ilb-gke-forwarding-rule
  4. 协议字段中,选择 HTTPS (includes HTTP/2)
  5. 子网设置为“backend-subnet”
  6. 内部 IP 下,选择“预留静态内部 IP 地址”
  7. 在显示的面板中提供以下详细信息:
    1. 名称:l7-ilb-gke-ip
    2. 静态 IP 地址部分,选择“让我选择”
    3. 自定义 IP 地址部分,输入 10.1.2.199
    4. 点击保留
  8. 确保将端口设置为 443,以允许 HTTPS 流量。
  9. 点击证书下拉列表。
  10. 如果您已经拥有要用作主要 SSL 证书的自行管理的 SSL 证书资源,请从下拉菜单中选择该证书。
  11. 否则,请选择创建新证书
  12. 填写名称 l7-ilb-cert
  13. 在相应字段中上传您的 PEM 格式的文件:
    • 公钥证书
    • 证书链
    • 私钥
  14. 点击创建
  • 要添加除了主要 SSL 证书资源之外的其他证书资源,请执行以下操作:
    1. 点击添加证书
    2. 证书列表中选择一个证书,或点击创建新证书并按照上述说明操作。
  • 点击完成
  • 完成配置

    1. 点击创建

    gcloud

    1. 使用 gcloud compute health-checks create http 命令定义 HTTP 运行状况检查。

      gcloud compute health-checks create http l7-ilb-gke-basic-check \
         --region=us-west1 \
         --use-serving-port
      
    2. 使用 gcloud compute backend-services create 命令定义后端服务。

      gcloud compute backend-services create l7-ilb-gke-backend-service \
        --load-balancing-scheme=INTERNAL_MANAGED \
        --protocol=HTTP \
        --health-checks=l7-ilb-gke-basic-check \
        --health-checks-region=us-west1 \
        --region=us-west1
      
    3. 使用 gcloud compute backend-services add-backend 命令将 NEG 后端添加到后端服务。

      gcloud compute backend-services add-backend l7-ilb-gke-backend-service \
         --network-endpoint-group=$DEPLOYMENT_NAME \
         --network-endpoint-group-zone=us-west1-b \
         --region=us-west1 \
         --balancing-mode=RATE \
         --max-rate-per-endpoint=5
      
    4. 使用 gcloud compute url-maps create 命令创建网址映射。

      gcloud compute url-maps create l7-ilb-gke-map \
        --default-service=l7-ilb-gke-backend-service \
        --region=us-west1
      
    5. 创建目标代理。

      对于 HTTP

      使用 gcloud compute target-http-proxies create 命令。

      gcloud compute target-http-proxies create l7-ilb-gke-proxy \
        --url-map=l7-ilb-gke-map \
        --url-map-region=us-west1 \
        --region=us-west1
      

      对于 HTTPS

      请参阅 SSL 证书,了解如何创建 SSL 证书资源。 内部 HTTP(S) 负载平衡器目前不支持由 Google 管理的证书。

      将文件路径分配给变量名称。

      `export LB_CERT=path to PEM-formatted file`
      
      `export LB_PRIVATE_KEY=path to PEM-formatted file`
      

      使用 gcloud compute ssl-certificates create 命令创建区域性 SSL 证书。

      gcloud compute ssl-certificates create

      gcloud compute ssl-certificates create l7-ilb-cert \
        --certificate=$LB_CERT \
        --private-key=$LB_PRIVATE_KEY \
        --region=us-west1
      

      使用区域性 SSL 证书,通过 gcloud compute target-https-proxies create 命令创建目标代理。

      gcloud compute target-https-proxies create

      gcloud compute target-https-proxies create l7-ilb-gke-proxy \
        --url-map=l7-ilb-gke-map \
        --region=us-west1 \
        --ssl-certificates=l7-ilb-cert
      
    6. 创建转发规则。

      对于自定义网络,必须在转发规则中引用子网。请注意,这是虚拟机子网,而非代理子网。

      对于 HTTP

      使用带有正确标志的 gcloud compute forwarding-rules create 命令。

      gcloud compute forwarding-rules create l7-ilb-gke-forwarding-rule \
        --load-balancing-scheme=INTERNAL_MANAGED \
        --network=lb-network \
        --subnet=backend-subnet \
        --address=10.1.2.199 \
        --ports=80 \
        --region=us-west1 \
        --target-http-proxy=l7-ilb-gke-proxy \
        --target-http-proxy-region=us-west1
      

      对于 HTTPS

      使用带有正确标志的 gcloud compute forwarding-rules create 命令。

      gcloud compute forwarding-rules create l7-ilb-gke-forwarding-rule \
        --load-balancing-scheme=INTERNAL_MANAGED \
        --network=lb-network \
        --subnet=backend-subnet \
        --address=10.1.2.199 \
        --ports=443 \
        --region=us-west1 \
        --target-https-proxy=l7-ilb-gke-proxy \
        --target-https-proxy-region=us-west1
      

    API

    regionHealthChecks.insert 方法发出 POST 请求以创建运行状况检查,注意要将 [project-id] 替换为您的项目 ID。

    POST https://compute.googleapis.com/compute/v1/projects/[project-id]/regions/{region}/healthChecks
    {
      "name": "l7-ilb-gke-basic-check",
      "type": "HTTP",
      "httpHealthCheck": {
        "portSpecification": "USE_SERVING_PORT"
      }
    }
    

    通过向 regionBackendServices.insert 方法发出 POST 请求来创建区域性后端服务,注意要将 [project-id] 替换为您的项目 ID,将 [neg-name] 替换为您创建的 NEG 的名称

    POST https://www.googleapis.com/compute/v1/projects/[project-id]/regions/us-west1/backendServices
    {
      "name": "l7-ilb-gke-backend-service",
      "backends": [
        {
          "group": "https://www.googleapis.com/compute/v1/projects/[project-id]/zones/us-west1-b/networkEndpointGroups/[neg-name]",
          "balancingMode": "RATE",
          "maxRatePerEndpoint": 5
        }
      ],
      "healthChecks": [
        "projects/[project-id]/regions/us-west1/healthChecks/l7-ilb-gke-basic-check"
      ],
      "loadBalancingScheme": "INTERNAL_MANAGED"
    }
    

    regionUrlMaps.insert 方法发出 POST 请求以创建网址映射,注意要将 [project-id] 替换为您的项目 ID。

    POST https://compute.googleapis.com/compute/v1/projects/[project-id]/regions/us-west1/urlMaps
    {
      "name": "l7-ilb-gke-map",
      "defaultService": "projects/[project-id]/regions/us-west1/backendServices/l7-ilb-gke-backend-service"
    }
    

    regionTargetHttpProxies.insert 方法发出 POST 请求以创建目标 HTTP 代理,注意要将 [project-id]替换为您的项目 ID。

    POST https://www.googleapis.com/compute/v1/projects/[project-id]/regions/us-west1/targetHttpProxy
    {
      "name": "l7-ilb-gke-proxy",
      "urlMap": "projects/[project-id]/global/urlMaps/l7-ilb-gke-map",
      "region": "us-west1"
    }
    

    forwardingRules.insert 方法发出 POST 请求以创建转发规则,注意要将 [project-id] 替换为您的项目 ID。

    POST https://www.googleapis.com/compute/v1/projects/[project-id]/regions/us-west1/forwardingRules
    {
      "name": "l7-ilb-gke-forwarding-rule",
      "IPAddress": "10.1.2.199",
      "IPProtocol": "TCP",
      "portRange": "80-80",
      "target": "projects/[project-id]/regions/us-west1/targetHttpProxies/l7-ilb-gke-proxy",
      "loadBalancingScheme": "INTERNAL_MANAGED",
      "subnetwork": "projects/[project-id]/regions/us-west1/subnetworks/backend-subnet",
      "network": "projects/[project-id]/global/networks/lb-network",
      "networkTier": "PREMIUM",
    }
    

    测试

    在地区中创建虚拟机实例以测试连接

    gcloud compute instances create l7-ilb-client-us-west1-b \
        --image-family=debian-9 \
        --image-project=debian-cloud \
        --zone=us-west1-b \
        --network=lb-network \
        --subnet=backend-subnet \
        --tags=l7-ilb-client,allow-ssh
    

    测试负载平衡器

    登录客户端实例,以便可以通过内部 HTTP(S) 负载平衡器的转发规则 IP 地址访问后端的 HTTP(S) 服务,并且流量正在 NEG 中的端点之间进行负载平衡。

    通过 SSH 连接到每个客户端实例

    gcloud compute ssh l7-ilb-client-us-west1-b \
        --zone=us-west1-b
    

    验证 IP 是否提供其主机名

    curl 10.1.2.199
    

    对于 HTTPS 测试,请将 curl 替换为:

    curl -k -s 'https://test.example.com:443' --connect-to test.example.com:443:10.1.2.199:443
    

    -k 标志会导致 curl 跳过证书验证。

    运行 100 个请求并确认它们已进行负载平衡

    对于 HTTP

    {
    RESULTS=
    for i in {1..100}
    do
        RESULTS="$RESULTS:$(curl --silent 10.1.2.199)"
    done
    echo "***"
    echo "*** Results of load-balancing to 10.1.2.199: "
    echo "***"
    echo "$RESULTS" | tr ':' '\n' | grep -Ev "^$" | sort | uniq -c
    echo
    }
    

    对于 HTTPS

    {
    RESULTS=
    for i in {1..100}
    do
        RESULTS="$RESULTS:$(curl -k -s 'https://test.example.com:443' --connect-to test.example.com:443:10.1.2.199:443
    )"
    done
    echo "***"
    echo "*** Results of load-balancing to 10.1.2.199: "
    echo "***"
    echo "$RESULTS" | tr ':' '\n' | grep -Ev "^$" | sort | uniq -c
    echo
    }
    

    请注意,多个代理执行负载平衡,一个代理对应于一个 curl 命令,并且它们不协调后端选择。因此,在此测试中,后端不会收到相同数量的请求。 但是,从长期来看(换句话说,数千到数百万个请求),每个后端接收到的请求所占的比例几乎相等。

    其他配置选项

    本部分对配置示例进行了详细讲解,提供了一些其他替代配置选项。所有任务均为可选任务。 您可以按任意顺序执行这些任务。

    启用会话亲和性

    这些流程介绍了如何为示例内部 HTTP(S) 负载平衡器更新后端服务,以便后端服务使用生成的 Cookie 亲和性、标头字段亲和性或 HTTP Cookie 亲和性。

    启用生成的 Cookie 亲和性时,负载平衡器会针对第一个请求发出一个 Cookie。对于具有相同 Cookie 的每个后续请求,负载平衡器会将请求定向到同一个后端虚拟机或端点。对于内部 HTTP(S) 负载平衡器,Cookie 名为 GCILB

    启用标头字段亲和性时,负载平衡器会根据 --custom-request-header 标志中指定的 HTTP 标头的值将请求路由到 NEG 中的后端虚拟机或端点。仅当负载平衡位置政策为 RING_HASHMAGLEV 且后端服务的一致性哈希指定 HTTP 标头的名称时,标头字段亲和性才有效。

    启用 HTTP Cookie 亲和性时,负载平衡器根据带可选 --affinity-cookie-ttl 标志的 HTTP_COOKIE 标志中指定的 HTTP Cookie,将请求路由到 NEG 中的后端虚拟机或端点。如果客户端未在其 HTTP 请求中提供 Cookie,则代理会生成 Cookie 并通过 Set-Cookie 标头将其返回给客户端。仅当负载平衡位置政策为 RING_HASHMAGLEV 且后端服务的一致性哈希指定 HTTP Cookie 时,HTTP Cookie 亲和性才有效。

    控制台

    要为后端服务启用或更改会话亲和性,请执行以下操作:

    1. 转到 Google Cloud Console 中的“负载平衡”页面。
      转到“负载平衡”页面
    2. 点击后端
    3. 点击 l7-ilb-backend-service(您为此示例创建的后端服务的名称),然后点击修改
    4. 后端服务详情页面上,点击高级配置
    5. 会话亲和性下,从菜单中选择所需的会话亲和性类型。
    6. 点击更新

    gcloud

    使用以下 gcloud 命令将 l7-ilb-backend-service 后端服务更新为不同类型的会话亲和性:

    gcloud compute backend-services update l7-ilb-backend-service \
        --session-affinity=[GENERATED_COOKIE | HEADER_FIELD | HTTP_COOKIE | CLIENT_IP ]
        --region=us=west1
    

    API

    要设置会话亲和性,请向 regionBackendServices/patch 方法发出 PATCH 请求。

    PATCH https://www.googleapis.com/compute/v1/projects/[PROJECT_ID]/regions/us-west1/regionBackendServices/l7-ilb-backend-service
    {
      "sessionAffinity": ["GENERATED_COOKIE" | "HEADER_FIELD" | "HTTP_COOKIE" | "CLIENT_IP" ]
    }
    

    后续步骤