Este guia implementa um cluster Kubernetes altamente disponível com 3 nós control plane (mestres) e 3 nós workers, totalizando 6 máquinas virtuais. A alta disponibilidade é alcançada através de:
kube-lb) executa HAProxy em modo TCP puro, distribuindo conexões para a API Kubernetes entre os 3 control planes sem encerrar TLS. ┌─────────────────┐
│ kube-lb │
Clientes ───▶ │ 10.48.9.100 │ HAProxy (TCP passthrough :6443)
│ HAProxy :6443 │
└────────┬────────┘
│ roundrobin
┌──────────────────┼──────────────────┐
│ │ │
┌────────▼──────┐ ┌────────▼──────┐ ┌────────▼──────┐
│ kube-ctrl-01 │ │ kube-ctrl-02 │ │ kube-ctrl-03 │
│ 10.48.9.2 │ │ 10.48.9.3 │ │ 10.48.9.4 │
│ API :6443 │ │ API :6443 │ │ API :6443 │
│ etcd :2379 │◀─▶ etcd :2379 │◀─▶ etcd :2379 │
└───────┬───────┘ └───────┬───────┘ └───────┬───────┘
│ │ │
┌───────▼──────────────────▼──────────────────▼───────┐
│ kube-worker-01 / 02 / 03 │
│ 10.48.9.20 / 10.48.9.21 / 10.48.9.22 │
└──────────────────────────────────────────────────────┘
etcd Stacked (Embutido): O etcd não é executado em nós dedicados. Cada control plane sobe seu próprio membro etcd, formando um cluster de quórum com 3 membros. Com 3 membros, o cluster tolera a falha de 1 membro (⌊3/2⌋ = 1) e continua operacional. Nunca execute um cluster etcd com número par de membros.
HAProxy — TCP Passthrough: O HAProxy opera em modo TCP (mode tcp) na porta 6443. Ele não encerra o TLS — apenas repassa os pacotes ao control plane de destino. Isso significa que o certificado da API Server é validado diretamente pelo cliente, sem intermediário. O controlPlaneEndpoint (api.cluster.geanmartins.net:6443) aponta para o IP do kube-lb (10.48.9.100 | fd00:09::63), que é o único endereço que kubeadm, kubectl e os workers precisam conhecer para falar com a API.
| Hostname | vCPU | RAM | IPv4 | IPv6 | Função |
|---|---|---|---|---|---|
| kube-lb | 2 | 1G | 10.48.9.100 | fd00:0 |
HAProxy load balancer |
| kube-ctrl-01 | 2 | 4G | 10.48.9.2 | fd00:0 |
Control plane 1 |
| kube-ctrl-02 | 2 | 4G | 10.48.9.3 | fd00:0 |
Control plane 2 |
| kube-ctrl-03 | 2 | 4G | 10.48.9.4 | fd00:0 |
Control plane 3 |
| kube-worker-01 | 4 | 8G | 10.48.9.20 | fd00:0 |
Worker 1 |
| kube-worker-02 | 4 | 8G | 10.48.9.21 | fd00:0 |
Worker 2 |
| kube-worker-03 | 4 | 8G | 10.48.9.22 | fd00:0 |
Worker 3 |
Configurações Comuns:
enp1s0 em todos os nós/dev/vdb (32 GB) em todos os control planes e workers → montado em /var/lib/containers (para armazenar pods do CRI-O)| Rede | CIDR IPv4 | CIDR IPv6 | Propósito |
|---|---|---|---|
| MetalLB | 10.48.11.0/24 | fd00:0 |
IPs de serviços LoadBalancer (Ingress) |
| Services | 10.49.0.0/16 | fd00:0 |
ClusterIP de serviços Kubernetes |
| Pods | 10.51.0.0/16 | fd00:0 |
IPs dos pods (Calico) |
| Registro | Destino IPv4 | Destino IPv6 |
|---|---|---|
| api.cluster.geanmartins.net | 10.48.9.100 | fd00:0 |
| kube-lb.geanmartins.net | 10.48.9.100 | fd00:0 |
| kube-ctrl-01.geanmartins.net | 10.48.9.2 | fd00:0 |
| kube-ctrl-02.geanmartins.net | 10.48.9.3 | fd00:0 |
| kube-ctrl-03.geanmartins.net | 10.48.9.4 | fd00:0 |
| kube-worker-01.geanmartins.net | 10.48.9.20 | fd00:0 |
| kube-worker-02.geanmartins.net | 10.48.9.21 | fd00:0 |
| kube-worker-03.geanmartins.net | 10.48.9.22 | fd00:0 |
O Kubernetes e o etcd dependem de relógios perfeitamente sincronizados. Qualquer desvio significativo pode causar problemas de autenticação e consenso.
Implemente em todos os 7 nós (incluindo kube-lb):
sudo apt update
sudo apt install systemd-timesyncd
sudo systemctl enable --now systemd-timesyncd
Edite o arquivo /etc/systemd/timesyncd.conf:
[Time]
NTP=10.48.1.1
FallbackNTP=
Nota: O IP
10.48.1.1é o Gateway (servidor NTP). Ajuste conforme sua infraestrutura.
Reinicie e valide:
sudo systemctl restart systemd-timesyncd
timedatectl timesync-status
timedatectl
kube-lb)O kube-lb é um nó dedicado que executa HAProxy para distribuir tráfego da API Kubernetes entre os 3 control planes.
sudo apt install haproxy
/etc/haproxy/haproxy.cfgFaça backup da configuração original:
sudo cp -a /etc/haproxy/haproxy.cfg{,.dist-$(date +%Y%m%d)}
Substitua o arquivo com a seguinte configuração:
global
log /dev/log local0
log /dev/log local1 notice
daemon
defaults
log global
mode tcp
option tcplog
option dontlognull
timeout connect 5000
timeout client 30000
timeout server 30000
retries 3
option redispatch
# Resolvedor DNS para descoberta dinâmica dos control planes
resolvers dns
nameserver dns1 10.48.1.2:53
resolve_retries 3
timeout resolve 1s
timeout retry 1s
hold valid 10s
hold obsolete 30s
hold nx 30s
hold timeout 30s
# Frontend: Escuta nas portas 6443 (IPv4 e IPv6)
frontend kubernetes-frontend
bind 10.48.9.100:6443
bind [fd00:0:b:9::63]:6443
default_backend kubernetes-backend
# Backend: Distribui entre os 3 control planes
backend kubernetes-backend
option tcp-check
tcp-check connect port 6443
balance roundrobin
server kube-ctrl-01 kube-ctrl-01.geanmartins.net:6443 check inter 5s fall 3 rise 2 resolvers dns
server kube-ctrl-02 kube-ctrl-02.geanmartins.net:6443 check inter 5s fall 3 rise 2 resolvers dns
server kube-ctrl-03 kube-ctrl-03.geanmartins.net:6443 check inter 5s fall 3 rise 2 resolvers dns
Explicação dos Parâmetros:
mode tcp: Modo TCP puro (não HTTP). Necessário para TLS passthrough.balance roundrobin: Distribui conexões sequencialmente entre os backends.check inter 5s: Verifica saúde a cada 5 segundos.fall 3: Marca como DOWN após 3 verificações falhadas.rise 2: Marca como UP após 2 verificações bem-sucedidas.resolvers dns: Usa DNS para descobrir IPs dos control planes dinamicamente.sudo systemctl enable haproxy --now
sudo systemctl restart haproxy
sudo systemctl status haproxy
Execute as seções abaixo em todos os 6 nós (3 control planes + 3 workers) usando o script TMUX para sincronização:
multi-ssh-tmux --sync \
suporte@10.48.9.2 suporte@10.48.9.3 suporte@10.48.9.4 \
suporte@10.48.9.20 suporte@10.48.9.21 suporte@10.48.9.22
O CRI-O (Container Runtime Interface) armazena imagens e containers em /var/lib/containers. Para melhor performance e isolamento, usaremos o disco extra (/dev/vdb).
MOUNT_POINT=/var/lib/containers
DISK_DEVICE=/dev/vdb
Particionar e formatar:
sudo apt install parted
sudo parted -s ${DISK_DEVICE} mklabel gpt
sudo parted -s ${DISK_DEVICE} mkpart primary ext4 0% 100%
sudo mkfs.ext4 ${DISK_DEVICE}1
Obter o UUID e adicionar ao /etc/fstab:
UUID=$(sudo blkid -o export ${DISK_DEVICE}1 | grep '^UUID=')
sudo mkdir -p ${MOUNT_POINT}
sudo cp -p /etc/fstab{,.dist}
echo "${UUID} ${MOUNT_POINT} ext4 defaults 0 2" | sudo tee -a /etc/fstab
Montar a partição:
sudo systemctl daemon-reload
sudo mount ${MOUNT_POINT}
df -hT | grep containers
O CRI-O é o container runtime que o Kubernetes usa para executar pods.
Definir versões:
KUBERNETES_VERSION=v1.35
CRIO_VERSION=v1.35
Pré-requisitos do Kernel:
Carregar módulos necessários para networking de containers:
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF
sudo modprobe overlay
sudo modprobe br_netfilter
Configurar parâmetros de rede:
cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
net.ipv6.conf.all.forwarding = 1
EOF
sudo sysctl --system
Instalar CRI-O:
sudo apt-get update
sudo apt-get install apt-transport-https ca-certificates curl gnupg jq vim git gpg
curl -fsSL https://download.opensuse.org/repositories/isv:/cri-o:/stable:/$CRIO_VERSION/deb/Release.key |
sudo gpg --dearmor -o /etc/apt/keyrings/cri-o-apt-keyring.gpg
echo "deb [signed-by=/etc/apt/keyrings/cri-o-apt-keyring.gpg] https://download.opensuse.org/repositories/isv:/cri-o:/stable:/$CRIO_VERSION/deb/ /" |
sudo tee /etc/apt/sources.list.d/cri-o.list
sudo apt update
sudo apt install crio
Habilitar e Iniciar:
sudo systemctl enable crio --now
sudo systemctl status crio
Desativar Swap:
O kubelet rejeita inicialização se a swap estiver ativa. Nossas imagens Packer já foram criadas sem swap, mas se necessário:
sudo swapoff -a
sudo sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab
Adicionar Repositório do Kubernetes:
curl -fsSL https://pkgs.k8s.io/core:/stable:/$KUBERNETES_VERSION/deb/Release.key |
sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/$KUBERNETES_VERSION/deb/ /" |
sudo tee /etc/apt/sources.list.d/kubernetes.list
Instalar Pacotes:
sudo apt-get update
sudo apt-get install kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl
Nota:
apt-mark holdprevine atualizações acidentais desses pacotes críticos.
Habilitar kubelet:
sudo systemctl enable kubelet --now
Nota: O kubelet ficará em loop de reinicialização até que o kubeadm inicialize o nó — esse comportamento é esperado.