Docker: Do Código à Orquestração — O Guia Que Explica o Caminho Inteiro
Aprenda o fluxo completo do desenvolvimento ao deployment. Do Dockerfile e suas camadas até o Kubernetes, entenda como orquestrar containers, gerenciar networking e garantir alta disponibilidade.

Você sabe rodar docker run. Talvez saiba escrever um Dockerfile básico. Mas se alguém te perguntasse agora — "o que acontece entre o momento que você digita docker build e o momento que seu app está rodando em produção com auto-scaling?" — você conseguiria explicar cada etapa?
A maioria dos devs não consegue. E não é por falta de inteligência. É porque Docker é ensinado em pedaços desconectados: um tutorial sobre Dockerfile aqui, um vídeo sobre Kubernetes ali, um artigo sobre networking lá. Ninguém mostra o caminho inteiro de uma vez.
Este artigo mostra.
Do Dockerfile até a orquestração com Kubernetes — cinco etapas, cada uma construindo sobre a anterior. Quando terminar de ler, você vai entender não só como cada peça funciona, mas por que ela existe.

1. O Dockerfile: a receita
Tudo começa com um arquivo de texto. Sem extensão, sem magia — só instruções que dizem ao Docker como montar o ambiente onde seu código vai rodar.
Pensa no Dockerfile como uma receita de cozinha. FROM escolhe a base ("comece com uma cozinha que já tem forno e pia"). COPY traz os ingredientes ("coloque seu código aqui"). RUN prepara ("instale as dependências"). CMD serve ("quando o container iniciar, execute isso").
# Exemplo: app Node.jsFROM node:20-alpineWORKDIR /appCOPY package*.json ./RUN npm ci --only=productionCOPY . .EXPOSE 3000CMD ["node", "server.js"]Cada instrução do Dockerfile se torna uma camada na imagem final. E é exatamente isso que a próxima etapa explica.
2. Build e Layers: a imagem imutável
Quando você roda docker build, o Docker lê o Dockerfile e cria uma imagem — um template read-only que contém tudo que seu app precisa pra rodar: sistema operacional base, dependências, código, configurações.
A imagem é construída em camadas empilhadas. Cada instrução do Dockerfile gera uma camada. A camada 1 (Layer 1) é o sistema operacional base — no exemplo acima, Alpine Linux com Node.js 20. A camada 2 são as dependências instaladas pelo npm ci. A camada 3 é o código do seu app copiado pelo COPY . ..
Duas propriedades fazem esse sistema funcionar:
Imutabilidade. Uma vez criada, a imagem não muda. Se você precisa alterar algo, cria uma nova imagem. Isso garante que o que roda em dev é idêntico ao que roda em produção — o famoso "funciona na minha máquina" deixa de ser problema.
Reutilização de camadas. Se duas imagens compartilham a mesma base (node:20-alpine), o Docker armazena essa camada uma vez só. Dez apps diferentes com a mesma base não ocupam 10x o espaço — ocupam 1x a base + o delta de cada um.
# Construir a imagemdocker build -t meu-app:1.0 .# Ver as camadas da imagemdocker history meu-app:1.0A imagem é o blueprint. Pra transformá-la em algo que realmente roda, você precisa de um container.
3. Container Runtime: onde o código ganha vida
Quando você roda docker run, o Docker pega a imagem (read-only) e cria uma instância viva dela — o container. É como a diferença entre uma classe e um objeto em programação: a imagem é a classe, o container é a instância.
O container recebe uma camada de escrita (writable layer) por cima das camadas read-only da imagem. Tudo que o app escreve em runtime — logs, arquivos temporários, dados de sessão — vai nessa camada. Quando o container é destruído, essa camada é descartada. Os dados somem.
Mas o que torna um container diferente de simplesmente rodar o app direto no sistema operacional? Duas tecnologias do kernel Linux:
Controlam o que o container ENXERGA. Isolam processo (PID), rede (NET), sistema de arquivos (MNT), hostname (UTS), usuários (USER) e comunicação entre processos (IPC). O container acha que é o único processo no mundo.
Controlam o que o container CONSOME. Limitam CPU, memória, swap e I/O de disco. Sem cgroups, um container com vazamento de memória derrubaria o host inteiro.
Namespaces = isolamento de visão. Cgroups = isolamento de recursos. Juntos, criam a ilusão de uma máquina dedicada sem o peso de uma VM.
Na prática, quem faz o trabalho pesado é o containerd (o daemon de runtime) junto com o runc (que cria o container usando as APIs do kernel). O Docker CLI é uma interface — o trabalho real acontece nesses dois.
# Rodar o containerdocker run -d --name meu-app -p 3000:3000 meu-app:1.0# Ver os containers rodandodocker ps# Ver o consumo de recursosdocker stats meu-app4. Networking: como containers conversam
Um container isolado não serve pra muita coisa. Seu app precisa receber requisições do mundo externo. Seu app precisa falar com o banco de dados. E o banco de dados é... outro container.
O Docker resolve isso com uma bridge network — uma rede virtual interna (docker0) que conecta containers entre si. Containers na mesma rede bridge podem se comunicar diretamente pelo nome. O container do app chama o banco por db:5432 em vez de um IP. O Docker cuida da resolução DNS.
Pra o mundo externo acessar um container, você faz port mapping: mapeia uma porta do host pra uma porta do container.
# -p hostPort:containerPortdocker run -d -p 80:3000 meu-app:1.0# Agora: http://localhost:80 → container:3000Com Docker Compose, a rede é criada automaticamente:
# docker-compose.ymlservices: app: build: . ports: - "80:3000" depends_on: - db db: image: postgres:16-alpine environment: POSTGRES_PASSWORD: secret volumes: - pgdata:/var/lib/postgresql/datavolumes: pgdata:5. Orquestração com Kubernetes: quando um container não basta
Docker Compose funciona bem pra dev local e projetos simples. Mas em produção real — com milhares de requisições, necessidade de alta disponibilidade, deploys sem downtime — você precisa de algo mais.
É aí que entra o Kubernetes (K8s). Se o Docker é o que cria e roda containers, o Kubernetes é o que gerencia containers em escala.
A arquitetura do K8s tem dois níveis:
Control Plane (Master Node) — o cérebro. Contém o API Server (recebe comandos), o Scheduler (decide onde rodar cada container), o Controller Manager (garante que o estado real corresponde ao estado desejado) e o etcd (banco de dados do cluster).
Worker Nodes — os braços. Cada node é uma máquina (física ou virtual) que roda containers. Os containers vivem dentro de Pods — a menor unidade do Kubernetes. Um Pod geralmente contém um container, embora possa conter mais de um quando precisam compartilhar recursos.
O que o Kubernetes faz que o Docker Compose não faz:
Auto-escala Pods automaticamente baseado em CPU, memória ou métricas customizadas. Tráfego subiu? Mais Pods. Tráfego caiu? Menos Pods.
Se um Pod morre, o K8s reinicia automaticamente. Se um node inteiro cai, os Pods são reagendados em outros nodes.
Deploy gradual — atualiza Pods um por um, verificando saúde a cada passo. Se algo quebra, faz rollback automático.
Services e DNS interno. Pods encontram outros Pods pelo nome, com load balancing automático entre réplicas.
K8s gerencia clusters de containers (Pods) pra resiliência, escalabilidade e automação em escala.
# deployment.yaml — definir o estado desejadoapiVersion: apps/v1kind: Deploymentmetadata: name: meu-appspec: replicas: 3 # quero 3 instâncias rodando selector: matchLabels: app: meu-app template: metadata: labels: app: meu-app spec: containers: - name: meu-app image: meu-app:1.0 ports: - containerPort: 3000 resources: limits: memory: "256Mi" cpu: "500m" # meio core de CPU requests: memory: "128Mi" cpu: "250m"# service.yaml — expor o app pro mundoapiVersion: v1kind: Servicemetadata: name: meu-app-servicespec: type: LoadBalancer selector: app: meu-app ports: - port: 80 # porta externa targetPort: 3000 # porta do containerQuando usar o quê
Nem todo projeto precisa de Kubernetes. E essa é uma distinção que muitos artigos sobre Docker ignoram.
Só Docker (sem Compose) — quando você tem um container único. Um script, uma ferramenta CLI, um serviço isolado. Docker Compose — quando você tem múltiplos containers que precisam conversar (app + banco + cache). Ideal pra dev local e projetos de time pequeno. Kubernetes — quando você precisa de auto-scaling, self-healing, rollouts sem downtime, e está rodando em produção com tráfego real. K8s adiciona complexidade significativa — só use quando a escala justifica.


