Em um cluster Kubernetes bare-metal (on-premises), serviços do tipo LoadBalancer não funcionam nativamente como em provedores de nuvem. Para resolver isso, utilizaremos o MetalLB operando em modo BGP.
A arquitetura consiste em:
O gateway precisa de um daemon de roteamento para entender o protocolo BGP e distribuir o tráfego corretamente para os nós do Kubernetes.
Execute estes comandos no seu servidor Gateway (10.48.9.1):
# Atualizar listas de pacotes
sudo apt update
# Instalar pacotes básicos úteis para diagnóstico
sudo apt install vim wget htop tcpdump traceroute bind9-dnsutils bash-completion apt-transport-https ca-certificates curl gnupg lsb-release
# Adicionar a chave GPG do repositório FRR
curl -s https://deb.frrouting.org/frr/keys.gpg | sudo tee /usr/share/keyrings/frrouting.gpg > /dev/null
# Configurar o repositório para Debian 13 (trixie)
FRRVER="frr-stable"
echo deb '[signed-by=/usr/share/keyrings/frrouting.gpg]' https://deb.frrouting.org/frr \
$(lsb_release -s -c) $FRRVER | sudo tee -a /etc/apt/sources.list.d/frr.list
# Atualizar pacotes e instalar o FRR
sudo apt update
sudo apt install frr frr-pythontools
O FRR possui vários daemons (OSPF, RIP, BGP). Precisamos habilitar especificamente o bgpd.
# Alterar bgpd=no para bgpd=yes
sudo sed -i 's/bgpd=no/bgpd=yes/' /etc/frr/daemons
# Reiniciar o serviço para aplicar
sudo systemctl restart frr
sudo systemctl status frr
vtyshA configuração abaixo estabelece o Gateway (AS 64512) como peer remoto para todos os nós do Kubernetes (AS 64513). O parâmetro no bgp default ipv4-unicast garante que IPv4 e IPv6 sejam configurados de forma independente.
Acesse o shell interativo do FRR:
sudo vtysh
Aplique a configuração:
configure terminal
! Route-map permissivo (necessário com frr defaults traditional)
route-map PERMIT_ALL permit 10
! Configuração do roteador BGP (Gateway AS 64512)
router bgp 64512
bgp router-id 10.48.9.1
no bgp default ipv4-unicast
! Configuração do Peer Group para os nós do Kubernetes (AS 64513)
neighbor KUBE_NODES peer-group
neighbor KUBE_NODES remote-as 64513
neighbor KUBE_NODES description "Nos do Cluster Kubernetes"
! Associação dos nós (IPv4) ao Peer Group
neighbor 10.48.9.2 peer-group KUBE_NODES
neighbor 10.48.9.3 peer-group KUBE_NODES
neighbor 10.48.9.4 peer-group KUBE_NODES
neighbor 10.48.9.20 peer-group KUBE_NODES
neighbor 10.48.9.21 peer-group KUBE_NODES
neighbor 10.48.9.22 peer-group KUBE_NODES
! Associação dos nós (IPv6) ao Peer Group
neighbor fd00:0:b:9::2 peer-group KUBE_NODES
neighbor fd00:0:b:9::3 peer-group KUBE_NODES
neighbor fd00:0:b:9::4 peer-group KUBE_NODES
neighbor fd00:0:b:9::14 peer-group KUBE_NODES
neighbor fd00:0:b:9::15 peer-group KUBE_NODES
neighbor fd00:0:b:9::16 peer-group KUBE_NODES
! Ativação da família IPv4
address-family ipv4 unicast
neighbor KUBE_NODES activate
neighbor KUBE_NODES route-map PERMIT_ALL in
neighbor KUBE_NODES route-map PERMIT_ALL out
! Anunciar a rede do pool do MetalLB
network 10.48.11.0/24
exit-address-family
! Ativação da família IPv6
address-family ipv6 unicast
neighbor KUBE_NODES activate
neighbor KUBE_NODES route-map PERMIT_ALL in
neighbor KUBE_NODES route-map PERMIT_ALL out
! Anunciar a rede do pool do MetalLB
network fd00:0:b:b::/64
exit-address-family
exit
exit
write memory
Reinicie o FRR para garantir que as configurações entrem em vigor:
sudo systemctl restart frr
sudo systemctl enable --now frr
Execute os comandos a partir de um nó com kubectl configurado (ex: kube-ctrl-01).
Instalaremos o MetalLB ativando o backend nativo do FRR, que oferece suporte BGP superior.
# Baixar e instalar o Helm (se não tiver)
curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-4 | bash
# Adicionar repositório do MetalLB
helm repo add metallb https://metallb.github.io/metallb
helm repo update
# Criar arquivo de valores para habilitar o FRR
cat > metallb-values.yaml << 'EOF'
speaker:
frr:
enabled: true
EOF
# Instalar o MetalLB
helm install metallb metallb/metallb \
--version 0.15.3 \
--namespace metallb-system \
--create-namespace \
-f metallb-values.yaml
Aguarde até que os pods (controller e speakers) estejam em execução:
kubectl get pods -n metallb-system
Esta configuração diz ao MetalLB quais IPs ele pode distribuir (IPAddressPool) e para quem ele deve anunciar as rotas (BGPPeer).
cat > metallb-bgp-config.yaml << 'EOF'
---
# 1. Pool de Endereços Dual-Stack (Rede externa ao cluster)
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: dualstack-pool
namespace: metallb-system
spec:
addresses:
- 10.48.11.0/24
- fd00:0:b:b::/64
autoAssign: true
---
# 2. Configuração do Peer BGP IPv4 (Apontando para o Gateway)
apiVersion: metallb.io/v1beta2
kind: BGPPeer
metadata:
name: gateway-ipv4
namespace: metallb-system
spec:
myASN: 64513
peerASN: 64512
peerAddress: 10.48.9.1
---
# 3. Configuração do Peer BGP IPv6 (Apontando para o Gateway)
apiVersion: metallb.io/v1beta2
kind: BGPPeer
metadata:
name: gateway-ipv6
namespace: metallb-system
spec:
myASN: 64513
peerASN: 64512
peerAddress: fd00:0:b:9::1
---
# 4. Anúncio BGP das rotas do pool
apiVersion: metallb.io/v1beta1
kind: BGPAdvertisement
metadata:
name: bgp-advertisement
namespace: metallb-system
spec:
ipAddressPools:
- dualstack-pool
EOF
kubectl apply -f metallb-bgp-config.yaml
Vamos criar um Nginx temporário para validar se ele recebe IPs IPv4 e IPv6.
kubectl create deployment nginx-test --image=nginx
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
name: nginx-test
spec:
selector:
app: nginx-test
ports:
- port: 80
targetPort: 80
type: LoadBalancer
ipFamilies:
- IPv4
- IPv6
ipFamilyPolicy: PreferDualStack
EOF
Verifique se o serviço recebeu IPs de ambos os pools:
kubectl get svc nginx-test
# EXTERNAL-IP deve mostrar algo como: 10.48.11.0,fd00:0:b:b::
Acesse a aplicação (do Gateway ou máquina na mesma rede):
curl http://10.48.11.0
Se responder com o HTML do Nginx, o BGP e o MetalLB estão funcionando perfeitamente! Remova o teste:
kubectl delete deployment nginx-test
kubectl delete svc nginx-test
A Gateway API é o sucessor moderno do Ingress. O Envoy Gateway atuará como o controlador, utilizando os IPs fornecidos pelo MetalLB para expor serviços.
helm install eg oci://docker.io/envoyproxy/gateway-helm \
--version v1.7.1 \
-n envoy-gateway-system \
--create-namespace
Aguarde o controlador ficar disponível:
kubectl wait --timeout=5m -n envoy-gateway-system deployment/envoy-gateway --for=condition=Available
Por padrão, o Envoy pode solicitar apenas IPs IPv4. Vamos forçá-lo a solicitar IPs Dual-Stack (IPv4 + IPv6) do MetalLB.
cat > envoy-gateway-config.yaml << 'EOF'
# Define o comportamento do Proxy (DualStack)
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyProxy
metadata:
name: dual-stack-proxy
namespace: envoy-gateway-system
spec:
ipFamily: DualStack
---
# Cria a classe de Gateway vinculada ao proxy acima
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: envoy-gateway-class
spec:
controllerName: gateway.envoyproxy.io/gatewayclass-controller
parametersRef:
group: gateway.envoyproxy.io
kind: EnvoyProxy
name: dual-stack-proxy
namespace: envoy-gateway-system
EOF
kubectl apply -f envoy-gateway-config.yaml
Valide a criação:
kubectl get GatewayClass
# ACCEPTED deve ser "True"
Se as rotas não estiverem sendo anunciadas, use estes comandos para diagnóstico.
vtysh)! Status geral das sessões BGP (devem mostrar números em MsgRcvd/MsgSent)
show bgp summary
show bgp ipv4 unicast summary
show bgp ipv6 unicast summary
! Tabela de rotas recebidas do MetalLB
show bgp ipv4 unicast
show bgp ipv6 unicast
! Detalhes de um peer específico (ex: control plane 1)
show bgp neighbors 10.48.9.2
# Verificar status dos pods do MetalLB
kubectl get pods -n metallb-system -o wide
# Verificar configuração BGP aplicada
kubectl get ipaddresspool,bgpadvertisement -n metallb-system
kubectl get bgppeer.metallb.io -A
# Ler logs do daemon FRR dentro do speaker do MetalLB
kubectl logs -n metallb-system -l app.kubernetes.io/component=speaker -c frr --since=5m
# Verificar se a Gateway API instalou seus CRDs
kubectl get crd | grep gateway