使用 GKE Dataplane V2

本页面介绍了如何为 Google Kubernetes Engine (GKE) 集群启用 GKE Dataplane V2 以及如何排查它的相关问题。

在新的 Autopilot 集群中,GKE Dataplane V2 始终处于启用状态。如果您在使用 GKE Dataplane V2 时遇到问题,请跳到问题排查。

准备工作

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

  • 启用 Google Kubernetes Engine API。
  • 启用 Google Kubernetes Engine API
  • 如果您要使用 Google Cloud CLI 执行此任务,请安装并初始化 gcloud CLI。如果您之前安装了 gcloud CLI,请通过运行 gcloud components update 命令来获取最新版本。较早版本的 gcloud CLI 可能不支持运行本文档中的命令。

所需的角色

如需获得创建 GKE 集群所需的权限,请让您的管理员为您授予项目的 Kubernetes Engine Cluster Admin (container.clusterAdmin) IAM 角色。 如需详细了解如何授予角色,请参阅管理对项目、文件夹和组织的访问权限。

您也可以通过自定义角色或其他预定义角色来获取所需的权限。

使用 GKE Dataplane V2 创建 GKE 集群

您只能在创建新的 GKE 集群时启用 GKE Dataplane V2。您无法为现有集群修改此设置。

如需创建使用 GKE Dataplane V2 的 Standard 集群,请选择以下选项之一:

控制台

  1. 在 Cloud de Confiance 控制台中,前往创建 Kubernetes 集群页面。

    前往“创建一个 Kubernetes 集群”页面

  2. 在导航菜单中,点击网络。

  3. 展开容器网络接口 (CNI) 部分。

  4. 选中 Dataplane V2 复选框。

  5. 点击创建。

gcloud

运行以下命令:

gcloud container clusters create CLUSTER_NAME \
    --location=CONTROL_PLANE_LOCATION \
    --enable-dataplane-v2

替换以下内容:

  • CLUSTER_NAME:新集群的名称。
  • CONTROL_PLANE_LOCATION:集群控制平面的位置。

API

如需使用 GKE Dataplane V2 创建新集群,请在集群 create 请求中指定 networkConfig 对象的 datapathProvider 字段。

以下 JSON 代码段显示了启用 GKE Dataplane V2 所需的配置:

"cluster":{
    "networkConfig":{
      "datapathProvider":"ADVANCED_DATAPATH"
    }
}

GKE Dataplane V2 问题排查

本部分介绍如何调查和解决 GKE Dataplane V2 的问题。

  1. 确认已启用 GKE Dataplane V2:

    kubectl -n kube-system get pods -l k8s-app=cilium -o wide
    

    如果 GKE Dataplane V2 正在运行,则输出包含前缀为 anetd- 的 Pod。anetd 是 GKE Dataplane V2 的网络控制器。

  2. 如果问题在于服务或网络政策强制执行方式,请检查 anetd Pod 日志。在 Cloud Logging 中使用以下日志选择器:

    resource.type="k8s_container"
    labels."k8s-pod/k8s-app"="cilium"
    resource.labels.cluster_name="CLUSTER_NAME"
    
  3. 如果 Pod 创建失败,请检查 kubelet 日志以获取相关线索。在 Cloud Logging 中使用以下日志选择器:

    resource.type="k8s_node"
    log_name=~".*/logs/kubelet"
    resource.labels.cluster_name="CLUSTER_NAME"
    

    将 CLUSTER_NAME 替换为集群的名称,或将其完全移除以查看所有集群的日志。

  4. 如果 anetd Pod 未运行,请检查 cilium-config ConfigMap 是否有任何修改。请避免更改此 ConfigMap 中的现有字段,因为此类更改可能会导致集群不稳定并中断 anetd。仅当向 ConfigMap 添加新字段时,它才会修补回默认状态。对现有字段进行的任何更改都不会得到修补,因此我们建议不要更改或自定义 ConfigMap。

已知问题

使用 GKE Dataplane V2 时,您可能会遇到以下已知问题。

未就绪 Pod 的连接超时

如果 Pod 未准备就绪,与所关联服务的连接可能会超时。这是 GKE Dataplane V2 的预期行为,与 kube-proxy 不同,后者可以更快地返回 connection refused 错误。

针对 Cilium Identity 的身份相关标签过滤功能未生效,并且 Pod 卡在 ContainerCreating 状态

受影响的版本:1.34、1.35

在 GKE Dataplane V2 集群中,通过 kube-system/cilium-config-emergency-override ConfigMap 紧急使用身份相关标签过滤功能时,在受影响的版本中无法正确应用。

此方法限制了用于生成 Cilium Identity 的 Pod 标签。

当无法使用其他机制来防止/移除 Pod 中的高基数标签键/值时(例如当标签由工具或框架应用时),可以使用“与身份相关的标签”过滤功能将标签键从 Cilium 身份计算中排除。如需详细了解如何配置这些规则,请参阅 Cilium 文档中的与身份相关的标签。

对于受影响的 GKE 版本,由操作员创建的 Cilium 身份会继续包含排除的标签。

表现

  • 具有应过滤以生成 Cilium Identity 的标签的 Pod 可能会无法启动并卡在 ContainerCreating 状态。Pod 事件可能会显示超时错误:

      {"level":"warning", "msg":"Error changing endpoint identity", "error":"unable to resolve identity: timed out waiting for cilium-operator to allocate CiliumIdentity for key ...;, error: exponential backoff cancelled via context: context canceled", "k8sPodName":"...", "subsys":"endpoint"}
    
  • 具有唯一标签值的 Pod 不会根据过滤后的标签共享身份,而是继续生成唯一的 Cilium 身份。这可能会导致身份数量急剧增加,从而可能耗尽可用的 Cilium 身份(上限为 65,536),并导致可伸缩性问题。

已修复的版本

如需解决此问题,请将集群升级到以下某个 GKE 版本:

  • 1.34.6-gke.1307000 或更高版本
  • 1.35.2-gke.1962000 或更高版本

临时解决方法

作为一种解决方法,请将标签过滤规则应用于主 cilium-config ConfigMap 中的 data.labels 字段,并从 cilium-config-emergency-override 中移除这些规则。这种情况会一直存在,即使在控制平面操作(例如升级)期间也是如此,因为 GKE 会保留用户对 cilium-config ConfigMap 中不受其管理的字段所做的修改。

  1. 从 cilium-config-emergency-override ConfigMap 的 data 部分中移除 labels 键(如果存在)。
  2. 通过在 data 部分中添加或修改 labels 键来修改 cilium-config ConfigMap。例如,如需防止将名为 uuid 的标签用于生成身份,请执行以下操作:

    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: cilium-config
      namespace: kube-system
    data:
      # ... other existing keys
      labels: "!uuid"
      # ... other existing keys
    
  3. 通过将控制平面升级到其正在运行的同一版本,在控制平面上重启 anet-operator。这会强制操作员重新启动并重新加载其配置:

    gcloud container clusters upgrade CLUSTER_NAME \
        --location CLUSTER_LOCATION \
        --project PROJECT_ID \
        --cluster-version $(gcloud container clusters describe CLUSTER_NAME --location CLUSTER_LOCATION --project PROJECT_ID --format="value(currentMasterVersion)") \
        --master
    
  4. 控制平面重启后,重启 anetd DaemonSet 以确保节点代理也选取所有必需的更改:

    kubectl rollout restart daemonset anetd -n kube-system
    

GKE Dataplane V2 集群中与 NodePort 范围冲突相关的间歇性连接问题

在 GKE Dataplane V2 集群中,伪装流量或使用临时端口可能会遇到间歇性连接问题。这些问题是端口与预留的 NodePort 范围可能存在冲突导致的,通常在以下情况下会发生:

  • 自定义 ip-masq-agent:如果您使用的是自定义 ip-masq-agent(2.10 版或更高版本),并且集群包含 NodePort 或负载均衡器服务,则可能会因与 NodePort 范围存在冲突而出现间歇性连接问题。从 2.10 版及更高版本开始,ip-masq-agent 默认在内部实施了 --random-fully 参数。为了缓解此问题,请在 ip-masq-agent 配置的参数下明确设置 --random-fully=false(适用于 2.11 版及更高版本)。如需了解配置详情,请参阅在 Standard 集群中配置 IP 伪装代理。

  • 临时端口范围重叠:如果 GKE 节点上的 net.ipv4.ip_local_port_range 定义的临时端口范围与 NodePort 范围 (30000-32767) 重叠,也可能会触发连接问题。为避免出现此问题,请确保这两个范围不重叠。

检查您的 ip-masq-agent 配置和临时端口范围设置,确保它们不会与 NodePort 范围冲突。如果您遇到间歇性连接问题,请考虑以下可能的原因,并相应地调整您的配置。

GKE Dataplane V2 集群中的 hostPort 连接问题

受影响的 GKE 版本:所有可用版本

在使用 GKE Dataplane V2 的集群中,当流量的目标是节点的“IP:端口”且其中的端口是 Pod 上定义的 hostPort 时,您可能会遇到连接失败问题。这些问题主要出现在以下两种情况下:

  • hostPort 位于直通式网络负载均衡器后面的节点:

    hostPort 会将 Pod 绑定到特定节点的端口,而直通式网络负载均衡器会在所有节点之间分配流量。当您使用 hostPort 和直通式网络负载均衡器将 Pod 公开到互联网时,负载均衡器可能会将流量发送到未运行 Pod 的节点,从而导致连接失败。这是由于 GKE Dataplane V2 存在已知限制,即直通式网络负载均衡器流量不会始终转发到 hostPort Pod。

    权宜解决方法:在节点上使用直通式网络负载均衡器公开 Pod 的 hostPort 时,请在 Pod 的 hostIP 字段中指定网络负载均衡器的内部或外部 IP 地址。

    ports:
    - containerPort: 62000
      hostPort: 62000
      protocol: TCP
      hostIP: 35.232.62.64
    - containerPort: 60000
      hostPort: 60000
      protocol: TCP
      hostIP: 35.232.62.64
      # Assuming 35.232.62.64 is the external IP address of a passthrough Network Load Balancer.
    
  • hostPort 与预留的 NodePort 范围冲突:

    如果 Pod 的 hostPort 与预留的 NodePort 范围 (30000-32767) 冲突,Cilium 可能会无法将流量转发到 Pod。出现此行为是因为 Cilium 管理 hostPort 功能,取代了之前的 Portmap 方法。对于 Cilium 而言,这是预期行为,在其公开文档中提到了这一点。

我们不打算在后续版本中修复这些限制。这些问题的根本原因与 Cilium 的行为有关,不在 GKE 的直接控制范围内。

建议:我们建议您迁移到 NodePort Service(而不是 hostPort)以提高可靠性。NodePort Service 提供类似的功能。

网络政策的端口范围未生效

受影响的 GKE 版本:1.32 之前的版本

如果您在已启用 GKE Dataplane V2 且运行 1.32 之前 GKE 版本的集群的 NetworkPolicy 对象中指定 endPort 字段,Kubernetes 会忽略该字段。

借助 Kubernetes NetworkPolicy API,您可以指定 Kubernetes 强制执行网络政策的端口范围。具有 Calico 网络政策的集群支持此 API,运行 GKE 1.32 版或更高版本的具有 GKE Dataplane V2 的集群也支持此 API。运行低于 1.32 版本的 GKE Dataplane V2 集群不支持此 API。

如需验证 NetworkPolicy 对象的行为,您可以在将它们写入 API 服务器后重新读取这些对象。如果对象仍包含 endPort 字段,则 Kubernetes 会强制执行该功能。如果缺少 endPort 字段,Kubernetes 不会强制执行该功能。存储在 API 服务器中的对象是网络政策的可靠来源。

如需了解详情,请参阅 KEP-2079:支持端口范围的网络政策。

固定版本

如需解决此问题,请将集群升级到 GKE 1.32 版或更高版本。

由于缺少 containerID 错误,节点处于 NodeNotReady 状态

当集群升级到 GKE 版本 1.35.1-gke.1616000 及更高版本时,如果同时启用了 GKE Dataplane V2 和 Cloud Service Mesh,节点可能会立即进入 NodeNotReady 状态。

原因

从 GKE 1.35.1-gke.1616000 版开始,GKE Dataplane V2 集群在其 CNI 配置文件中使用 CNI 版本 1.1.0。此项更改要求下游 CNI 插件(例如 Google 管理的 Istio)也支持 CNI 版本 1.1.0。由于受管 Istio 的推出延迟,部分集群尚未收到兼容版本 (1.23),导致初始化失败。

表现

受影响的节点会立即显示为 NodeNotReady。containerd 日志中会显示以下错误消息:

NetworkPluginNotReady message:Network plugin returns error: missing containerID

临时解决方法

如需缓解此问题,请将受影响的集群降级到低于 1.35.1-gke.1616000 的 GKE 版本。

自定义 eBPF 程序的干扰

GKE 使用 eBPF 程序来管理 GKE Dataplane V2 的网络。 如果您在 GKE 管理的节点网络接口上部署自定义 eBPF 程序,这些程序可能会干扰 GKE 管理的 eBPF 程序,并导致网络问题。

GKE 不支持附加到以下网络接口的自定义 eBPF 程序:

  • eth*
  • ens4
  • lo
  • cilium*
  • gke*
  • veth*

这些接口上存在自定义 eBPF 程序可能会干扰 GKE Dataplane V2 anetd 代理安装的程序,从而导致集群网络中断。我们建议您从集群中移除所有自定义 eBPF 程序或注入此类程序的工作负载。

发现自定义 eBPF 程序

如需发现集群节点上运行的自定义 eBPF 程序,您可以创建一个配置了 hostNetwork: true 设置的 DaemonSet,该 DaemonSet 使用 bpftool 查询此类 eBPF 程序:

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: bpftool-logger
  labels:
    app: bpftool-logger
spec:
  selector:
    matchLabels:
      app: bpftool-logger
  template:
    metadata:
      labels:
        app: bpftool-logger
    spec:
      hostPID: true
      hostNetwork: true
      containers:
      - name: bpftool
        image: ubuntu:22.04
        securityContext:
          privileged: true
        env:
        - name: NODE_NAME
          valueFrom:
            fieldRef:
              fieldPath: spec.nodeName
        command:
        - /bin/bash
        - -c
        - |
          echo "Installing dependencies..."
          apt-get update -y > /dev/null 2>&1
          apt-get install -y curl tar > /dev/null 2>&1

          echo "Downloading and setting up bpftool..."
          curl -sL https://github.com/libbpf/bpftool/releases/download/v7.7.0/bpftool-v7.7.0-amd64.tar.gz | tar xz
          chmod +x bpftool
          mv bpftool /usr/local/bin/

          echo "========== $(date) | Node: ${NODE_NAME} =========="
          bpftool net | grep -E '^(eth|ens4|lo|cilium|gke|veth)' | grep -v ' cil_'
          sleep infinity
  1. 将清单保存为 ebpf-discovery.yaml 并应用 DaemonSet:

    kubectl apply -f ebpf-discovery.yaml
    
  2. 等待 Pod 运行:

    kubectl rollout status ds/bpftool-logger
    
  3. 检查来自 Pod 的日志以发现 eBPF 程序:

    kubectl logs -l app=bpftool-logger
    
  4. 完成后,删除 DaemonSet:

    kubectl delete -f ebpf-discovery.yaml
    

后续步骤