Docker & Kubernetes

Como Usar Pod Disruption Budgets e Topology Spread Constraints em Produção

13 min de leitura

Como Usar Pod Disruption Budgets e Topology Spread Constraints em Produção

Pod Disruption Budgets (PDB): Garantindo Alta Disponibilidade 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. 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 , 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. Como funcionam os Budgets Um PDB funciona em dois modos: você pode especificar um número absoluto de Pods que devem permanecer rodando ( ) ou quantos Pods

<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 &quot;no máximo 1 Pod pode ser removido&quot;:</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 &quot;topologias&quot; — 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: &quot;distribua meus Pods para que nenhuma zona tenha mais que 2 Pods dessa aplicação&quot;. 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 &lt;pod-name&gt; -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 &quot;0/3 nodes are available: 3 node(s) didn&#039;t match topology spread constraints&quot;. 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 &quot;Pending&quot;, 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 &quot;agendados&quot; 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 &amp; Brewer, John - O&#039;Reilly Media</a></li>

</ol>

<p>&lt;!-- FIM --&gt;</p>

Comentários

Mais em Docker & Kubernetes

O que Todo Dev Deve Saber sobre Docker Engine: Arquitetura, Daemon, CLI e o Ciclo de Vida de Containers
O que Todo Dev Deve Saber sobre Docker Engine: Arquitetura, Daemon, CLI e o Ciclo de Vida de Containers

Docker Engine: Arquitetura Fundamental O Docker Engine é o coração de toda a...

Dominando Segurança em Docker: Rootless Containers, Seccomp e AppArmor em Projetos Reais
Dominando Segurança em Docker: Rootless Containers, Seccomp e AppArmor em Projetos Reais

Introdução: O Cenário Atual de Segurança em Containers Docker revolucionou a...

O que Todo Dev Deve Saber sobre Volumes em Docker: bind mounts, named volumes e tmpfs Comparados
O que Todo Dev Deve Saber sobre Volumes em Docker: bind mounts, named volumes e tmpfs Comparados

O Problema: Persistência de Dados em Containers Quando você cria um container...