使用 IPVLAN 和 Whereabouts 設定 Multus

本文說明如何使用 Multus CNI、IPVLAN CNI 外掛程式和 Whereabouts IPAM 外掛程式,在 Google Kubernetes Engine (GKE) 中設定具有多個網路介面的 Pod。

IPVLAN CNI 外掛程式會為額外的 Pod 介面提供第 2 層連線,而 Whereabouts IPAM 外掛程式則會動態指派 IP 位址給這些介面。

這項設定可啟用進階網路設定,例如分隔控制層和資料層流量,以強化網路隔離和區隔。

本文適用於負責設計及建構機構網路的雲端架構師和網路專家。如要進一步瞭解我們在內容中提及的常見角色和範例工作,請參閱「常見的 GKE 使用者角色和工作」。 Google Cloud

閱讀本文前,請務必先熟悉下列概念:

搭配使用 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)。

下圖說明 Multus 架構,其中包含 IPVLAN 和 Whereabouts 外掛程式。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 外掛程式。這項設定可為次要虛擬私有雲網路提供第 2 層連線。
  • IP 位址管理 (IPAM):您可以在 NetworkAttachmentDefinition 中設定 Whereabouts IPAM 外掛程式。Whereabouts IPAM 外掛程式會從預先定義的範圍,動態指派 IP 位址給額外的 IPVLAN 介面。

使用多個網路排程 Pod

建立 Pod 並在註解中指定 NetworkAttachmentDefinition 時,GKE 排程器只會將 Pod 放在可滿足網路需求的節點上。排程器會找出節點集區中已設定必要次要網路介面的節點。這個節點識別程序可確保排程器將 Pod 排程至可連線至額外網路,並從指定範圍接收 IP 位址的節點。

以下各節將逐步引導您在 GKE 叢集上,使用 IPVLAN 和 Whereabouts 外掛程式設定 Multus。

事前準備

開始之前,請確認您已完成下列工作:

  • 啟用 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 高可用性政策功能,因此您不必手動設定節點間的連線。
  • 使用 GKE 驗證的 Multus CNI 版本 (例如 v4.2.1),確保相容性。

設定虛擬私有雲

如要設定虛擬私有雲 (VPC),以便搭配 Multus 使用 (包括為節點網路建立子網路,以及為 Pod 網路建立次要範圍),請完成下列步驟:

  1. 建立新的虛擬私有雲或使用現有虛擬私有雲:

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

    VPC_NAME 替換為 VPC 名稱。

  2. 在這個虛擬私有雲中建立新的子網路:

    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:虛擬私有雲的名稱。
    • 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 節點集區

建立連至其他虛擬私有雲網路的 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:額外虛擬私有雲的名稱。
  • 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>

以下範例顯示附加至 default 命名空間中名為 NAD_NAMENetworkAttachmentDefinition 的 Pod 資訊清單:

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,並將額外的網路介面 (net1) 連線至 NetworkAttachmentDefinition 指定的網路。

驗證 Pod 的額外 IP 位址

如要確認將 Pod 附加至其他網路後,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 位址。

後續步驟