Docker para Desenvolvedores: Imagens, Contêineres e Compose
Você já ouviu falar sobre Docker e contêineres, mas quando tenta usar, fica sobrecarregado com termos como imagens, contêineres, volumes e Compose. Ou talvez você já use Docker, mas suas imagens são enormes, os builds são lentos e gerenciar múltiplos serviços parece uma tarefa árdua. Este guia elimina o ruído. Vamos cobrir os fundamentos do Docker para desenvolvedores, focando no que você realmente precisa saber para construir, executar e gerenciar aplicações containerizadas de forma eficiente.
Por que Docker? O Problema que Ele Resolve
Antes do Docker, implantar um app significava corresponder ao ambiente exato: versões específicas de SO, bibliotecas e configurações. "Funciona na minha máquina" tornou-se um clichê porque era verdade. O Docker resolve isso empacotando sua aplicação com suas dependências em uma única unidade portátil chamada contêiner. Contêineres são leves, isolados e rodam de forma consistente em qualquer lugar onde o Docker esteja instalado.
Imagens vs. Contêineres: A Distinção Central
Uma imagem é um template somente leitura que contém o código da sua aplicação, runtime, bibliotecas e configurações. Um contêiner é uma instância em execução de uma imagem. Pense em uma imagem como uma classe e um contêiner como um objeto. Você pode executar vários contêineres a partir da mesma imagem, cada um isolado dos outros.
Como as Imagens São Construídas
As imagens são construídas a partir de um Dockerfile, um arquivo de texto com instruções. Aqui está um exemplo mínimo para um app Node.js:
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]
Cada instrução cria uma camada. As camadas são armazenadas em cache, então se você alterar apenas seu código-fonte, o Docker reutiliza as camadas em cache para dependências, tornando as reconstruções rápidas.
Construindo e Executando
Construa a imagem e execute um contêiner:
docker build -t my-app .
docker run -p 3000:3000 my-app
A flag -p mapeia a porta 3000 no seu host para a porta 3000 no contêiner.
Melhores Práticas para Imagens Docker Eficientes
Imagens grandes atrasam builds, pushes e implantações. Siga estas práticas para manter as imagens enxutas:
- Use imagens base oficiais slim ou alpine quando possível. Por exemplo,
node:18-alpineé muito menor quenode:18. - Aproveite builds multi-stage para separar dependências de build do runtime. Isso mantém a imagem final mínima.
- Combine comandos RUN para reduzir camadas e limpar caches do gerenciador de pacotes na mesma camada.
- Use um arquivo .dockerignore para excluir arquivos desnecessários (ex.: node_modules, .git) do contexto de build.
Aqui está um Dockerfile multi-stage para uma aplicação Go:
# Build stage
FROM golang:1.21-alpine AS builder
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o /app/main .
# Final stage
FROM alpine:latest
COPY --from=builder /app/main /main
CMD ["/main"]
A imagem final contém apenas o binário compilado e o Alpine, não o toolchain Go.
Gerenciando Dados com Volumes
Contêineres são efêmeros; quando são removidos, seus dados são perdidos. Volumes persistem dados fora do ciclo de vida do contêiner. Use-os para bancos de dados, uploads ou qualquer estado que precise sobreviver a reinicializações.
docker run -v my-data:/var/lib/postgresql/data postgres
Isso cria um volume nomeado my-data que o Docker gerencia. Para desenvolvimento, você também pode montar seu código-fonte no contêiner para recarregamento ao vivo:
docker run -v $(pwd):/app -p 3000:3000 my-app
Docker Compose: Orquestrando Múltiplos Contêineres
Aplicações reais frequentemente precisam de múltiplos serviços: um servidor web, um banco de dados, um cache. Iniciar cada um com docker run é tedioso e propenso a erros. O Docker Compose permite definir e executar apps multi-contêiner com um único arquivo YAML.
Um docker-compose.yml Prático
Aqui está um arquivo Compose para um app Node.js com PostgreSQL e Redis:
version: '3.8'
services:
web:
build: .
ports:
- "3000:3000"
environment:
- DATABASE_URL=postgres://user:pass@db:5432/mydb
- REDIS_URL=redis://cache:6379
depends_on:
- db
- cache
db:
image: postgres:15-alpine
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
POSTGRES_DB: mydb
volumes:
- postgres_data:/var/lib/postgresql/data
cache:
image: redis:7-alpine
volumes:
postgres_data:
Execute docker compose up para iniciar todos os serviços. O Compose cria uma rede padrão para que os contêineres possam se comunicar pelo nome do serviço (ex.: db, cache).
Comandos Comuns do Compose
docker compose up -d– inicia em modo detached (segundo plano).docker compose logs -f web– acompanha os logs de um serviço.docker compose exec web sh– abre um shell em um contêiner em execução.docker compose down– para e remove contêineres, redes e volumes (a menos que os volumes sejam externos).
Quando Usar Docker Compose vs. Kubernetes
O Docker Compose é ideal para desenvolvimento local, testes e pequenas implantações. O Kubernetes se destaca na orquestração de contêineres em clusters para produção em escala. Para muitos desenvolvedores, o Compose é suficiente para o trabalho diário, e você pode migrar para o Kubernetes quando precisar de auto-escalonamento, autocorreção e redes avançadas.
| Recurso | Docker Compose | Kubernetes |
|---|---|---|
| Caso de uso | Dev local, pequenas implantações | Clusters de produção |
| Complexidade | Baixa | Alta |
| Escalonamento | Manual | Automático |
| Curva de aprendizado | Suave | Íngreme |
Perguntas Frequentes
Qual é a diferença entre uma imagem e um contêiner?
Uma imagem é um template somente leitura com seu app e dependências. Um contêiner é uma instância em execução de uma imagem. Você pode executar muitos contêineres a partir de uma imagem.
Como reduzo o tamanho da imagem Docker?
Use imagens base menores (como Alpine), builds multi-stage, combine comandos RUN e use um arquivo .dockerignore para excluir arquivos desnecessários.
Posso usar Docker Compose em produção?
Sim, para implantações em pequena escala. Para ambientes maiores e dinâmicos, Kubernetes ou Docker Swarm são mais adequados.
Pronto para otimizar seu fluxo de trabalho com Docker? Confira nosso Analisador de Logs Nginx para depurar e monitorar seus servidores web containerizados.