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