使用 IPVLAN 和 Whereabouts 配置 Multus

本文档介绍了如何使用 Multus CNI、IPVLAN CNI 插件和 Whereabouts IPAM 插件在 Google Kubernetes Engine (GKE) 中配置具有多个网络接口的 Pod。

IPVLAN CNI 插件为额外的 Pod 接口提供第 2 层连接,而 Whereabouts IPAM 插件则会动态地为这些接口分配 IP 地址。

此设置支持高级网络配置,例如分离控制平面和数据平面流量,以增强网络隔离和分段。

本文档适用于为组织设计和架构网络的云架构师和网络专家。如需详细了解我们在 Google Cloud 内容中提及的常见角色和示例任务,请参阅常见的 GKE 用户角色和任务

在阅读本文档之前,请确保您熟悉以下概念:

将 Multus 与 IPVLAN 搭配使用的优势

使用此解决方案为 Pod 配置多个网络接口可带来多项关键优势。在第 2 层模式下配置 Multus 和 IPVLAN 的主要使用场景是需要第 2 层邻接的网络分段:

  • 流量隔离:隔离不同类型的流量,以提高安全性和性能。例如,您可以将敏感的管理流量与应用数据流量分开。
  • 控制平面和数据平面分离:将主网络接口专用于控制平面流量,同时通过辅助 IPVLAN 接口引导高吞吐量数据平面流量。
  • 第 2 层邻接:满足需要在同一辅助网络上的 Pod 之间建立直接第 2 层连接的应用的要求。

限制

配置了 Multus 接口的 Pod 无法同时使用 GKE 的内置多网络功能。Pod 的网络配置必须使用 Multus 或集群的内置多网络功能。

Multus 如何与 IPVLAN 和 Whereabouts 搭配使用

Multus 是一种 CNI 元插件,可让 Pod 连接到多个网络。Multus 充当调度程序,调用其他 CNI 插件以根据 NetworkAttachmentDefinition 资源配置网络接口。您可以使用 NetworkAttachmentDefinition 定义每个附加网络,该参数用于指定要为相应网络使用哪个 CNI 插件(例如 IPVLAN)和 IPAM 插件(例如 Whereabouts)。

下图展示了使用 IPVLAN 和 Whereabouts 插件的 Multus 架构。Whereabouts 插件与 Multus 和 IPVLAN 协同工作,以处理 Pod 的其他网络接口的 IP 地址管理 (IPAM)。

图表:展示了 Multus、IPVLAN 和 Whereabouts 在 GKE 中如何协同工作。
图 1:采用 IPVLAN 和 Whereabouts 插件的 Multus 架构。

此图显示了两个节点,每个节点都有一个 Pod。每个 Pod 都有一个主接口和一个附加接口。两个主要接口连接到共享网络接口卡,而两个附加接口连接到另一个共享网络接口卡。

在 GKE 上将 Multus 与 IPVLAN 和 Whereabouts 搭配使用时,Pod 通常具有以下接口配置:

  • 主接口 (eth0):GKE Dataplane V2 会管理此接口,提供默认的集群连接。
  • 其他接口(net1 等):Multus 会管理这些接口。 对于您在 Pod 注释中指定的每个 NetworkAttachmentDefinition,Multus 都会在第 2 层模式下调用 IPVLAN CNI 插件。此配置可提供与辅助 VPC 网络的第 2 层连接。
  • IP 地址管理 (IPAM):您可以在 NetworkAttachmentDefinition 中配置 Whereabouts IPAM 插件。Whereabouts IPAM 插件会从预定义的范围内动态为额外的 IPVLAN 接口分配 IP 地址。

使用多个网络进行 Pod 调度

当您创建 Pod 并在其注释中指定 NetworkAttachmentDefinition 时,GKE 调度程序只会将该 Pod 放置在能够满足网络要求的节点上。调度程序会识别节点池中已配置必要辅助网络接口的节点。此节点识别过程可确保调度程序将 Pod 调度到可以连接到附加网络并从指定范围接收 IP 地址的节点上。

以下部分将指导您在 GKE 集群上配置 Multus 与 IPVLAN 和 Whereabouts 插件。

准备工作

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

  • 启用 Google Kubernetes Engine API。
  • 启用 Google Kubernetes Engine API
  • 如果您要使用 Google Cloud CLI 执行此任务,请安装初始化 gcloud CLI。 如果您之前安装了 gcloud CLI,请通过运行 gcloud components update 命令来获取最新版本。较早版本的 gcloud CLI 可能不支持运行本文档中的命令。
  • 安装 kubectl 命令行工具。
  • 设置一个运行 1.28 版或更高版本的 GKE 集群,并启用 Dataplane V2、IP 别名和多网络。如需了解具体操作方法,请参阅设置 Pod 的多网络支持。 启用多网络功能还会启用多 IP 子网和永久性 IP HA 政策功能,从而无需手动设置节点间连接。
  • 使用经过 GKE 验证的 Multus CNI 版本(例如 v4.2.1)以确保兼容性。

设置 VPC

如需设置虚拟私有云 (VPC) 以搭配 Multus 使用,包括为节点网络创建子网和为 Pod 网络创建次要范围,请完成以下步骤:

  1. 创建新的 VPC 或使用现有 VPC:

    gcloud compute networks create VPC_NAME \
    --subnet-mode=custom
    

    VPC_NAME 替换为 VPC 的名称。

  2. 在此 VPC 中创建一个新子网:

    gcloud compute networks subnets create SUBNET_NAME \
        --range=PRIMARY_RANGE \
        --network=VPC_NAME \
        --region=REGION \
        --secondary-range=SECONDARY_RANGE_NAME=SECONDARY_RANGE_CIDR
    

    替换以下内容:

    • SUBNET_NAME:新子网的名称。
    • PRIMARY_RANGE:子网的主要 CIDR 范围,例如 10.0.1.0/24。此命令将此范围用于节点接口。
    • VPC_NAME:VPC 的名称。
    • REGION:子网的区域,例如 us-central1
    • SECONDARY_RANGE_NAME:子网中 Pod 的次要 IP 地址范围的名称。
    • SECONDARY_RANGE_CIDR:Pod 的次要 CIDR 范围,例如 172.16.1.0/24。Pod 上的其他接口使用此范围。

    此命令会创建一个子网,其中包含一个用于额外节点接口的主要 CIDR 范围和一个用于额外 Pod 接口的次要范围。

创建 GKE Standard 集群

创建启用了多网络的 GKE Standard 集群:

gcloud container clusters create CLUSTER_NAME \
    --cluster-version=CLUSTER_VERSION \
    --enable-dataplane-v2 \
    --enable-ip-alias \
    --enable-multi-networking

替换以下内容:

  • CLUSTER_NAME:新集群的名称。
  • CLUSTER_VERSION:GKE 集群的版本。您必须使用 1.28 或更高版本。

启用多网络功能后,您可以创建具有多个网络接口的节点池,而这是 Multus CNI 所必需的。

创建 GKE Standard 节点池

创建连接到其他 VPC 网络的 GKE Standard 节点池:

gcloud container node-pools create NODEPOOL_NAME \
    --cluster CLUSTER_NAME \
    --zone "ZONE" \
    --additional-node-network network=VPC_NAME,subnetwork=SUBNET_NAME \
    --additional-pod-network subnetwork=SUBNET_NAME,pod-ipv4-range=SECONDARY_RANGE_NAME,max-pods-per-node=8

替换以下内容:

  • NODEPOOL_NAME:新节点池的名称。
  • CLUSTER_NAME:您的集群的名称。
  • ZONE:节点池的可用区,例如 us-central1-c
  • VPC_NAME:附加 VPC 的名称。
  • SUBNET_NAME:子网的名称。
  • SECONDARY_RANGE_NAME:子网中 Pod 的次要 IP 地址范围的名称。

此命令会创建一个节点池,其中节点在 SUBNET_NAME 中具有额外的网络接口,并且这些节点上的 Pod 可以使用 SECONDARY_RANGE_NAME 中的 IP 地址。

如需详细了解如何创建具有多网络功能的 GKE 集群,请参阅设置 Pod 的多网络支持

应用 Multus 部署

如需为 pod 启用多个网络接口,请安装 Multus CNI 插件。将以下清单(包含必需的 DaemonSet 和自定义资源定义 [CRD])保存为 multus-manifest.yaml

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: ippools.whereabouts.cni.cncf.io
spec:
  group: whereabouts.cni.cncf.io
  names:
    kind: IPPool
    listKind: IPPoolList
    plural: ippools
    singular: ippool
  scope: Namespaced
  versions:
  - name: v1alpha1
    schema:
      openAPIV3Schema:
        description: IPPool is the Schema for the ippools API
        properties:
          apiVersion:
            description: 'APIVersion defines the versioned schema of this representation
                of an object. Servers should convert recognized schemas to the latest
                internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources'
            type: string
          kind:
            description: 'Kind is a string value representing the REST resource this
                object represents. Servers may infer this from the endpoint the client
                submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds'
            type: string
          metadata:
            type: object
          spec:
            description: IPPoolSpec defines the desired state of IPPool
            properties:
              allocations:
                additionalProperties:
                  description: IPAllocation represents metadata about the pod/container
                    owner of a specific IP
                  properties:
                    id:
                      type: string
                    podref:
                      type: string
                  required:
                  - id
                  type: object
                description: Allocations is the set of allocated IPs for the given
                  range. Its indices are a direct mapping to the IP with the same
                  index/offset for the pools range.
                type: object
              range:
                description: Range is a RFC 4632/4291-style string that represents
                  an IP address and prefix length in CIDR notation
                type: string
            required:
            - allocations
            - range
            type: object
        type: object
    served: true
    storage: true
status:
  acceptedNames:
    kind: ""
    plural: ""
  conditions: []
  storedVersions: []
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  annotations:
    controller-gen.kubebuilder.io/version: v0.4.1
  name: overlappingrangeipreservations.whereabouts.cni.cncf.io
spec:
  group: whereabouts.cni.cncf.io
  names:
    kind: OverlappingRangeIPReservation
    listKind: OverlappingRangeIPReservationList
    plural: overlappingrangeipreservations
    singular: overlappingrangeipreservation
  scope: Namespaced
  versions:
  - name: v1alpha1
    schema:
      openAPIV3Schema:
        description: OverlappingRangeIPReservation is the Schema for the OverlappingRangeIPReservations
          API
        properties:
          apiVersion:
            description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
            type: string
          kind:
            description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
            type: string
          metadata:
            type: object
          spec:
            description: OverlappingRangeIPReservationSpec defines the desired state
              of OverlappingRangeIPReservation
            properties:
              containerid:
                type: string
              podref:
                type: string
            required:
            - containerid
            type: object
        required:
        - spec
        type: object
    served: true
    storage: true
status:
  acceptedNames:
    kind: ""
    plural: ""
  conditions: []
  storedVersions: []
---
kind: ConfigMap
apiVersion: v1
metadata:
  name: multus-cni-config
  namespace: kube-system
  labels:
    app: gke-multinet
data:
  cni-conf.json: |
    {
      "name": "multus-cni-network",
      "type": "multus",
      "confDir": "/etc/cni/net.d",
      "namespaceIsolation": true,
      "logLevel": "verbose",
      "logFile": "/var/log/multus.log",
      "kubeconfig": "/var/lib/kubelet/kubeconfig",
      "clusterNetwork": "gke-pod-network"
    }
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: network-attachment-definitions.k8s.cni.cncf.io
spec:
  group: k8s.cni.cncf.io
  scope: Namespaced
  names:
    plural: network-attachment-definitions
    singular: network-attachment-definition
    kind: NetworkAttachmentDefinition
    shortNames:
    - net-attach-def
  versions:
  - name: v1
    served: true
    storage: true
    schema:
      openAPIV3Schema:
        description: 'NetworkAttachmentDefinition is a CRD schema specified by the Network Plumbing
            Working Group to express the intent for attaching pods to one or more logical or physical
            networks. More information available at: https://github.com/k8snetworkplumbingwg/multi-net-spec'
        type: object
        properties:
          apiVersion:
            description: 'APIVersion defines the versioned schema of this represen
                tation of an object. Servers should convert recognized schemas to the
                latest internal value, and may reject unrecognized values. More info:
                https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
            type: string
          kind:
            description: 'Kind is a string value representing the REST resource this
                object represents. Servers may infer this from the endpoint the client
                submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
            type: string
          metadata:
            type: object
          spec:
            description: 'NetworkAttachmentDefinition spec defines the desired state of a network attachment'
            type: object
            properties:
              config:
                description: 'NetworkAttachmentDefinition config is a JSON-formatted CNI configuration'
                type: string
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: multus-role
rules:
- apiGroups: ["k8s.cni.cncf.io"]
  resources:
  - '*'
  verbs:
  - '*'
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: whereabouts
rules:
- apiGroups:
  - whereabouts.cni.cncf.io
  resources:
  - ippools
  - overlappingrangeipreservations
  verbs:
  - get
  - list
  - watch
  - create
  - update
  - patch
  - delete
- apiGroups:
  - coordination.k8s.io
  resources:
  - leases
  verbs:
  - create
- apiGroups:
  - coordination.k8s.io
  resources:
  - leases
  resourceNames:
  - whereabouts
  verbs:
  - '*'
- apiGroups: [""]
  resources:
  - pods
  verbs:
  - list
  - get
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: multus-role-binding
subjects:
- kind: Group
  name: system:nodes
roleRef:
  kind: ClusterRole
  name: multus-role
  apiGroup: rbac.authorization.k8s.io
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: whereabouts-role-binding
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: whereabouts
subjects:
- kind: ServiceAccount
  name: whereabouts-sa
  namespace: kube-system
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: whereabouts-sa
  namespace: kube-system
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: gke-multinet
  namespace: kube-system
  labels:
    app: gke-multinet
spec:
  selector:
    matchLabels:
      app: gke-multinet
  template:
    metadata:
      labels:
        app: gke-multinet
    spec:
      priorityClassName: system-node-critical
      hostNetwork: true
      tolerations:
      - operator: Exists
      serviceAccountName: whereabouts-sa
      containers:
      - name: whereabouts-gc
        command: [/ip-control-loop]
        args:
        - "--log-level=debug"
        - "--enable-pod-watch=false"
        - "--cron-schedule=* * * * *"
        image: gcr.io/gke-release/whereabouts:v0.7.0-gke.3@sha256:2bb8450a99d86c73b262f5ccd8c433d3e3abf17d36ee5c3bf1056a1fe479e8c2
        env:
        - name: NODENAME
          valueFrom:
            fieldRef:
              apiVersion: v1
              fieldPath: spec.nodeName
        - name: WHEREABOUTS_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        resources:
          requests:
            cpu: "100m"
            memory: "50Mi"
          limits:
            cpu: "100m"
            memory: "50Mi"
      initContainers:
      - name: install-multus-config
        image: gcr.io/gke-release/multus-cni:v4.2.1-gke.6@sha256:25b48b8dbbf6c78a10452836f52dee456514783565b70633a168a39e6d322310
        args:
        - "--cni-conf-dir=/host/etc/cni/net.d"
        - "--multus-conf-file=/tmp/multus-conf/00-multus.conf"
        - "--multus-log-level=verbose"
        - "--multus-kubeconfig-file-host=/var/lib/kubelet/kubeconfig"
        - "--skip-multus-binary-copy=true"
        - "--skip-config-watch=true"
        resources:
          requests:
            cpu: "100m"
            memory: "50Mi"
          limits:
            cpu: "100m"
            memory: "50Mi"
        securityContext:
          privileged: true
        volumeMounts:
        - name: cni
          mountPath: /host/etc/cni/net.d
        - name: multus-cfg
          mountPath: /tmp/multus-conf
      - name: install-whereabouts
        command: ["/bin/sh"]
        args:
        - -c
        - >
          SLEEP=false /install-cni.sh
        image: gcr.io/gke-release/whereabouts:v0.7.0-gke.3@sha256:2bb8450a99d86c73b262f5ccd8c433d3e3abf17d36ee5c3bf1056a1fe479e8c2
        env:
        - name: NODENAME
          valueFrom:
            fieldRef:
              apiVersion: v1
              fieldPath: spec.nodeName
        - name: WHEREABOUTS_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        resources:
          requests:
            cpu: "100m"
            memory: "50Mi"
          limits:
            cpu: "100m"
            memory: "50Mi"
        securityContext:
          privileged: true
        volumeMounts:
        - name: cni
          mountPath: /host/etc/cni/net.d
        - name: cnibin
          mountPath: /host/opt/cni/bin
      - name: install-binary
        image: gcr.io/gke-release/multus-cni:v4.2.1-gke.6@sha256:25b48b8dbbf6c78a10452836f52dee456514783565b70633a168a39e6d322310
        command: ["/gkecmd"]
        args:
        - "-operation=copy"
        - "-cni-bin-dir=/host/opt/cni/bin"
        resources:
          requests:
            cpu: "10m"
            memory: "100Mi"
          limits:
            cpu: "10m"
            memory: "100Mi"
        securityContext:
          privileged: true
        volumeMounts:
        - name: cnibin
          mountPath: /host/opt/cni/bin
      volumes:
      - hostPath:
          path: /var/lib/kubelet/kubeconfig
          type: File
        name: kubelet-credentials
      - name: cni
        hostPath:
          path: /etc/cni/net.d
          type: DirectoryOrCreate
      - name: cnibin
        hostPath:
          path: /home/kubernetes/bin
          type: DirectoryOrCreate
      - name: multus-cfg
        configMap:
          name: multus-cni-config
          items:
          - key: cni-conf.json
            path: 00-multus.conf
  updateStrategy:
    rollingUpdate:
      maxUnavailable: 2
    type: RollingUpdate

然后,将清单应用到您的集群:

kubectl apply -f multus-manifest.yaml

创建NetworkAttachmentDefinition清单

如需让 Pod 连接到其他网络,请创建 NetworkAttachmentDefinition 清单。此清单定义了 Pod 如何连接到网络,并指定了 IPAM 插件(例如 Whereabouts)分配的 IP 地址范围。此范围必须属于连接节点额外网络接口的子网。

  1. 将此清单保存为 nad.yaml。 此清单使用 IPVLAN 和 Whereabouts 插件。

    apiVersion: "k8s.cni.cncf.io/v1"
    kind: NetworkAttachmentDefinition
    metadata:
      name: NAD_NAME
    spec:
      config: '{
        "cniVersion": "0.3.1",
        "plugins": [
          {
            "type": "ipvlan",
            "master": "eth1",
            "mode": "l2",
            "ipam": {
              "type": "whereabouts",
              "range": SECONDARY_RANGE_NAME
            }
          }
        ]
      }'
    

    清单包含以下字段:

    • NAD_NAMENetworkAttachmentDefinition 的名称。
    • master:节点的辅助网络接口的名称,该接口充当 IPVLAN 的 master 接口。在 GKE 上,辅助网络接口通常以 eth1 开头,并按顺序命名。如需确认接口名称,请使用 SSH 连接到节点并运行 ip addr 命令。
    • range:Pod 接口的 IP 地址范围,与您为 Pod 创建的次要 IPv4 范围 (SECONDARY_RANGE_NAME) 相同。例如,172.16.1.0/24
  2. 将清单应用到您的集群:

    kubectl apply -f nad.yaml
    

将 Pod 附加到其他网络

如需将 Pod 附加到其他网络,请将 k8s.v1.cni.cncf.io/networks 注释添加到 Pod 清单中。对于多个网络,请提供以英文逗号分隔的 NetworkAttachmentDefinition 名称列表,格式如下:<namespace>/<nad-name>

以下示例展示了一个 Pod 清单,该清单附加到 default 命名空间中名为 NAD_NAMENetworkAttachmentDefinition

apiVersion: v1
kind: Pod
metadata:
  name: samplepod
  annotations:
    k8s.v1.cni.cncf.io/networks: default/NAD_NAME
spec:
  containers:
  - name: sample-container
    image: nginx

NAD_NAME 替换为您创建的 NetworkAttachmentDefinition 的名称。

应用此清单后,Kubernetes 会创建 Pod,该 Pod 具有一个额外的网络接口 (net1),该接口连接到 NetworkAttachmentDefinition 指定的网络。

验证 Pod 的其他 IP 地址

如需验证 Pod 在附加到其他网络后是否会收到额外的 IP 地址,请检查 Pod 内的网络接口:

  1. 如需检查 samplepod 并验证其他 IP 地址,请使用以下命令:

    $kubectl describe pod PODNAME
    

    PODNAME 替换为您的 Pod 名称,例如 samplepod

  2. 检查输出。eth0 接口具有 Pod 的主要 IP 地址。Whereabouts 插件会将额外的 IP 地址分配给另一个接口,例如 net1

    输出类似于以下内容:

    k8s.v1.cni.cncf.io/network-status:
      [{
        "name": "gke-pod-network",
        "interface": "eth0",
        "ips": [
          "10.104.3.4"
        ],
        "mac": "ea:e2:f6:ce:18:b5",
        "default": true,
        "dns": {},
        "gateway": [
          "\u003cnil\u003e"
        ]
      },{
        "name": "default/my-nad",
        "interface": "net1",
        "ips": [
          "10.200.1.1"
        ],
        "mac": "42:01:64:c8:c8:07",
        "dns": {}
      }]
    k8s.v1.cni.cncf.io/networks: default/my-nad
    

    在此示例中,10.104.5.19eth0 上的主要 IP 地址,而 10.200.1.1net1 上的附加 IP 地址。

后续步骤