documentation.suse.com / Documentación de SUSE Edge / Guías prácticas / Uso de ClusterClass para desplegar clústeres descendentes

29 Uso de ClusterClass para desplegar clústeres descendentes

29.1 Introducción

El aprovisionamiento de clústeres de Kubernetes es una tarea compleja que requiere una gran experiencia a la hora de configurar los componentes del clúster. A medida que las configuraciones se vuelven más complejas, o que las exigencias de los diferentes proveedores introducen numerosas definiciones de recursos específicas para cada uno, la creación de clústeres puede resultar abrumadora. Afortunadamente, la API de clústeres de Kubernetes (Cluster API, CAPI) ofrece un enfoque elegante y claro aún más reforzado gracias a ClusterClass. Esta función introduce un modelo basado en plantillas que permite definir una clase de clúster reutilizable que encapsula la complejidad y promueve la coherencia.

29.2 ¿Qué es ClusterClass?

El proyecto CAPI introdujo la función ClusterClass como un cambio de paradigma en la gestión del ciclo de vida de los clústeres de Kubernetes mediante la adopción de una metodología basada en plantillas para la instanciación de clústeres. En lugar de definir los recursos de forma independiente para cada clúster, los usuarios definen una ClusterClass, que sirve como un modelo completo y reutilizable. Esta representación abstracta encapsula el estado y la configuración deseados de un clúster de Kubernetes, lo que permite la creación rápida y coherente de múltiples clústeres que se ajustan a las especificaciones definidas. Esta abstracción reduce la carga de configuración, lo que da como resultado manifiestos de despliegue más manejables. Esto significa que los componentes centrales de un clúster de carga de trabajo se definen a nivel de clase, lo que permite a los usuarios utilizar estas plantillas como variantes de clústeres de Kubernetes que se pueden reutilizar una o varias veces para el aprovisionamiento de clústeres. La implementación de ClusterClass ofrece varias ventajas clave que abordan los retos inherentes a la gestión tradicional de CAPI a gran escala:

  • Reducción sustancial de la complejidad y la redundancia de YAML

  • Procesos de mantenimiento y actualización optimizados

  • Mejora en la coherencia y la estandarización entre despliegues

  • Mejora en la escalabilidad y las capacidades de automatización

  • Gestión declarativa y control de versiones robusto

clusterclass

29.3 Ejemplo de archivo de aprovisionamiento de CAPI actual

El despliegue de un clúster de Kubernetes que aproveche Cluster API (CAPI) y el proveedor RKE2 requiere la definición de varios recursos personalizados. Estos recursos definen el estado deseado del clúster y su infraestructura subyacente, lo que permite a CAPI orquestar el ciclo de vida del aprovisionamiento y gestión. El fragmento de código siguiente ilustra los tipos de recursos que deben configurarse:

  • Cluster: este recurso encapsula configuraciones generales, incluida la topología de red que regirá la comunicación entre nodos y la detección de servicios. Además, establece los vínculos esenciales con la especificación del plano de control y el recurso del proveedor de infraestructura designado, informando así a CAPI sobre la arquitectura de clúster deseada y la infraestructura subyacente sobre la que se aprovisionará.

  • Metal3Cluster: este recurso define los atributos a nivel de infraestructura exclusivos de Metal3; por ejemplo, el punto final externo a través del cual se podrá acceder al servidor de API de Kubernetes.

  • RKE2ControlPlane: este recurso define las características y el comportamiento de los nodos de plano de control del clúster. Dentro de esta especificación, se configuran parámetros como el número deseado de réplicas del plano de control (fundamental para garantizar una alta disponibilidad y tolerancia a fallos), la versión específica de la distribución de Kubernetes (alineada con la versión elegida de RKE2) y la estrategia para distribuir actualizaciones en los componentes de plano de control. Además, este recurso dicta la interfaz de red de contenedores (CNI) que se empleará dentro del clúster y facilita la inyección de configuraciones específicas del agente, a menudo aprovechando Ignition para el aprovisionamiento automatizado y sin interrupciones de los agentes RKE2 en los nodos de plano de control.

  • Metal3MachineTemplate: este recurso actúa como un modelo para la creación de las instancias de computación individuales que formarán los nodos de trabajador del clúster de Kubernetes, definiendo la imagen que se utilizará.

  • Metal3DataTemplate: como complemento de Metal3MachineTemplate, el recurso Metal3DataTemplate permite especificar metadatos adicionales para los equipos recién aprovisionados.

---
apiVersion: cluster.x-k8s.io/v1beta1
kind: Cluster
metadata:
  name: emea-spa-cluster-3
  namespace: emea-spa
spec:
  clusterNetwork:
    pods:
      cidrBlocks:
        - 192.168.0.0/18
    services:
      cidrBlocks:
        - 10.96.0.0/12
  controlPlaneRef:
    apiVersion: controlplane.cluster.x-k8s.io/v1beta1
    kind: RKE2ControlPlane
    name: emea-spa-cluster-3
  infrastructureRef:
    apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
    kind: Metal3Cluster
    name: emea-spa-cluster-3
---
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: Metal3Cluster
metadata:
  name: emea-spa-cluster-3
  namespace: emea-spa
spec:
  controlPlaneEndpoint:
    host: 192.168.122.203
    port: 6443
  noCloudProvider: true
---
apiVersion: controlplane.cluster.x-k8s.io/v1beta1
kind: RKE2ControlPlane
metadata:
  name: emea-spa-cluster-3
  namespace: emea-spa
spec:
  infrastructureRef:
    apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
    kind: Metal3MachineTemplate
    name: emea-spa-cluster-3
  replicas: 1
  version: v1.32.4+rke2r1
  rolloutStrategy:
    type: "RollingUpdate"
    rollingUpdate:
      maxSurge: 1
  registrationMethod: "control-plane-endpoint"
  registrationAddress: 192.168.122.203
  serverConfig:
    cni: cilium
    cniMultusEnable: true
    tlsSan:
      - 192.168.122.203
      - https://192.168.122.203.sslip.io
  agentConfig:
    format: ignition
    additionalUserData:
      config: |
        variant: fcos
        version: 1.4.0
        storage:
          files:
            - path: /var/lib/rancher/rke2/server/manifests/endpoint-copier-operator.yaml
              overwrite: true
              contents:
                inline: |
                  apiVersion: helm.cattle.io/v1
                  kind: HelmChart
                  metadata:
                    name: endpoint-copier-operator
                    namespace: kube-system
                  spec:
                    chart: oci://registry.suse.com/edge/charts/endpoint-copier-operator
                    targetNamespace: endpoint-copier-operator
                    version: 303.0.0+up0.2.1
                    createNamespace: true
            - path: /var/lib/rancher/rke2/server/manifests/metallb.yaml
              overwrite: true
              contents:
                inline: |
                  apiVersion: helm.cattle.io/v1
                  kind: HelmChart
                  metadata:
                    name: metallb
                    namespace: kube-system
                  spec:
                    chart: oci://registry.suse.com/edge/charts/metallb
                    targetNamespace: metallb-system
                    version: 303.0.0+up0.14.9
                    createNamespace: true

            - path: /var/lib/rancher/rke2/server/manifests/metallb-cr.yaml
              overwrite: true
              contents:
                inline: |
                  apiVersion: metallb.io/v1beta1
                  kind: IPAddressPool
                  metadata:
                    name: kubernetes-vip-ip-pool
                    namespace: metallb-system
                  spec:
                    addresses:
                      - 192.168.122.203/32
                    serviceAllocation:
                      priority: 100
                      namespaces:
                        - default
                      serviceSelectors:
                        - matchExpressions:
                          - {key: "serviceType", operator: In, values: [kubernetes-vip]}
                  ---
                  apiVersion: metallb.io/v1beta1
                  kind: L2Advertisement
                  metadata:
                    name: ip-pool-l2-adv
                    namespace: metallb-system
                  spec:
                    ipAddressPools:
                      - kubernetes-vip-ip-pool
            - path: /var/lib/rancher/rke2/server/manifests/endpoint-svc.yaml
              overwrite: true
              contents:
                inline: |
                  apiVersion: v1
                  kind: Service
                  metadata:
                    name: kubernetes-vip
                    namespace: default
                    labels:
                      serviceType: kubernetes-vip
                  spec:
                    ports:
                    - name: rke2-api
                      port: 9345
                      protocol: TCP
                      targetPort: 9345
                    - name: k8s-api
                      port: 6443
                      protocol: TCP
                      targetPort: 6443
                    type: LoadBalancer
        systemd:
          units:
            - name: rke2-preinstall.service
              enabled: true
              contents: |
                [Unit]
                Description=rke2-preinstall
                Wants=network-online.target
                Before=rke2-install.service
                ConditionPathExists=!/run/cluster-api/bootstrap-success.complete
                [Service]
                Type=oneshot
                User=root
                ExecStartPre=/bin/sh -c "mount -L config-2 /mnt"
                ExecStart=/bin/sh -c "sed -i \"s/BAREMETALHOST_UUID/$(jq -r .uuid /mnt/openstack/latest/meta_data.json)/\" /etc/rancher/rke2/config.yaml"
                ExecStart=/bin/sh -c "echo \"node-name: $(jq -r .name /mnt/openstack/latest/meta_data.json)\" >> /etc/rancher/rke2/config.yaml"
                ExecStartPost=/bin/sh -c "umount /mnt"
                [Install]
                WantedBy=multi-user.target
    kubelet:
      extraArgs:
        - provider-id=metal3://BAREMETALHOST_UUID
    nodeName: "localhost.localdomain"
---
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: Metal3MachineTemplate
metadata:
  name: emea-spa-cluster-3
  namespace: emea-spa
spec:
  nodeReuse: True
  template:
    spec:
      automatedCleaningMode: metadata
      dataTemplate:
        name: emea-spa-cluster-3
      hostSelector:
        matchLabels:
          cluster-role: control-plane
          deploy-region: emea-spa
          node: group-3
      image:
        checksum: http://fileserver.local:8080/eibimage-downstream-cluster.raw.sha256
        checksumType: sha256
        format: raw
        url: http://fileserver.local:8080/eibimage-downstream-cluster.raw
---
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: Metal3DataTemplate
metadata:
  name: emea-spa-cluster-3
  namespace: emea-spa
spec:
  clusterName: emea-spa-cluster-3
  metaData:
    objectNames:
      - key: name
        object: machine
      - key: local-hostname
        object: machine
      - key: local_hostname
        object: machine

29.4 Transformación del archivo de aprovisionamiento de CAPI en ClusterClass

29.4.1 Definición de ClusterClass

El siguiente código define un recurso ClusterClass, una plantilla declarativa para desplegar de forma coherente un tipo específico de clúster de Kubernetes. Esta especificación incluye configuraciones comunes de infraestructura y plano de control, lo que permite un aprovisionamiento eficiente y una gestión uniforme del ciclo de vida en toda una flota de clústeres. En el siguiente ejemplo de ClusterClass, algunas variables se sustituirán durante el proceso de instanciación del clúster por los valores reales. En el ejemplo se utilizan las siguientes variables:

  • controlPlaneMachineTemplate: este es el nombre que define la referencia de la plantilla del equipo de plano de control que se va a utilizar.

  • controlPlaneEndpointHost: este es el nombre de host o la dirección IP del punto final del plano de control.

  • tlsSan: este es el nombre alternativo del sujeto TLS para el punto final del plano de control.

El archivo de definición de ClusterClass se basa en los tres recursos siguientes:

  • ClusterClass: este recurso encapsula toda la definición de ClusterClass, incluyendo el plano de control y las plantillas de infraestructura. Además, incluye la lista de variables que se sustituirán durante el proceso de instanciación.

  • RKE2ControlPlaneTemplate: este recurso define la plantilla del plano de control y especifica la configuración deseada para los nodos de plano de control. Incluye parámetros como el número de réplicas, la versión de Kubernetes y la CNI que se va a utilizar. Además, algunos parámetros se sustituirán por los valores correctos durante el proceso de instanciación.

  • Metal3ClusterTemplate: este recurso define la plantilla de infraestructura y especifica la configuración deseada para la infraestructura subyacente. Incluye parámetros como el punto final del plano de control y el indicador noCloudProvider. Además, algunos parámetros se sustituirán por los valores correctos durante el proceso de instanciación.

apiVersion: controlplane.cluster.x-k8s.io/v1beta1
kind: RKE2ControlPlaneTemplate
metadata:
  name: example-controlplane-type2
  namespace: emea-spa
spec:
  template:
    spec:
      infrastructureRef:
        apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
        kind: Metal3MachineTemplate
        name: example-controlplane    # This will be replaced by the patch applied in each cluster instances
        namespace: emea-spa
      replicas: 1
      version: v1.32.4+rke2r1
      rolloutStrategy:
        type: "RollingUpdate"
        rollingUpdate:
          maxSurge: 1
      registrationMethod: "control-plane-endpoint"
      registrationAddress: "default"  # This will be replaced by the patch applied in each cluster instances
      serverConfig:
        cni: cilium
        cniMultusEnable: true
        tlsSan:
          - "default"  # This will be replaced by the patch applied in each cluster instances
      agentConfig:
        format: ignition
        additionalUserData:
          config: |
            default
        kubelet:
          extraArgs:
            - provider-id=metal3://BAREMETALHOST_UUID
        nodeName: "localhost.localdomain"
---
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: Metal3ClusterTemplate
metadata:
  name: example-cluster-template-type2
  namespace: emea-spa
spec:
  template:
    spec:
      controlPlaneEndpoint:
        host: "default"  # This will be replaced by the patch applied in each cluster instances
        port: 6443
      noCloudProvider: true
---
apiVersion: cluster.x-k8s.io/v1beta1
kind: ClusterClass
metadata:
  name: example-clusterclass-type2
  namespace: emea-spa
spec:
  variables:
    - name: controlPlaneMachineTemplate
      required: true
      schema:
        openAPIV3Schema:
          type: string
    - name: controlPlaneEndpointHost
      required: true
      schema:
        openAPIV3Schema:
          type: string
    - name: tlsSan
      required: true
      schema:
        openAPIV3Schema:
          type: array
          items:
            type: string
  infrastructure:
    ref:
      kind: Metal3ClusterTemplate
      apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
      name: example-cluster-template-type2
  controlPlane:
    ref:
      kind: RKE2ControlPlaneTemplate
      apiVersion: controlplane.cluster.x-k8s.io/v1beta1
      name: example-controlplane-type2
  patches:
    - name: setControlPlaneMachineTemplate
      definitions:
        - selector:
            apiVersion: controlplane.cluster.x-k8s.io/v1beta1
            kind: RKE2ControlPlaneTemplate
            matchResources:
              controlPlane: true
          jsonPatches:
            - op: replace
              path: "/spec/template/spec/infrastructureRef/name"
              valueFrom:
                variable: controlPlaneMachineTemplate
    - name: setControlPlaneEndpoint
      definitions:
        - selector:
            apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
            kind: Metal3ClusterTemplate
            matchResources:
              infrastructureCluster: true  # Added to select InfraCluster
          jsonPatches:
            - op: replace
              path: "/spec/template/spec/controlPlaneEndpoint/host"
              valueFrom:
                variable: controlPlaneEndpointHost
    - name: setRegistrationAddress
      definitions:
        - selector:
            apiVersion: controlplane.cluster.x-k8s.io/v1beta1
            kind: RKE2ControlPlaneTemplate
            matchResources:
              controlPlane: true  # Added to select ControlPlane
          jsonPatches:
            - op: replace
              path: "/spec/template/spec/registrationAddress"
              valueFrom:
                variable: controlPlaneEndpointHost
    - name: setTlsSan
      definitions:
        - selector:
            apiVersion: controlplane.cluster.x-k8s.io/v1beta1
            kind: RKE2ControlPlaneTemplate
            matchResources:
              controlPlane: true  # Added to select ControlPlane
          jsonPatches:
            - op: replace
              path: "/spec/template/spec/serverConfig/tlsSan"
              valueFrom:
                variable: tlsSan
    - name: updateAdditionalUserData
      definitions:
        - selector:
            apiVersion: controlplane.cluster.x-k8s.io/v1beta1
            kind: RKE2ControlPlaneTemplate
            matchResources:
              controlPlane: true
          jsonPatches:
            - op: replace
              path: "/spec/template/spec/agentConfig/additionalUserData"
              valueFrom:
                template: |
                  config: |
                    variant: fcos
                    version: 1.4.0
                    storage:
                      files:
                        - path: /var/lib/rancher/rke2/server/manifests/endpoint-copier-operator.yaml
                          overwrite: true
                          contents:
                            inline: |
                              apiVersion: helm.cattle.io/v1
                              kind: HelmChart
                              metadata:
                                name: endpoint-copier-operator
                                namespace: kube-system
                              spec:
                                chart: oci://registry.suse.com/edge/charts/endpoint-copier-operator
                                targetNamespace: endpoint-copier-operator
                                version: 303.0.0+up0.2.1
                                createNamespace: true
                        - path: /var/lib/rancher/rke2/server/manifests/metallb.yaml
                          overwrite: true
                          contents:
                            inline: |
                              apiVersion: helm.cattle.io/v1
                              kind: HelmChart
                              metadata:
                                name: metallb
                                namespace: kube-system
                              spec:
                                chart: oci://registry.suse.com/edge/charts/metallb
                                targetNamespace: metallb-system
                                version: 303.0.0+up0.14.9
                                createNamespace: true
                        - path: /var/lib/rancher/rke2/server/manifests/metallb-cr.yaml
                          overwrite: true
                          contents:
                            inline: |
                              apiVersion: metallb.io/v1beta1
                              kind: IPAddressPool
                              metadata:
                                name: kubernetes-vip-ip-pool
                                namespace: metallb-system
                              spec:
                                addresses:
                                  - {{ .controlPlaneEndpointHost }}/32
                                serviceAllocation:
                                  priority: 100
                                  namespaces:
                                    - default
                                  serviceSelectors:
                                    - matchExpressions:
                                      - {key: "serviceType", operator: In, values: [kubernetes-vip]}
                              ---
                              apiVersion: metallb.io/v1beta1
                              kind: L2Advertisement
                              metadata:
                                name: ip-pool-l2-adv
                                namespace: metallb-system
                              spec:
                                ipAddressPools:
                                  - kubernetes-vip-ip-pool
                        - path: /var/lib/rancher/rke2/server/manifests/endpoint-svc.yaml
                          overwrite: true
                          contents:
                            inline: |
                              apiVersion: v1
                              kind: Service
                              metadata:
                                name: kubernetes-vip
                                namespace: default
                                labels:
                                  serviceType: kubernetes-vip
                              spec:
                                ports:
                                - name: rke2-api
                                  port: 9345
                                  protocol: TCP
                                  targetPort: 9345
                                - name: k8s-api
                                  port: 6443
                                  protocol: TCP
                                  targetPort: 6443
                                type: LoadBalancer
                    systemd:
                      units:
                        - name: rke2-preinstall.service
                          enabled: true
                          contents: |
                            [Unit]
                            Description=rke2-preinstall
                            Wants=network-online.target
                            Before=rke2-install.service
                            ConditionPathExists=!/run/cluster-api/bootstrap-success.complete
                            [Service]
                            Type=oneshot
                            User=root
                            ExecStartPre=/bin/sh -c "mount -L config-2 /mnt"
                            ExecStart=/bin/sh -c "sed -i \"s/BAREMETALHOST_UUID/$(jq -r .uuid /mnt/openstack/latest/meta_data.json)/\" /etc/rancher/rke2/config.yaml"
                            ExecStart=/bin/sh -c "echo \"node-name: $(jq -r .name /mnt/openstack/latest/meta_data.json)\" >> /etc/rancher/rke2/config.yaml"
                            ExecStartPost=/bin/sh -c "umount /mnt"
                            [Install]
                            WantedBy=multi-user.target

29.4.2 Definición de la instancia del clúster

En el contexto de ClusterClass, una instancia de clúster es una instancia concreta y en ejecución de un clúster creado a partir de una ClusterClass definida. Representa un despliegue concreto con sus configuraciones, recursos y estado operativo únicos, que se derivan directamente del modelo especificado en la ClusterClass. Esto incluye el conjunto específico de equipos, configuraciones de red y componentes de Kubernetes asociados que se están ejecutando activamente. Comprender la instancia del clúster es fundamental para gestionar su ciclo de vida, realizar actualizaciones, ejecutar operaciones de escalado y supervisar un clúster desplegado concreto que se ha aprovisionado utilizando el marco ClusterClass.

Para definir una instancia de clúster, debemos definir los siguientes recursos:

  • Cluster

  • Metal3MachineTemplate

  • Metal3DataTemplate

Las variables definidas previamente en la plantilla (archivo de definición de ClusterClass) se sustituirán por los valores finales para esta instanciación del clúster:

apiVersion: cluster.x-k8s.io/v1beta1
kind: Cluster
metadata:
  name: emea-spa-cluster-3
  namespace: emea-spa
spec:
  topology:
    class: example-clusterclass-type2  # Correct way to reference ClusterClass
    version: v1.32.4+rke2r1
    controlPlane:
      replicas: 1
    variables:                         # Variables to be replaced for this cluster instance
      - name: controlPlaneMachineTemplate
        value: emea-spa-cluster-3-machinetemplate
      - name: controlPlaneEndpointHost
        value: 192.168.122.203
      - name: tlsSan
        value:
          - 192.168.122.203
          - https://192.168.122.203.sslip.io
---
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: Metal3MachineTemplate
metadata:
  name: emea-spa-cluster-3-machinetemplate
  namespace: emea-spa
spec:
  nodeReuse: True
  template:
    spec:
      automatedCleaningMode: metadata
      dataTemplate:
        name: emea-spa-cluster-3
      hostSelector:
        matchLabels:
          cluster-role: control-plane
          deploy-region: emea-spa
          cluster-type: type2
      image:
        checksum: http://fileserver.local:8080/eibimage-downstream-cluster.raw.sha256
        checksumType: sha256
        format: raw
        url: http://fileserver.local:8080/eibimage-downstream-cluster.raw
---
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: Metal3DataTemplate
metadata:
  name: emea-spa-cluster-3
  namespace: emea-spa
spec:
  clusterName: emea-spa-cluster-3
  metaData:
    objectNames:
      - key: name
        object: machine
      - key: local-hostname
        object: machine

Este enfoque permite un proceso más ágil, ya que es posible desplegar un clúster con solo 3 recursos una vez que se ha definido ClusterClass.

Documentation survey