Docker
Plataforma de containerização que empacota aplicação + dependências em uma unidade isolada e portátil. Roda de forma idêntica em qualquer ambiente.
Container vs Máquina Virtual
Container compartilha o kernel do host via namespaces + cgroups do Linux. Não emula hardware — muito mais leve.
Arquitetura
🧠 Conceitos Fundamentais
Image
Template read-only. Blueprint do container. Composta de layers imutáveis e compartilhadas.
Container
Instância em execução de uma image. Isolado, efêmero por padrão. Dados perdidos ao remover — use volumes.
Dockerfile
Script de instruções para construir uma image. Define base, comandos, arquivos, usuário, porta.
Registry
Repositório de images. Docker Hub é o padrão público. GHCR, ECR, GCR para privados.
Volume
Armazenamento persistente fora do ciclo de vida do container. Dados sobrevivem a docker rm.
Network
Rede virtual que conecta containers entre si e ao host. Containers na mesma rede se comunicam por nome.
Como images funcionam — layers
⚙️ Instalação
# Dependências e repositório sudo apt update sudo apt install -y ca-certificates curl gnupg sudo install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg sudo chmod a+r /etc/apt/keyrings/docker.gpg echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \ https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \ sudo tee /etc/apt/sources.list.d/docker.list > /dev/null # Instalar sudo apt update sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin # Verificar docker --version docker compose version
sudo usermod -aG docker $USER newgrp docker # aplica sem fazer logout docker run hello-world # testar
Usar Docker Desktop — instala daemon, CLI e Docker Compose de uma vez. Download em docs.docker.com/desktop
🖼️ Images
# Listar locais docker images docker image ls # Baixar docker pull nginx docker pull nginx:1.25-alpine # tag específica — sempre preferir docker pull ubuntu:22.04 # Remover docker rmi nginx docker image rm nginx:1.25 docker rmi $(docker images -q) # remover todas # Inspecionar docker inspect nginx docker image history nginx # ver layers e comandos # Tag docker tag minha-app:latest usuario/minha-app:1.0 # Buscar no Docker Hub docker search nginx # Limpar images não usadas docker image prune # só dangling (sem tag) docker image prune -a # todas não usadas por container
:latest em produção
Tag inespecífica. Um pull pode trazer versão diferente e quebrar a aplicação. Use sempre nginx:1.25-alpine, node:20.11, etc.
📦 Containers
Comandos essenciais
# Criar e iniciar docker run nginx docker run -d nginx # detached (background) docker run -it ubuntu:22.04 bash # interativo com TTY docker run --name meu-nginx -d nginx # com nome docker run --rm -it ubuntu bash # remove ao sair # Listar docker ps # rodando docker ps -a # todos docker ps -q # só IDs # Controle docker stop meu-nginx docker start meu-nginx docker restart meu-nginx docker rm meu-nginx docker rm -f meu-nginx # força mesmo rodando docker container prune # remove todos parados # Executar comando em container rodando docker exec meu-nginx ls /etc/nginx docker exec -it meu-nginx bash # shell interativo docker exec -it meu-nginx sh # Alpine (sem bash) # Logs docker logs meu-nginx docker logs -f meu-nginx # follow docker logs --tail 50 meu-nginx docker logs --since 1h meu-nginx # Copiar arquivos docker cp arquivo.txt meu-nginx:/tmp/ docker cp meu-nginx:/etc/nginx/nginx.conf ./ # Monitorar docker stats docker stats meu-nginx docker top meu-nginx # processos do container docker inspect meu-nginx # tudo (JSON completo)
Flags do docker run
| Flag | Descrição |
|---|---|
-d | Detached — roda em background |
-it | Interativo com pseudo-TTY |
--name | Nome do container |
--rm | Remove ao parar |
-p 8080:80 | Mapear porta host:container |
-P | Mapear todas as EXPOSE para portas aleatórias |
-e VAR=valor | Variável de ambiente |
--env-file .env | Arquivo de variáveis |
-v /host:/container | Bind mount |
-v nome:/container | Named volume |
--network nome | Rede específica |
--restart unless-stopped | Política de restart |
--memory 512m | Limite de memória |
--cpus 1.5 | Limite de CPU |
-u 1000:1000 | Rodar como user:group |
--read-only | Filesystem read-only |
Exemplo completo
docker run -d \ --name minha-app \ -p 3000:3000 \ -e NODE_ENV=production \ -e DB_HOST=db \ --env-file .env \ -v $(pwd)/uploads:/app/uploads \ --network minha-rede \ --restart unless-stopped \ --memory 512m \ --cpus 1 \ minha-app:latest
📄 Dockerfile
Instruções
&& para reduzir layers.--build-arg. Não persiste no container.-p no run.docker run <cmd>.docker run <cmd> adiciona args.depends_on.docker inspect.Exemplo completo — Node.js
FROM node:20-alpine LABEL maintainer="alfredo@email.com" ARG NODE_ENV=production ENV NODE_ENV=$NODE_ENV ENV PORT=3000 WORKDIR /app # Dependências ANTES do código — aproveita cache de layer COPY package*.json ./ RUN npm ci --only=production COPY . . EXPOSE 3000 # Usuário não-root RUN addgroup -g 1001 -S appgroup && \ adduser -u 1001 -S appuser -G appgroup && \ chown -R appuser:appgroup /app USER appuser HEALTHCHECK --interval=30s --timeout=10s --retries=3 \ CMD wget -qO- http://localhost:3000/health || exit 1 CMD ["node", "server.js"]
CMD vs ENTRYPOINT
docker run img <cmd>ENTRYPOINT ["nginx"] CMD ["-g", "daemon off;"] # docker run img -t → nginx -t (substitui CMD, não ENTRYPOINT)
Multi-stage build — imagem final sem ferramentas de build
# Stage 1: build FROM node:20-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build # Stage 2: produção — só o necessário FROM node:20-alpine AS production WORKDIR /app COPY package*.json ./ RUN npm ci --only=production COPY --from=builder /app/dist ./dist RUN adduser -S appuser USER appuser CMD ["node", "dist/server.js"]
.dockerignore
node_modules/ .git/ *.log .env .env.* dist/ coverage/ .DS_Store Dockerfile docker-compose*.yml
Boas práticas
node:20.11-alpine3.19 não node:latestUSER appuser antes do CMDnode_modules, .git, .env no contextodocker history&& rm -rf /var/lib/apt/lists/* no mesmo RUN💾 Volumes
Dados dentro do container são efêmeros — perdidos ao remover. Volumes persistem fora do ciclo de vida do container.
# Named volume docker volume create meus-dados docker run -v meus-dados:/app/data nginx # Bind mount docker run -v $(pwd):/app node # desenvolvimento docker run -v /etc/nginx:/etc/nginx:ro nginx # read-only # tmpfs docker run --tmpfs /tmp node # Listar / inspecionar / remover docker volume ls docker volume inspect meus-dados docker volume rm meus-dados docker volume prune # remove não usados
🔀 Networks
db chega no container chamado db.# Criar rede customizada docker network create minha-rede # Containers na mesma rede se comunicam por nome docker run --network minha-rede --name app minha-app docker run --network minha-rede --name db postgres # dentro de `app`: psql -h db -U postgres # Listar / inspecionar docker network ls docker network inspect minha-rede # Conectar/desconectar container rodando docker network connect minha-rede meu-container docker network disconnect minha-rede meu-container # Remover docker network rm minha-rede docker network prune
🌐 Registry
# Login docker login # Docker Hub docker login ghcr.io # GitHub Container Registry docker login registry.example.com # registry privado # Tag e push docker tag minha-app:latest usuario/minha-app:1.0 docker push usuario/minha-app:1.0 docker push usuario/minha-app:latest # Pull docker pull usuario/minha-app:1.0 # Build + tag para push em uma etapa docker build -t usuario/minha-app:1.0 . # Build multi-arch (amd64 + arm64) com push direto docker buildx create --use docker buildx build --platform linux/amd64,linux/arm64 -t usuario/app:latest --push .
🐙 Docker Compose
Orquestra múltiplos containers com um único arquivo YAML. Define services, volumes e networks em conjunto. Ideal para desenvolvimento local e deploys simples.
Declarativo
Um arquivo YAML descreve toda a stack. Versionar junto ao código.
Um comando
docker compose up -d sobe toda a stack. down derruba tudo.
DNS automático
Services se comunicam pelo nome. db chega no Postgres, redis no Redis.
O plugin moderno é docker compose (sem hífen), instalado junto ao Docker Engine. docker-compose é o binário legado standalone — deprecated. Use sempre o v2.
📋 docker-compose.yml
Chaves principais
host:container. Só expor o necessário..env com variáveis para o service.no / always / on-failure / unless-stopped--profile nome.Exemplo completo — App + DB + Nginx
name: minha-app services: app: build: context: . dockerfile: Dockerfile args: NODE_ENV: production image: minha-app:latest container_name: app restart: unless-stopped ports: - "3000:3000" environment: - NODE_ENV=production - DB_HOST=db env_file: - .env volumes: - ./uploads:/app/uploads - app-logs:/app/logs networks: - backend depends_on: db: condition: service_healthy healthcheck: test: ["CMD", "wget", "-qO-", "http://localhost:3000/health"] interval: 30s timeout: 10s retries: 3 start_period: 10s deploy: resources: limits: cpus: '1' memory: 512M db: image: postgres:16-alpine container_name: db restart: unless-stopped environment: POSTGRES_DB: mydb POSTGRES_USER: myuser POSTGRES_PASSWORD_FILE: /run/secrets/db_password volumes: - postgres-data:/var/lib/postgresql/data - ./init.sql:/docker-entrypoint-initdb.d/init.sql networks: - backend healthcheck: test: ["CMD-SHELL", "pg_isready -U myuser -d mydb"] interval: 10s timeout: 5s retries: 5 nginx: image: nginx:1.25-alpine container_name: nginx restart: unless-stopped ports: - "80:80" - "443:443" volumes: - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro networks: - backend depends_on: - app volumes: postgres-data: app-logs: networks: backend: driver: bridge
💻 Compose — Comandos
# Subir docker compose up docker compose up -d # background docker compose up --build # rebuild antes docker compose up --build -d docker compose up -d app # service específico # Parar e remover docker compose down docker compose down -v # também remove volumes docker compose down --rmi all # também remove images # Controle docker compose stop / start / restart docker compose restart app # Status e logs docker compose ps docker compose ps -a docker compose logs docker compose logs -f docker compose logs -f app docker compose logs --tail 50 app # Executar comandos docker compose exec app bash docker compose exec app npm run migrate docker compose exec db psql -U myuser mydb # Rodar container one-off (não usa depends_on) docker compose run --rm app npm test docker compose run --rm app node scripts/seed.js # Build docker compose build docker compose build app docker compose build --no-cache # Config final (variáveis resolvidas) docker compose config docker compose config --quiet && echo "OK" # Processos docker compose top # Arquivo alternativo docker compose -f docker-compose.prod.yml up -d
🔑 Variáveis e .env
APP_PORT=3000 DB_PASSWORD=secret NODE_ENV=production
services:
app:
ports:
- "${APP_PORT}:3000"
environment:
- NODE_ENV=${NODE_ENV}
- DB_PASSWORD=${DB_PASSWORD}
# Arquivo .env alternativo docker compose --env-file .env.staging up # Sobrescrever variável inline APP_PORT=8080 docker compose up
🔀 Override Files
docker-compose.override.yml é aplicado automaticamente sobre o base. Ideal para separar config de desenvolvimento da produção.
services:
app:
build:
context: .
volumes:
- .:/app # hot reload
- /app/node_modules # preserva node_modules do container
command: npm run dev
environment:
- NODE_ENV=development
ports:
- "9229:9229" # debug port
db:
ports:
- "5432:5432" # acesso direto pelo cliente local
# Development: carrega base + override automaticamente docker compose up -d # Produção: só o base (sem override) docker compose -f docker-compose.yml up -d # Staging: combinar múltiplos docker compose -f docker-compose.yml -f docker-compose.staging.yml up -d
🏷️ Profiles
Services opcionais ativados sob demanda. Evita subir ferramentas de debug em produção.
services:
app:
image: minha-app # sempre sobe
db:
image: postgres:16 # sempre sobe
adminer:
image: adminer
profiles: [debug] # só com --profile debug
ports:
- "8080:8080"
seed:
image: minha-app
command: npm run seed
profiles: [setup]
depends_on:
- db
docker compose up -d # app + db docker compose --profile debug up -d # + adminer docker compose --profile setup run seed # roda seed
🔗 depends_on e ordem de startup
services:
app:
depends_on:
db:
condition: service_healthy # aguarda healthcheck OK
redis:
condition: service_started # aguarda só subir
migrations:
condition: service_completed_successfully # aguarda exit 0
migrations:
image: minha-app
command: npm run migrate
depends_on:
db:
condition: service_healthy
| condition | Quando continua |
|---|---|
service_started | Container subiu (padrão) |
service_healthy | Healthcheck retorna sucesso |
service_completed_successfully | Container saiu com exit code 0 |
🔨 Build e Registry
# Build básico docker build -t minha-app . docker build -t minha-app:1.0 . docker build -t minha-app:latest -f Dockerfile.prod . # Build args e sem cache docker build --build-arg NODE_ENV=production -t minha-app . docker build --no-cache -t minha-app . # Stage específico (multi-stage) docker build --target production -t minha-app:prod . docker build --target builder -t minha-app:build . # Build multi-arch (amd64 + arm64) docker buildx create --use docker buildx build --platform linux/amd64,linux/arm64 -t usuario/app:latest --push .
🔍 Debug e Diagnóstico
# Shell dentro do container docker exec -it container bash docker exec -it container sh # Alpine # Container parou? Inspecionar sobrescrevendo entrypoint docker run --rm -it --entrypoint sh minha-app # Inspecionar tudo docker inspect container docker inspect container | jq '.[0].Config.Env' # variáveis de ambiente docker inspect container | jq '.[0].Mounts' # volumes montados docker inspect container | jq '.[0].NetworkSettings' # rede # Diferença do container vs image (arquivos modificados) docker diff container # Espaço em disco docker system df docker system df -v # Eventos do daemon docker events # Compose docker compose config # config final resolvida docker compose port app 3000 # porta mapeada docker compose top # processos por service # Limpeza completa — remove tudo não usado docker system prune docker system prune -a --volumes # inclusive volumes e todas images
🛡️ Segurança
USER appuser no Dockerfile--read-only + tmpfs para /tmp--memory + --cpus evita DoS acidentalports no composesecrets: no compose ou _FILE varsdocker scout cves ou trivy imagedocker history e layers da imageSecrets no Compose
services:
db:
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/db_password # lê do arquivo
secrets:
- db_password
secrets:
db_password:
file: ./secrets/db_password.txt # arquivo fora do repositório
Read-only com tmpfs
services:
app:
read_only: true
tmpfs:
- /tmp
- /app/tmp
📐 Padrões Comuns
Node.js com hot reload em desenvolvimento
services:
app:
volumes:
- .:/app
- /app/node_modules # evita sobrescrever node_modules do container
command: npm run dev
environment:
- NODE_ENV=development
ports:
- "9229:9229" # Node.js debugger
Worker + Redis
services:
app:
build: .
depends_on: [redis, db]
worker:
build: .
command: npm run worker
depends_on: [redis, db]
deploy:
replicas: 2 # escalar workers
redis:
image: redis:7-alpine
volumes:
- redis-data:/data
volumes:
redis-data:
Migrations como service one-shot
services:
migrations:
build: .
command: npm run migrate
depends_on:
db:
condition: service_healthy
profiles: [setup] # ou remover profiles para rodar sempre
app:
depends_on:
migrations:
condition: service_completed_successfully