配置使用 MetalLB 进行捆绑式负载均衡

本页面介绍了如何为 Google Distributed Cloud 配置使用 MetalLB 进行捆绑式负载均衡。MetalLB 负载均衡器在专用的工作器节点池或与控制平面相同的节点上运行。

如需查看 Google Distributed Cloud 中提供的负载均衡拓扑示例,请参阅负载均衡器概览

要求

  • 所有负载均衡器节点都必须位于同一 L2 子网中。
  • 所有 VIP 都必须位于负载均衡器的节点子网中,并且可通过子网的网关路由。
  • 负载均衡器子网的网关必须侦听免费 ARP 消息,并将 ARP 数据包转发到负载均衡器节点。

配置字段

修改集群配置文件的 cluster.spec.loadBalancer 部分以配置捆绑式负载均衡。如需了解集群配置文件和有效配置的示例,请参阅以下某个页面:

loadBalancer.mode

此值必须为 bundled 才能启用捆绑式负载均衡。

loadBalancer.ports.controlPlaneLBPort

此值指定发送到 Kubernetes 控制平面(Kubernetes API 服务器)的流量要使用的目标端口。

loadBalancer.vips.controlPlaneVIP

此值指定发送到 Kubernetes 控制平面(Kubernetes API 服务器)的流量要使用的目标 IP 地址。此 IP 地址必须与集群中的节点位于同一个第 2 层子网中。请勿在配置文件的 address pools 部分中列出此地址。

loadBalancer.vips.ingressVIP

此值指定要用于在负载均衡器后用于入站流量的 Service 的 IP 地址。管理员集群配置文件中不允许使用此字段。此地址必须列在配置的地址池部分中。

loadBalancer.addressPools

配置的这一部分包含一个或多个地址池。每个地址池都会指定 IP 地址范围列表。创建 LoadBalancer 类型的 Service 时,系统会从这些范围中选择该 Service 的外部 IP 地址。

地址池按以下格式指定:

- name: POOL_NAME
  avoidBuggyIPs: BOOLEAN
  manualAssign: BOOLEAN
  addresses:
  - IP_RANGE
  - IP_RANGE2
  • name:地址池的名称 (pool-name),以便您进行整理。该字段不可更改。
  • avoidBuggyIPs:(可选)truefalse。如果为 true,则池会忽略以 .0.255 结尾的 IP 地址。某些网络硬件会丢弃流向这些特殊地址的流量。您可以省略此字段,其默认值为 false。 此字段可更改。
  • manualAssign:(可选)truefalse。如果为 true,则此池中的地址不会自动分配给 Kubernetes Service。如果为 true,则只有在服务明确指定时,此池中的 IP 地址才会被使用。您可以省略此字段,其默认值为 false。此字段可更改。
  • addresses:包含一个或多个不重叠的 IP 地址范围的列表。可以用 CIDR 表示法(如 198.51.100.0/24)或范围表示法(如 198.51.100.0-198.51.100.10,短划线前后没有空格)指定 ip-range。该字段不可更改。

addresses 列表中的 IP 地址范围不得重叠,并且必须与运行负载均衡器的节点位于同一子网中。

loadBalancer.nodePoolSpec

配置的这一部分指定要运行负载均衡器的节点列表。默认情况下,负载均衡器节点可以运行常规工作负载;这些节点上没有特殊的污点。虽然负载均衡器节点池中的节点可以运行工作负载,但它们与工作器节点池中的节点是分开的。一个给定的集群节点不能添加到多个节点池中。节点池之间重叠的节点 IP 地址会阻止创建集群以及其他集群操作。

如果要阻止工作负载在负载均衡器节点池中的节点上运行,请将以下污点添加到该节点:

node-role.kubernetes.io/load-balancer:NoSchedule

Google Distributed Cloud 会向负载均衡所需的 Pod 添加对此污点的容忍设置。

以下示例展示了包含两个节点的负载均衡节点池。第一个节点具有标准 IP 地址 nodePoolSpec.nodes.address(“1.2.3.4”)和 Kubernetes IP 地址 nodePoolSpec.nodes.k8sIP (10.0.0.32)。为节点指定可选的 k8sIP 地址后,该地址会专用于处理该节点的数据流量,例如,针对 Kubernetes API、kubelet 及工作负载的请求和响应。在这种情况下,标准 IP 地址 nodePoolSpec.nodes.address 用于通过 SSH 连接到节点,以执行管理集群操作。如果您未指定 k8sIP 地址,则标准节点 IP 地址会处理节点的所有流量。

nodePoolSpec:
  nodes:
  - address: 1.2.3.4
    k8sIP: 10.0.0.32
  - address: 10.0.0.33

默认情况下,负载均衡器节点池中的所有节点必须与在配置文件的 loadBalancer.addressPools 部分中配置的负载均衡器 VIP 位于同一第 2 层子网中。不过,如果您为节点指定了 Kubernetes IP 地址 k8sIP,则只有该地址需要与其他负载均衡器 VIP 位于同一第 2 层子网中。

如果未设置 nodePoolSpec,捆绑式负载均衡器会在控制平面节点上运行。如果可能,建议您在不同的节点池中运行负载均衡器。

控制平面负载均衡

控制平面负载均衡器提供控制平面虚拟 IP 地址 (VIP)。Google Distributed Cloud 将 Keepalived 和 HAProxy 作为负载均衡器节点上的 Kubernetes 静态 Pod 运行,以通告控制平面 VIP。keepalived 在负载均衡器节点上使用虚拟路由器冗余协议 (VRRP) 以实现高可用性。

数据平面负载均衡

数据平面负载均衡器用于类型为 LoadBalancer 的所有 Kubernetes Service。Google Distributed Cloud 使用在第 2 层模式下运行的 MetalLB 进行数据平面负载均衡。数据平面负载均衡只能通过 Google Distributed Cloud 进行配置,请勿直接修改 MetalLB ConfigMap。您可以使用所有 MetalBL 功能,包括跨 Service 共享 IP 地址。如需了解功能信息,请参阅 MetalLB 文档。

MetalBL 使用 daemonset 在每个节点上运行一个 speaker pod,通过 memberlist 实现高可用性。每个 Kubernetes Service 都有一个 MetalLB 专用负载均衡器节点,而不是整个集群一个。这样一来,如果存在多个 Service,流量会在负载均衡器节点之间分配。

数据平面负载均衡器可以在控制平面节点或一组工作器节点上运行。将数据平面负载均衡器捆绑到控制平面节点可提高控制平面节点的利用率。 此外,捆绑到控制平面节点也会增加控制平面的过载风险并增加控制平面上的机密信息(如 SSH 密钥)的风险。

负载均衡器分离

在 1.32 版之前,当您使用 MetalLB 配置第 2 层负载均衡时,控制平面负载均衡器和数据平面负载均衡器在同一节点上运行。根据您的配置,负载均衡器全部在控制平面节点上运行,或者全部在负载均衡器节点池中运行。

下图展示了默认的捆绑式负载均衡器配置,其中控制平面负载均衡器和数据平面负载均衡器均在控制平面节点上运行,或者均在负载均衡器节点池中运行:

默认负载均衡器配置

对于 1.32 版集群,您可以将控制平面负载均衡器配置为在控制平面节点上运行,并将数据平面负载均衡器配置为在负载均衡器节点池中运行。您可以在创建新的 1.32 版集群时指定这种负载均衡器分离,也可以更新 1.32 版集群,以将数据平面负载均衡器从控制平面节点迁移到负载均衡器节点池。

分离式负载均衡器的集群配置应类似于以下示例:

apiVersion: baremetal.cluster.gke.io/v1
kind: Cluster
metadata:
  name: hybrid-ha-lb
  namespace: cluster-hybrid-ha-lb
spec:
  type: hybrid
  profile: default
  anthosBareMetalVersion: 1.32
  gkeConnect:
    projectID: project-fleet
  controlPlane:
    loadBalancer:
      mode: bundled
    nodePoolSpec:
      nodes:
      - address: 10.200.0.2
      - address: 10.200.0.3
      - address: 10.200.0.4
  clusterNetwork:
    pods:
      cidrBlocks:
      - 192.168.0.0/16
    services:
      cidrBlocks:
      - 10.96.0.0/20
  ...
  loadBalancer:
    mode: bundled
    ...
    nodePoolSpec:
      nodes:
      - address: 10.200.0.5
      - address: 10.200.0.6
      - address: 10.200.0.7
  clusterOperations:
  ...

创建集群时分离负载均衡器

如果您要创建新的 1.32 版或更高版本的集群,您可以配置负载均衡器,以在控制平面节点上运行控制平面负载均衡器,并在负载均衡器节点池上运行数据平面负载均衡器。

下图显示了控制平面和数据平面负载均衡器分别位于不同的节点上:

分离的控制平面和数据平面负载均衡器

如需在创建集群时分离负载均衡器,请按以下步骤操作:

  1. 在集群配置文件中,指定一个具有 loadBalancer.nodePoolSpec 的负载均衡器节点池,如本文档的 loadBalancer.nodePoolSpec 部分中所述。

  2. 向集群配置文件添加 controlPlane.loadBalancer.mode,并将 mode 值设置为 bundled

  3. 完成集群配置,然后运行 bmctl create cluster 以创建集群。

将数据平面负载均衡器从控制平面迁移出去

如果您有版本为 1.32 或更高版本的现有集群,且未设置 controlPlane.loadBalancer.modeloadBalancer.nodePoolSpec,则控制平面负载均衡器和数据平面负载均衡器都在控制平面节点池中运行。您可以更新集群,以将数据平面负载均衡器迁移到负载均衡器节点池。

下图展示了在数据平面负载均衡器从控制平面节点迁移后,控制平面和数据平面负载均衡器分离的情况:

数据平面负载均衡器已迁移到负载均衡器节点池

如需在更新集群时将数据平面负载均衡器迁移到负载均衡器节点池,请按以下步骤操作:

  1. 在集群配置文件中,指定一个具有 loadBalancer.nodePoolSpec 的负载均衡器节点池,如本文档的 loadBalancer.nodePoolSpec 部分中所述。

  2. 向集群配置文件添加 controlPlane.loadBalancer.mode,并将 mode 值设置为 bundled

  3. 通过运行以下命令来更新集群:

    bmctl update cluster -c CLUSTER_NAME --kubeconfig=ADMIN_KUBECONFIG
    

    替换以下内容:

    • CLUSTER_NAME:您要更新的集群的名称。

    • ADMIN_KUBECONFIG:管理员集群 kubeconfig 文件的路径。

保留客户端来源 IP 地址

使用捆绑式第 2 层负载均衡解决方案创建的 LoadBalancer Service 使用外部流量政策的默认 Cluster 设置。spec.externalTrafficPolicy: Cluster 设置会将外部流量路由到集群范围的端点,但也会遮掩客户端来源 IP 地址。

Google Distributed Cloud 支持两种保留客户端来源 IP 地址的方法:

  • 将负载均衡的转发模式设置为直接服务器返回 (DSR)。如需详细了解 DSR 转发模式(包括启用说明),请参阅配置负载均衡转发模式

  • LoadBalancer Service 的外部流量政策设置为本地,并相应地配置相关 Services 和 Ingress。以下部分介绍了如何配置集群以使用此方法。

LoadBalancer 服务

LoadBalancer Service 中使用 externalTrafficPolicy: Local 时,请将应用 pod 设置为在负载均衡器节点上运行。将以下 nodeSelector 添加到您的应用 Pod 以进行此更改:

apiVersion: v1
kind: Pod
...
spec:
  nodeSelector:
      baremetal.cluster.gke.io/lbnode: "true"
...

NodePort 服务

Kubernetes 会为 NodePort Service 执行来源网络地址转换 (SNAT)。如需保留客户端来源 IP 地址,请将 service.spec.externalTrafficPolicy 设置为 Local。Kubernetes 不会再执行 SNAT,但您必须确保有 Pod 正在您选择的节点 IP 地址上运行。

入站流量

如果您的应用是 HTTP 服务,您可以通过配置入站流量组件来实现客户端 IP 地址可见性:

  1. 打开 istio-ingress Service 进行修改:

    kubectl edit service -n gke-system istio-ingress
    
  2. externalTrafficPolicy: Local 添加到 spec,然后保存并退出编辑器。

    apiVersion: v1
    kind: Service
    ...
    spec:
    ...
      externalTrafficPolicy: Local
    
  3. 打开 istio-ingress Deployment 进行修改:

    kubectl edit deployment -n gke-system istio-ingress
    
  4. 将以下 nodeSelector 添加到 Deployment,然后保存并退出编辑器。

    apiVersion: apps/v1
    kind: Deployment
    ...
    spec:
      ...
      template:
        ...
        spec:
          ...
          nodeSelector:
              baremetal.cluster.gke.io/lbnode: "true"
    ...
    

现在,Ingress 后面的所有服务都会看到具有客户端 IP 地址的 X-Forwarded-For 标头,如以下示例所示:

X-Forwarded-For: 21.0.104.4