Use o MetalLB em modo Layer 2 para dar IPs públicos do seu bloco /28 a Services LoadBalancer no k3s, com IP fixo por serviço e failover entre nós.
Para que serve
Em nuvem pública, um Service do tipo LoadBalancer ganha um IP externo automaticamente. Em servidor dedicado não existe esse serviço, e o Service fica eternamente em <pending>. O MetalLB resolve isso: ele entrega aos Services os IPs públicos do seu bloco /28 e responde ARP por eles no nó escolhido, como se o IP estivesse configurado ali. Assim você pode ter, por exemplo, um IP para o Ingress (sites), outro para um servidor de jogos e outro para um banco exposto só a parceiros.
Pré-requisitos
- Cluster k3s com nós em VMs no Proxmox ligadas em modo bridge à
vmbr0(veja Configurar a rede do Proxmox para as VMs). Cada VM precisa estar no mesmo segmento do gateway do bloco. - IPs livres do bloco reservados para o MetalLB. Nos exemplos: bloco
203.0.113.0/28, gateway.1, Proxmox.2, VMs do cluster.3a.5e IPs para o MetalLB de.10a.13. Troque pelos seus. - Acesso ao console IPMI/iLO, caso um conflito de IP derrube o acesso a alguma VM.
kubectl -n metallb-system delete ipaddresspool --all a partir de um nó do cluster.Como o modo Layer 2 funciona
Para cada IP, o MetalLB elege um nó "líder" que passa a responder ARP com o seu MAC. O roteador do datacenter entrega o tráfego àquele nó, e o kube-proxy encaminha aos pods. Se o nó cair, outro assume em poucos segundos e anuncia a mudança com ARP gratuito. Consequências práticas:
- Não é balanceamento entre nós: todo o tráfego de um IP entra por um nó só (limite de 1 Gbps do uplink, de qualquer forma).
- O failover depende de o equipamento vizinho atualizar a tabela ARP. Teste antes de depender disso.
Etapa 1: desativar o ServiceLB do k3s
O k3s traz o ServiceLB (Klipper), que usa os IPs dos próprios nós e conflita com o MetalLB. Em todos os servidores, edite /etc/rancher/k3s/config.yaml:
disable:
- servicelb
Depois, um servidor por vez: systemctl restart k3s. O Service do Traefik ficará em <pending> até o MetalLB estar pronto; os sites ficam fora do ar nesse intervalo, então faça em janela de manutenção.
Etapa 2: instalar o MetalLB
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.16.1/config/manifests/metallb-native.yaml
kubectl -n metallb-system wait --for=condition=ready pod --all --timeout=180s
Ou, pelo Helm: helm repo add metallb https://metallb.github.io/metallb e helm install metallb metallb/metallb -n metallb-system --create-namespace. Confira a versão atual na documentação. O k3s usa kube-proxy em modo iptables, então o ajuste strictARP (necessário só no modo IPVS) não se aplica.
Etapa 3: criar o pool com os IPs do bloco
Salve como metallb-pool.yaml:
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: publicos
namespace: metallb-system
spec:
addresses:
- 203.0.113.10-203.0.113.13
autoAssign: false
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
name: publicos-l2
namespace: metallb-system
spec:
ipAddressPools:
- publicos
interfaces:
- ens18
Com autoAssign: false, nenhum Service pega um IP público por acidente: é preciso pedir explicitamente. O campo interfaces limita os anúncios à placa ligada na vmbr0 (confira o nome com ip -br addr), evitando ARP pela rede privada do cluster. Aplique com kubectl apply -f metallb-pool.yaml.
Etapa 4: dar um IP fixo ao Traefik e a outros serviços
Para o Traefik do k3s, crie (ou complemente) o HelmChartConfig em /var/lib/rancher/k3s/server/manifests/traefik-config.yaml:
apiVersion: helm.cattle.io/v1
kind: HelmChartConfig
metadata:
name: traefik
namespace: kube-system
spec:
valuesContent: |-
service:
annotations:
metallb.io/loadBalancerIPs: 203.0.113.10
# mantenha aqui também outras opções que você já usa, como additionalArguments
Para um serviço qualquer, use a mesma anotação:
apiVersion: v1
kind: Service
metadata:
name: jogo
annotations:
metallb.io/loadBalancerIPs: 203.0.113.11
spec:
type: LoadBalancer
externalTrafficPolicy: Local
selector:
app: jogo
ports:
- name: game
port: 27015
protocol: UDP
O externalTrafficPolicy: Local preserva o IP real do cliente (importante para logs, bloqueios e jogos), mas só entrega tráfego aos pods que estão no nó líder; o MetalLB escolhe como líder um nó que tenha pod do serviço.
Como saber se funcionou
kubectl get svc -A | grep LoadBalancer
kubectl -n metallb-system logs -l component=speaker --tail=50
# de fora do servidor (seu computador):
ping -c3 203.0.113.10
curl -I http://203.0.113.10
A coluna EXTERNAL-IP deve mostrar o IP pedido. Para testar o failover, desligue a VM líder (os logs do speaker mostram quem anuncia cada IP) e veja quanto tempo o IP leva para voltar a responder.
Problemas comuns
Service continua em pending
Veja kubectl describe svc NOME. Causas: pool com autoAssign: false e Service sem anotação, IP pedido fora do pool, IP já usado por outro Service ou ServiceLB do k3s ainda ativo.
IP aparece no Service mas não responde de fora
Confira se a interface do L2Advertisement está certa e se o firewall da VM ou do Proxmox permite o tráfego. Se a rede do Proxmox estiver em modo roteado em vez de bridge, o roteador do datacenter não enxerga o ARP das VMs; nesse caso o host precisa rotear cada IP do MetalLB para a VM, e a solução fica bem mais trabalhosa.
Failover demora minutos
O equipamento vizinho está ignorando o ARP gratuito e esperando o cache expirar. Registre o horário do teste e abra um ticket para avaliarmos.
Perguntas frequentes
Posso usar o modo BGP?
O BGP exige uma sessão com o roteador do datacenter, que não faz parte da entrega padrão. Para servidores dedicados, use o modo Layer 2.
Funciona com IPv6?
Sim. O MetalLB responde NDP para endereços IPv6; inclua uma faixa do seu /56 no pool e configure o cluster em pilha dupla (dual-stack).
Preciso de MetalLB com um nó só?
Não necessariamente. O ServiceLB do k3s já publica os serviços no IP do nó. O MetalLB vale a pena quando você quer IPs diferentes por serviço ou failover entre nós.
Leitura complementar
- MetalLB: conceitos do modo Layer 2
- MetalLB: uso, anotações e compartilhamento de IP
- k3s: ServiceLB e como desativá-lo
Precisa de ajuda?
Se precisar confirmar quais IPs do bloco estão livres ou suspeitar de conflito de IP, abra um ticket com a lista de IPs em uso e a saída de kubectl get svc -A.
