为 pod 配置多个网络接口

本文档介绍了如何配置 Google Distributed Cloud,以便为 Pod 提供多个网络接口(多 NIC)。pod 的多 NIC 功能可以帮助分离控制层面流量和数据层面流量,从而在层面之间创建隔离。额外的网络接口还可以支持 pod 的多播功能。用户集群支持 Pod 的多 NIC 功能,但管理员集群支持。

网络层面隔离对使用网络功能虚拟化 (NFV) 的系统非常重要,NFV 包括广域网 (SD-WAN) 中的软件定义网络、云访问安全代理 (CASB) 和下一代防火墙 (NG-FW)。这些类型的 NFV 依赖于访问多个接口分隔控制层面和数据层面。

多网络接口配置支持将网络接口与节点池相关联,从而提供性能优势。例如,集群可以包含多种节点类型。将高性能机器分组到一个节点池中时,您可以在节点池中创建更多接口以改善流量。

设置多个网络接口

通常,为 pod 设置多个网络接口包括三个步骤:

  1. 使用集群配置文件中的 multipleNetworkInterfacesenableDataplaneV2 字段为您的用户集群启用多 NIC

  2. 使用集群配置文件中的 additionalNodeInterfaces 部分指定网络接口,并创建一个或多个 NetworkAttachmentDefinition 自定义资源。

  3. 使用 k8s.v1.cni.cncf.io/networks 注解向 pod 分配网络接口

启用多 NIC

通过将用户集群配置文件中的 multipleNetworkInterfacesenableDataplaneV2 字段设置为 true,为您的 Pod 启用多 NIC。

apiVersion: v1
multipleNetworkInterfaces: true
enableDataplaneV2: true
  ...

指定网络接口

在集群配置文件中,在 additionalNodeInterfaces 部分中指定增加的节点网络接口。

例如,以下是用户集群配置文件的一部分,其中显示了一个增加的节点网络接口:

apiVersion: v1
multipleNetworkInterfaces: true
enableDataplaneV2: true
network:
  serviceCIDR: "10.96.0.0/20"
  podCIDR: "192.168.0.0/16"
  vCenter:
    networkName: network-private310
  ...
  # New multiple network configs
  additionalNodeInterfaces:
  - networkName: "gke-network-1"
    ipBlockFilePath: "my-block-yaml"
    type: static

使用上述配置创建集群后,您必须在用户集群中创建一个或多个 NetworkAttachmentDefinition (NAD) 自定义资源,并在其中指定增加的网络接口。NetworkAttachmentDefinitions 对应于 pod 可用的网络。以下示例展示了 NetworkAttachmentDefinition 的清单:

apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
  name: gke-network-1
  namespace: default
spec:
  config: '{
  "cniVersion":"0.3.0",
  "type": "ipvlan",
  "master": "ens224", # defines the node interface that this Pod interface would map to
  "mode": "l2",
  "ipam": {
    "type": "whereabouts",
    "range": "172.16.0.0/24"
   }
}'

将清单保存为 YAML 文件(例如 my-nad.yaml),然后创建 NetworkAttachmentDefinition

kubectl --kubeconfig [USER_CLUSTER_KUBECONFIG] apply -f my-nad.yaml

将网络接口分配给 pod

使用 k8s.v1.cni.cncf.io/networks 注解为 pod 分配一个或多个网络接口。每个网络接口都指定了命名空间和 NetworkAttachmentDefinition 的名称,并以正斜杠 (/) 分隔。使用以英文逗号分隔的列表来指定多个网络接口。

在以下示例中,为 samplepod Pod 分配了两个网络接口。网络接口由创建于 default 命名空间中的两个 NetworkAttachmentDefinitionsgke-network-1gke-network-2 的名称指定。

---
apiVersion: v1
kind: Pod
metadata:
  name: samplepod
  annotations:
    k8s.v1.cni.cncf.io/networks: default/gke-network-1,default/gke-network-2
spec:
  containers:
  ...

将网络接口限制为一组节点

如果您不希望 NetworkAttachmentDefinition 适用于整个集群,则可以将其功能范围限制为一组节点。

您可以使用分配给节点的标准标签或您自己的自定义标签对集群节点进行分组。然后,您可以使用 k8s.v1.cni.cncf.io/nodeSelector 注解在 NetworkAttachmentDefinition 清单中指定节点标签。GKE on VMware 会强制将引用此自定义资源的所有 Pod 部署到具有此标签的节点上。

apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
  annotations:
    k8s.v1.cni.cncf.io/nodeSelector: LABEL_KEY=LABEL_VALUE
  name: gke-network-1
spec:
...

以下示例展示了 NetworkAttachmentDefinition 上指示的 my-label=multinicNP 标签,并强制将已获分配 gke-network-1 网络的所有 Pod 部署到具有此标签的节点。

apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
  annotations:
    k8s.v1.cni.cncf.io/nodeSelector: my-label=multinicNP
  name: gke-network-1
spec:
...

如需将自定义标签应用于节点,请使用 kubectl label nodes 命令:

kubectl --kubeconfig [USER_CLUSTER_KUBECONFIG] label nodes NODE_NAME LABEL_KEY=LABEL_VALUE 

请替换以下内容:

  • NODE_NAME:您要为其添加标签的节点的名称。
  • LABEL_KEY:要用于标签的键。
  • LABEL_VALUE:标签值。

在此示例中,为节点 my-node 提供 environment=production 标签:

kubectl --kubeconfig [USER_CLUSTER_KUBECONFIG] label nodes my-node environment=production

安全考量

NetworkAttachmentDefinition 提供对网络的完整访问权限,因此集群管理员在向其他用户提供创建、更新或删除权限时必须小心谨慎。如果必须隔离给定 NetworkAttachmentDefinition,则可以在创建时指定非默认命名空间,这样只有该命名空间中的 Pod 可以访问它。

在下图中,default 命名空间中的 pod 无法访问 privileged 命名空间中的网络接口。

使用命名空间隔离网络流量

支持的 CNI 插件

本部分列出了 Google Distributed Cloud 的多 NIC 功能支持的 CNI 插件。在指定 NetworkAttachmentDefinition 时,请仅使用以下插件。

接口创建:

  • ipvlan
  • macvlan
  • bridge

Meta 插件:

  • portmap
  • sbr
  • tuning

IPAM 插件:

  • host-local
  • static
  • whereabouts

路由配置

具有一个或多个已分配 NetworkAttachmentDefinitions 的 pod 具有多个网络接口。默认情况下,在这种情况下,Pod 的路由表仅使用已分配 NetworkAttachmentDefinitions 中的本地可用其他接口进行扩展。绑定到默认网关的数据包仍配置为使用 Pod 的默认接口 eth0。您可以使用以下 CNI 插件修改此行为:

  • sbr
  • static
  • whereabouts

例如,您可能希望大多数流量通过默认网关,这意味着流量将通过默认网络接口。但是,您希望某些特定流量通过其中一个非默认接口。根据目标 IP(正常路由)可能难以区分流量,因为这两种接口类型上的可用端点是同一个。在这种情况下,基于来源的路由 (SBR) 将能够发挥作用。

SBR 插件

sbr 插件可让应用控制路由决策。应用可以控制其建立的连接的来源 IP 地址。如果应用选择使用 NetworkAttachmentDefinition 的 IP 地址作为其来源 IP 地址,则数据包会到达 sbr 设置的额外路由表。sbr 路由表通过自己的默认网关发送流量,该网关将通过 NetworkAttachmentDefinition 的接口传输。该表中的默认网关 IP 由 whereaboutsstatic 插件中的 gateway 字段控制。sbr 插件作为链式插件运行。如需详细了解 sbr 插件(包括使用方法信息),请参阅基于来源的路由插件

以下示例显示了 whereabouts 中设置的 "gateway":"21.0.111.254",以及 ipvlan 之后设置为链式插件的 sbr

# ip route
default via 192.168.0.64 dev eth0  mtu 1500
192.168.0.64 dev eth0 scope link
# ip route list table 100
default via 21.0.111.254 dev net1
21.0.104.0/21 dev net1 proto kernel scope link src 21.0.111.1

static 插件和 whereabouts 插件

whereabouts 插件基本上是 static 插件的扩展,二者共享路由配置。如需查看配置示例,请参阅静态 IP 地址管理插件。您可以定义要添加到 pod 的路由表的网关和路由。但是,您无法以这种方式修改 pod 的默认网关。

以下示例展示了在 NetworkAttachmentDefinition 中添加 "routes": [{ "dst": "172.31.0.0/16" }]

# ip route
default via 192.168.0.64 dev eth0  mtu 1500
172.31.0.0/16 via 21.0.111.254 dev net1
21.0.104.0/21 dev net1 proto kernel scope link src 21.0.111.1
192.168.0.64 dev eth0 scope link

配置示例

本部分说明了多 NIC 功能支持的一些常见网络配置。

多个 pod 使用一个网络连接:

多个 pod 使用一个网络连接。

一个 pod 使用多个网络连接:

一个 pod 使用多个网络连接。

多个网络连接指向一个 pod 使用的同一接口:

多个网络连接指向一个 pod 使用的同一接口。

一个 pod 多次使用同一网络连接:

一个 pod 多次使用同一网络连接。

问题排查

如果额外的网络接口配置有误,则它们分配到的 pod 将不会启动。本部分重点介绍如何查找信息以排查多 NIC 功能的问题。

检查 pod 事件

Multus 通过 Kubernetes pod 事件报告故障。使用以下 kubectl describe 命令查看给定 pod 的事件:

kubectl describe pod POD_NAME

检查日志

对于每个节点,您可以在以下位置找到 Whereabouts 和 Multus 日志:

  • /var/log/whereabouts.log
  • /var/log/multus.log

检查 pod 接口

使用 kubectl exec 命令检查 pod 接口。成功应用 NetworkAttachmentDefinitions 后,pod 接口将如下输出所示:

user@node1:~$ kubectl exec samplepod-5c6df74f66-5jgxs -- ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
2: net1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default
    link/ether 00:50:56:82:3e:f0 brd ff:ff:ff:ff:ff:ff
    inet 21.0.103.112/21 scope global net1
       valid_lft forever preferred_lft forever
38: eth0@if39: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 36:23:79:a9:26:b3 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 192.168.2.191/32 scope global eth0
       valid_lft forever preferred_lft forever

获取 pod 状态

使用 kubectl get 检索给定 pod 的网络状态:

kubectl get pods POD_NAME -oyaml

以下示例输出显示了具有多个网络的 pod 的状态:

apiVersion: v1
kind: Pod
metadata:
  annotations:
    k8s.v1.cni.cncf.io/network-status: |-
      [{
          "name": "",
          "interface": "eth0",
          "ips": [
              "192.168.1.88"
          ],
          "mac": "36:0e:29:e7:42:ad",
          "default": true,
          "dns": {}
      },{
          "name": "default/gke-network-1",
          "interface": "net1",
          "ips": [
              "21.0.111.1"
          ],
          "mac": "00:50:56:82:a7:ab",
          "dns": {}
      }]
    k8s.v1.cni.cncf.io/networks: gke-network-1