Kubernetes como Serviço Bare Metal com Sidero Metal e Talos

Gerenciar clusters Kubernetes em servidores bare metal pode ser um desafio, mas com as ferramentas certas, o processo se torna mais eficiente e automatizado. Este artigo explora como configurar 3 nós de plano de controle (master) e 4 máquinas de trabalho dinamicamente em servidores bare metal, utilizando Sidero Cluster API. Vamos mergulhar nos detalhes de como simular um cenário onde um data center (DC) fornece as máquinas e como inicializar o Talos pela rede com PXE & Sidero Cluster API para conectá-las.

Para simplificar a configuração, vamos usar o DHCP server, que não necessita de next-server boot e TFTP, pois já estão implementados no Sidero controller-manager, incluindo o DHCP proxy. Este guia prático oferece uma visão clara de como orquestrar seus clusters Kubernetes em ambientes bare metal de forma eficaz.

Configuração do Ambiente DHCP com Sidero

A versão 0.6 do Sidero já vem com o DHCP proxy, que aprimora o serviço DHCP do ambiente de rede com instruções de boot PXE automaticamente. A configuração é bem simples: basta configurar o servidor DHCP para atribuir IPs às máquinas. Veja como ficaria a configuração dnsmasq:

services:
  dnsmasq:
    image: quay.io/poseidon/dnsmasq:v0.5.0-32-g4327d60-amd64
    container_name: dnsmasq
    cap_add:
      - NET_ADMIN
    network_mode: host
    command: >
      -d -q -p0
      --dhcp-range=10.1.1.3,10.1.1.30
      --dhcp-option=option:router,10.1.1.1
      --log-queries
      --log-dhcp

Para rodar o Sidero, você precisa de um cluster Kubernetes de “gerenciamento”. Os requisitos são:

  • Kubernetes v1.26 ou posterior
  • Capacidade de expor serviços TCP e UDP para as máquinas do cluster de trabalho
  • Acesso ao cluster: como estamos implantando um cluster Talos de 1 nó, o acesso pode ser feito via talosctl kubeconfig

Criaremos um cluster de um nó com allowSchedulingOnControlPlanes: true, permitindo que a carga de trabalho seja executada nos nós do plano de controle.

k get node -o wide
NAME            STATUS   ROLES           AGE   VERSION   INTERNAL-IP   EXTERNAL-IP   OS-IMAGE         KERNEL-VERSION   CONTAINER-RUNTIME
talos-74m-2r5   Ready    control-plane   11m   v1.31.2   10.1.1.18     <none>        Talos (v1.8.3)   6.6.60-talos     containerd://2.0.0

Instalando o Sidero Cluster API

O Sidero já está incluso como um provedor de infraestrutura padrão no clusterctl. A instalação do Sidero e dos componentes da Sidero Cluster API (CAPI) é facilitada pelo uso da ferramenta clusterctl.

Primeiro, configuramos o Sidero para usar hostNetwork: true. Isso faz com que ele vincule suas portas diretamente ao host, em vez de estar disponível apenas de dentro do cluster. Existem várias maneiras de expor os serviços, mas esta é a mais simples para o cluster de gerenciamento de um nó. Ao escalar o cluster de gerenciamento, você precisará usar um método alternativo, como um balanceador de carga externo ou algo como MetalLB.

export SIDERO_CONTROLLER_MANAGER_HOST_NETWORK=true
export SIDERO_CONTROLLER_MANAGER_DEPLOYMENT_STRATEGY=Recreate
export SIDERO_CONTROLLER_MANAGER_API_ENDPOINT=10.1.1.18
export SIDERO_CONTROLLER_MANAGER_SIDEROLINK_ENDPOINT=10.1.1.18

clusterctl init -b talos -c talos -i sidero

k get po
NAMESPACE       NAME                                         READY   STATUS    RESTARTS      AGE
cabpt-system    cabpt-controller-manager-6b8b989d68-lwxbw    1/1     Running   0             39h
cacppt-system   cacppt-controller-manager-858fccc654-xzfds   1/1     Running   0             39h
capi-system     capi-controller-manager-564745d4b-hbh7x      1/1     Running   0             39h
cert-manager    cert-manager-5c887c889d-dflnl                1/1     Running   0             39h
cert-manager    cert-manager-cainjector-58f6855565-5wf5z     1/1     Running   0             39h
cert-manager    cert-manager-webhook-6647d6545d-k7qhf        1/1     Running   0             39h
sidero-system   caps-controller-manager-67f75b9cb-9z2fq      1/1     Running   0             39h
sidero-system   sidero-controller-manager-97cb45f57-v7cv2    4/4     Running   0             39h
curl -I http://10.1.1.18:8081/tftp/snp.efi
HTTP/1.1 200 OK
Accept-Ranges: bytes
Content-Length: 1020416
Content-Type: application/octet-stream

Gerenciando Ambientes com Metal Controller Manager

Os “ambientes” são um recurso personalizado fornecido pelo Metal Controller Manager. Eles codificam o que o servidor PXE deve retornar quando um servidor físico tenta fazer o boot via PXE.

Os ambientes podem ser fornecidos a um determinado servidor no nível do Servidor ou da Classe de Servidor. A hierarquia de respeito é:

.spec.environmentRef provided at Server level
.spec.environmentRef provided at ServerClass level
"default" Environment created automatically and modified by an administrator
kubectl edit environment default

apiVersion: metal.sidero.dev/v1alpha2
kind: Environment
metadata:
  creationTimestamp: "2024-11-23T13:16:12Z"
  generation: 1
  name: default
  resourceVersion: "6527"
  uid: 9e069ed5-886c-4b3c-9875-fe8e7f453dda
spec:
  initrd:
    url: https://github.com/siderolabs/talos/releases/download/v1.8.3/initramfs-amd64.xz
  kernel:
    args:
    - console=tty0
    - console=ttyS0
    - consoleblank=0
    - earlyprintk=ttyS0
    - ima_appraise=fix
    - ima_hash=sha512
    - ima_template=ima-ng
    - init_on_alloc=1
    - initrd=initramfs.xz
    - nvme_core.io_timeout=4294967295
    - printk.devkmsg=on
    - pti=on
    - slab_nomerge=
    - talos.platform=metal
    url: https://github.com/siderolabs/talos/releases/download/v1.8.3/vmlinuz-amd64

Servers e ServerClasses

Os Servers são o recurso básico de bare metal no Metal Controller Manager. Eles são criados por PXE booting dos servidores e permitindo que enviem uma solicitação de registro para o plano de gerenciamento.

As ServerClasses são uma forma de agrupar recursos de servidor distintos. Os qualificadores e as chaves de seleção permitem que o administrador especifique critérios para agrupar esses servidores.

Criaremos duas ServerClasses para os masters:

apiVersion: metal.sidero.dev/v1alpha1
kind: ServerClass
metadata:
  name: masters
spec:
  qualifiers:
    hardware:
      - system:
          manufacturer: QEMU
        memory:
          totalSize: "12 GB"
  configPatches:
    - op: add
      path: /machine/network/interfaces
      value:
         - deviceSelector:
             busPath: "0*"
           dhcp: true
           vip:
              ip: "10.1.1.50"
    - op: add
      path: /machine/network/nameservers
      value:
        - 1.1.1.1
        - 1.0.0.1
    - op: replace
      path: /machine/install
      value:
        disk: none
        diskSelector:
          size: '< 100GB'
    - op: replace
      path: /cluster/network/cni
      value:
        name: none
        # name: "custom"
        # urls:
        #   - "https://raw.githubusercontent.com/kubebn/talos-proxmox-kaas/main/manifests/talos/cilium.yaml"
    - op: replace
      path: /cluster/proxy
      value:
        disabled: true
    - op: replace
      path: /machine/kubelet/extraArgs
      value:
        rotate-server-certificates: true
    - op: replace
      path: /cluster/inlineManifests
      value:
        - name: cilium
          contents: |- 
            apiVersion: v1
            kind: Namespace
            metadata:
                name: cilium
                labels:
                  pod-security.kubernetes.io/enforce: "privileged"
    - op: replace
      path: /cluster/extraManifests
      value:
        - https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
        - https://raw.githubusercontent.com/alex1989hu/kubelet-serving-cert-approver/main/deploy/standalone-install.yaml

E para os workers:

apiVersion: metal.sidero.dev/v1alpha1
kind: ServerClass
metadata:
  name: workers
spec:
  qualifiers:
    hardware:
      - system:
          manufacturer: QEMU
        memory:
          totalSize: "19 GB"
  configPatches:
    - op: add
      path: /machine/network/interfaces
      value:
         - deviceSelector:
             busPath: "0*"
           dhcp: true
    - op: add
      path: /machine/network/nameservers
      value:
        - 1.1.1.1
        - 1.0.0.1
    - op: replace
      path: /machine/install
      value:
        disk: none
        diskSelector:
          size: '< 100GB'
    - op: replace
      path: /cluster/proxy
      value:
        disabled: true
    - op: replace
      path: /machine/kubelet/extraArgs
      value:
        rotate-server-certificates: true

Vamos inicializar as máquinas:

# 3 nodes with 12GB memory
for id in {105..107}; do
  qm create $id --name vm$id --memory 12288 --cores 3 --net0 virtio,bridge=vmbr0 --ostype l26 --scsihw virtio-scsi-pci --sata0 lvm1:32 --cpu host && qm start $id
done

# 4 nodes with 19GB memory
for id in {108..111}; do
  qm create $id --name vm$id --memory 20288 --cores 3 --net0 virtio,bridge=vmbr0 --ostype l26 --scsihw virtio-scsi-pci --sata0 lvm1:32 --cpu host && qm start $id
done

Na ServerClass, usamos a diferença entre as alocações de memória para os nós:

    hardware:
      - system:
          manufacturer: QEMU
        memory:
          totalSize: "12 GB" # masters
          ---
          totalSize: "19 GB" # workers
kubectl get serverclasses
NAME      AVAILABLE   IN USE   AGE
any       []          []       18m
masters   []          []       9s
workers   []          []       9s


kubectl get servers
NAME                                   HOSTNAME   ACCEPTED   CORDONED   ALLOCATED   CLEAN   POWER   AGE
13f56641-ff59-467c-94df-55a2861146d9   (none)     true                              true    on      96s
26f39da5-c622-42e0-b160-ff0eb58eb56b   (none)     true                              true    on      75s
4e56c769-8a35-4a68-b90b-0e1dca530fb0   (none)     true                              true    on      107s
5211912d-8f32-4ea4-8738-aaff57386391   (none)     true                              true    on      96s
a0b83613-faa9-468a-9289-1aa270117d54   (none)     true                              true    on      104s
a6c8afca-15b9-4254-82b4-91bbcd76dba0   (none)     true                              true    on      96s
ec39bf0e-632d-4dca-9ae0-0b3509368de6   (none)     true                              true    on      108s
f426397a-76ff-4ea6-815a-2be97265f5e6   (none)     true                              true    on      107s

Podemos descrever o servidor para ver outros detalhes:

kubectl get server 10ea52da-e1fc-4b83-81ef-b9cd40d1d25e -o yaml
apiVersion: metal.sidero.dev/v1alpha2
kind: Server
metadata:
creationTimestamp: "2024-11-25T04:26:35Z"
finalizers:
- storage.finalizers.server.k8s.io
generation: 1
name: 10ea52da-e1fc-4b83-81ef-b9cd40d1d25e
resourceVersion: "562154"
uid: 4c08c327-8aba-4af0-8204-5985b2a76e95
spec:
accepted: false
hardware:
compute:
processorCount: 1
processors:
- coreCount: 3
manufacturer: QEMU
productName: pc-i440fx-9.0
speed: 2000
threadCount: 3
totalCoreCount: 3
totalThreadCount: 3
memory:
moduleCount: 1
modules:
- manufacturer: QEMU
size: 12288
type: ROM
totalSize: 12 GB
network:
interfaceCount: 3
interfaces:
- flags: broadcast|multicast
index: 2
mac: 36:d0:4f:23:f7:03
mtu: 1500
name: bond0
- flags: broadcast
index: 3
mac: d6:9b:8b:99:6f:d0
mtu: 1500
name: dummy0
- addresses:
- 10.1.1.6/24
flags: up|broadcast|multicast
index: 8
mac: bc:24:11:a1:77:25
mtu: 1500
name: eth0
storage:
deviceCount: 1
devices:
- deviceName

Leave a Comment