Programacao & Dev

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.

Equipe Blueprintblog10 min
Docker: Do Código à Orquestração — O Guia Que Explica o Caminho Inteiro

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").

dockerfile
# Exemplo: app Node.js
FROM node:20-alpine

WORKDIR /app

COPY package*.json ./
RUN npm ci --only=production

COPY . .

EXPOSE 3000
CMD ["node", "server.js"]

A ordem das instruções importa. O Docker cacheia cada camada. Se você colocar COPY . . antes de RUN npm ci, qualquer mudança em qualquer arquivo do projeto invalida o cache de instalação — e o Docker reinstala tudo do zero. Copiando primeiro só o package.json, o cache de dependências sobrevive enquanto as dependências não mudarem.

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.

bash
# Construir a imagem
docker build -t meu-app:1.0 .

# Ver as camadas da imagem
docker history meu-app:1.0

A 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:

As duas tecnologias que fazem containers funcionarem

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.

👁️

Namespaces

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.

📊

Cgroups

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.

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.

bash
# Rodar o container
docker run -d --name meu-app -p 3000:3000 meu-app:1.0

# Ver os containers rodando
docker ps

# Ver o consumo de recursos
docker stats meu-app

Container ≠ VM. Uma máquina virtual roda um sistema operacional completo com seu próprio kernel. Um container compartilha o kernel do host e usa namespaces e cgroups pra simular isolamento. Por isso containers iniciam em milissegundos e consomem uma fração dos recursos de uma VM.

4. 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.

bash
# -p hostPort:containerPort
docker run -d -p 80:3000 meu-app:1.0
# Agora: http://localhost:80 → container:3000

Com Docker Compose, a rede é criada automaticamente:

yaml
# docker-compose.yml
services:
  app:
    build: .
    ports:
      - "80:3000"
    depends_on:
      - db

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_PASSWORD: secret
    volumes:
      - pgdata:/var/lib/postgresql/data

volumes:
  pgdata:

O volume pgdata é crítico. Lembra que a writable layer do container é descartada quando ele morre? Sem o volume, seus dados do Postgres morrem junto. Volumes vivem fora do container — persistem entre reinicializações, upgrades e recriações.

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:

O que Kubernetes adiciona

K8s gerencia clusters de containers (Pods) pra resiliência, escalabilidade e automação em escala.

📈

Scaling

Auto-escala Pods automaticamente baseado em CPU, memória ou métricas customizadas. Tráfego subiu? Mais Pods. Tráfego caiu? Menos Pods.

💚

Self-healing

Se um Pod morre, o K8s reinicia automaticamente. Se um node inteiro cai, os Pods são reagendados em outros nodes.

🔄

Rollouts

Deploy gradual — atualiza Pods um por um, verificando saúde a cada passo. Se algo quebra, faz rollback automático.

🔍

Discovery

Services e DNS interno. Pods encontram outros Pods pelo nome, com load balancing automático entre réplicas.

yaml
# deployment.yaml — definir o estado desejado
apiVersion: apps/v1
kind: Deployment
metadata:
  name: meu-app
spec:
  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"
yaml
# service.yaml — expor o app pro mundo
apiVersion: v1
kind: Service
metadata:
  name: meu-app-service
spec:
  type: LoadBalancer
  selector:
    app: meu-app
  ports:
  - port: 80                     # porta externa
    targetPort: 3000              # porta do container

Kubernetes é declarativo. Você não diz "inicie 3 containers". Você diz "eu quero 3 réplicas rodando". O K8s se encarrega de chegar nesse estado — e de manter. Se uma réplica morre, ele cria outra. Você declara o destino; o K8s cuida da jornada.

Quando 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.

Se você está começando com Docker, não pule pra Kubernetes. Domine o Dockerfile, entenda layers, pratique Compose. Kubernetes é a camada 5 — e ela só faz sentido quando as camadas 1 a 4 estão sólidas.

O caminho inteiro em 6 frases

  • Dockerfile — a receita. Define o ambiente, copia código, instala dependências, declara o comando de inicialização. A ordem das instruções afeta o cache.
  • Image — o template imutável. Construída em layers empilhadas, read-only, reutilizáveis. docker build cria, docker history inspeciona.
  • Container — a instância viva. Imagem + writable layer. Namespaces isolam o que o container vê. Cgroups limitam o que consome.
  • Networking — bridge network conecta containers entre si. Port mapping expõe pro mundo externo. Volumes persistem dados entre reinicializações.
  • Kubernetes — orquestração em escala. Auto-scaling, self-healing, rollouts, service discovery. Declarativo: você define o estado desejado, o K8s mantém.
  • Docker Compose pra dev e projetos pequenos. Kubernetes pra produção com escala. Não pule etapas.

Tags do artigo

Artigos relacionados

Receba os ultimos artigos no seu email.

Follow Us: