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

Gerenciar um cluster Kubernetes em bare metal pode parecer complicado, mas com as ferramentas certas, o processo se torna mais simples e eficiente. Este artigo explica como criar uma configuração com três nós de plano de controle (master) e quatro máquinas de trabalho, tudo dinamicamente em servidores bare metal. A ideia é simular um cenário onde um data center fornece as máquinas físicas, utilizando Talos para inicialização via rede com PXE e a Sidero Cluster API para conectar tudo. Vamos descobrir como essa abordagem pode facilitar a vida dos administradores de sistemas.

Preparando o Ambiente com DHCP e Sidero

Para começar, vamos configurar um servidor DHCP. No entanto, não será necessário um servidor de inicialização next-server e TFTP, pois o controller-manager da Sidero já implementa isso, incluindo um DHCP proxy. A versão 0.6 da Sidero já vem com esse DHCP proxy, que facilita a atribuição de IPs às máquinas, integrando automaticamente as instruções de boot PXE. A configuração do dnsmasq ficaria assim:

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 a Sidero, é preciso primeiro um Management cluster do Kubernetes. Ele precisa ser a versão 1.26 ou superior, com capacidade de expor serviços TCP e UDP para as máquinas do workload cluster e acesso via talosctl kubeconfig.

Criaremos um cluster de um nó com allowSchedulingOnControlPlanes: true, o que permite rodar o workload nos nós do plano de controle. Veja como o nó aparece:

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

Com a Sidero inclusa como provedora de infraestrutura padrão no clusterctl, instalar tanto a Sidero quanto os componentes da Cluster API (CAPI) se resume a usar a ferramenta clusterctl.

Primeiro, dizemos para a Sidero usar hostNetwork: true para que ela vincule suas portas diretamente ao host, em vez de estar disponível só de dentro do cluster. Há várias formas de expor os serviços, mas essa é a mais simples para o management cluster de um nó. Ao escalar o management cluster, será preciso usar outro método, como um load balancer 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 o Ambiente e Classes de Servidores

Os Environments são um recurso personalizado fornecido pelo Metal Controller Manager. Um environment é uma descrição codificada do que o servidor PXE deve retornar quando um servidor físico tenta fazer o boot via PXE.

Os environments podem ser fornecidos a um determinado servidor no nível do Server ou do ServerClass. 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 são o recurso básico de bare metal no Metal Controller Manager. Eles são criados fazendo o boot PXE dos servidores e permitindo que enviem uma requisição de registro ao management plane.
Server classes são uma forma de agrupar recursos de servidor distintos. Os qualificadores e as chaves de seleção permitem que o administrador especifique os critérios para agrupar esses servidores.

Criaremos duas ServerClasses para 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: /dev/sda
productName: QEMU HARDDISK
size: 34359738368
type: HDD
wwid: t10.ATA QEMU HARDDISK QM00005
totalSize: 32 GB
system:
manufacturer: QEMU
productName: Relacionado

Leave a Comment