Pular para o conteúdo
← Blog
kubernetescontainersdevopsproducaosre

Kubernetes em produção: boas práticas e armadilhas comuns

Luriel Santana · 03 de julho de 2026

Kubernetes virou o padrão de fato para orquestração de containers, mas o caminho entre "subiu no cluster" e "roda bem em produção" é cheio de detalhes que só aparecem quando algo quebra às 3 da manhã. O projeto é um dos mais maduros do ecossistema cloud-native: foi o primeiro a se graduar na CNCF (Cloud Native Computing Foundation), sinalizando estabilidade e ampla adoção. Maturidade do projeto, porém, não significa que sua aplicação esteja pronta para produção. Este post reúne boas práticas concretas e as armadilhas mais comuns que vejo derrubarem clusters.

Requests e limits: o alicerce do scheduling

Definir requests e limits de CPU e memória não é opcional em produção. O request informa ao scheduler quanto de recurso o Pod precisa para ser alocado num nó; o limit define o teto que ele pode consumir.

resources:
  requests:
    cpu: '250m'
    memory: '256Mi'
  limits:
    cpu: '500m'
    memory: '512Mi'

A armadilha número um é não definir limits. Sem teto de memória, um Pod com vazamento pode consumir toda a RAM do nó, disparando o OOM killer e derrubando processos vizinhos — o clássico problema do noisy neighbor. Sem requests corretos, o scheduler toma decisões ruins de alocação e o cluster fica desbalanceado.

Atenção a uma sutileza: CPU é um recurso compressível (o container é apenas throttled ao bater o limit), mas memória não é. Estourar o limit de memória resulta em OOMKilled, não em lentidão. Por isso, evite definir limits de memória muito baixos sem medir o consumo real da aplicação sob carga.

Liveness e readiness probes: saiba o que cada uma faz

Esse é um ponto onde muita gente erra por confundir as responsabilidades:

  • readiness probe: decide se o Pod recebe tráfego. Se falha, o endereço do Pod é removido dos endpoints do Service — mas o container não é reiniciado.
  • liveness probe: decide se o container deve ser reiniciado. Se falha repetidamente, o kubelet mata e recria o container.
readinessProbe:
  httpGet:
    path: /healthz
    port: 8080
  initialDelaySeconds: 5
  periodSeconds: 10
livenessProbe:
  httpGet:
    path: /healthz
    port: 8080
  initialDelaySeconds: 15
  periodSeconds: 20

A armadilha clássica é uma liveness probe agressiva demais apontando para um endpoint que depende de banco de dados ou de serviços externos. Quando a dependência fica lenta, a probe falha, o container é reiniciado, e o reinício piora a situação — você cria um loop de restarts no pior momento possível. Liveness deve checar apenas se o processo está vivo e não travado; dependências externas pertencem à readiness. Use startupProbe para aplicações com inicialização lenta, evitando que a liveness mate o container antes dele terminar de subir.

PodDisruptionBudget: proteja a disponibilidade durante manutenções

Quando um nó é drenado (upgrade do cluster, autoscaling, manutenção), o Kubernetes despeja os Pods. Sem um PodDisruptionBudget (PDB), todas as réplicas de um serviço podem sair ao mesmo tempo.

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: api-pdb
spec:
  minAvailable: 2
  selector:
    matchLabels:
      app: api

O PDB garante um mínimo de Pods disponíveis durante disruptions voluntárias. Cuidado: ele não protege contra falhas involuntárias (queda física de um nó) e, se mal configurado, pode bloquear a drenagem de nós indefinidamente. Combine PDB com múltiplas réplicas e anti-affinity para espalhar os Pods entre nós.

HPA: escale com base em métricas reais

O HorizontalPodAutoscaler ajusta o número de réplicas conforme a carga. Para funcionar com métricas de recurso, ele depende do metrics-server instalado e — ponto crítico — de requests definidos, já que o cálculo de utilização é relativo ao request.

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: api-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: api
  minReplicas: 2
  maxReplicas: 10
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 70

Sem requests de CPU, o HPA não tem base para calcular o percentual de utilização e não escala por essa métrica. Para cargas que não são CPU-bound (filas, latência), considere métricas customizadas ou externas.

Namespaces e RBAC: isole e restrinja

Use namespaces para separar ambientes, times ou domínios — isso habilita quotas, network policies e limites de acesso por fronteira lógica. Sobre acesso, aplique RBAC com o princípio do menor privilégio: conceda a cada ServiceAccount apenas as permissões que ele realmente precisa.

A armadilha aqui é o comodismo de usar cluster-admin ou bindings amplos "para não ter dor de cabeça". Isso transforma qualquer credencial vazada num comprometimento total do cluster. Prefira Role + RoleBinding por namespace e revise periodicamente quem tem o quê.

Segurança de container: não rode como root

Containers rodando como root ampliam drasticamente o impacto de uma falha de aplicação. Configure o securityContext para rodar como usuário sem privilégios e com sistema de arquivos somente leitura quando possível. Atenção: nem todos os campos vivem no mesmo nível — runAsNonRoot e runAsUser podem ficar no securityContext do Pod ou do container, enquanto allowPrivilegeEscalation, readOnlyRootFilesystem e capabilities são do securityContext do container:

# securityContext do container
securityContext:
  runAsNonRoot: true
  runAsUser: 1000
  allowPrivilegeEscalation: false
  readOnlyRootFilesystem: true
  capabilities:
    drop: ['ALL']

Evite a tag :latest

Usar image: minha-app:latest é uma das armadilhas mais traiçoeiras. Você perde a noção de qual versão está realmente rodando, rollbacks viram adivinhação, e o comportamento do imagePullPolicy pode trazer uma imagem diferente em cada nó. Sempre fixe uma tag imutável de versão e, idealmente, referencie pelo digest (@sha256:...) para garantir reprodutibilidade.

GitOps: o cluster reflete o Git

Aplicar mudanças com kubectl apply manual a partir da máquina de alguém não escala e não deixa rastro. O padrão GitOps — com ferramentas como Argo CD ou Flux, ambos projetos da CNCF — coloca o estado desejado do cluster num repositório Git, que vira a fonte única da verdade. Um controlador reconcilia continuamente o cluster com o que está versionado.

Os ganhos são concretos: todo deploy é auditável via histórico de commits, rollback é um git revert, e o drift entre o que está no Git e o que roda no cluster é detectado e corrigido automaticamente. Pull requests passam a ser o ponto de revisão e aprovação de mudanças de infraestrutura.

Fechando

Nenhuma dessas práticas é exótica — são higiene básica de produção. O padrão que se repete nas armadilhas é a omissão: faltou limit, faltou request, a probe foi configurada no automático, o RBAC ficou aberto. Comece definindo requests/limits e probes corretas em todos os workloads, proteja a disponibilidade com PDB e réplicas, restrinja acesso com RBAC e securityContext, abandone o :latest e mova seus deploys para GitOps. O resultado é um cluster previsível, auditável e que não te acorda de madrugada.

Quer praticar isso de verdade?

Terminal real, ProMind e jornadas guiadas. Conta Essencial grátis, sem cartão.

Criar conta grátis e começar