Configure o Multus com o IPVLAN e o Whereabouts

Este documento explica como configurar pods no Google Kubernetes Engine (GKE) com várias interfaces de rede através do Multus CNI, do plug-in IPVLAN CNI e do plug-in Whereabouts IPAM.

O plug-in CNI IPVLAN oferece conetividade da camada 2 para interfaces de pods adicionais e o plug-in IPAM Whereabouts atribui-lhes dinamicamente endereços IP.

Esta configuração permite configurações de rede avançadas, como a separação do tráfego do plano de controlo e do plano de dados para um isolamento e uma segmentação da rede melhorados.

Este documento destina-se a arquitetos da nuvem e especialistas em redes que concebem e arquitetam a rede para a respetiva organização. Para saber mais acerca das funções comuns e das tarefas de exemplo que referimos no Google Cloud conteúdo, consulte o artigo Funções e tarefas comuns de utilizadores do GKE.

Antes de ler este documento, certifique-se de que conhece os seguintes conceitos:

Vantagens da utilização do Multus com o IPVLAN

A configuração dos seus pods com várias interfaces de rede através desta solução oferece várias vantagens importantes. Os principais exemplos de utilização para configurar o Multus com o IPVLAN no modo de camada 2 destinam-se à segmentação de rede que requer adjacência da camada 2:

  • Isolamento do tráfego: isole diferentes tipos de tráfego para melhorar a segurança e o desempenho. Por exemplo, pode separar o tráfego de gestão sensível do tráfego de dados de aplicações.
  • Separação do plano de controlo e do plano de dados: dedique a interface de rede principal ao tráfego do plano de controlo enquanto direciona o tráfego do plano de dados de elevado débito através de uma interface IPVLAN secundária.
  • Adjacência da camada 2: cumpre os requisitos para aplicações que precisam de conetividade direta da camada 2 entre os pods na mesma rede secundária.

Limitações

Os pods configurados com interfaces Multus não podem usar simultaneamente as capacidades de várias redes incorporadas do GKE. A configuração de rede de um pod tem de usar o Multus ou a capacidade de várias redes integrada do cluster.

Como o Multus funciona com o IPVLAN e o Whereabouts

O Multus é um metaplugin CNI que permite que os pods se associem a várias redes. O Multus atua como um distribuidor, chamando outros plugins CNI para configurar interfaces de rede com base em recursos NetworkAttachmentDefinition. Define cada rede adicional através de um NetworkAttachmentDefinition, que especifica que plug-in CNI (como IPVLAN) e plug-in IPAM (como Whereabouts) usar para essa rede.

O diagrama seguinte ilustra a arquitetura Multus com plug-ins IPVLAN e Whereabouts.O plug-in Whereabouts funciona com o Multus e o IPVLAN para gerir o endereço IP (IPAM) das interfaces de rede adicionais dos pods.

Diagrama que mostra como o Multus, o IPVLAN e o Whereabouts funcionam em conjunto no GKE.
Figura 1. Arquitetura Multus com plug-ins IPVLAN e Whereabouts.

Este diagrama mostra dois nós que têm cada um um Pod. Cada Pod tem uma interface principal e uma interface adicional. As duas interfaces principais ligam-se a uma placa de rede partilhada e as duas interfaces adicionais ligam-se a uma placa de rede partilhada diferente.

Quando usa o Multus com o IPVLAN e o Whereabouts no GKE, os pods têm normalmente a seguinte configuração de interface:

  • Interface principal (eth0): o GKE Dataplane V2 gere esta interface, oferecendo conectividade de cluster predefinida.
  • Interfaces adicionais (net1, etc.): O Multus gere estas interfaces. O Multus invoca o plug-in CNI IPVLAN no modo de camada 2 para cada NetworkAttachmentDefinition que especificar nas anotações de um pod. Esta configuração fornece conetividade da camada 2 a uma rede VPC secundária.
  • Gestão de endereços IP (IPAM): configura o plug-in IPAM do Whereabouts no NetworkAttachmentDefinition. O plug-in IPAM Whereabouts atribui dinamicamente endereços IP às interfaces IPVLAN adicionais a partir de um intervalo predefinido.

Agendamento de pods com várias redes

Quando cria um agrupamento e especifica um NetworkAttachmentDefinition nas respetivas anotações, o programador do GKE coloca o agrupamento apenas num nó que possa satisfazer os requisitos de rede. O programador identifica os nós num conjunto de nós que têm a interface de rede secundária necessária configurada. Este processo de identificação de nós garante que o programador agenda o pod num nó que se pode ligar à rede adicional e receber um endereço IP do intervalo especificado.

As secções seguintes explicam como configurar o Multus com os plug-ins IPVLAN e Whereabouts no seu cluster do GKE.

Antes de começar

Antes de começar, certifique-se de que realizou as seguintes tarefas:

  • Ative a API Google Kubernetes Engine.
  • Ative a API Google Kubernetes Engine
  • Se quiser usar a CLI gcloud para esta tarefa, instale-a e, em seguida, inicialize a CLI gcloud. Se instalou anteriormente a CLI gcloud, execute o comando gcloud components update para obter a versão mais recente. As versões anteriores da CLI gcloud podem não suportar a execução dos comandos neste documento.
  • Instale a ferramenta de linha de comandos kubectl.
  • Configure um cluster do GKE com a versão 1.28 ou posterior com o Dataplane V2, o alias de IP e a rede múltipla ativados. Para saber como, consulte o artigo Configure o suporte de várias redes para os Pods. A ativação da multirrede também ativa as funcionalidades de política de HA de sub-rede de vários IPs e IP persistente, que eliminam a necessidade de configuração manual da conetividade entre nós.
  • Use uma versão validada pelo GKE do Multus CNI (como a v4.2.1) para garantir a compatibilidade.

Configure a VPC

Para configurar a nuvem privada virtual (VPC) para utilização com o Multus, incluindo a criação de uma sub-rede para a rede de nós e intervalos secundários para a rede de pods, conclua os seguintes passos:

  1. Crie uma nova VPC ou use uma existente:

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

    Substitua VPC_NAME pelo nome da VPC.

  2. Crie uma nova sub-rede nesta VPC:

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

    Substitua o seguinte:

    • SUBNET_NAME: o nome da nova sub-rede.
    • PRIMARY_RANGE: o intervalo CIDR principal para a sub-rede, como 10.0.1.0/24. Este comando usa este intervalo para interfaces de nós.
    • VPC_NAME: o nome da VPC.
    • REGION: a região da sub-rede, como us-central1.
    • SECONDARY_RANGE_NAME: o nome do intervalo de endereços IP secundário para agrupamentos na sub-rede.
    • SECONDARY_RANGE_CIDR: o intervalo CIDR secundário para pods, como 172.16.1.0/24. As interfaces adicionais nos pods usam este intervalo.

    Este comando cria uma sub-rede com um intervalo CIDR principal para uma interface de nó adicional e um intervalo secundário para as interfaces de pod adicionais.

Crie um cluster padrão do GKE

Crie um cluster padrão do GKE com várias redes ativadas:

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

Substitua o seguinte:

  • CLUSTER_NAME: o nome do novo cluster.
  • CLUSTER_VERSION: a versão do seu cluster do GKE. Tem de usar a versão 1.28 ou posterior.

A ativação da rede múltipla permite-lhe criar node pools com várias interfaces de rede, o que o CNI Multus requer.

Crie um node pool padrão do GKE

Crie um pool de nós padrão do GKE ligado a redes VPC adicionais:

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

Substitua o seguinte:

  • NODEPOOL_NAME: o nome do novo node pool.
  • CLUSTER_NAME: o nome do seu cluster.
  • ZONE: a zona do node pool, como us-central1-c.
  • VPC_NAME: o nome da VPC adicional.
  • SUBNET_NAME: o nome da sub-rede.
  • SECONDARY_RANGE_NAME: o nome do intervalo de endereços IP secundários para agrupamentos na sub-rede.

Este comando cria um node pool onde os nós têm uma interface de rede adicional em SUBNET_NAME e os pods nestes nós podem usar endereços IP de SECONDARY_RANGE_NAME.

Para mais informações sobre a criação de clusters do GKE com capacidades de várias redes, consulte o artigo Configure o suporte de várias redes para pods.

Aplique a implementação do Multus

Para ativar várias interfaces de rede para os seus pods, instale o plug-in Multus CNI. Guarde o seguinte manifesto, que inclui o DaemonSet necessário e a definição de recursos personalizados (CRD), como 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

Em seguida, aplique o manifesto ao cluster:

kubectl apply -f multus-manifest.yaml

Crie um manifesto NetworkAttachmentDefinition

Para permitir que os Pods se liguem a redes adicionais, crie um NetworkAttachmentDefinition manifesto. Este manifesto define como os pods se ligam a uma rede e especifica o intervalo de endereços IP que um plug-in IPAM, como o Whereabouts, atribui. Este intervalo tem de fazer parte da sub-rede que liga as interfaces de rede adicionais dos seus nós.

  1. Guarde este manifesto como nad.yaml. Este manifesto usa plug-ins IPVLAN e 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
            }
          }
        ]
      }'
    

    O manifesto inclui os seguintes campos:

    • NAD_NAME: o nome do seu NetworkAttachmentDefinition.
    • master: o nome da interface de rede secundária do nó, que atua como a interface master para IPVLAN. No GKE, as interfaces de rede secundárias começam normalmente com eth1 e são denominadas sequencialmente. Para confirmar o nome da interface, estabeleça ligação a um nó através de SSH e execute o comando ip addr.
    • range: o intervalo de endereços IP para interfaces de pods, que é igual ao intervalo IPv4 secundário que criou para os pods (SECONDARY_RANGE_NAME). Por exemplo, 172.16.1.0/24.
  2. Aplique o manifesto ao cluster:

    kubectl apply -f nad.yaml
    

Anexe Pods a redes adicionais

Para anexar um Pod a uma rede adicional, adicione a anotação k8s.v1.cni.cncf.io/networks ao manifesto do Pod. Para várias redes, indique uma lista de nomes de NetworkAttachmentDefinition separados por vírgulas no seguinte formato: <namespace>/<nad-name>.

O exemplo seguinte mostra um manifesto de podcast que está anexado ao elemento NetworkAttachmentDefinition com o nome NAD_NAME no espaço de nomes default:

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

Substitua NAD_NAME pelo nome do NetworkAttachmentDefinition que criou.

Quando aplica este manifesto, o Kubernetes cria o pod com uma interface de rede adicional (net1) ligada à rede especificada por NetworkAttachmentDefinition.

Valide o endereço IP adicional do Pod

Para verificar se o Pod recebe um endereço IP adicional depois de o anexar a uma rede adicional, inspecione as interfaces de rede no Pod:

  1. Para inspecionar o samplepod e validar o endereço IP adicional, use o seguinte comando:

    $kubectl describe pod PODNAME
    

    Substitua PODNAME pelo nome do seu Pod, como samplepod.

  2. Examine o resultado. A interface eth0 tem o endereço IP principal do Pod. O plug-in Whereabouts atribui o endereço IP adicional a outra interface, como net1.

    O resultado é semelhante ao seguinte:

    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
    

    Neste exemplo, 10.104.5.19 é o endereço IP principal em eth0 e 10.200.1.1 é o endereço IP adicional em net1.

O que se segue?