Docker e Contêineres
Contêineres, imagens, boas práticas de Dockerfile e multi-stage builds para empacotar e distribuir aplicações.
O que é Docker?
Docker é uma plataforma que permite empacotar aplicações junto com todas as suas dependências em unidades isoladas chamadas contêineres. Um contêiner inclui o código, o runtime, as bibliotecas e a configuração necessária para que a aplicação funcione de forma idêntica em qualquer máquina.
Diferentemente das máquinas virtuais, os contêineres compartilham o kernel do sistema operacional host, o que os torna muito mais leves e rápidos de iniciar.
Conceitos fundamentais
Imagem
Uma imagem é um pacote imutável que contém tudo o que é necessário para executar uma aplicação. Ela é construída a partir de um Dockerfile e armazenada em um registry (como Docker Hub, ECR ou GCR).
As imagens são compostas por camadas (layers). Cada instrução do Dockerfile cria uma nova camada. O Docker armazena em cache as camadas que não mudam, o que acelera as builds seguintes.
Contêiner
Um contêiner é uma instância em execução de uma imagem. É efêmero por natureza — quando é interrompido, seu estado interno é perdido (a menos que se usem volumes).
Registry
Um registry é um repositório de imagens. Os mais comuns são:
- Docker Hub: registry público padrão
- Amazon ECR: registry privado na AWS
- Google GCR / Artifact Registry: registry no GCP
- GitHub Container Registry: integrado com o GitHub
Dockerfile: boas práticas
O Dockerfile define como uma imagem é construída. Um Dockerfile bem escrito produz imagens pequenas, seguras e rápidas de construir.
Exemplo básico
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 3000
CMD ["node", "dist/server.js"]
Boas práticas
- Usar imagens base pequenas: preferir variantes
alpineouslimem vez das imagens completas - Ordenar instruções por frequência de mudança: as camadas que mudam menos vêm primeiro para aproveitar o cache
- Copiar dependências antes do código:
COPY package*.jsonantes deCOPY .para cachear onpm install - Não executar como root: criar um usuário sem privilégios com
USER - Usar
.dockerignore: excluirnode_modules,.git, arquivos de teste e documentação - Minimizar o número de camadas: combinar comandos
RUNrelacionados com&& - Não instalar ferramentas desnecessárias: cada pacote extra aumenta a superfície de ataque
Exemplo com usuário não root
FROM node:20-alpine
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
WORKDIR /app
COPY --chown=appuser:appgroup package*.json ./
RUN npm ci --only=production
COPY --chown=appuser:appgroup . .
USER appuser
EXPOSE 3000
CMD ["node", "dist/server.js"]
Multi-stage builds
Os multi-stage builds permitem usar múltiplas etapas em um único Dockerfile. Isso é especialmente útil para separar a etapa de compilação da etapa de execução, produzindo imagens finais muito menores.
Por que usar multi-stage?
- A imagem final não inclui ferramentas de compilação (TypeScript compiler, build tools, etc.)
- O tamanho da imagem é reduzido drasticamente
- A superfície de ataque em produção é minimizada
Exemplo: aplicação Node.js com TypeScript
# Etapa 1: Build
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Etapa 2: Produção
FROM node:20-alpine AS production
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/package*.json ./
RUN npm ci --only=production
USER appuser
EXPOSE 3000
CMD ["node", "dist/server.js"]
Neste exemplo, a imagem final contém apenas o código compilado e as dependências de produção — sem TypeScript, sem devDependencies, sem código-fonte.
Docker Compose para desenvolvimento
O Docker Compose permite definir e executar aplicações multi-contêiner. É ideal para o ambiente de desenvolvimento local:
version: '3.8'
services:
app:
build: .
ports:
- "3000:3000"
environment:
- DATABASE_URL=postgres://postgres:password@db:5432/myapp
depends_on:
- db
- redis
db:
image: postgres:16-alpine
environment:
POSTGRES_DB: myapp
POSTGRES_PASSWORD: password
volumes:
- pgdata:/var/lib/postgresql/data
redis:
image: redis:7-alpine
volumes:
pgdata:
Segurança em contêineres
- Escanear imagens: usar ferramentas como Trivy, Snyk ou Docker Scout para detectar vulnerabilidades
- Atualizar imagens base: manter as imagens base atualizadas com patches de segurança
- Não armazenar segredos em imagens: usar variáveis de ambiente ou gerenciadores de segredos em runtime
- Limitar recursos: configurar limites de CPU e memória para evitar que um contêiner consuma todos os recursos do host
- Usar imagens assinadas: verificar a procedência das imagens com Docker Content Trust
Resumo
O Docker simplifica o empacotamento e a distribuição de aplicações ao garantir que o mesmo artefato funcione em qualquer ambiente. As chaves são: imagens pequenas com multi-stage builds, boas práticas de Dockerfile, segurança desde o design e Docker Compose para o desenvolvimento local.