Docker e Kubernetes: o caminho dos containers até a produção
"Na minha máquina funciona." Poucas frases causaram tanto prejuízo em engenharia de software. O código roda no notebook do desenvolvedor, quebra em produção, e horas são gastas caçando a diferença: uma versão de biblioteca, uma variável de ambiente, um pacote de sistema ausente. Containers nasceram para matar essa classe de problema, empacotando a aplicação junto com tudo de que ela precisa para rodar igual em qualquer lugar.
Este guia percorre o caminho completo: o que o Docker resolve, como uma imagem é construída, por que o Kubernetes entra em cena quando os containers se multiplicam, e como decidir se você realmente precisa dessa orquestração no seu estágio.
O problema que o Docker resolve
Um container empacota a aplicação e todas as suas dependências (bibliotecas, runtime, arquivos de configuração) em uma unidade isolada que roda de forma idêntica no notebook do dev, no servidor de testes e na produção. A promessa é a paridade de ambientes: se roda no container aqui, roda no container lá.
Container não é máquina virtual. A VM virtualiza um sistema operacional inteiro, com kernel próprio, e pesa gigabytes. O container compartilha o kernel do host e isola apenas o necessário no nível de processo, então sobe em segundos e ocupa muito menos recurso. Essa leveza é o que torna prático rodar dezenas ou centenas deles na mesma máquina.
| Aspecto | Máquina virtual | Container |
|---|---|---|
| Isolamento | Sistema operacional completo | Processo, sobre o kernel do host |
| Tamanho | Gigabytes | Megabytes |
| Tempo de boot | Minutos | Segundos |
| Densidade por host | Poucas | Dezenas ou centenas |
| Portabilidade | Média | Alta |
Imagens e Dockerfile
Um container roda a partir de uma imagem, um pacote imutável e versionado com o sistema de arquivos e a configuração da aplicação. A imagem é a receita; o container é o prato pronto rodando. Você descreve a imagem em um Dockerfile, um arquivo de instruções passo a passo:
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]
Boas práticas que separam uma imagem profissional de uma amadora:
- Imagens pequenas. Parta de uma base enxuta (como
alpine) para reduzir tamanho, superfície de ataque e tempo de deploy. - Multi-stage build. Compile em um estágio pesado e copie só o artefato final para uma imagem magra, deixando ferramentas de build para trás.
- Aproveite o cache de camadas. Copie o manifesto de dependências e instale antes de copiar o código; assim, mudar uma linha do código não reinstala tudo.
- Nunca embuta segredos na imagem. Senhas e chaves entram por variáveis de ambiente ou gerenciadores de segredo em tempo de execução, jamais no Dockerfile.
Com a imagem publicada em um registry (um repositório de imagens), qualquer ambiente puxa exatamente a mesma versão e a executa. Esse é o mesmo artefato único que um bom pipeline de CI/CD promove de staging para produção sem recompilar.
Por que o Kubernetes existe
Rodar um container é fácil. Rodar centenas deles, com alta disponibilidade, é outro problema. Quando um container morre, quem o reinicia? Quando o tráfego dobra, quem sobe mais réplicas? Como distribuir a carga entre elas? Como publicar uma nova versão sem derrubar o serviço? Fazer isso à mão não escala. É aí que entra a orquestração de containers, e o Kubernetes é o padrão dominante.
O Kubernetes cuida do trabalho pesado de operar containers em escala:
- Auto-healing: se um container falha, ele o reinicia ou o substitui automaticamente.
- Scheduling: decide em qual máquina do cluster cada container roda, respeitando recursos disponíveis.
- Balanceamento de carga: distribui o tráfego entre as réplicas saudáveis.
- Escalonamento: sobe e desce o número de réplicas conforme a demanda.
- Rollout e rollback: publica novas versões de forma gradual e reverte se algo der errado.
Os conceitos que você precisa entender
O Kubernetes tem vocabulário próprio, mas quatro conceitos cobrem a maior parte do dia a dia:
Pod
O pod é a menor unidade que o Kubernetes gerencia. Normalmente ele contém um container (às vezes alguns que precisam viver juntos). Pods são efêmeros: nascem, morrem e são recriados o tempo todo. Você não trata um pod como um bicho de estimação, e sim como gado, descartável e substituível.
Deployment
O deployment declara o estado desejado: "quero 5 réplicas desta imagem, na versão X". O Kubernetes trabalha continuamente para fazer a realidade bater com essa declaração. Se um pod cai e sobram 4, ele cria um novo. Se você muda a versão da imagem, ele faz o rolling update, substituindo os pods aos poucos, sem derrubar o serviço.
Service
Como os pods têm IPs que mudam a cada recriação, você não pode apontar para eles diretamente. O service dá um endereço estável e distribui o tráfego entre os pods saudáveis por trás dele. É a porta de entrada estável para um conjunto instável de pods.
Health checks: liveness e readiness
O Kubernetes só sabe cuidar dos seus containers se eles disserem como estão. Dois sinais são essenciais:
- Liveness probe: responde "estou vivo?". Se falha, o Kubernetes reinicia o container.
- Readiness probe: responde "estou pronto para receber tráfego?". Se falha, o pod sai do balanceamento até se recuperar, sem ser morto.
Sem essas sondas, o cluster entrega requisições para pods que ainda estão iniciando ou já travaram, e a orquestração perde metade do valor.
Autoscaling: ajustar a capacidade à demanda
Um dos maiores atrativos do Kubernetes é escalar sozinho. O Horizontal Pod Autoscaler observa métricas (uso de CPU, memória ou sinais customizados) e aumenta ou reduz o número de réplicas automaticamente. No pico da manhã, sobe pods; na madrugada, reduz e economiza recurso. Bem configurado, o autoscaling mantém a experiência estável sob picos sem exigir superprovisionamento permanente.
Você realmente precisa de Kubernetes?
Aqui vai a parte que muita gente pula: Kubernetes é poderoso e caro de operar. Ele resolve problemas reais de escala, mas cobra em complexidade, curva de aprendizado e sobrecarga operacional. Adotá-lo cedo demais é um clássico erro de engenharia, gastar semanas domando um cluster para servir um tráfego que uma solução simples atenderia.
Um guia honesto de decisão:
- Poucos serviços, tráfego modesto, time pequeno? Um container rodando em uma plataforma gerenciada (PaaS) ou um serviço de container gerenciado costuma bastar, com uma fração da complexidade.
- Muitos serviços, necessidade de autoscaling fino, deploys frequentes e independentes, requisitos de alta disponibilidade? Aí o Kubernetes começa a pagar o próprio custo.
- Quer os benefícios sem operar o cluster? Kubernetes gerenciado (as ofertas dos provedores de nuvem) tira de você a parte mais dolorosa, a manutenção do plano de controle.
A pergunta certa não é "como uso Kubernetes?", e sim "qual é o menor grau de orquestração que resolve o meu problema hoje?". Comece simples e suba de degrau quando a dor aparecer, não antes.
Do container ao SaaS confiável
Containers deram à engenharia moderna a paridade de ambientes; a orquestração deu escala e resiliência. Juntos, eles transformam deploy de um evento arriscado em uma operação previsível. Mas a tecnologia é meio, não fim: o objetivo é entregar software que sobe rápido, se recupera sozinho e escala com a demanda, sem transformar a infraestrutura em um projeto paralelo que consome mais tempo do que o produto.
Sua infraestrutura escala com o produto ou virou um gargalo? Na ConstructorTI, desenhamos a jornada de containers no ponto certo para o seu estágio, de um deploy simples e gerenciado a clusters Kubernetes com autoscaling e alta disponibilidade, sem overengineering. Se você quer paridade de ambientes e deploys previsíveis sem afogar o time em complexidade, fale com a nossa engenharia e receba um diagnóstico da sua infraestrutura.