29 Usando clusterclass para implantar clusters downstream #
29.1 Introdução #
O provisionamento de clusters Kubernetes é uma tarefa complexa que demanda profunda experiência na configuração de componentes de cluster. À medida que as configurações se tornam cada vez mais complexas, ou as demandas de diversos provedores geram inúmeras definições de recursos específicas do provedor, a criação de clusters pode ser um processo assustador. Felizmente, a Kubernetes Cluster API (CAPI) oferece uma abordagem declarativa e mais refinada que é aprimorada pelo ClusterClass. Esse recurso apresenta um modelo orientado por gabarito que permite definir uma classe de cluster reutilizável que encapsula a complexidade e promove a consistência.
29.2 O que é ClusterClass? #
O projeto CAPI lançou o recurso ClusterClass como uma mudança de paradigma no gerenciamento do ciclo de vida de clusters Kubernetes por meio da adoção de uma metodologia baseada em gabarito para criação de instâncias de cluster. Em vez de definir recursos separadamente para cada cluster, os usuários definem um ClusterClass, que serve como um diagrama de referência abrangente e reutilizável. Essa representação abstrata encapsula o estado e a configuração desejados de um cluster Kubernetes, o que permite a criação rápida e consistente de vários clusters que seguem as especificações definidas. Essa abstração reduz a sobrecarga de configuração, resultando em manifestos de implantação mais gerenciáveis. Isso significa que os componentes principais de um cluster de carga de trabalho são definidos no nível da classe, permitindo que os usuários recorram a esses gabaritos como variantes de cluster Kubernetes que podem ser reutilizadas uma ou várias vezes para provisionamento de clusters. A implementação do ClusterClass oferece diversas vantagens importantes que abordam os desafios inerentes ao gerenciamento tradicional da CAPI em escala:
Redução significativa na complexidade e detalhamento do YAML
Processos otimizados de manutenção e atualização
Consistência e padronização aprimoradas nas implantações
Escalabilidade e recursos de automação aprimorados
Gerenciamento declarativo e controle de versão robusto
29.3 Exemplo de arquivo de provisionamento da CAPI atual #
A implantação de um cluster Kubernetes por meio do provedor Cluster API (CAPI) e RKE2 requer a definição de vários recursos personalizados. Esses recursos definem o estado desejado do cluster e sua infraestrutura subjacente, o que permite à CAPI orquestrar o provisionamento e o ciclo de vida de gerenciamento. O trecho do código a seguir ilustra os tipos de recursos que devem ser configurados:
Cluster: esse recurso encapsula as configurações de alto nível, incluindo a topologia de rede que controla a comunicação entre os nós e a descoberta de serviços. Ele também estabelece vínculos essenciais com a especificação do plano de controle e o recurso do provedor de infraestrutura designado e, desse modo, informa a CAPI sobre a arquitetura desejada do cluster e a infraestrutura subjacente na qual ele deverá ser provisionado.
Metal3Cluster: esse recurso define os atributos no nível da infraestrutura exclusivos do Metal3, por exemplo, o endpoint externo pelo qual o servidor da API Kubernetes estará acessível.
RKE2ControlPlane: esse recurso define as características e o comportamento dos nós do plano de controle do cluster. Nessa especificação, os parâmetros, como o número desejado de réplicas do plano de controle (essencial para garantir alta disponibilidade e tolerância a falhas), a versão da distribuição Kubernetes específica (alinhada à versão do RKE2 selecionada) e a estratégia de distribuição das atualizações para os componentes do plano de controle são configurados. Além disso, esse recurso determina a interface de rede de contêiner (CNI, Container Network Interface) que será usada dentro do cluster e facilita a injeção das configurações específicas do agente, em geral aproveitando o Ignition para provisionamento contínuo e automatizado dos agentes do RKE2 nos nós do plano de controle.
Metal3MachineTemplate: esse recurso funciona como um diagrama de referência para criação das instâncias de computação individuais que formam os nós do worker do cluster Kubernetes definindo a imagem que será usada.
Metal3DataTemplate: como complemento ao Metal3MachineTemplate, o recurso Metal3DataTemplate permite especificar metadados adicionais para instâncias de máquina recém-provisionadas.
---
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 Transformando o arquivo de provisionamento da CAPI em ClusterClass #
29.4.1 Definição do ClusterClass #
O código a seguir define o recurso ClusterClass, um gabarito declarativo para implantação consistente de um tipo específico de cluster Kubernetes. Essa especificação inclui as configurações comuns de infraestrutura e de plano de controle, o que permite o provisionamento eficiente e o gerenciamento do ciclo de vida uniforme de toda a frota de clusters. Há algumas variáveis no exemplo abaixo do clusterclass que serão substituídas pelos valores reais durante o processo de criação de instância do cluster. As seguintes variáveis são usadas no exemplo:
controlPlaneMachineTemplate
: esse é o nome para definir a referência de gabarito da máquina do ControlPlane que será usadacontrolPlaneEndpointHost
: esse é o nome de host ou endereço IP do endpoint do plano de controletlsSan
: esse é o nome alternativo da entidade TLS para o endpoint do plano de controle
O arquivo de definição de clusterclass é definido com base nos três recursos a seguir:
ClusterClass: esse recurso encapsula a definição da classe do cluster inteira, incluindo os gabaritos de plano de controle e de infraestrutura. Além disso, ele inclui a lista das variáveis que serão substituídas durante o processo de criação de instâncias.
RKE2ControlPlaneTemplate: esse recurso define o gabarito do plano de controle, especificando a configuração desejada para os nós do plano de controle. Ele inclui os parâmetros, como número de réplicas, versão do Kubernetes e CNI, que serão usados. Alguns parâmetros também serão substituídos pelos valores corretos durante o processo de criação de instâncias.
Metal3ClusterTemplate: esse recurso define o gabarito de infraestrutura, especificando a configuração desejada da infraestrutura subjacente. Ele inclui parâmetros, como o endpoint do plano de controle e o sinalizador noCloudProvider. Alguns parâmetros também serão substituídos pelos valores corretos durante o processo de criação de instâncias.
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 Definição da instância do cluster #
No contexto do ClusterClass, uma instância de cluster se refere à criação de instância em execução específica de um cluster que foi criado com base no ClusterClass definido. Isso representa a implantação concreta com suas configurações, recursos e estado operacional exclusivos, derivados diretamente do diagrama de referência especificado no ClusterClass. Isso inclui o conjunto específico de máquinas, as configurações de rede e os componentes associados do Kubernetes que estão ativamente em execução. Saber o que é uma instância de cluster é essencial para gerenciar o ciclo de vida, fazer upgrades, executar operações de ajuste de escala e monitorar um determinado cluster implantado que foi provisionado usando a estrutura do ClusterClass.
Para definir uma instância de cluster, precisamos definir os seguintes recursos:
Cluster
Metal3MachineTemplate
Metal3DataTemplate
As variáveis definidas no gabarito (arquivo de definição de clusterclass) serão substituídas pelos valores finais para esta criação de instância do cluster:
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
Essa abordagem permite um processo mais ágil, implantando um cluster com apenas três recursos assim que você define o clusterclass.