Controlando onde seus pods rodam juntos com affinity e anti-affinity
Tabela de Conteúdo
Neste post, vamos configurar regras para controlar exatamente onde nossos pods devem rodar, garantindo alta disponibilidade e performance.
Vamos entender os conceitos
A ideia é simples:
- node affinity = controlamos em qual nó o pod roda (hardware, zona)
- pod affinity = colocamos pods próximos de outros pods
- pod anti-affinity = colocamos pods longe de outros pods
required vs preferred
- required: pod NÃO escala se regra não for atendida (use com cautela)
- preferred: scheduler tenta atender mas escala mesmo se não conseguir
Dica: sempre comece com preferred e mude para required apenas se necessário.
Vamos configurar: node affinity para hardware específico
Vamos criar um deployment que só rode em nós com GPU:
apiVersion: apps/v1
kind: Deployment
metadata:
name: ml-training
spec:
replicas: 2
selector:
matchLabels:
app: ml
template:
metadata:
labels:
app: ml
spec:
affinity:
nodeAffinity:
# Preferência: tenta usar instâncias específicas
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
preference:
matchExpressions:
- key: node.kubernetes.io/instance-type
operator: In
values:
- g5.xlarge # instância com GPU
# Requisito obrigatório: só roda em nós com GPU
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: accelerator
operator: In
values:
- nvidia-gpu
containers:
- name: ml
image: tensorflow/tensorflow:latest-gpu
resources:
requests:
memory: "4Gi"
cpu: "2000m"
limits:
memory: "8Gi"
cpu: "4000m"
O que configuramos aqui?
- preferred: tentamos usar instâncias g5.xlarge (peso 100)
- required: obrigatório ter nvidia-gpu
- resources: reservamos recursos adequados para ML
Vamos configurar: pod affinity para performance
Agora vamos colocar nossa API perto do banco de dados:
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-com-banco
spec:
replicas: 3
selector:
matchLabels:
app: api
template:
metadata:
labels:
app: api
spec:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- database
topologyKey: "kubernetes.io/hostname"
containers:
- name: api
image: nginx:1.27
resources:
requests:
memory: "256Mi"
cpu: "200m"
limits:
memory: "512Mi"
cpu: "500m"
O que isso faz?
- podAffinity: API só roda na mesma máquina que o banco
- topologyKey: kubernetes.io/hostname = mesma máquina física
- required: obrigatório estar perto do banco
Vamos configurar: pod anti-affinity para alta disponibilidade
Vamos espalhar nossos web servers para evitar single point of failure:
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-servers
spec:
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- web
topologyKey: "kubernetes.io/hostname"
containers:
- name: web
image: nginx:1.27
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "200m"
O que isso garante?
- podAntiAffinity: prefere não colocar web servers no mesmo nó
- preferred: se não tiver nós suficientes, escala mesmo assim
- weight: 100: força máxima da preferência
Vamos testar nossas configurações
Agora que você entendeu o conceito, vamos ao código para testar:
# Aplicar nossos deployments
kubectl apply -f ml-training.yaml
kubectl apply -f api-com-banco.yaml
kubectl apply -f web-servers.yaml
# Verificar onde os pods estão rodando
kubectl get pods -o wide
# Verificar distribuição por nó
kubectl get pods -o custom-columns=NAME:.metadata.name,NODE:.spec.nodeName
# Ver eventos de scheduling
kubectl get events --sort-by=.lastTimestamp | grep "FailedScheduling"
# Descrever um pod para ver as regras de affinity
kubectl describe pod <pod-name> | grep -A 10 "Affinity"
Boas práticas que aprendemos
Para alta disponibilidade
- Use pod anti-affinity para aplicações críticas
- Combine com PodDisruptionBudgets para manutenções
- Monitore a distribuição dos pós regularmente
Para performance
- Use pod affinity para comunicação rápida
- Considere node affinity para hardware específico
- Monitore latência de rede entre nós/zonas
Para segurança e compliance
- Use node affinity para isolar workloads
- Combine com taints e tolerations
- Implemente network policies adicionalmente
Para custos
- Use node affinity para workloads em nós spot
- Combine com cluster autoscaler
- Monitore utilização dos recursos
Troubleshooting rápido
Se os pods ficam pendentes:
# Ver detalhes do pod
kubectl describe pod <pod-name>
# Ver eventos recentes
kubectl get events --sort-by=.lastTimestamp
# Ver se há nós disponíveis
kubectl get nodes -o wide
# Ver recursos disponíveis
kubectl top nodes
Problemas comuns que podemos encontrar:
- Affinity muito restritiva: use
preferredem vez derequired - Labels incorretas: verifique se os pods alvo existem
- Recursos insuficientes: monitore uso de CPU/memória
Se você quiser entender melhor como recursos afetam scheduling, dá uma olhada neste post: Kubernetes requests vs limits: como isso afeta scheduling e throttling.
Simples assim! :)