<h2>Pod Disruption Budgets (PDB): Garantindo Alta Disponibilidade</h2>
<p>Pod Disruption Budgets é um recurso nativo do Kubernetes que define o número mínimo de réplicas de um Pod que devem permanecer disponíveis durante uma interrupção planejada. Quando você tem um cluster Kubernetes em produção, interrupções planejadas são inevitáveis: atualizações de nodes, manutenção de hardware, ou rebalanceamento de recursos. Sem um PDB, o Kubernetes pode drenar todos os Pods de um controlador (como um Deployment) simultaneamente, causando downtime.</p>
<p>A ideia central é simples: você especifica quantos Pods de uma aplicação podem ser interrompidos simultaneamente sem comprometer o serviço. O Kubernetes respeita esse contrato durante operações como <code>kubectl drain</code>, atualizações de nodes ou qualquer evento que force a reschedule de Pods. Sem PDB, o cluster não tem informação sobre qual aplicação é crítica e qual pode tolerar interrupções.</p>
<h3>Como funcionam os Budgets</h3>
<p>Um PDB funciona em dois modos: você pode especificar um número absoluto de Pods que devem permanecer rodando (<code>minAvailable</code>) ou quantos Pods podem ser interrompidos (<code>maxUnavailable</code>). Se você tiver 5 réplicas de uma aplicação e definir <code>minAvailable: 3</code>, o Kubernetes garantirá que pelo menos 3 Pods permaneçam disponíveis a qualquer momento durante uma interrupção.</p>
<p>A implementação usa seletores de labels para identificar quais Pods são afetados pelo budget. Isso significa que você pode ter múltiplos PDBs no mesmo namespace, cada um protegendo diferentes aplicações ou versões. O controlador de admissão do Kubernetes verifica cada operação de drenagem de node contra todos os PDBs aplicáveis antes de permitir que a ação prossiga.</p>
<h3>Exemplo prático de PDB</h3>
<pre><code class="language-yaml">apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: nginx-pdb
namespace: production
spec:
minAvailable: 2
selector:
matchLabels:
app: nginx
tier: frontend
unhealthyPodEvictionPolicy: AlwaysAllow</code></pre>
<p>Este manifesto garante que a aplicação <code>nginx</code> sempre tenha pelo menos 2 Pods disponíveis. O campo <code>unhealthyPodEvictionPolicy: AlwaysAllow</code> permite que Pods não saudáveis sejam removidos mesmo se violar o <code>minAvailable</code>. Você também pode usar <code>maxUnavailable</code> para dizer "no máximo 1 Pod pode ser removido":</p>
<pre><code class="language-yaml">apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: database-pdb
namespace: production
spec:
maxUnavailable: 1
selector:
matchLabels:
app: postgres
unhealthyPodEvictionPolicy: IfHealthyBudget</code></pre>
<p>A diferença entre <code>minAvailable</code> e <code>maxUnavailable</code> é semântica, mas importante: use <code>minAvailable</code> quando você sabe exatamente quantas réplicas saudáveis precisa, e <code>maxUnavailable</code> quando você quer permitir uma certa quantidade de falhas. Evite usar ambos no mesmo PDB — o Kubernetes usa <code>minAvailable</code> se ambos estiverem presentes.</p>
<h2>Topology Spread Constraints: Distribuindo Pods Inteligentemente</h2>
<p>Topology Spread Constraints permite que você distribua Pods de forma equilibrada entre os nodes do cluster, garantindo que sua aplicação não se concentre em um único ponto de falha. Enquanto o PDB protege contra interrupções planejadas, Topology Spread Constraints protege contra falhas de infraestrutura, melhorando a resiliência e otimizando o uso de recursos.</p>
<p>O conceito é baseado em "topologias" — agrupamentos lógicos de nodes definidos por labels, como zonas de disponibilidade, racks físicos ou grupos de nós específicos. Você define restrições para garantir que Pods sejam distribuídos de acordo com essas topologias, evitando que todos os Pods de uma aplicação rodem no mesmo zone e caiam juntos.</p>
<h3>Conceito de Topologias e Spread</h3>
<p>Kubernetes define topologias usando labels do node. Por exemplo, todos os nodes em uma zona de disponibilidade compartilham o label <code>topology.kubernetes.io/zone=us-east-1a</code>. Ao criar um Deployment, você pode dizer: "distribua meus Pods para que nenhuma zona tenha mais que 2 Pods dessa aplicação". Isso garante que se uma zona inteira cair, você ainda terá Pods rodando em outras zonas.</p>
<p>O campo <code>maxSkew</code> é o coração de uma Topology Spread Constraint. Ele define a diferença máxima aceitável de Pods entre as topologias menos e mais preenchidas. Se você tiver 3 zonas e <code>maxSkew: 1</code>, significa que uma zona não pode ter mais de 1 Pod a mais que qualquer outra zona para essa aplicação. O scheduler do Kubernetes respeita essa restrição ao fazer o placement de novos Pods.</p>
<h3>Exemplo prático com múltiplas zonas</h3>
<pre><code class="language-yaml">apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app
namespace: production
spec:
replicas: 9
selector:
matchLabels:
app: web-app
template:
metadata:
labels:
app: web-app
version: v1
spec:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: web-app
- maxSkew: 2
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: ScheduleAnyway
labelSelector:
matchLabels:
app: web-app
containers:
- name: web
image: nginx:1.21
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi</code></pre>
<p>Neste exemplo, temos duas constraints: a primeira garante que cada zona não terá mais que 1 Pod a mais que outra zona (distribuição perfeita entre zonas). A segunda constraint garante que cada node não terá mais que 2 Pods a mais que qualquer outro node. O <code>whenUnsatisfiable: DoNotSchedule</code> na primeira constraint significa que se a restrição não puder ser satisfeita, o Pod não será agendado. Na segunda, <code>whenUnsatisfiable: ScheduleAnyway</code> permite que o Pod seja agendado mesmo se violar a constraint, evitando deadlock.</p>
<h3>Combinando com Anti-Affinity</h3>
<p>Embora Topology Spread Constraints seja suficiente em muitos casos, você pode combiná-lo com Pod Anti-Affinity para controle mais fino. Pod Anti-Affinity força Pods a não estar no mesmo node ou zona, enquanto Topology Spread Constraints apenas tenta balancear a distribuição.</p>
<pre><code class="language-yaml">apiVersion: apps/v1
kind: Deployment
metadata:
name: critical-service
namespace: production
spec:
replicas: 3
selector:
matchLabels:
app: critical-service
template:
metadata:
labels:
app: critical-service
tier: critical
spec:
topologySpreadConstraints:
- maxSkew: 0
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: critical-service
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
tier: critical
topologyKey: kubernetes.io/hostname
containers:
- name: service
image: myapp:latest
resources:
requests:
cpu: 250m
memory: 256Mi</code></pre>
<p>Aqui, <code>maxSkew: 0</code> é uma constraint muito agressiva que garante distribuição absolutamente uniforme entre zonas. A anti-affinity adiciona uma preferência (soft constraint) para evitar que Pods rodem no mesmo node. Isso garante máxima resiliência — cada zona tem exatamente a mesma quantidade de Pods, e eles são espalhados entre nodes diferentes quando possível.</p>
<h2>Cenários Reais: PDB e Topology Spread Constraints em Ação</h2>
<p>Na prática, essas duas features trabalham juntas para criar um sistema robusto. PDBs protegem durante manutenção deliberada do cluster, enquanto Topology Spread Constraints protege contra falhas infraestruturais. Vamos explorar um cenário real: um sistema de e-commerce com requisitos de alta disponibilidade.</p>
<h3>Cenário: Sistema de E-commerce com Múltiplos Tiers</h3>
<pre><code class="language-yaml"># Tier 1: API Gateway - crítico, nunca pode cair completamente
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: api-gateway-pdb
namespace: ecommerce
spec:
minAvailable: 3
selector:
matchLabels:
app: api-gateway
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-gateway
namespace: ecommerce
spec:
replicas: 6
selector:
matchLabels:
app: api-gateway
template:
metadata:
labels:
app: api-gateway
tier: frontend
spec:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: api-gateway
- maxSkew: 2
topologyKey: node.kubernetes.io/instance-type
whenUnsatisfiable: ScheduleAnyway
labelSelector:
matchLabels:
app: api-gateway
containers:
- name: gateway
image: api-gateway:v2.1.0
resources:
requests:
cpu: 500m
memory: 512Mi
limits:
cpu: 1000m
memory: 1Gi
---
Tier 2: Worker de processamento de pedidos - tolerável com menos réplicas
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: order-worker-pdb
namespace: ecommerce
spec:
maxUnavailable: 2
selector:
matchLabels:
app: order-worker
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-worker
namespace: ecommerce
spec:
replicas: 8
selector:
matchLabels:
app: order-worker
template:
metadata:
labels:
app: order-worker
tier: backend
spec:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: ScheduleAnyway
labelSelector:
matchLabels:
app: order-worker
containers:
- name: worker
image: order-worker:v1.3.0
resources:
requests:
cpu: 200m
memory: 256Mi
limits:
cpu: 800m
memory: 1Gi</code></pre>
<p>Neste cenário, o <code>api-gateway</code> é crítico — temos 6 réplicas mas garantimos que no mínimo 3 estejam sempre disponíveis. A constraint de topologia garante distribuição uniforme entre zonas. O <code>order-worker</code> é menos crítico — pode tolerar que 2 Pods sejam removidos de uma vez (máximo 6 trabalhando simultaneamente), mas ainda assim tentamos distribuir entre zonas com <code>ScheduleAnyway</code> para não bloquear o agendamento se não for perfeito.</p>
<h3>Testando o comportamento</h3>
<p>Para validar que seu PDB e Topology Spread Constraints estão funcionando, você pode simular uma drenagem de node:</p>
<pre><code class="language-bash"># Ver status de todos os PDBs
kubectl get pdb -n ecommerce -o wide
Ver detalhes de um PDB específico
kubectl describe pdb api-gateway-pdb -n ecommerce
Simular drenagem (isso respeita o PDB)
kubectl drain node-1 --ignore-daemonsets --pod-selector=app=api-gateway
Verificar distribuição de Pods por zona
kubectl get pods -n ecommerce -L topology.kubernetes.io/zone,kubernetes.io/hostname
Ver porque um Pod não foi agendado
kubectl describe pod <pod-name> -n ecommerce</code></pre>
<p>A chave para entender o comportamento é monitorar os eventos do cluster. Quando o Kubernetes tenta agendar um Pod e há constraints violadas, você verá eventos como "0/3 nodes are available: 3 node(s) didn't match topology spread constraints". Isso é esperado e normal — significa que o constraint está funcionando e protegendo sua aplicação.</p>
<h2>Armadilhas Comuns e Boas Práticas</h2>
<p>A maioria dos problemas com PDB e Topology Spread Constraints vem de configurações contraditórias ou expectativas irreais. A armadilha mais comum é definir um <code>minAvailable</code> muito alto para o número de réplicas disponíveis. Se você tem 3 réplicas e define <code>minAvailable: 5</code>, seus Pods nunca serão drenados (o que pode parecer bom, mas bloqueia manutenção do cluster).</p>
<p>Outra armadilha clássica é usar <code>maxSkew: 0</code> sem ter exatamente múltiplos de topologias. Se você tem 3 zonas e 10 réplicas, é impossível distribuir uniformemente (você precisaria de 9 ou 12 réplicas). Nesse caso, o Kubernetes fará o melhor possível, mas alguns Pods podem não ser agendados com <code>whenUnsatisfiable: DoNotSchedule</code>. Use <code>maxSkew: 1</code> para permitir distribuição mais realista.</p>
<pre><code class="language-yaml"></code></pre>
<p>Uma boa prática é sempre monitorar a métrica <code>kube_pod_status_scheduled_time</code> para detectar Pods que estão esperando para ser agendados. Se muitos Pods ficam em estado "Pending", suas constraints estão muito agressivas. Comece conservador (<code>maxSkew: 2</code>, <code>maxUnavailable: 1</code>) e ajuste conforme a necessidade.</p>
<h2>Conclusão</h2>
<p>Pod Disruption Budgets e Topology Spread Constraints são complementares e essenciais para produção. PDBs permitem que o Kubernetes realize manutenção planejada sem interromper seu serviço, definindo explicitamente quantas réplicas precisam permanecer ativas. Topology Spread Constraints distribui seus Pods inteligentemente across infraestrutura, protegendo contra falhas catastróficas de zonas ou nós individuais.</p>
<p>A combinação dessas duas features cria um sistema que é simultaneamente resiliente (se um zone cair, você ainda tem Pods em outras) e sustentável (você pode fazer manutenção sem downtime). Começar pequeno com valores conservadores e monitorar o comportamento é a melhor estratégia — ajuste conforme aprenda sobre o padrão de falhas real do seu cluster.</p>
<p>O terceiro aprendizado crítico é que essas features funcionam melhor quando acompanhadas de resource requests/limits bem configurados e health checks robustos (readiness e liveness probes). Sem isso, você pode ter Pods "agendados" mas não realmente saudáveis, violando o contrato que o PDB e Topology Spread Constraints tentam manter.</p>
<h2>Referências</h2>
<ol>
<li><a href="https://kubernetes.io/docs/tasks/run-application/configure-pdb/" target="_blank" rel="noopener noreferrer">Kubernetes Pod Disruption Budgets - Documentação Oficial</a></li>
<li><a href="https://kubernetes.io/docs/concepts/scheduling-eviction/topology-spread-constraints/" target="_blank" rel="noopener noreferrer">Pod Topology Spread Constraints - Kubernetes Docs</a></li>
<li><a href="https://www.manning.com/books/kubernetes-in-action-second-edition" target="_blank" rel="noopener noreferrer">Kubernetes in Action - Second Edition, Marko Lukša - Capítulo sobre High Availability</a></li>
<li><a href="https://aws.amazon.com/blogs/containers/cost-optimization-for-kubernetes-on-aws/" target="_blank" rel="noopener noreferrer">Best Practices for Running Cost-optimized Kubernetes Clusters - AWS Documentation</a></li>
<li><a href="https://www.oreilly.com/library/view/production-kubernetes/9781492092292/" target="_blank" rel="noopener noreferrer">Production Kubernetes - Rosso, Luc & Brewer, John - O'Reilly Media</a></li>
</ol>
<p><!-- FIM --></p>