通过独立区域级 NEG 实现容器原生负载平衡机制

本页面介绍了如何创建由地区网络端点组 (NEG) 提供支持的 Kubernetes Service。

如需了解容器原生负载平衡的优势、要求和限制,请参阅容器原生负载平衡

概览

网络端点组 (NEG) 是由负载平衡器提供服务的一组后端。NEG 是由 NEG 控制器管理的 IP 地址列表,供 Google Cloud 负载平衡器使用。NEG 中的 IP 地址可以是虚拟机的主要或次要 IP 地址,这意味着它们可以是 Pod IP 地址。这样可实现容器原生负载平衡,直接从 Google Cloud 负载平衡器向 Pod 发送流量。

下图描述了 Kubernetes API 对象与 Compute Engine 对象之间的对应关系。

Kubernetes Service 对应于 Compute Engine 网络端点组,而 Kubernetes Pod 则对应于 Compute Engine 网络端点。控制层面的 NEG 控制器组件负责管理这些对应关系。

使用 NEG 的 Ingress

将 NEG 与 GKE Ingress 搭配使用时,Ingress 控制器会协助创建 L7 负载平衡器的所有方面。这包括创建虚拟 IP 地址、转发规则、运行状况检查、防火墙规则等等。

建议借助 Ingress 来使用容器原生负载平衡,因为它的许多功能都可简化 NEG 的管理。如果 Ingress 管理的 NEG 不符合您的使用场景,则可以选择使用独立 NEG。

独立 NEG

如果没有使用由 Ingress 预配的负载平衡器来部署 NEG ,则会被视为独立 NEG。独立 NEG 通过 NEG 控制器部署和管理,但转发规则、运行状况检查和其他负载平衡对象都需要手动部署。

独立 NEG 不会与已启用 Ingress 的容器原生负载平衡发生冲突。

下图展示了负载平衡对象在每种场景下的部署方式差异:

使用独立 NEG 以及 Ingress 托管的 NEG 时,GKE 控制层面上的 NEG 控制器负责管理 NEG 和网络端点对象。使用独立 NEG 时,其他所有组件都由用户管理,如前面几段内容所述。

防止泄露 NEG

使用独立 NEG 时,您需要负责管理 NEG 的生命周期以及构成负载平衡器的资源。您可能会通过以下方式泄露 NEG:

  • 删除 GKE 服务时,如果后端服务仍引用 NEG,则不会对关联的 NEG 进行垃圾回收。 从后端服务解引用 NEG,以便能够删除 NEG。
  • 删除集群时,独立 NEG 不会被删除。

独立 NEG 的使用场景

独立 NEG 有多种关键用途。独立 NEG 非常灵活。 这与 Ingress(搭配或不搭配 NEG)形成了对比,Ingress 定义了一组经过精选的特定负载平衡对象,目的是方便使用。

独立 NEG 的使用场景包括:

容器和虚拟机的异构服务

NEG 可以同时包含虚拟机和容器 IP 地址。这意味着单个虚拟 IP 地址可指向同时包含 Kubernetes 和非 Kubernetes 工作负载的后端。这也可用于将现有工作负载迁移到 GKE 集群。

独立 NEG 可以指向虚拟机 IP,从而可以手动将负载平衡器配置为指向由同一服务 VIP 的虚拟机和容器组成的后端。

自定义 Ingress 控制器

您可以使用自定义 Ingress 控制器(或者不使用 Ingress 控制器)来配置针对独立 NEG 的负载平衡器。

将 Traffic Director 与 GKE 搭配使用

Traffic Director 与 GKE 搭配使用。Traffic Director 使用独立 NEG 为托管式服务网格提供容器原生负载平衡。

将 TCP 代理负载平衡与 GKE 搭配使用

您可以使用独立 NEG,通过没有 Kubernetes/GKE 原生支持的 TCP 代理负载平衡器直接平衡容器负载。

Pod 就绪性

就绪性门控是 Kubernetes 的一项扩展功能,能够向 PodStatus 注入额外的反馈或信号,以允许 Pod 转换为就绪状态。NEG 控制器管理着自定义就绪性门控,以确保从 Compute Engine 负载平衡器到 Pod 的整条网络路径能正常工作。容器原生负载平衡中介绍了 GKE 内的 Pod 就绪性门控。

使用 NEG 的 Ingress 代表负载平衡器部署和管理 Compute Engine 运行状况检查。不过,独立 NEG 不会对 Compute Engine 运行状况检查做任何假设,因为它们预期会单独进行部署和管理。Compute Engine 运行状况检查应始终与负载平衡器一起配置,以防止流量发送到尚未准备好接收的后端。如果没有与 NEG 相关联的运行状况检查状态(通常是因为未配置任何运行状况检查),则 NEG 控制器会在其对应端点在 NEG 中编程时将 Pod 就绪性门控的值标记为 True。

要求

GKE 1.10 及更高版本中提供了独立 NEG。版本 1.16.4 及更高版本中的独立 NEG 已启用 Pod 就绪性反馈。

您的集群必须是 VPC 原生集群。如需了解详情,请参阅创建使用别名 IP 的 VPC 原生集群

您的集群必须启用 HTTP 负载平衡。GKE 集群默认启用了 HTTP 负载平衡;您不得将其停用。

准备工作

在开始之前,请确保您已执行以下任务:

使用以下任一方法设定默认的 gcloud 设置:

  • 使用 gcloud init(如果您想要在系统引导下完成默认设置)。
  • 使用 gcloud config(如果您想单独设置项目 ID、区域和地区)。

使用 gcloud init

如果您收到 One of [--zone, --region] must be supplied: Please specify location 错误,请完成本部分。

  1. 运行 gcloud init 并按照说明操作:

    gcloud init

    如果您要在远程服务器上使用 SSH,请使用 --console-only 标志来防止命令启动浏览器:

    gcloud init --console-only
  2. 按照说明授权 gcloud 使用您的 Google Cloud 帐号。
  3. 创建新配置或选择现有配置。
  4. 选择 Google Cloud 项目。
  5. 选择默认的 Compute Engine 区域。

使用 gcloud config

  • 设置默认项目 ID
    gcloud config set project project-id
  • 如果您使用的是区域级集群,请设置默认计算区域
    gcloud config set compute/zone compute-zone
  • 如果您使用的是地区级集群,请设置默认计算地区
    gcloud config set compute/region compute-region
  • gcloud 更新到最新版本:
    gcloud components update

使用独立 NEG

以下说明展示了如何在 GKE 上将独立 NEG 与外部 HTTP 负载平衡器配合使用。

这涉及到创建多个对象:

  • 用于创建和管理 Pod 的 Deployment
  • 用于创建 NEG 的 Service
  • 使用 Compute Engine API 创建的负载平衡器。这与通过使用 NEG 的 Ingress 创建的负载平衡器不同;在后一种情况下,Ingress 会为您创建并配置负载平衡器。使用独立 NEG 时,您负责关联 NEG 和后端服务,以将 Pod 连接到负载平衡器。负载平衡器由几个组件组成,如下图所示:

负载平衡器的组件包括转发规则、目标 HTTP 代理、网址映射、运行状况检查和后端服务。这会将流量引导至包含 Pod IP 地址的 NEG。

创建 VPC 原生集群

如需使用容器原生负载平衡,必须创建已启用别名 IP 地址的集群。此集群需满足以下条件:

  • 必须运行 Google Kubernetes Engine 1.16.4 或更高版本。
  • 必须是 VPC 原生集群。
  • 必须启用 HTTP 负载平衡插件。GKE 集群默认启用了 HTTP 负载平衡;您不得将其停用。

以下命令会在区域 us-central1-a 中创建一个集群 neg-demo-cluster,其中带有一个自动预配的子网:

gcloud container clusters create neg-demo-cluster \
    --enable-ip-alias \
    --create-subnetwork="" \
    --network=default \
    --zone=us-central1-a \
    --cluster-version version

其中集群 version 必须为 1.16.4 或更高版本。

创建 Deployment

以下清单指定了一个 Deployment neg-demo-app,它运行某容器化 HTTP 服务器的三个实例。该 HTTP 服务器使用应用服务器的主机名(运行服务器所在的 Pod 的名称)来响应请求。

如果您使用的 GKE 版本中提供了 Pod 就绪性反馈,我们建议您使用利用该反馈的工作负载。如需了解详情,请参阅上面的“Pod 就绪性”部分;如需了解使用 Pod 就绪性反馈时对 GKE 版本的要求,并参阅“要求”部分。请考虑升级您的集群以使用 Pod 就绪性反馈。

使用 Pod 就绪性反馈

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    run: neg-demo-app # Label for the Deployment
  name: neg-demo-app # Name of Deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      run: neg-demo-app
  template: # Pod template
    metadata:
      labels:
        run: neg-demo-app # Labels Pods from this Deployment
    spec: # Pod specification; each Pod created by this Deployment has this specification
      containers:
      - image: k8s.gcr.io/serve_hostname:v1.4 # Application to run in Deployment's Pods
        name: hostname
  

使用硬编码延迟

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    run: neg-demo-app # Label for the Deployment
  name: neg-demo-app # Name of Deployment
spec:
  minReadySeconds: 60 # Number of seconds to wait after a Pod is created and its status is Ready
  replicas: 3
  selector:
    matchLabels:
      run: neg-demo-app
  template: # Pod template
    metadata:
      labels:
        run: neg-demo-app # Labels Pods from this Deployment
    spec: # Pod specification; each Pod created by this Deployment has this specification
      containers:
      - image: k8s.gcr.io/serve_hostname:v1.4 # Application to run in Deployment's Pods
        name: hostname
      # Note: The following line is necessary only on clusters running GKE v1.11 and lower.
      # For details, see https://cloud.google.com/kubernetes-engine/docs/how-to/container-native-load-balancing#align_rollouts
      terminationGracePeriodSeconds: 60 # Number of seconds to wait for connections to terminate before shutting down Pods
  

将此清单保存为 neg-demo-app.yaml,然后通过运行以下命令创建 Deployment:

kubectl apply -f neg-demo-app.yaml

创建一个 Service

以下清单指定了一个 Service neg-demo-svc

  • 具有 run: neg-demo-app 标签的任何 Pod 都是此 Service 的成员。
  • 该 Service 具有一个 ServicePort 字段,相应端口为 80。
  • cloud.google.com/neg 注释指定端口 80 将与 NEG 关联。
  • 每个成员 Pod 必须有一个侦听 TCP 端口 9376 的容器。
apiVersion: v1
kind: Service
metadata:
  name: neg-demo-svc
  annotations:
    cloud.google.com/neg: '{"exposed_ports": {"80":{}}}'
spec:
  type: ClusterIP
  selector:
    run: neg-demo-app # Selects Pods labelled run: neg-demo-app
  ports:
  - port: 80
    protocol: TCP
    targetPort: 9376

将此清单保存为 neg-demo-svc.yaml,然后通过运行以下命令创建 Service:

kubectl apply -f neg-demo-svc.yaml

Service 类型

虽然此示例使用的是 ClusterIP Service,但所有五种 Service 类型都支持独立 NEG。我们建议您使用默认类型 ClusterIP

将端口映射到多个 NEG

一个 Service 可以侦听多个端口。根据定义,NEG 只有一个 IP 地址和端口。这意味着,如果您指定具有多个端口的 Service,则会为每个端口创建一个 NEG。

cloud.google.com/neg 注释的格式如下:

cloud.google.com/neg: '{
   "exposed_ports":{
      "service-port-1":{},
      "service-port-2":{},
      "service-port-3":{},
      ...
   }'

其中,service-port-n 是指代 Service 的现有 Service 端口的不同端口号。对于列出的每个 Service 端口,NEG 控制器会在集群占用的每个区域中创建一个 NEG。

检索 NEG 状态

使用以下命令可检索集群的 Service 的状态:

kubectl get service neg-demo-svc -o yaml

此命令按以下格式输出 NEG 的状态:

cloud.google.com/neg-status: '{
   "network-endpoint-groups":{
      "service-port-1": "neg-name-1",
      "service-port-1": "neg-name-2",
      ...
   },
   "zones":["zone-1", "zone-2", ...]
}

其中,network-endpoint-groups 映射中的每个元素都是一个 Service 端口 (service-port-1) 以及相应的托管 NEG 的名称(例如 neg-name-1)。zones 列表包含具有 NEG 的每个区域(例如 zone-1)。

以下示例是该命令的完整输出:

apiVersion: v1
kind: Service
metadata:
  annotations:
    cloud.google.com/neg: '{"exposed_ports": {"80":{}}}'
    cloud.google.com/neg-status: '{"network_endpoint_groups":{"80":"k8s1-cca197ad-default-neg-demo-app-80-4db81e02"},"zones":["us-central1-a", "us-central1-b"]}'
  labels:
    run: neg-demo-app
  name: neg-demo-app
  namespace: default
  selfLink: /api/v1/namespaces/default/services/neg-demo-app
  ...
spec:
  clusterIP: 10.0.14.252
  ports:
  - port: 80
    protocol: TCP
    targetPort: 9376
  selector:
    run: neg-demo-app
  sessionAffinity: None
status:
  loadBalancer: {}

此示例的注释表明,Service 端口 80 已公开给地区 us-central1-aus-central1-b 中名为 k8s1-cca197ad-default-neg-demo-app-80-4db81e02 的 NEG。

验证 NEG 创建

在创建 Service 后的几分钟内,将创建一个 NEG。如果存在与 Service 清单中指定的标签匹配的 Pod,则 NEG 在创建时将包含 Pod 的 IP 地址。

验证该 NEG 是否存在,方法是列出 Google Cloud 项目中的 NEG 并检查是否有 NEG 与您创建的 Service 相匹配。该 NEG 的名称采用以下格式:k8s1-cluster-uid-namespace-service-port-random-hash

使用以下命令列出 NEG:

gcloud compute network-endpoint-groups list

输出类似于以下内容:

NAME                                          LOCATION       ENDPOINT_TYPE   SIZE
k8s1-70aa83a6-default-my-service-80-c9710a6f  us-central1-a  GCE_VM_IP_PORT  3

此输出表明 NEG 的 SIZE 是 3,这意味着它有三个端点,分别对应于 Deployment 中的三个 Pod。

使用以下命令标识各个端点:

gcloud compute network-endpoint-groups list-network-endpoints \
    k8s1-70aa83a6-default-my-service-80-c9710a6f

输出显示了三个端点,每个端点都有一个 Pod 的 IP 地址和端口:

INSTANCE                                           IP_ADDRESS  PORT
gke-standard-cluster-3-default-pool-4cc71a15-qlpf  10.12.1.43  9376
gke-standard-cluster-3-default-pool-4cc71a15-qlpf  10.12.1.44  9376
gke-standard-cluster-3-default-pool-4cc71a15-w9nk  10.12.2.26  9376

将负载平衡器连接到独立 NEG

您现已创建 NEG,接下来可以将其用作以下类型的负载平衡器的后端:

  • 外部 HTTP(S) 负载平衡器
  • 内部 HTTP(S) 负载平衡器
  • SSL 代理负载平衡器
  • TCP 代理负载平衡器

以下示例展示了操作方法:

将外部 HTTP(S) 负载平衡器连接到独立 NEG

以下步骤显示了如何使用 Compute Engine API 创建外部 HTTP 负载平衡器。

  1. 创建防火墙规则,负载平衡器需要访问集群端点才能执行运行状况检查。以下命令会创建允许相关访问的防火墙规则:

    gcloud compute firewall-rules create fw-allow-health-check-and-proxy \
      --network=network-name \
      --action=allow \
      --direction=ingress \
      --target-tags=gke-node-network-tags \
      --source-ranges=130.211.0.0/22,35.191.0.0/16 \
      --rules=tcp:9376

    其中,gke-node-network-tags 是 GKE 节点上的网络标记network-name 是集群运行所在的网络。

    如果您没有为节点创建自定义网络标记,则 GKE 会为您生成标记。您可以通过以下命令查看节点,以查找这些自动生成的标记:

    gcloud compute instances describe node-name
    
  2. 为负载平衡器创建全局虚拟 IP 地址:

    gcloud compute addresses create hostname-server-vip \
      --ip-version=IPV4 \
      --global
  3. 创建运行状况检查。负载平衡器会使用运行状况检查来检测 NEG 中各个端点的活跃度。

    gcloud compute health-checks create http http-basic-check \
      --use-serving-port
  4. 创建后端服务,指明这是一个全局外部 HTTP(S) 负载平衡器:

    gcloud compute backend-services create my-bes \
      --protocol HTTP \
      --health-checks http-basic-check \
      --global
  5. 为负载平衡器创建网址映射目标代理。这个示例非常简单,因为本指南中使用的 serve_hostname 应用只有一个端点,且不包含网址。

    gcloud compute url-maps create web-map \
      --default-service my-bes
    gcloud compute target-http-proxies create http-lb-proxy \
      --url-map web-map
  6. 创建转发规则。 这个过程会创建负载平衡器。

    gcloud compute forwarding-rules create http-forwarding-rule \
      --address=hostname-server-vip \
      --global \
      --target-http-proxy=http-lb-proxy \
      --ports=80

    hostname-server-vip 是用于负载平衡器的 IP 地址。 为此,您可以预留新的静态外部 IP 地址。您还可以省略 --address 选项,系统会自动分配临时 IP 地址。

检查点

下面列出了到目前为止您已创建的资源:

  • 外部虚拟 IP 地址
  • 转发规则
  • 防火墙规则
  • 目标 HTTP 代理
  • 网址映射
  • 后端服务
  • Compute Engine 运行状况检查

这些资源之间的关系如下图所示:

这些资源共同构成了负载平衡器。在下一步中,您将向负载平衡器添加后端。

此处所示的独立 NEG 的优势之一在于,负载平衡器和后端的生命周期可以完全独立。负载平衡器可在应用、相关 Service 或 GKE 集群删除后继续运行。您可以向负载平衡器添加新的 NEG 或移除多个 NEG,而无需更改任何前端负载平衡器对象。

向负载平衡器添加后端

使用 gcloud compute backend-services add-backend 将 NEG 连接到负载平衡器,方法是将其添加为 my-bes 后端服务的后端:

gcloud compute backend-services add-backend my-bes --global \
   --network-endpoint-group network-endpoint-group-name \
   --network-endpoint-group-zone network-endpoint-group-zone \
   --balancing-mode RATE --max-rate-per-endpoint 5

其中:

  • network-endpoint-group-name 是网络端点组的名称。请参阅以下说明,查找此值。
  • network-endpoint-group-zone 是网络端点组所在的区域。请参阅以下说明,查找此值。

使用以下命令获取 NEG 的名称和位置:

gcloud compute network-endpoint-groups list

输出类似于以下内容:

NAME                                          LOCATION       ENDPOINT_TYPE   SIZE
k8s1-70aa83a6-default-my-service-80-c9710a6f  us-central1-a  GCE_VM_IP_PORT  3

在此示例输出中,NEG 的名称为 k8s1-70aa83a6-default-my-service-80-c9710a6f,位于地区“us-central1-a”中

您可以将多个 NEG 添加到同一后端服务。全局后端服务(如 my-bes)可在不同地区具有 NEG 后端,而地区后端服务只能在单个地区中具有后端。

验证负载平衡器是否正常运行

有两种方法可以验证您设置的负载平衡器是否正常运行:验证运行状况检查是否已正确配置且通过报告表明它运行正常;此外,还可以访问应用并验证其响应

验证运行状况检查

检查后端服务是否与运行状况检查和网络端点组相关联,以及各个端点是否运行正常。

使用以下命令检查后端服务是否与您的运行状况检查和网络端点组相关联:

gcloud compute backend-services describe my-bes --global

输出应如下所示:

backends:
- balancingMode: RATE
  capacityScaler: 1.0
  group: ... /networkEndpointGroups/k8s1-70aa83a6-default-my-service-80-c9710a6f
...
healthChecks:
- ... /healthChecks/http-basic-check
...
name: my-bes
...

接下来,检查各个端点的运行状况:

gcloud compute backend-services get-health my-bes --global

输出的 status: 部分应如下所示:

status:
  healthStatus:
  - healthState: HEALTHY
    instance: ... gke-standard-cluster-3-default-pool-4cc71a15-qlpf
    ipAddress: 10.12.1.43
    port: 50000
  - healthState: HEALTHY
    instance: ... gke-standard-cluster-3-default-pool-4cc71a15-qlpf
    ipAddress: 10.12.1.44
    port: 50000
  - healthState: HEALTHY
    instance: ... gke-standard-cluster-3-default-pool-4cc71a15-w9nk
    ipAddress: 10.12.2.26
    port: 50000

访问应用

通过负载平衡器的 IP 地址访问应用,以确认是否一切正常。

首先,获取负载平衡器的虚拟 IP 地址:

gcloud compute addresses describe hostname-server-vip --global | grep "address:"

输出将包含 IP 地址。接下来,向该 IP 地址(在此示例中为 34.98.102.37)发送请求:

curl 34.98.102.37

来自 serve_hostname 应用的响应应为 neg-demo-app

将内部 HTTP(S) 负载平衡器连接到独立 NEG

本部分介绍如何为在独立 GKE Pod 中运行的服务配置内部 HTTP(S) 负载平衡。

配置代理专用子网

代理专用子网适用于负载平衡器区域(在此示例中为 us-west1)内的所有内部 HTTP(S) 负载平衡器。

控制台

如果您使用的是 GCP Console,则可以稍后在负载平衡界面中创建代理专用子网。

gcloud

使用 gcloud compute networks subnets create 命令创建代理专用子网。

gcloud compute networks subnets create proxy-only-subnet \
  --purpose=INTERNAL_HTTPS_LOAD_BALANCER \
  --role=ACTIVE \
  --region=us-west1 \
  --network=lb-network \
  --range=10.129.0.0/23

API

使用 subnetworks.insert 方法创建代理专用子网,并将 project-id 替换为您的项目 ID。

POST https://compute.googleapis.com/compute/projects/project-id/regions/us-west1/subnetworks

{
  "name": "proxy-only-subnet",
  "ipCidrRange": "10.129.0.0/23",
  "network": "projects/project-id/global/networks/lb-network",
  "region": "projects/project-id/regions/us-west1",
  "purpose": "INTERNAL_HTTPS_LOAD_BALANCER",
  "role": "ACTIVE"
}

配置防火墙规则

此示例使用以下防火墙规则:

  • fw-allow-ssh:适用于负载平衡实例的入站流量规则,该规则允许从任何地址到 TCP 端口 22 的传入 SSH 连接。您可以为此规则选择限制性更高的来源 IP 地址范围;例如,您可以仅指定要从中启动 SSH 会话的系统的 IP 地址范围。此示例使用目标标记 allow-ssh 来标识防火墙规则应该应用到的虚拟机。

  • fw-allow-health-check:适用于负载平衡实例的入站流量规则,该规则允许来自 Google Cloud 运行状况检查系统(130.211.0.0/2235.191.0.0/16)的所有 TCP 流量。此示例使用目标标记 load-balanced-backend 来标识它应该应用到的实例。

  • fw-allow-proxies:适用于负载平衡实例的入站流量规则,该规则允许从内部 HTTP(S) 负载平衡器的托管式代理发送到端口 804438000 的 TCP 流量。此示例使用目标标记 load-balanced-backend 来标识它应该应用到的实例。

如果不使用上述防火墙规则,则默认拒绝入站流量规则会阻止传入后端实例的流量。

控制台

  1. 转到 Google Cloud Console 中的“防火墙规则”页面。
    转到“防火墙规则”页面
  2. 点击创建防火墙规则,以创建允许传入 SSH 连接的规则:
    • 名称fw-allow-ssh
    • 网络lb-network
    • 流量方向:入站
    • 对匹配项执行的操作:允许
    • 目标:指定的目标标记
    • 目标标记allow-ssh
    • 来源过滤条件IP ranges
    • 来源 IP 地址范围0.0.0.0/0
    • 协议和端口
      • 选择指定的协议和端口
      • 勾选 tcp 并输入端口号 22
  3. 点击创建
  4. 再次点击创建防火墙规则,创建允许 Google Cloud 运行状况检查的规则:
    • 名称fw-allow-health-check
    • 网络lb-network
    • 流量方向:入站
    • 对匹配项执行的操作:允许
    • 目标:指定的目标标记
    • 目标标记load-balanced-backend
    • 来源过滤条件IP ranges
    • 来源 IP 地址范围130.211.0.0/2235.191.0.0/16
    • 协议和端口
      • 选择指定的协议和端口
      • 勾选 tcp,然后输入 80
        最佳做法是将此规则限制为仅使用与运行状况检查所使用的协议和端口匹配的协议和端口。如果您使用 tcp:80 协议和端口,则 Google Cloud 可以使用 HTTP 协议通过端口 80 联系您的虚拟机,但无法使用 HTTPS 协议通过端口 443 联系这些虚拟机。
  5. 点击创建
  6. 第三次点击创建防火墙规则,以创建允许负载平衡器的代理服务器连接后端的规则:
    • 名称fw-allow-proxies
    • 网络lb-network
    • 流量方向:入站
    • 对匹配项执行的操作:允许
    • 目标:指定的目标标记
    • 目标标记load-balanced-backend
    • 来源过滤条件IP ranges
    • 来源 IP 地址范围10.129.0.0/23
    • 协议和端口
      • 选择指定的协议和端口
      • 勾选 tcp 并输入端口号 80, 443, 8000
  7. 点击创建

gcloud

  1. 创建 fw-allow-ssh 防火墙规则,允许通过 SSH 连接到网络标记为 allow-ssh 的虚拟机。如果省略 source-ranges,Google Cloud 会将规则解释为表示所有来源

    gcloud compute firewall-rules create fw-allow-ssh \
        --network=lb-network \
        --action=allow \
        --direction=ingress \
        --target-tags=allow-ssh \
        --rules=tcp:22
    
  2. 创建 fw-allow-health-check 规则以允许 Google Cloud 运行状况检查。本示例允许来自运行状况检查探测工具的所有 TCP 流量;但是,您可以根据自己的需求配置较小范围的端口集。

    gcloud compute firewall-rules create fw-allow-health-check \
        --network=lb-network \
        --action=allow \
        --direction=ingress \
        --source-ranges=130.211.0.0/22,35.191.0.0/16 \
        --target-tags=load-balanced-backend \
        --rules=tcp
    
  3. 创建 fw-allow-proxies 规则以允许内部 HTTP(S) 负载平衡器的代理连接到您的后端。

    gcloud compute firewall-rules create fw-allow-proxies \
      --network=lb-network \
      --action=allow \
      --direction=ingress \
      --source-ranges=10.129.0.0/23 \
      --target-tags=load-balanced-backend \
      --rules=tcp:80,tcp:443,tcp:8000
    

API

通过向 firewalls.insert 方法发出 POST 请求以创建 fw-allow-ssh 防火墙规则,注意要将 project-id 替换为您的项目 ID。

POST https://compute.googleapis.com/compute/v1/projects/project-id/global/firewalls

{
  "name": "fw-allow-ssh",
  "network": "projects/project-id/global/networks/lb-network",
  "sourceRanges": [
    "0.0.0.0/0"
  ],
  "targetTags": [
    "allow-ssh"
  ],
  "allowed": [
   {
     "IPProtocol": "tcp",
     "ports": [
       "22"
     ]
   }
  ],
 "direction": "INGRESS"
}

通过向 firewalls.insert 方法发出 POST 请求以创建 fw-allow-health-check 防火墙规则,注意要将 project-id 替换为您的项目 ID。

POST https://compute.googleapis.com/compute/v1/projects/project-id/global/firewalls

{
  "name": "fw-allow-health-check",
  "network": "projects/project-id/global/networks/lb-network",
  "sourceRanges": [
    "130.211.0.0/22",
    "35.191.0.0/16"
  ],
  "targetTags": [
    "load-balanced-backend"
  ],
  "allowed": [
    {
      "IPProtocol": "tcp"
    }
  ],
  "direction": "INGRESS"
}

通过 firewalls.insert 方法创建 fw-allow-proxies 防火墙规则,以允许代理子网内的 TCP 流量,创建时需要将 project-id 替换为您的项目 ID。

POST https://compute.googleapis.com/compute/v1/projects/{project}/global/firewalls

{
  "name": "fw-allow-proxies",
  "network": "projects/project-id/global/networks/lb-network",
  "sourceRanges": [
    "10.129.0.0/23"
  ],
  "targetTags": [
    "load-balanced-backend"
  ],
  "allowed": [
    {
      "IPProtocol": "tcp",
      "ports": [
        "80"
      ]
    },
  {
      "IPProtocol": "tcp",
      "ports": [
        "443"
      ]
    },
    {
      "IPProtocol": "tcp",
      "ports": [
        "8000"
      ]
    }
  ],
  "direction": "INGRESS"
}

配置负载平衡器

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

控制台

选择负载平衡器类型

  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 名称,请参阅验证 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. 点击证书下拉列表。
    1. 如果您已经拥有要用作主要 SSL 证书的自行管理的 SSL 证书资源,请从下拉菜单中选择该证书。
    2. 否则,请选择创建新证书
      1. 填写名称 l7-ilb-cert
      2. 在相应字段中上传您的 PEM 格式的文件:
        • 公钥证书
        • 证书链
        • 私钥
      3. 点击创建
  10. 如需添加除了主要 SSL 证书资源之外的其他证书资源,请执行以下操作:
    1. 点击添加证书
    2. 证书列表中选择一个证书,或点击创建新证书并按照上述说明操作。
  11. 点击完成

完成配置

  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 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/us-west1/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://compute.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://compute.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://compute.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
}

实现异构服务(虚拟机和容器)

负载平衡器可以作为混合 Kubernetes 和非 Kubernetes 工作负载的前端。这可能是从虚拟机到容器的迁移的一部分,也可能是受益于共享负载平衡器的永久架构。这可以通过创建针对不同类型的后端(包括独立 NEG 在内)的负载平衡器来实现。

同一后端服务中的虚拟机和容器

此示例展示了如何创建指向运行某工作负载的现有虚拟机的 NEG,以及如何将此 NEG 添加为现有 backendService 的另一个后端。这样,单个负载平衡器就可以平衡虚拟机与 GKE 容器之间的负载。

此示例拓展了上文中使用外部 HTTP 负载平衡器的那个示例

由于所有端点都由同一 backendService 分组,因此,虚拟机和容器端点会被视为同一服务。这意味着主机/路径匹配将根据网址映射规则以完全相同的方式处理所有后端。

展示上述架构的示意图。之前创建的负载平衡器指向两个 NEG,即之前创建的容器的 NEG 以及包含虚拟机 IP 地址的新 NEG

在将 NEG 用作后端服务的后端时,该后端服务中的所有其他后端也必须是 NEG。您不能将实例组和 NEG 用作同一后端服务的后端。此外,容器和虚拟机不能作为端点存在于同一 NEG 中,因此,它们必须始终配置单独的 NEG。

  1. 使用以下命令将虚拟机部署到 Compute Engine:

    gcloud compute instances create vm1 --zone zone --network=network \
     --subnet=subnet --image-project=cos-cloud \
     --image-family=cos-stable --tags=vm-neg-tag
  2. 将应用部署到虚拟机:

    gcloud compute ssh vm1 --zone=${ZONE} --command="docker run -d --rm --network=host \
     k8s.gcr.io/serve_hostname:v1.4 && sudo iptables -P INPUT ACCEPT"

    此命令会将之前示例中的那个示例应用部署到虚拟机。为简单起见,应用作为 Docker 容器运行,但这不是必需的。如需允许防火墙访问正在运行的容器,必须使用 iptables 命令。

  3. 验证应用是否在端口 9376 上运行并且通过报告表明它正在 vm1 上运行:

    gcloud compute ssh vm1 --zone=zone --command="curl -s localhost:9376"

    服务器会返回 vm1 响应。

  4. 创建一个能与虚拟机端点配合使用的 NEG。容器和虚拟机可以均为 NEG 端点,但单个 NEG 不能同时具有虚拟机和容器端点。

    gcloud compute network-endpoint-groups create vm-neg \
    --subnet=subnet --zone=zone
  5. 将虚拟机端点连接到 NEG:

    gcloud compute network-endpoint-groups update vm-neg --zone=zone \
     --add-endpoint="instance=vm1,ip=vm-primary-ip,port=9376"
  6. 确认 NEG 具有虚拟机端点:

    gcloud compute network-endpoint-groups list-network-endpoints vm-neg --zone zone
  7. 使用用于添加容器后端的命令将 NEG 附加到后端服务:

    gcloud compute backend-services add-backend my-bes --global \
     --network-endpoint-group vm-neg \
     --network-endpoint-group-zone zone \
     --balancing-mode RATE --max-rate-per-endpoint 10
  8. 打开防火墙以允许执行虚拟机运行状况检查:

    gcloud compute firewall-rules create fw-allow-health-check-to-vm1 \
    --network=network \
    --action=allow \
    --direction=ingress \
    --target-tags=vm-neg-tag \
    --source-ranges=130.211.0.0/22,35.191.0.0/16 \
    --rules=tcp:9376
    
  9. 通过发送测试流量,验证负载平衡器是否正将流量转发到新的 vm1 后端和现有容器后端:

    for i in `seq 1 100`; do curl ${VIP};echo; done

    您应该会看到来自容器 (neg-demo-app) 和虚拟机 (vm1) 端点的响应。

不同后端服务的虚拟机和容器

此示例展示了如何创建指向运行某工作负载的现有虚拟机的 NEG,以及如何将此 NEG 添加为新 backendService 的后端。如果容器和虚拟机是不同的 Service,但需要共用同一 L7 负载平衡器(例如,Service 共用同一 IP 地址或域名),则此方法非常有用。

此示例扩展了上文中在与容器后端相同的后端服务中具有虚拟机后端的那个示例。此示例重复使用了那个虚拟机。

由于容器和虚拟机端点被分组到不同的 backendService 中,因此它们被视为不同的 Service。这意味着,网址映射会匹配后端,并根据主机名将流量引导至虚拟机或容器。

下图显示了单个虚拟 IP 地址如何与两个主机名对应,继而又对应于基于容器的后端服务和基于虚拟机的后端服务。

下图展示了如上所述的架构:

该架构有两个 NEG,一个针对使用容器实现的 Service,另一个针对使用虚拟机实现的 Service。每个 NEG 都有一个后端服务对象。网址映射对象会根据请求的网址将流量引导至正确的后端服务。

  1. 为虚拟机创建新的后端服务:

    gcloud compute backend-services create my-vm-bes \
      --protocol HTTP \
      --health-checks http-basic-check \
      --global
  2. 将虚拟机的 NEG vm-neg 附加到后端服务:

    gcloud compute backend-services add-backend my-vm-bes --global \
     --network-endpoint-group vm-neg \
     --network-endpoint-group-zone zone \
     --balancing-mode RATE --max-rate-per-endpoint 10
  3. 网址映射添加一条主机规则,以将对 container.example.com 主机的请求引导至容器后端服务:

    gcloud compute url-maps add-path-matcher web-map \
      --path-matcher-name=container-path --default-service=my-bes \
      --new-hosts=container.example.com --global
    
  4. 向网址映射添加另一条主机规则,以将对 vm.example.com 主机的请求引导至虚拟机后端服务:

    gcloud compute url-maps add-path-matcher web-map \
      --path-matcher-name=vm-path --default-service=my-vm-bes \
      --new-hosts=vm.example.com --global
    
  5. 验证负载平衡器是否根据请求的路径将流量发送到虚拟机后端:

    curl -H "HOST:vm.example.com" virtual-ip

独立 NEG 所受的限制

价格

如需详细了解负载平衡器的价格,请参阅价格页面的负载平衡部分。NEG 不会额外产生费用。

问题排查

未配置独立 NEG

具体情况:未创建 NEG。

可能的解决方法

  • 检查与 Service 相关联的事件,并查找错误消息。
  • 验证独立 NEG 注释是否为格式正确的 JSON,以及显示的端口是否与服务规范中的现有端口匹配。
  • 验证 NEG 状态注释,并查看预期的服务端口是否具有相应的 NEG。
  • 使用命令 gcloud compute network-endpoint-groups list 验证是否在预期的地区中创建了 NEG。

流量未到达端点

具体情况:出现 502 错误或连接被拒。

可能的解决方法

  • 配置 Service 后,如果新端点能够响应运行状况检查,那么在将新端点附加到 NEG 后,通常就可以访问新端点。
  • 如果在此之后,流量仍然无法到达端点,从而导致出现 HTTP(S) 的 502 错误代码或 TCP/SSL 负载平衡器的连接被拒,请检查以下内容:
    • 验证防火墙规则是否允许传入的 TCP 流量传输到以下范围内的端点:130.211.0.0/2235.191.0.0/16
    • 验证您的端点是否运行正常,方法是使用 gcloud,或者,对 BackendService 调用 getHealth API 或在 showHealth 参数设置为 SHOW 的情况下对 NEG 调用 listEndpoints API。

停止发布

具体情况:发布更新后 Deployment 的操作处于停止状态,并且最新副本的数量与所需的副本数量不一致。

可能的解决方法

Deployment 的运行状况检查失败。容器映像可能已损坏,或者运行状况检查的配置可能有误。Pod 的滚动替换会等待新启动的 Pod 通过其 Pod 就绪性门控。仅当 Pod 正在响应负载平衡器运行状况检查时,才会出现这种情况。如果 Pod 没有响应,或者运行状况检查的配置有误,则无法满足就绪性门控条件,并且无法继续发布。

  • 如果您使用的是 kubectl 1.13 或更高版本,则可以使用以下命令检查 Pod 就绪性门控的状态:

    kubectl get my-Pod -o wide

    查看 READINESS GATES 列。

    kubectl 1.12 及更低版本中不存在此列。标记为 READY 状态的 Pod 的就绪性门控可能已失败。如需就此进行验证,请使用以下命令:

    kubectl get my-pod -o yaml

    输出中会列出就绪性门控及其状态。

  • 验证 Deployment 的 Pod 规范中的容器映像是否正常运行,以及是否能够响应运行状况检查。

  • 验证运行状况检查是否已正确配置。

后续步骤