Configurar o Multus com IPVLAN e Whereabouts

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

O plug-in CNI IPVLAN oferece conectividade da camada 2 para interfaces de pod adicionais, e o plug-in IPAM Whereabouts atribui endereços IP dinamicamente a elas.

Essa configuração permite configurações de rede avançadas, como a separação do tráfego do plano de controle e do plano de dados para melhorar o isolamento e a segmentação da rede.

Este documento é destinado a arquitetos de nuvem e especialistas em redes que projetam e arquitetam a rede para a organização. Para saber mais sobre papéis comuns e tarefas de exemplo referenciados no conteúdo do Google Cloud , consulte Tarefas e funções de usuário comuns do GKE.

Antes de ler este documento, confira se você conhece os seguintes conceitos:

Benefícios de usar o Multus com IPVLAN

Configurar seus pods com várias interfaces de rede usando essa solução oferece várias vantagens importantes. Os principais casos de uso para configurar o Multus com IPVLAN no modo da Camada 2 são para segmentação de rede que exige adjacência da Camada 2:

  • Isolamento de tráfego:isole diferentes tipos de tráfego para aumentar a segurança e o desempenho. Por exemplo, é possível separar o tráfego de gerenciamento sensível do tráfego de dados do aplicativo.
  • Separação do plano de controle e de dados:dedique a interface de rede principal ao tráfego do plano de controle e direcione o tráfego do plano de dados de alta capacidade por uma interface IPVLAN secundária.
  • Adjacência da camada 2:atende aos requisitos de aplicativos que precisam de conectividade direta da camada 2 entre pods na mesma rede secundária.

Limitações

Os pods configurados com interfaces Multus não podem usar simultaneamente os recursos multirrede integrados do GKE. A configuração de rede de um pod precisa usar o Multus ou a multirrede integrada do cluster.

Como o Multus funciona com IPVLAN e Whereabouts

O Multus é um metaplugin CNI que permite que os pods se conectem a várias redes. O Multus atua como um dispatcher, chamando outros plug-ins CNI para configurar interfaces de rede com base em recursos NetworkAttachmentDefinition. Você define cada rede adicional usando um NetworkAttachmentDefinition, que especifica qual plug-in CNI (como IPVLAN) e IPAM (como Whereabouts) usar para essa rede.

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

Diagrama mostrando como o Multus, o IPVLAN e o Whereabouts funcionam juntos no GKE.
Figura 1. Arquitetura do Multus com plug-ins IPVLAN e Whereabouts.

Este diagrama mostra dois nós, cada um com um pod. Cada pod tem uma interface principal e outra adicional. As duas interfaces principais se conectam a uma placa de rede compartilhada, e as duas interfaces adicionais se conectam a uma placa de rede compartilhada diferente.

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

  • Interface principal (eth0): o GKE Dataplane V2 gerencia essa interface, fornecendo conectividade padrão do cluster.
  • Outras interfaces (net1 etc.): O Multus gerencia essas interfaces. O Multus invoca o plug-in CNI IPVLAN no modo da camada 2 para cada NetworkAttachmentDefinition especificado nas anotações de um pod. Essa configuração fornece conectividade da Camada 2 a uma rede VPC secundária.
  • Gerenciamento de endereços IP (IPAM): configure o plug-in IPAM do Whereabouts no NetworkAttachmentDefinition. O plug-in Whereabouts IPAM atribui dinamicamente endereços IP às interfaces IPVLAN adicionais de um intervalo predefinido.

Programação de pods com várias redes

Quando você cria um pod e especifica um NetworkAttachmentDefinition nas anotações dele, o programador do GKE coloca o pod apenas em um nó que pode atender aos requisitos de rede. O programador identifica os nós em um pool de nós que têm a interface de rede secundária necessária configurada. Esse processo de identificação de nó garante que o programador agende o pod em um nó que possa se conectar à rede adicional e receber um endereço IP do intervalo especificado.

As seções a seguir orientam você na configuração do Multus com plug-ins IPVLAN e Whereabouts no cluster do GKE.

Antes de começar

Antes de começar, verifique se você realizou as tarefas a seguir:

  • Ativar a API Google Kubernetes Engine.
  • Ativar a API Google Kubernetes Engine
  • Se você quiser usar a CLI do Google Cloud para essa tarefa, instale e inicialize a gcloud CLI. Se você instalou a gcloud CLI anteriormente, instale a versão mais recente executando o comando gcloud components update. Talvez as versões anteriores da gcloud CLI não sejam compatíveis com a execução dos comandos neste documento.
  • Instale a ferramenta de linha de comando kubectl.
  • Configure um cluster do GKE executando a versão 1.28 ou mais recente com Dataplane V2, alias de IP e várias redes ativadas. Para saber como, consulte Configurar o suporte a várias redes para pods. A ativação de várias redes também ativa os recursos de política de alta disponibilidade de várias sub-redes de IP e IP persistente, o que elimina a necessidade de configuração manual da conectividade entre nós.
  • Use uma versão do Multus CNI validada pelo GKE (como a v4.2.1) para compatibilidade.

Configurar a VPC

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

  1. Crie uma VPC ou use uma existente:

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

    Substitua VPC_NAME pelo nome da VPC.

  2. Crie uma sub-rede na 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:

    • SUBNET_NAME: o nome da nova sub-rede;
    • PRIMARY_RANGE: o intervalo CIDR principal da sub-rede, como 10.0.1.0/24. Esse comando usa esse 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 pods na sub-rede.
    • SECONDARY_RANGE_CIDR: o intervalo CIDR secundário para pods, como 172.16.1.0/24. Outras interfaces em pods usam esse intervalo.

    Esse 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.

Criar um cluster do GKE Standard

Crie um cluster do GKE Standard 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:

  • CLUSTER_NAME: o nome do novo cluster;
  • CLUSTER_VERSION: a versão do cluster do GKE. Use a versão 1.28 ou posterior.

Ao ativar várias redes, é possível criar pools de nós com várias interfaces de rede, o que é necessário para a CNI do Multus.

Criar um pool de nós do GKE Standard

Crie um pool de nós do GKE Standard conectado a outras redes VPC:

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:

  • NODEPOOL_NAME: o nome do novo pool de nós.
  • CLUSTER_NAME: o nome do cluster.
  • ZONE: a zona do pool de nós, 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ário para pods na sub-rede.

Esse comando cria um pool de nós em que os nós têm uma interface de rede adicional em SUBNET_NAME, e os pods nesses nós podem usar endereços IP de SECONDARY_RANGE_NAME.

Para mais informações sobre como criar clusters do GKE com recursos de várias redes, consulte Configurar o suporte a várias redes para pods.

Aplicar a implantação do Multus

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

Criar um manifesto NetworkAttachmentDefinition

Para permitir que os pods se conectem a outras redes, crie um manifesto NetworkAttachmentDefinition. Esse manifesto define como os pods se conectam a uma rede e especifica o intervalo de endereços IP que um plug-in IPAM, como Whereabouts, atribui. Esse intervalo precisa fazer parte da sub-rede que conecta as interfaces de rede adicionais dos nós.

  1. Salve este manifesto como nad.yaml. Esse manifesto usa plugins 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 geralmente começam com eth1 e são nomeadas em sequência. Para confirmar o nome da interface, conecte-se a um nó usando SSH e execute o comando ip addr.
    • range: o intervalo de endereços IP para interfaces de pod, que é o mesmo intervalo IPv4 secundário criado para os pods (SECONDARY_RANGE_NAME). Por exemplo, 172.16.1.0/24.
  2. Aplique o manifesto ao cluster:

    kubectl apply -f nad.yaml
    

Anexar pods a outras redes

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

O exemplo a seguir mostra um manifesto de pod que se conecta ao NetworkAttachmentDefinition chamado NAD_NAME no namespace 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 você criou.

Quando você aplica esse manifesto, o Kubernetes cria o pod com uma interface de rede adicional (net1) conectada à rede especificada por NetworkAttachmentDefinition.

Verificar o endereço IP adicional do pod

Para verificar se o pod recebe um endereço IP adicional depois de ser anexado a uma rede extra, inspecione as interfaces de rede no pod:

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

    $kubectl describe pod PODNAME
    

    Substitua PODNAME pelo nome do pod, como samplepod.

  2. Analise a saída. 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 será o 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.

A seguir