Configura Multus con IPVLAN y Whereabouts

En este documento, se explica cómo configurar Pods en Google Kubernetes Engine (GKE) con varias interfaces de red mediante el CNI de Multus, el complemento de CNI de IPVLAN y el complemento de IPAM de Whereabouts.

El complemento de CNI de IPVLAN proporciona conectividad de capa 2 para interfaces de Pod adicionales, y el complemento de IPAM de Whereabouts les asigna direcciones IP de forma dinámica.

Esta configuración permite establecer configuraciones de red avanzadas, como la separación del tráfico del plano de control y el plano de datos para mejorar el aislamiento y la segmentación de la red.

Este documento está dirigido a arquitectos de nube y especialistas en herramientas de redes que diseñan la red para su organización. Para obtener más información sobre los roles comunes y las tareas de ejemplo a las que hacemos referencia en el contenido de Google Cloud , consulta Roles y tareas comunes de los usuarios de GKE.

Antes de leer este documento, asegúrate de estar familiarizado con los siguientes conceptos:

Beneficios de usar Multus con IPVLAN

Configurar tus Pods con varias interfaces de red a través de esta solución proporciona varias ventajas clave. Los casos de uso principales para configurar Multus con IPVLAN en el modo de capa 2 son para la segmentación de redes que requiere adyacencia de capa 2:

  • Aislamiento del tráfico: Aísla diferentes tipos de tráfico para mejorar la seguridad y el rendimiento. Por ejemplo, puedes separar el tráfico de administración sensible del tráfico de datos de la aplicación.
  • Separación del plano de control y el plano de datos: Dedica la interfaz de red principal al tráfico del plano de control y dirige el tráfico del plano de datos de alta capacidad de procesamiento a través de una interfaz IPVLAN secundaria.
  • Adyacencia de capa 2: Cumple con los requisitos de las aplicaciones que necesitan conectividad directa de capa 2 entre Pods en la misma red secundaria.

Limitaciones

Los Pods configurados con interfaces de Multus no pueden usar simultáneamente las capacidades integradas de varias redes de GKE. La configuración de red de un Pod debe usar Multus o la función integrada de varias redes del clúster.

Cómo funciona Multus con IPVLAN y Whereabouts

Multus es un metaplugin de CNI que permite que los Pods se conecten a varias redes. Multus actúa como un distribuidor, ya que llama a otros complementos de CNI para configurar interfaces de red según los recursos de NetworkAttachmentDefinition. Defines cada red adicional con un NetworkAttachmentDefinition, que especifica qué complemento de CNI (como IPVLAN) y qué complemento de IPAM (como Whereabouts) se deben usar para esa red.

En el siguiente diagrama, se ilustra la arquitectura de Multus con los complementos IPVLAN y Whereabouts.El complemento Whereabouts funciona con Multus y IPVLAN para controlar la administración de direcciones IP (IPAM) de las interfaces de red adicionales de los Pods.

Diagrama que muestra cómo funcionan en conjunto Multus, IPVLAN y Whereabouts en GKE.
Figura 1. Arquitectura de Multus con complementos de IPVLAN y Whereabouts

En este diagrama, se muestran dos nodos, cada uno con un Pod. Cada Pod tiene una interfaz principal y una interfaz adicional. Las dos interfaces principales se conectan a una tarjeta de interfaz de red compartida, y las dos interfaces adicionales se conectan a una tarjeta de interfaz de red compartida diferente.

Cuando se usa Multus con IPVLAN y Whereabouts en GKE, los Pods suelen tener la siguiente configuración de interfaz:

  • Interfaz principal (eth0): GKE Dataplane V2 administra esta interfaz y proporciona conectividad predeterminada del clúster.
  • Interfaces adicionales (net1, etcétera): Multus administra estas interfaces. Multus invoca el complemento de CNI de IPVLAN en modo de capa 2 para cada NetworkAttachmentDefinition que especifiques en las anotaciones de un Pod. Esta configuración proporciona conectividad de capa 2 a una red de VPC secundaria.
  • Administración de direcciones IP (IPAM): Configuras el complemento IPAM de Whereabouts dentro de NetworkAttachmentDefinition. El complemento Whereabouts IPAM asigna de forma dinámica direcciones IP a las interfaces IPVLAN adicionales desde un rango predefinido.

Programación de Pods con varias redes

Cuando creas un Pod y especificas un NetworkAttachmentDefinition en sus anotaciones, el programador de GKE coloca el Pod solo en un nodo que puede satisfacer los requisitos de red. El programador identifica los nodos dentro de un grupo de nodos que tienen configurada la interfaz de red secundaria necesaria. Este proceso de identificación de nodos garantiza que el programador programe el Pod en un nodo que pueda conectarse a la red adicional y recibir una dirección IP del rango especificado.

En las siguientes secciones, se te guiará para configurar Multus con los complementos IPVLAN y Whereabouts en tu clúster de GKE.

Antes de comenzar

Antes de comenzar, asegúrate de haber realizado las siguientes tareas:

  • Habilita la API de Google Kubernetes Engine.
  • Habilitar la API de Google Kubernetes Engine
  • Si deseas usar Google Cloud CLI para esta tarea, instala y, luego, inicializa gcloud CLI. Si ya instalaste gcloud CLI, ejecuta el comando gcloud components update para obtener la versión más reciente. Es posible que las versiones anteriores de gcloud CLI no admitan la ejecución de los comandos que se describen en este documento.
  • Instala la herramienta de línea de comandos de kubectl.
  • Configura un clúster de GKE que ejecute la versión 1.28 o posterior con Dataplane V2, alias de IP y redes múltiples habilitados. Para obtener más información, consulta Configura la compatibilidad con varias redes para Pods. Habilitar las redes múltiples también habilita las funciones de política de alta disponibilidad de subred de varias IPs y de IP persistente, lo que elimina la necesidad de configurar manualmente la conectividad entre nodos.
  • Usa una versión validada por GKE de Multus CNI (como la v4.2.1) para garantizar la compatibilidad.

Configura la VPC

Para configurar la nube privada virtual (VPC) para usarla con Multus, lo que incluye la creación de una subred para la red de nodos y rangos secundarios para la red de Pods, completa los siguientes pasos:

  1. Crea una VPC nueva o usa una existente:

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

    Reemplaza VPC_NAME por el nombre de la VPC.

  2. Crea una subred nueva dentro de esta VPC:

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

    Reemplaza lo siguiente:

    • SUBNET_NAME: es el nombre de la subred nueva.
    • PRIMARY_RANGE: Es el rango de CIDR principal de la subred, como 10.0.1.0/24. Este comando usa este rango para las interfaces de nodos.
    • VPC_NAME: El nombre de la VPC.
    • REGION: Es la región de la subred, como us-central1.
    • SECONDARY_RANGE_NAME: Es el nombre del rango de direcciones IP secundario para los Pods en la subred.
    • SECONDARY_RANGE_CIDR: Es el rango de CIDR secundario para Pods, como 172.16.1.0/24. Las interfaces adicionales en los Pods usan este rango.

    Este comando crea una subred con un rango CIDR principal para una interfaz de nodo adicional y un rango secundario para las interfaces de Pod adicionales.

Crea un clúster de GKE Standard

Crea un clúster de GKE Standard con varias redes habilitadas:

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

Reemplaza lo siguiente:

  • CLUSTER_NAME es el nombre del clúster nuevo.
  • CLUSTER_VERSION: Es la versión del clúster de GKE. Debes usar la versión 1.28 o una posterior.

Habilitar varias redes te permite crear grupos de nodos con varias interfaces de red, lo que requiere Multus CNI.

Crea un grupo de nodos de GKE Standard

Crea un grupo de nodos estándar de GKE conectado a redes de VPC adicionales:

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

Reemplaza lo siguiente:

  • NODEPOOL_NAME: el nombre del grupo de nodos nuevo
  • CLUSTER_NAME: El nombre de tu clúster.
  • ZONE: Es la zona del grupo de nodos, como us-central1-c.
  • VPC_NAME: Es el nombre de la VPC adicional.
  • SUBNET_NAME: Es el nombre de la subred.
  • SECONDARY_RANGE_NAME: Es el nombre del rango de direcciones IP secundario para los Pods en la subred.

Este comando crea un grupo de nodos en el que los nodos tienen una interfaz de red adicional en SUBNET_NAME, y los Pods en estos nodos pueden usar direcciones IP de SECONDARY_RANGE_NAME.

Para obtener más información sobre cómo crear clústeres de GKE con capacidades de varias redes, consulta Configura la compatibilidad con varias redes para Pods.

Aplica la implementación de Multus

Para habilitar varias interfaces de red para tus Pods, instala el complemento de CNI de Multus. Guarda el siguiente manifiesto, que incluye el DaemonSet y la definición de recurso personalizado (CRD) obligatorios, 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

Luego, aplica el manifiesto al clúster:

kubectl apply -f multus-manifest.yaml

Crea un manifiesto NetworkAttachmentDefinition

Para permitir que los Pods se conecten a redes adicionales, crea un manifiesto NetworkAttachmentDefinition. Este manifiesto define cómo los Pods se conectan a una red y especifica el rango de direcciones IP que asigna un complemento de IPAM, como Whereabouts. Este rango debe ser parte de la subred que conecta las interfaces de red adicionales de tus nodos.

  1. Guarda este manifiesto como nad.yaml. Este manifiesto usa los complementos IPVLAN y 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
            }
          }
        ]
      }'
    

    El manifiesto incluye los siguientes campos:

    • NAD_NAME: El nombre de tu NetworkAttachmentDefinition.
    • master: Es el nombre de la interfaz de red secundaria del nodo, que actúa como la interfaz master para IPVLAN. En GKE, las interfaces de red secundarias suelen comenzar con eth1 y se nombran de forma secuencial. Para confirmar el nombre de la interfaz, conéctate a un nodo con SSH y ejecuta el comando ip addr.
    • range: Es el rango de direcciones IP para las interfaces de Pod, que es el mismo que el rango de IPv4 secundario que creaste para los Pods (SECONDARY_RANGE_NAME). Por ejemplo, 172.16.1.0/24.
  2. Aplica el manifiesto al clúster:

    kubectl apply -f nad.yaml
    

Cómo conectar los Pods a redes adicionales

Para adjuntar un Pod a una red adicional, agrega la anotación k8s.v1.cni.cncf.io/networks al manifiesto del Pod. Para varias redes, proporciona una lista separada por comas de los nombres de NetworkAttachmentDefinition en el siguiente formato: <namespace>/<nad-name>.

En el siguiente ejemplo, se muestra un manifiesto de Pod que se adjunta al NetworkAttachmentDefinition llamado NAD_NAME en el espacio de nombres 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

Reemplaza NAD_NAME por el nombre del NetworkAttachmentDefinition que creaste.

Cuando aplicas este manifiesto, Kubernetes crea el Pod con una interfaz de red adicional (net1) conectada a la red que especifica NetworkAttachmentDefinition.

Verifica la dirección IP adicional del Pod

Para verificar que el Pod recibe una dirección IP adicional después de que lo adjuntas a una red adicional, inspecciona las interfaces de red dentro del Pod:

  1. Para inspeccionar samplepod y verificar la dirección IP adicional, usa el siguiente comando:

    $kubectl describe pod PODNAME
    

    Reemplaza PODNAME por el nombre de tu Pod, como samplepod.

  2. Examina el resultado. La interfaz eth0 tiene la dirección IP principal del Pod. El complemento Whereabouts asigna la dirección IP adicional a otra interfaz, como net1.

    El resultado es similar a lo siguiente:

    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
    

    En este ejemplo, 10.104.5.19 es la dirección IP principal en eth0 y 10.200.1.1 es la dirección IP adicional en net1.

¿Qué sigue?