Cheatsheet Docker
Plataforma de contentores para criar, distribuir e executar aplicações
Docker
Imagens
Listar imagens
docker images
docker image ls -a
docker images --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}"
docker images --filter "dangling=true"
docker images --filter "reference=nginx*"docker images mostra imagens locais. --format personaliza colunas com templates Go. dangling=true filtra imagens sem tag (intermédias de build). reference filtra por nome.
Remover imagens
# Remover por nome:tag: docker rmi nginx:latest # Remover por ID: docker rmi a1b2c3d4e5f6 # Forçar (mesmo com contentores parados): docker rmi -f minha-app:old # Remover todas sem tag (dangling): docker image prune # Remover TODAS as não utilizadas: docker image prune -a
docker rmi remove imagens. -f força mesmo com contentores associados. docker image prune limpa dangling. prune -a remove todas sem contentores ativos — liberta muito espaço em CI/CD.
Search e Docker Hub
# Procurar imagens no Docker Hub: docker search nginx docker search --filter "is-official=true" python docker search --filter "stars=100" redis # Resultados: NAME, DESCRIPTION, STARS, OFFICIAL # Imagens oficiais (curadas): docker pull nginx # oficial (sem prefixo) docker pull bitnami/nginx # comunidade (com prefixo)
docker search pesquisa no Docker Hub via CLI. is-official=true filtra imagens curadas pela Docker. Imagens oficiais não têm prefixo. Comunidade usa user/imagem. Prefere sempre oficiais ou verificadas.
Pull de imagem
docker pull nginx:latest docker pull node:20-alpine docker pull postgres:16 # Tag específica: docker pull python:3.12-slim # Todas as tags (cuidado, pesado): docker pull -a ubuntu # Verificar: docker images nginx
docker pull descarrega imagem do registry. Usa sempre tags específicas (node:20-alpine) em vez de latest. Tags alpine e slim são mais leves. -a descarrega todas as tags.
Inspect e history
# Detalhes completos (JSON):
docker inspect nginx:latest
# Campos específicos:
docker inspect --format="{{.Config.ExposedPorts}}" nginx
# Histórico de camadas:
docker history nginx:latest
docker history --no-trunc nginx # comandos completos
# Tamanho por camada:
docker history --format "{{.Size}}\t{{.CreatedBy}}" nginxdocker inspect mostra configuração completa (env, ports, volumes). docker history lista camadas e comandos que as criaram. --no-trunc mostra comandos inteiros. Útil para debug e otimização de tamanho.
Imagens multi-arch (ARM/AMD)
# Ver plataformas suportadas: docker manifest inspect nginx:latest # Pull para arquitetura específica: docker pull --platform linux/arm64 nginx:latest docker pull --platform linux/amd64 node:20 # Build multi-arch (com buildx): docker buildx create --use docker buildx build --platform linux/amd64,linux/arm64 -t user/app:1.0 --push .
Imagens multi-arch suportam AMD64 e ARM64 no mesmo tag. --platform força arquitetura. docker buildx cria imagens para múltiplas plataformas. Essencial para Apple Silicon (ARM) e servidores cloud (AMD64).
Build de imagem
# Build a partir do Dockerfile no diretório atual: docker build -t minha-app:1.0 . # Com ficheiro específico: docker build -f Dockerfile.prod -t minha-app:prod . # Com build args: docker build --build-arg NODE_ENV=production -t app . # Sem cache: docker build --no-cache -t app . # Verificar: docker images minha-app
docker build -t cria imagem com nome e tag. O . é o build context (ficheiros acessíveis). -f especifica Dockerfile alternativo. --no-cache força rebuild completo. --build-arg passa variáveis.
Save e Load (transferir)
# Exportar imagem para ficheiro tar: docker save -o nginx-backup.tar nginx:latest # Comprimir: docker save nginx:latest | gzip > nginx.tar.gz # Importar noutro servidor: docker load -i nginx-backup.tar gunzip -c nginx.tar.gz | docker load # Verificar: docker images nginx
docker save exporta imagem como .tar (com todas as camadas). docker load importa. Ideal para transferir entre servidores sem registry. Comprime com gzip para reduzir tamanho.
Tag e renomear
# Criar nova tag para imagem existente: docker tag minha-app:1.0 minha-app:latest docker tag minha-app:1.0 registry.io/user/minha-app:1.0 # Formato: docker tag ORIGEM DESTINO # A tag é apenas um ponteiro — não duplica dados docker images # mesmo IMAGE ID, tags diferentes
docker tag cria um alias para a mesma imagem (não duplica). Necessário antes de push para registries: o nome deve incluir o registry (registry.io/user/app). Múltiplas tags podem apontar para o mesmo ID.
Import e Export (contentor)
# Exportar filesystem de um contentor: docker export -o app-fs.tar meu-contentor # Importar como nova imagem: docker import app-fs.tar minha-app:imported # Diferença vs save/load: # save → imagem completa (camadas + metadata) # export → só filesystem (sem histórico) # import → imagem plana (1 camada)
docker export extrai o filesystem de um contentor (sem camadas). docker import cria imagem plana. Perde-se histórico e metadata. Usa save/load para imagens; export/import para snapshots rápidos.
Contentores
Criar e executar (run)
# Básico: docker run nginx # Background + nome + porta: docker run -d --name web -p 8080:80 nginx # Interativo (shell): docker run -it ubuntu:22.04 /bin/bash # Com variáveis de ambiente: docker run -d -e POSTGRES_PASSWORD=secret postgres:16 # Auto-remover ao parar: docker run --rm alpine echo "olá"
docker run = create + start. -d corre em background. -it dá terminal interativo. -e define env vars. --rm remove ao parar. -p publica portas. É o comando mais usado.
Logs
# Ver logs: docker logs web # Follow (tempo real): docker logs -f web # Últimas 50 linhas: docker logs --tail 50 web # Com timestamps: docker logs -t web # Desde timestamp: docker logs --since "2024-01-01T00:00:00" web # Stderr apenas: docker logs web 2>&1 | grep ERROR
docker logs mostra stdout/stderr do contentor. -f faz follow (como tail -f). --tail limita linhas. -t adiciona timestamps. --since filtra por data. Essencial para debug de aplicações.
Stats e Top
# Recursos em tempo real (todos): docker stats # Contentor específico: docker stats web db # Sem stream (snapshot): docker stats --no-stream # Processos dentro do contentor: docker top web docker top web -ef # formato ps # Uso de disco: docker system df -v
docker stats mostra CPU, RAM, rede e I/O em tempo real. --no-stream dá um snapshot (útil em scripts). docker top lista processos internos. system df mostra espaço usado por imagens, contentores e volumes.
Listar contentores (ps)
# Só ativos:
docker ps
# Todos (incluindo parados):
docker ps -a
# Formato personalizado:
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
# Filtrar por estado:
docker ps --filter "status=exited"
docker ps --filter "ancestor=nginx"
# Último criado:
docker ps -ldocker ps lista contentores ativos. -a inclui parados. --format personaliza output. --filter filtra por estado, imagem, nome. -l mostra o último criado. Essencial para monitorização diária.
Inspect (detalhes)
# JSON completo:
docker inspect web
# Campos específicos (Go template):
docker inspect --format="{{.State.Status}}" web
docker inspect --format="{{.NetworkSettings.IPAddress}}" web
docker inspect --format="{{.Config.Image}}" web
# IP de rede:
docker inspect -f "{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}" webdocker inspect retorna JSON com toda a configuração: estado, rede, mounts, env. --format extrai campos com templates Go. Mostra IP, portas, volumes, restart policy. Ferramenta essencial de troubleshooting.
Rename, Pause e Wait
# Renomear: docker rename web antigo-web # Pausar (congelar processos): docker pause web docker unpause web # Esperar até parar (retorna exit code): docker wait web # Commit (contentor → imagem): docker commit web minha-app:snapshot
rename muda o nome. pause congela processos (SIGSTOP) sem parar. wait bloqueia até o contentor terminar. commit cria imagem do estado atual — evita em produção; usa Dockerfile.
Start, Stop, Restart, Kill
docker start web # iniciar contentor parado docker stop web # parar (SIGTERM, 10s, SIGKILL) docker restart web # stop + start docker kill web # SIGKILL imediato (forçar) # Com timeout custom: docker stop -t 30 web # esperar 30s antes de kill # Múltiplos: docker stop web api db docker start $(docker ps -aq) # iniciar todos
stop envia SIGTERM e espera 10s antes de SIGKILL. kill é imediato (sem graceful shutdown). restart = stop + start. -t ajusta timeout. Aceita nomes ou IDs, múltiplos de uma vez.
Remover contentores
# Remover parado: docker rm web # Forçar (mesmo ativo): docker rm -f web # Remover ao parar (automático): docker run --rm alpine echo "temporário" # Limpar todos os parados: docker container prune # Remover todos (ativos e parados): docker rm -f $(docker ps -aq)
docker rm remove contentores parados. -f força remoção de ativos (kill + rm). --rm no run remove automaticamente ao sair. container prune limpa todos os parados. Cuidado com $(docker ps -aq).
Exec (comandos no contentor)
# Executar comando em contentor ativo: docker exec web nginx -t # Shell interativo: docker exec -it web /bin/sh docker exec -it db psql -U postgres # Como root: docker exec -u root -it web /bin/sh # Com variável de ambiente: docker exec -e DEBUG=true web env
docker exec corre comandos dentro de um contentor em execução. -it para sessões interativas. -u define utilizador. Não reinicia o contentor. Ideal para debug, verificar configs e aceder a shells.
Copiar ficheiros (cp)
# Host → Contentor: docker cp ./config.yml web:/etc/app/config.yml # Contentor → Host: docker cp web:/var/log/app.log ./logs/ # Diretório completo: docker cp ./src web:/app/src # Sintaxe: docker cp ORIGEM DESTINO # Nome do contentor ou ID + caminho absoluto
docker cp copia ficheiros entre host e contentor. Funciona com contentores ativos ou parados. Sintaxe: contentor:/caminho. Não precisa de tar — copia diretamente. Útil para configs rápidas e extração de logs.
Redes
Listar e criar redes
# Listar redes: docker network ls # Criar rede bridge: docker network create minha-rede # Criar com subnet custom: docker network create --subnet 172.20.0.0/16 --gateway 172.20.0.1 app-net # Detalhes: docker network inspect minha-rede
docker network ls mostra redes (bridge, host, none por padrão). create cria rede bridge customizada. --subnet define range IP. Redes custom permitem DNS automático entre contentores.
Rede Host
# Contentor partilha rede do host (sem NAT): docker run -d --network host nginx # Não precisa de -p (usa portas do host diretamente) # Nginx acessível em localhost:80 # Limitações: # • Sem isolamento de rede # • Não funciona no Docker Desktop (Mac/Win) # • Ideal para performance máxima ou debug
--network host remove isolamento de rede — o contentor usa as interfaces do host diretamente. Sem NAT, sem -p. Melhor performance mas sem segurança. Só funciona em Linux. Útil para tools de rede e debugging.
Inspect de rede
docker network inspect app-net
# Campos úteis:
docker network inspect --format="{{range .Containers}}{{.Name}} {{end}}" app-net
# IP de um contentor:
docker inspect -f "{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}" web
# Gateway e subnet:
docker network inspect --format="{{(index .IPAM.Config 0).Subnet}}" app-netnetwork inspect mostra subnet, gateway, contentores ligados e IPs. --format extrai dados específicos. Mostra driver, opções e labels. Essencial para debug de conectividade entre serviços.
Ligar contentor a rede
# Ao criar: docker run -d --name api --network minha-rede minha-app # Conectar contentor existente: docker network connect minha-rede web # Desconectar: docker network disconnect minha-rede web # Verificar: docker network inspect minha-rede
--network no run liga à rede na criação. connect/disconnect gerem ligações em runtime. Um contentor pode estar em múltiplas redes. Na mesma rede, contentores comunicam por nome (DNS interno).
Rede None (isolamento)
# Sem rede (só loopback): docker run --network none alpine ip addr # Resultado: só interface lo (127.0.0.1) # Sem acesso externo, sem DNS # Casos de uso: # • Processamento seguro (sem internet) # • Testes de isolamento # • Jobs que não precisam de rede
--network none remove toda a conectividade. O contentor fica totalmente isolado (só loopback). Ideal para processamento sensível, sandboxes e testes. Nenhuma porta funciona. Segurança máxima por isolamento.
IPv6 e redes Macvlan
# Rede com IPv6: docker network create --ipv6 --subnet 2001:db8::/64 rede-v6 # Macvlan (contentor com IP na rede física): docker network create -d macvlan \ --subnet 192.168.1.0/24 \ --gateway 192.168.1.1 \ -o parent=eth0 macvlan-net docker run -d --network macvlan-net --ip 192.168.1.200 meu-servico
--ipv6 ativa IPv6 na rede. macvlan dá um IP da rede física ao contentor (como uma máquina real). Sem NAT, sem port mapping. Ideal para servidores que precisam de IP direto na LAN. Requer interface física.
Publicar portas (-p)
# host:contentor docker run -d -p 8080:80 nginx # TCP docker run -d -p 5353:53/udp dns-app # UDP docker run -d -p 127.0.0.1:3000:3000 app # só localhost # Múltiplas portas: docker run -d -p 80:80 -p 443:443 web # Porta aleatória no host: docker run -d -p 80 nginx docker port web # ver mapeamento
-p host:contentor expõe portas. Sem IP, escuta em todas as interfaces. 127.0.0.1: restringe a localhost. /udp para UDP. docker port mostra mapeamentos. Sem -p, portas ficam internas.
Remover e limpar redes
# Remover rede (sem contentores ligados): docker network rm minha-rede # Remover todas as não utilizadas: docker network prune # Forçar (desconecta contentores): docker network rm -f minha-rede # Redes padrão (bridge, host, none) não podem ser removidas
network rm remove redes sem contentores. prune limpa todas as não usadas. Redes padrão (bridge, host, none) são permanentes. Desconecta contentores antes de remover ou usa -f.
DNS entre contentores
# Criar rede e contentores: docker network create app-net docker run -d --name db --network app-net postgres docker run -d --name api --network app-net minha-api # Dentro de "api", resolve "db" por nome: docker exec api ping db # funciona! docker exec api getent hosts db # IP do db # DNS automático só em redes custom (não na bridge default)
Em redes bridge customizadas, o Docker ativa DNS interno. Contentores resolvem-se por nome (ex: ping db). Na rede default, DNS não funciona — precisa de --link (deprecated). Usa sempre redes custom.
Alias de rede
# Contentor com múltiplos nomes DNS: docker run -d --name db1 --network app-net --network-alias database postgres # Outros contentores resolvem ambos: # ping db1 → funciona # ping database → funciona (alias) # Útil para service discovery: docker run -d --name api1 --network app-net --network-alias api minha-app docker run -d --name api2 --network app-net --network-alias api minha-app
--network-alias adiciona nomes DNS extras. Múltiplos contentores com o mesmo alias = round-robin DNS (load balancing simples). Ideal para service discovery sem ferramentas externas. Funciona em redes custom.
Docker Compose
Estrutura do docker-compose.yml
services:
web:
image: nginx:alpine
ports:
- "8080:80"
volumes:
- ./html:/usr/share/nginx/html:ro
depends_on:
- api
api:
build: ./api
environment:
- DB_HOST=db
- DB_PORT=5432
depends_on:
- db
db:
image: postgres:16
volumes:
- pgdata:/var/lib/postgresql/data
environment:
POSTGRES_PASSWORD: secret
volumes:
pgdata:Ficheiro YAML com secção services (contentores), volumes (dados) e networks (opcional). Cada serviço tem image ou build. depends_on define ordem de arranque. Compose cria rede automática.
Redes no Compose
services:
frontend:
image: nginx
networks:
- frontend-net
api:
build: ./api
networks:
- frontend-net
- backend-net
db:
image: postgres:16
networks:
- backend-net
networks:
frontend-net:
backend-net:Serviços comunicam só na mesma network. O exemplo isola: frontend fala com api, api fala com db, mas frontend não acede a db diretamente. Compose cria rede default automaticamente se não definir.
Compose Watch (hot-reload)
services:
app:
build: .
develop:
watch:
- action: sync
path: ./src
target: /app/src
- action: rebuild
path: ./package.json
# Ativar:
# docker compose watch
# ou: docker compose up --watch -ddocker compose watch (Compose 2.22+) sincroniza alterações em tempo real. action: sync copia ficheiros sem rebuild. action: rebuild reconstrói imagem. Substitui bind mounts em muitos casos. Mais rápido e fiável.
Comandos essenciais
docker compose up -d # criar e iniciar (background) docker compose down # parar e remover tudo docker compose ps # listar serviços docker compose logs -f # logs em tempo real docker compose exec web sh # shell num serviço docker compose build # rebuild imagens docker compose pull # pull de todas as imagens docker compose restart api # reiniciar serviço
up -d cria rede, volumes e inicia tudo. down remove contentores e rede (volumes ficam). logs -f segue todos os serviços. exec acede a shell. build reconstrói imagens com build local.
Perfis e overrides
# docker-compose.yml com profiles: # services: # debug-tools: # profiles: ["debug"] # image: nicolaka/netshoot # Iniciar só serviços base: docker compose up -d # Incluir perfil debug: docker compose --profile debug up -d # Override para desenvolvimento: docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d
profiles ativam serviços condicionalmente. Sem --profile, serviços com perfil não iniciam. -f sobrepõe ficheiros (override). Padrão: base + dev/prod override. Segundo ficheiro sobrepõe o primeiro.
Logs e debugging
# Logs de todos os serviços: docker compose logs -f # Serviço específico: docker compose logs -f api # Últimas N linhas: docker compose logs --tail 50 db # Exec para debug: docker compose exec api sh docker compose exec db psql -U postgres # Config validada: docker compose config
compose logs -f agrega logs de todos os serviços. --tail limita linhas. exec abre shell num serviço. compose config valida e mostra o YAML resolvido (com overrides e .env aplicados).
Build com Compose
services:
app:
build:
context: .
dockerfile: Dockerfile.prod
args:
NODE_ENV: production
target: production # multi-stage
ports:
- "3000:3000"
# Ou simples:
worker:
build: ./worker
SH
) . '<p><code>build</code> pode ser string (caminho) ou objeto com <code>context</code>, <code>dockerfile</code>, <code>args</code>. <code>target</code> seleciona etapa em multi-stage. <code>docker compose build</code> reconstrói. <code>up --build</code> faz build + up.</p>',
],
[
'title' => 'Variáveis e .env',
'content' => $this->pre(
<<<'YAML'
# docker-compose.yml
services:
app:
image: minha-app:${TAG:-latest}
environment:
- DATABASE_URL=${DB_URL}
- SECRET=${SECRET_KEY}
# .env (mesmo diretório, carregado automaticamente):
TAG=2.1.0
DB_URL=postgres://user:pass@db:5432/app
SECRET_KEY=super-secretoO Compose carrega .env automaticamente para substituição de variáveis. ${VAR:-default} define fallback. environment passa vars ao contentor. Nunca commitar .env com segredos — usar .env.example.
Escalar serviços
# Escalar (múltiplas réplicas): docker compose up -d --scale worker=3 # Ver réplicas: docker compose ps # Logs de todas as réplicas: docker compose logs -f worker # Limitar (sem porta fixa para escalar): # ports: NÃO usar "8080:80" se escalar # Usar range: "8080-8082:80" ou sem ports
--scale worker=3 cria 3 réplicas do serviço. Não funciona com ports fixos (conflito). Usa range de portas ou service discovery interno. Ideal para workers e processamento paralelo. Load balancing via DNS.
depends_on e healthcheck
services:
app:
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
db:
image: postgres:16
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 3s
retries: 5
start_period: 10s
redis:
image: redis:7-alpinedepends_on com condition: service_healthy espera o healthcheck passar antes de iniciar dependentes. healthcheck define comando, intervalo e retries. service_started só espera iniciar (sem verificar saúde).
Restart policies
services:
app:
image: minha-app
restart: unless-stopped
db:
image: postgres:16
restart: always
worker:
image: worker-app
restart: on-failure:3 # 3 tentativas
# Opções:
# no → nunca reinicia (padrão)
# always → sempre (mesmo manual stop)
# on-failure → só se exit code != 0
# unless-stopped → sempre exceto stop manualrestart define política de reinício. unless-stopped é o mais usado (reinicia exceto se parado manualmente). on-failure:N limita tentativas. always reinicia mesmo após docker restart. Essencial em produção.
Dockerfile
FROM e imagens base
# Imagem base oficial: FROM node:20-alpine # Com versão específica: FROM python:3.12-slim # Imagem mínima: FROM alpine:3.19 # Scratch (vazia, para binários): FROM scratch # Escolha: alpine (5MB) < slim (30MB) < full (300MB+) # Alpine usa musl libc (pode ter incompatibilidades)
FROM define a imagem base (primeira instrução). alpine é mínima (~5MB). slim remove tools desnecessários. scratch é vazia (para binários Go/Rust). Escolhe a menor que funcione para a tua app.
WORKDIR e EXPOSE
# Definir diretório de trabalho: WORKDIR /app # Tudo a seguir é relativo a /app: COPY package.json . RUN npm install COPY . . # Documentar porta (NÃO publica!): EXPOSE 3000 EXPOSE 5432/tcp EXPOSE 53/udp # Publicar de facto: docker run -p 3000:3000
WORKDIR define o diretório (cria se não existir). Evita cd em RUN. EXPOSE é documentação — não publica porta. Publicação real é com -p no run ou ports no Compose. Sempre usa WORKDIR.
.dockerignore
# .dockerignore (na raiz do build context) node_modules .git .env *.md dist/ coverage/ .vscode docker-compose*.yml Dockerfile* # Reduz build context (mais rápido) # Evita copiar segredos (.env) # Semelhante ao .gitignore
.dockerignore exclui ficheiros do build context. Reduz tempo de build (menos dados enviados ao daemon). Evita copiar .env, node_modules, .git. Sintaxe igual ao .gitignore. Sempre criar um.
RUN (executar comandos)
# Instalar dependências:
RUN apt-get update && apt-get install -y \
curl \
git \
&& rm -rf /var/lib/apt/lists/*
# Múltiplos comandos numa camada:
RUN npm ci --production \
&& npm cache clean --force
# Alpine:
RUN apk add --no-cache python3 make g++RUN executa comandos durante o build. Cada RUN cria uma camada — combina com && para minimizar. rm -rf /var/lib/apt/lists/* limpa cache. --no-cache no apk evita cache. Menos camadas = imagem menor.
CMD e ENTRYPOINT
# CMD: comando padrão (pode ser sobrescrito) CMD ["node", "server.js"] # ENTRYPOINT: binário fixo (args são passados) ENTRYPOINT ["python", "app.py"] # Combinar (entrypoint + args padrão): ENTRYPOINT ["nginx"] CMD ["-g", "daemon off;"] # Sobrescrever CMD no run: # docker run minha-app --debug
CMD define comando padrão (substituível no run). ENTRYPOINT é fixo — argumentos são anexados. Combinação: ENTRYPOINT como binário, CMD como args default. Formato JSON ["exec"] é preferido (sem shell wrapper).
LABEL e STOPSIGNAL
# Metadata:
LABEL maintainer="dev@empresa.com"
LABEL version="2.1.0"
LABEL description="API de gestão de clientes"
# Visível em: docker inspect --format="{{.Config.Labels}}"
# Sinal de paragem:
STOPSIGNAL SIGTERM
# Para apps que usam SIGQUIT:
STOPSIGNAL SIGQUITLABEL adiciona metadata (autor, versão, descrição). Visível via inspect. Útil para catalogar imagens. STOPSIGNAL define sinal de shutdown (padrão: SIGTERM). Nginx usa SIGQUIT para graceful stop.
COPY e ADD
# Copiar ficheiros (preferido): COPY package*.json ./ COPY src/ ./src/ COPY config.yml /etc/app/ # ADD (extrai tar automaticamente): ADD app.tar.gz /opt/app/ # De URL (ADD só): ADD https://exemplo.com/file.zip /tmp/ # Com ownership: COPY --chown=node:node . /app
COPY copia ficheiros locais (transparente, preferido). ADD adiciona features: extração de tar e download de URL. --chown define dono. Copia só o necessário — cada COPY invalida cache se o ficheiro mudar.
USER e permissões
# Criar user não-root: RUN addgroup -S appgroup && adduser -S appuser -G appgroup # Copiar com ownership: COPY --chown=appuser:appgroup . /app # Mudar para non-root: USER appuser # A partir daqui, tudo corre como appuser: RUN whoami # appuser CMD ["node", "server.js"]
USER define o utilizador para comandos seguintes. Nunca correr como root em produção. Cria user com adduser -S (Alpine) ou useradd -r (Debian). --chown no COPY garante permissões corretas.
ENV e ARG
# ENV: variável em runtime (persiste no contentor)
ENV NODE_ENV=production
ENV APP_PORT=3000
# ARG: variável só durante build
ARG VERSION=1.0.0
ARG BUILD_DATE
# Usar ARG em RUN:
RUN echo "Building v${VERSION}"
# ARG não existe em runtime!
# Para passar ao runtime: ENV MY_VAR=$ARG_VARENV define variáveis que existem no contentor em runtime. ARG só existe durante build (não persiste). Usa ARG para versões e configs de build. ENV para configs da aplicação. Ambos acessíveis via $VAR.
HEALTHCHECK
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \ CMD curl -f http://localhost:3000/health || exit 1 # Sem curl (Alpine): HEALTHCHECK CMD wget -qO- http://localhost:80/ || exit 1 # Estados: starting → healthy / unhealthy # Visível em: docker ps (STATUS column)
HEALTHCHECK define verificação de saúde. --interval frequência, --timeout limite, --retries falhas até unhealthy. --start-period dá tempo para arrancar. Docker e Compose usam para restart e depends_on.
Registry e Distribuição
Docker Hub (push/pull)
# Login: docker login # Tag com username: docker tag minha-app:1.0 meuuser/minha-app:1.0 # Push: docker push meuuser/minha-app:1.0 # Pull noutro servidor: docker pull meuuser/minha-app:1.0 # Logout: docker logout
docker login autentica no Docker Hub. Tag deve incluir username/. push envia camadas. pull descarrega. Imagens públicas são grátis; privadas têm limite no plano free. Usa tokens em CI/CD.
Docker Content Trust
# Ativar assinatura de imagens: export DOCKER_CONTENT_TRUST=1 # Push agora assina automaticamente: docker push meuuser/app:1.0 # Pull verifica assinatura: docker pull meuuser/app:1.0 # Desativar temporariamente: docker pull --disable-content-trust meuuser/app:1.0
Docker Content Trust (DCT) assina e verifica imagens. Garante integridade e publisher. Ativa com DOCKER_CONTENT_TRUST=1. Push cria assinatura; pull verifica. Protege contra imagens adulteradas.
Scan de vulnerabilidades
# Docker Scout (sucessor do docker scan): docker scout cves minha-app:1.0 # Resumo rápido: docker scout quickview minha-app:1.0 # Comparar versões: docker scout diff minha-app:1.0 minha-app:1.1 # Trivy (alternativa open-source): docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \ aquasec/trivy image minha-app:1.0
docker scout analisa CVEs na imagem. quickview dá resumo. diff compara entre versões. Trivy é alternativa open-source. Integra em CI/CD para bloquear imagens com vulnerabilidades críticas.
Registry privado
# Correr registry local: docker run -d -p 5000:5000 --name registry registry:2 # Tag para registry local: docker tag minha-app localhost:5000/minha-app:1.0 # Push: docker push localhost:5000/minha-app:1.0 # Pull: docker pull localhost:5000/minha-app:1.0 # Listar imagens: curl http://localhost:5000/v2/_catalog
registry:2 é o registry oficial self-hosted. Corre na porta 5000. Tag inclui host:porta/. Sem auth por padrão (adicionar TLS + htpasswd em produção). API REST em /v2/ para gestão.
Imagens multi-registry
# Tag para múltiplos registries: docker tag app:1.0 ghcr.io/user/app:1.0 docker tag app:1.0 registry.gitlab.com/user/app:1.0 docker tag app:1.0 123456.dkr.ecr.us-east-1.amazonaws.com/app:1.0 # Push para todos: docker push ghcr.io/user/app:1.0 docker push registry.gitlab.com/user/app:1.0 # Mesmo IMAGE ID, registries diferentes
Uma imagem pode ter tags em múltiplos registries. Mesmo IMAGE ID, destinos diferentes. Útil para mirror (redundância) ou migração. Cada push envia só camadas que o registry não tem (deduplicação).
Manifest e attestation
# Ver manifest (plataformas, camadas): docker manifest inspect nginx:latest # Criar manifest list manual: docker manifest create meuuser/app:1.0 \ meuuser/app:1.0-amd64 \ meuuser/app:1.0-arm64 docker manifest push meuuser/app:1.0 # Attestation (SBOM, provenance): docker buildx build --sbom=true --provenance=true -t app .
manifest descreve plataformas e camadas de uma imagem. manifest create une imagens multi-arch. attestation adiciona SBOM e provenance ao build. Aumenta transparência e segurança na supply chain.
Login em registries
# Docker Hub: docker login # GitHub Container Registry: echo $TOKEN | docker login ghcr.io -u USER --password-stdin # AWS ECR: aws ecr get-login-password | docker login --username AWS --password-stdin ID.dkr.ecr.REGION.amazonaws.com # Google GCR: gcloud auth print-access-token | docker login -u oauth2accesstoken --password-stdin gcr.io # Azure ACR: az acr login --name meuregistry
Cada cloud tem método de login próprio. --password-stdin evita secrets no histórico. ghcr.io usa PAT. ECR usa token temporário da AWS CLI. Credenciais ficam em ~/.docker/config.json.
Registry com autenticação
# Criar password file: docker run --rm --entrypoint htpasswd httpd:2 -Bbn user pass > auth/htpasswd # Registry com auth + TLS: docker run -d -p 5000:5000 \ -v $(pwd)/auth:/auth \ -e REGISTRY_AUTH=htpasswd \ -e REGISTRY_AUTH_HTPASSWD_REALM="Registry" \ -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \ -v certs:/certs \ -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \ -e REGISTRY_HTTP_TLS_KEY=/certs/domain.key \ registry:2
Registry em produção precisa de TLS + htpasswd. Cria ficheiro com htpasswd -Bbn. Configura via variáveis de ambiente. Sem TLS, Docker recusa push (insecure-registry só para dev).
Tags e versionamento
# Boas práticas de tags: docker tag app minha-app:1.2.3 # semver docker tag app minha-app:latest # última docker tag app minha-app:$(git rev-parse --short HEAD) # git SHA # Push múltiplas tags: docker push meuuser/app:1.2.3 docker push meuuser/app:latest # Listar tags (Docker Hub API): curl "https://hub.docker.com/v2/repositories/meuuser/app/tags"
Versionamento: usa semver (1.2.3) + latest para a mais recente. Em CI/CD, tag com git SHA para rastreabilidade. Nunca usar só latest em produção — pode mudar inesperadamente.
Garbage collection
# Limpar blobs não referenciados no registry: docker exec registry registry garbage-collect /etc/docker/registry/config.yml # Parar registry primeiro (mais seguro): docker stop registry docker run --rm -v registry-data:/var/lib/registry registry:2 \ garbage-collect /etc/docker/registry/config.yml docker start registry # Verificar espaço: docker system df
garbage-collect remove blobs órfãos do registry (camadas sem tag). Acontece após delete de tags. Não liberta espaço imediatamente — precisa de GC. Corre com registry parado para consistência.
Sistema e Manutenção
docker info e version
# Informação do daemon: docker info # Mostra: Containers, Images, Storage Driver, # Cgroup, Kernel, OS, CPUs, Total Memory # Versão (client + server): docker version # Só versão curta: docker --version
docker info mostra estado completo: nº de contentores, imagens, storage driver, memória, CPUs. docker version mostra versão do client e server. Primeiro comando para diagnosticar problemas.
Eventos em tempo real
# Todos os eventos:
docker events
# Filtrar:
docker events --filter "type=container"
docker events --filter "event=start"
docker events --filter "image=nginx"
# Com formato:
docker events --format "{{.Time}} {{.Action}} {{.Actor.Attributes.name}}"
# Desde/até:
docker events --since "2024-01-01" --until "2024-01-02"docker events stream de eventos do daemon: create, start, stop, die, pull. --filter por tipo, evento ou imagem. Útil para auditoria, alertas e automação. Mostra timestamp, ação e metadata.
Troubleshooting comum
# "Cannot connect to Docker daemon"
sudo systemctl start docker
# "permission denied"
sudo usermod -aG docker $USER && newgrp docker
# "port already in use"
sudo lsof -i :8080 # ver quem usa a porta
# "no space left on device"
docker system prune -af
# Contentor não arranca:
docker logs contentor
docker inspect --format="{{.State.Error}}" contentorErros comuns: daemon parado (systemctl start), permissões (usermod), porta ocupada (lsof), disco cheio (prune). Sempre verifica docker logs e inspect para detalhes do erro.
Uso de disco (system df)
# Resumo: docker system df # Detalhado: docker system df -v # Output: # TYPE TOTAL ACTIVE SIZE RECLAIMABLE # Images 15 3 12.5GB 10.2GB (81%) # Containers 5 2 1.2GB 800MB (66%) # Local Volumes 8 3 500MB 200MB (40%) # Build Cache 30 0 3GB 3GB
docker system df mostra espaço por tipo. RECLAIMABLE indica o que pode ser limpo. -v lista cada item. Build Cache acumula muito em CI/CD. Verifica regularmente para evitar disco cheio.
Reiniciar daemon com segurança
# Sem live-restore: todos os contentores param!
sudo systemctl restart docker
# Com live-restore (daemon.json):
# { "live-restore": true }
# → contentores continuam ativos durante restart
# Verificar após restart:
docker ps
docker info
# Reload config sem restart:
sudo systemctl reload dockerrestart docker para todos os contentores (sem live-restore). Ativa live-restore: true no daemon.json para manter contentores ativos. reload aplica config sem reiniciar. Sempre verifica docker ps após.
Atualizar sem downtime
# 1. Ativar live-restore no daemon.json:
# { "live-restore": true }
sudo systemctl reload docker
# 2. Atualizar pacotes:
sudo apt-get install docker-ce docker-ce-cli containerd.io
# 3. Reiniciar (contentores continuam):
sudo systemctl restart docker
# 4. Verificar:
docker ps # todos ainda ativos?
docker info # versão nova?Com live-restore: true, contentores sobrevivem ao restart do daemon. Atualiza pacotes e reinicia — apps continuam ativas. Sem live-restore, há downtime. Em produção com Swarm/K8s, faz rolling update.
Prune (limpeza geral)
# Limpar tudo que não está em uso: docker system prune # Incluir volumes (CUIDADO - dados!): docker system prune --volumes # Incluir imagens sem contentores: docker system prune -a # Sem confirmação: docker system prune -af # Só build cache: docker builder prune
docker system prune remove contentores parados, redes órfãs e imagens dangling. -a inclui imagens sem contentores. --volumes remove volumes (perigoso!). builder prune limpa cache de build. Corre periodicamente.
Limitar logs de contentores
# Global (daemon.json):
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}
# Por contentor:
docker run -d --log-opt max-size=10m --log-opt max-file=3 app
# Ver tamanho dos logs:
sudo du -sh /var/lib/docker/containers/*/*-json.logLogs podem encher o disco! Configura max-size e max-file no daemon.json (global) ou por contentor. json-file é o driver padrão. Alternativas: syslog, fluentd, local.
Logs do daemon
# Linux (systemd): sudo journalctl -u docker.service -f sudo journalctl -u docker.service --since "1 hour ago" # Verificar erros: sudo journalctl -u docker.service | grep -i error # Docker Desktop (Mac/Win): # Settings → Troubleshoot → View logs # Logs de contentores: docker logs --tail 100 -f nome-contentor
No Linux, logs do daemon via journalctl -u docker.service. Mostra erros de arranque, OOM, problemas de rede. No Docker Desktop, logs na UI. Para contentores: docker logs. Essencial para troubleshooting.
Storage drivers
# Ver driver atual:
docker info | grep "Storage Driver"
# Configurar (daemon.json):
{ "storage-driver": "overlay2" }
# Drivers disponíveis:
# overlay2 → recomendado (Linux 4.0+)
# fuse-overlayfs → rootless
# btrfs/zfs → avançados (snapshots nativos)
# Verificar suporte:
grep overlay /proc/filesystemsoverlay2 é o storage driver recomendado. Usa camadas com copy-on-write. fuse-overlayfs para rootless. Nunca muda driver com dados existentes (perde tudo). Verifica suporte no kernel antes.
Avançado
Multi-stage builds
# Etapa 1: Build FROM node:20 AS builder WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build # Etapa 2: Produção (imagem mínima) FROM nginx:alpine AS production COPY --from=builder /app/dist /usr/share/nginx/html EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]
Multi-stage usa múltiplos FROM. Copia só artefactos com --from=builder. Imagem final não tem node_modules, source, etc. Reduz de ~1GB para ~25MB. Padrão essencial para produção.
Init e signal handling
# Problema: PID 1 não gere zombies/sinais # Solução: usar init: docker run -d --init minha-app # Ou no Dockerfile: # ENTRYPOINT ["docker-init", "--", "node", "server.js"] # Verificar sinais: docker stop app # envia SIGTERM # App deve tratar SIGTERM para graceful shutdown # Sem init: processos zombie acumulam
--init injeta tini como PID 1. Gere sinais e processos zombie corretamente. Sem init, o processo da app é PID 1 e pode ignorar SIGTERM. Sempre usa --init ou trata sinais na app.
Otimizar imagens
# 1. Base mínima: FROM node:20-alpine # 2. Ordem para cache (muda menos → primeiro): COPY package*.json ./ RUN npm ci --production COPY . . # 3. Multi-stage (não levar source): FROM alpine AS runtime COPY --from=builder /app/dist ./dist # 4. Combinar RUN: RUN apk add --no-cache curl && rm -rf /var/cache/apk/* # 5. .dockerignore (sem node_modules, .git)
Otimização: base alpine, multi-stage, ordem de COPY para cache, combinar RUN, .dockerignore. Copia package.json antes do source (cache de npm install). Objetivo: imagem menor, build mais rápido.
BuildKit e cache avançado
# syntax=docker/dockerfile:1
# Cache de npm entre builds:
RUN --mount=type=cache,target=/root/.npm \
npm ci --production
# Cache de apt:
RUN --mount=type=cache,target=/var/cache/apt \
apt-get update && apt-get install -y curl
# Segredo no build (sem ficar na imagem):
RUN --mount=type=secret,id=npm_token \
npm ci --registry=https://$(cat /run/secrets/npm_token)@npm.pkg.github.comBuildKit permite --mount=type=cache (cache persistente entre builds) e --mount=type=secret (segredos sem persistir na imagem). Builds paralelos e mais rápidos. Ativo por padrão em Docker 23+.
docker buildx
# Criar builder: docker buildx create --name meu-builder --use # Build multi-platform: docker buildx build --platform linux/amd64,linux/arm64 \ -t meuuser/app:1.0 --push . # Build com output local: docker buildx build --platform linux/arm64 -o type=docker -t app:arm . # Inspecionar builder: docker buildx inspect --bootstrap # Remover: docker buildx rm meu-builder
buildx estende o build: multi-platform, cache remoto, output custom. --platform define arquiteturas. --push envia direto ao registry. -o type=docker carrega localmente. Essencial para ARM + AMD.
Docker em CI/CD
# GitHub Actions (exemplo):
# - name: Build & Push
# run: |
# docker login -u ${{ secrets.DOCKER_USER }} -p ${{ secrets.DOCKER_PASS }}
# docker build -t user/app:${{ github.sha }} .
# docker push user/app:${{ github.sha }}
# Cache de camadas:
docker build --cache-from user/app:latest -t user/app:new .
# Testar antes de push:
docker run --rm user/app:new npm test
# Só push se testes passaremEm CI/CD: build com tag do git SHA, testa, e só depois push. --cache-from usa imagem anterior como cache. Login com secrets (nunca hardcoded). --rm limpa contentor de teste. Padrão para deploys fiáveis.
Docker Swarm (básico)
# Iniciar swarm: docker swarm init --advertise-addr 192.168.1.10 # Criar serviço: docker service create --name web --replicas 3 -p 80:80 nginx # Listar serviços: docker service ls docker service ps web # tarefas # Escalar: docker service scale web=5 # Update rolling: docker service update --image nginx:1.25 web
Swarm é orquestração nativa do Docker. swarm init cria cluster. service create com --replicas distribui em nós. scale ajusta réplicas. update faz rolling update. Mais simples que Kubernetes.
Entrypoint scripts
# entrypoint.sh #!/bin/sh set -e # Esperar BD: until pg_isready -h db -p 5432; do echo "À espera da BD..." sleep 2 done # Run migrations: npm run migrate # Executar comando principal: exec "$@"
Scripts de entrypoint preparam o ambiente antes da app. Espera dependências, corre migrations, configura env. exec "$@" substitui o shell pelo CMD (recebe sinais). set -e para em erros. Padrão em produção.
Healthcheck avançado
# No Compose com depends_on:
# app depende de db estar healthy
# Ver estado:
docker inspect --format="{{.State.Health.Status}}" app
docker inspect --format="{{range .State.Health.Log}}{{.Output}}{{end}}" app
# Estados: starting → healthy | unhealthy
# Sem healthcheck no Dockerfile (no run):
docker run -d --health-cmd="curl -f http://localhost/ || exit 1" \
--health-interval=30s --health-retries=3 appHealth.Status mostra starting/healthy/unhealthy. Health.Log tem output das verificações. Compose usa para depends_on condition. Pode definir no Dockerfile ou no run. Essencial para auto-healing.
Docker API
# Via socket Unix:
curl --unix-socket /var/run/docker.sock http://localhost/v1.45/containers/json
# Listar contentores:
curl --unix-socket /var/run/docker.sock http://localhost/containers/json?all=true
# Criar contentor:
curl --unix-socket /var/run/docker.sock -X POST \
-H "Content-Type: application/json" \
-d '{"Image":"alpine","Cmd":["echo","olá"]}' \
http://localhost/containers/create
# Info:
curl --unix-socket /var/run/docker.sock http://localhost/infoO Docker expõe API REST via /var/run/docker.sock. Tudo que o CLI faz, a API faz. Versão: /v1.45/. Usada por ferramentas (Portainer, Traefik). Cuidado: acesso ao socket = acesso root.
Instalação e Setup
Instalar no Linux (Ubuntu/Debian)
# Remover versões antigas sudo apt-get remove docker docker-engine docker.io # Instalar dependências sudo apt-get update sudo apt-get install ca-certificates curl gnupg # Adicionar chave GPG oficial 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 # Adicionar repositório echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list # Instalar Docker Engine sudo apt-get update sudo apt-get install docker-ce docker-ce-cli containerd.io docker-compose-plugin
Instala o Docker Engine no Ubuntu/Debian via repositório oficial. O docker-compose-plugin inclui o Compose V2. Usa sempre o repositório oficial para receber atualizações de segurança.
Primeiro contentor (hello-world)
docker run hello-world # O que acontece: # 1. Docker procura imagem localmente # 2. Não encontra → faz pull do Docker Hub # 3. Cria contentor a partir da imagem # 4. Executa o binário → imprime mensagem # 5. Contentor termina (exit 0) docker ps -a # ver contentor (estado Exited)
O hello-world demonstra o ciclo completo: pull → create → run → exit. O contentor para após executar. docker ps -a mostra-o como Exited. Base para entender o modelo de contentores.
Atualizar Docker
# Linux (Ubuntu/Debian): sudo apt-get update sudo apt-get install docker-ce docker-ce-cli containerd.io docker-compose-plugin # Verificar versão: docker --version # Windows/Mac: # Docker Desktop → Settings → Software Updates # Ou: winget upgrade Docker.DockerDesktop (Windows) # Notas de versão: # https://docs.docker.com/engine/release-notes/
No Linux, atualiza via apt-get (mesmo comando da instalação). No Windows/Mac, o Docker Desktop atualiza automaticamente ou via winget. Mantém atualizado para patches de segurança e novas features.
Instalar no Windows e Mac
# Windows / Mac: Docker Desktop # Download: https://www.docker.com/products/docker-desktop/ # Windows: requer WSL2 (Windows Subsystem for Linux) wsl --install # ativar WSL2 # Depois instalar Docker Desktop (installer .exe) # Mac (Apple Silicon ou Intel): # Download .dmg → arrastar para Applications # Verificar instalação: docker --version docker compose version
Docker Desktop é a forma recomendada para Windows e Mac. No Windows, usa WSL2 como backend (mais rápido que Hyper-V). No Mac, usa uma VM leve. Inclui Docker Engine, Compose e BuildKit.
docker run (primeiro servidor)
# Nginx em background, porta 8080 → 80 docker run -d --name web -p 8080:80 nginx # Abrir no browser: http://localhost:8080 # Ver logs: docker logs web # Parar e remover: docker stop web docker rm web
docker run -d executa em background (detached). -p 8080:80 mapeia porta host→contentor. --name dá um nome amigável. O Nginx serve a página padrão em localhost:8080. Primeiro passo para servir aplicações.
Conceitos fundamentais
# Imagem → template read-only (ex: nginx:alpine) # Contentor → instância em execução de uma imagem # Volume → dados persistentes fora do contentor # Rede → comunicação entre contentores # Registry → repositório de imagens (Docker Hub) # Daemon → serviço Docker (dockerd) # Client → CLI que fala com o daemon (docker) # Arquitetura: Client → Daemon → Registry # docker run = pull + create + start
Imagem é o template; contentor é a instância viva. Volumes persistem dados. Redes ligam contentores. O daemon (dockerd) executa tudo. O client (docker CLI) envia comandos via API REST.
Verificar instalação
docker --version # Docker version 27.x.x docker compose version # Docker Compose version v2.x.x docker info # detalhes do daemon docker run hello-world # teste completo # Se "permission denied" no Linux: sudo usermod -aG docker $USER newgrp docker # aplicar sem logout
docker --version confirma a instalação. docker run hello-world testa o fluxo completo (pull + create + run). No Linux, adiciona o utilizador ao grupo docker para evitar sudo.
Serviço Docker (systemd)
# Gerir o daemon como serviço: sudo systemctl start docker sudo systemctl stop docker sudo systemctl restart docker sudo systemctl status docker # Arrancar automaticamente no boot: sudo systemctl enable docker # Verificar se está ativo: systemctl is-active docker # "active"
No Linux, o Docker corre como serviço systemd. enable garante arranque no boot. status mostra estado e PID. No Docker Desktop (Windows/Mac), o serviço é gerido pela aplicação automaticamente.
Configurar o daemon (daemon.json)
# /etc/docker/daemon.json (Linux)
# ou Settings → Docker Engine (Docker Desktop)
{
"log-driver": "json-file",
"log-opts": { "max-size": "10m", "max-file": "3" },
"storage-driver": "overlay2",
"live-restore": true,
"default-address-pools": [
{ "base": "172.30.0.0/16", "size": 24 }
]
}
# Reiniciar após alterações:
sudo systemctl restart dockerO ficheiro daemon.json configura o Docker globalmente. log-opts limita logs (evita disco cheio). overlay2 é o storage driver recomendado. live-restore mantém contentores ativos ao reiniciar o daemon.
Docker Context (múltiplos hosts)
# Listar contextos: docker context ls # Criar contexto remoto: docker context create producao --docker "host=ssh://user@192.168.1.100" # Mudar de contexto: docker context use producao # Comandos agora correm no host remoto: docker ps # lista contentores do servidor remoto # Voltar ao local: docker context use default
Docker Context permite gerir múltiplos hosts sem alterar variáveis de ambiente. create define um endpoint (local ou SSH). use troca o alvo. Ideal para gerir servidores remotos com os mesmos comandos.
Volumes
Criar e listar volumes
# Criar volume nomeado: docker volume create dados-db # Listar: docker volume ls # Filtrar: docker volume ls --filter "dangling=true" # Detalhes: docker volume inspect dados-db # Mostra: Mountpoint, Driver, Labels, Scope
docker volume create cria volume gerido pelo Docker. ls lista todos. dangling=true mostra não usados. Dados ficam em /var/lib/docker/volumes/. Sobrevivem à remoção de contentores.
Partilhar dados entre contentores
# Mesmo volume em múltiplos contentores: docker volume create partilhado docker run -d --name app1 -v partilhado:/data app docker run -d --name app2 -v partilhado:/data app # Ambos leem/escrevem em /data # Volume de outro contentor (deprecated): docker run -d --volumes-from app1 novo-app
Múltiplos contentores podem montar o mesmo volume. Leituras e escritas são partilhadas. Cuidado com concorrência (sem locking). --volumes-from herda mounts de outro contentor (legado). Prefere volumes nomeados.
Volumes vs Bind Mounts
# Volume (gerido pelo Docker): docker run -v dados:/var/lib/mysql db # + Portável, backup fácil, drivers # + Funciona igual em Linux/Mac/Win # Bind mount (pasta do host): docker run -v $(pwd)/src:/app/src app # + Hot-reload em desenvolvimento # - Depende da estrutura do host # - Permissões podem conflitar
Volumes são geridos pelo Docker (portáveis, backup, drivers). Bind mounts ligam pasta do host (hot-reload, debug). Em produção: volumes. Em desenvolvimento: bind mounts para código. Nunca bind mount para dados de BD.
Montar volume no run
# Volume nomeado: docker run -d -v dados-db:/var/lib/postgresql/data postgres # Sintaxe: -v nome_volume:/caminho/contentor # Se volume não existe, Docker cria automaticamente: docker run -d -v novo-vol:/data alpine # Com driver específico: docker run -d --mount type=volume,dst=/data,volume-driver=local app
-v volume:/caminho monta volume nomeado. Se não existir, é criado automaticamente. Dados persistem entre restarts e remoções. --mount é a sintaxe explícita (preferida em produção). Caminho no contentor é absoluto.
Backup de volume
# Backup: volume → tar no host docker run --rm -v dados-db:/fonte -v $(pwd):/backup alpine \ tar czf /backup/backup-db.tar.gz -C /fonte . # Restore: tar → volume docker run --rm -v dados-db:/destino -v $(pwd):/backup alpine \ tar xzf /backup/backup-db.tar.gz -C /destino # Verificar: docker run --rm -v dados-db:/data alpine ls /data
Backup usa contentor temporário com dois mounts: volume fonte + pasta destino. tar czf comprime. Restore inverte: tar do host → volume. --rm remove contentor após operação. Padrão essencial para dados críticos.
Permissões e ownership
# Contentor corre como UID 1000: docker run -d --user 1000:1000 -v dados:/data app # Fix permissões no Dockerfile: # RUN chown -R node:node /app/data # Verificar dono dos ficheiros: docker exec app ls -la /data # Problema comum: ficheiros criados como root # Solução: --user ou chown no entrypoint
Problema comum: ficheiros criados como root dentro do contentor. Solução: --user UID:GID no run. Ou chown no Dockerfile. Bind mounts herdam permissões do host. Volumes novos são root por padrão.
Bind mount (pasta do host)
# Bind mount: pasta local → contentor docker run -d -v $(pwd)/src:/app/src node:20 # Windows: docker run -d -v C:\projeto\src:/app/src node:20 # Read-only: docker run -d -v ./config:/etc/app:ro minha-app # Sintaxe longa (recomendada): docker run -d --mount type=bind,source="$(pwd)/src",target=/app/src app
Bind mount mapeia pasta do host para o contentor. Alterações são bidirecionais e imediatas. :ro torna read-only. Ideal para desenvolvimento (hot-reload). --mount type=bind é mais explícito e seguro.
tmpfs mount
# Montar em RAM (não persiste): docker run -d --tmpfs /tmp:rw,size=100m minha-app # Sintaxe longa: docker run -d --mount type=tmpfs,destination=/cache,tmpfs-size=52428800 app # Casos de uso: # • Cache temporário rápido # • Dados sensíveis (nunca toca disco) # • Ficheiros de sessão
tmpfs monta em RAM — ultra-rápido mas volátil. Dados desaparecem ao parar o contentor. tmpfs-size limita tamanho em bytes. Ideal para cache, sessões e dados sensíveis que não devem tocar disco.
Remover e limpar volumes
# Remover volume: docker volume rm dados-db # Remover volumes de contentor ao apagar: docker rm -v meu-contentor # Limpar todos os não utilizados: docker volume prune # Remover volume específico de um contentor: docker rm -v --force contentor-com-dados
volume rm remove volume (dados perdidos!). docker rm -v remove volumes anónimos do contentor. prune limpa dangling. Volumes nomeados NÃO são removidos com rm -v — só com volume rm explícito.
Volume drivers
# Driver local (padrão): docker volume create --driver local meus-dados # Com opções (NFS, por exemplo): docker volume create --driver local \ --opt type=nfs \ --opt o=addr=192.168.1.50,rw \ --opt device=:/exports/dados \ nfs-vol # Usar: docker run -d -v nfs-vol:/data minha-app
O driver local é o padrão. Suporta opções como NFS, tmpfs. Drivers de terceiros: Azure File, AWS EBS, Ceph. --opt passa configurações do driver. Permite volumes em rede partilhados entre hosts.
Segurança e Recursos
Limitar memória
# Limite de RAM: docker run -d --memory 512m minha-app # Com swap: docker run -d --memory 512m --memory-swap 1g app # Reserva mínima: docker run -d --memory-reservation 256m app # Ver uso: docker stats minha-app # OOM Kill (sem limite): kernel mata processo
--memory limita RAM máxima. --memory-swap limita RAM+swap. --memory-reservation é soft limit. Sem limite, um contentor pode consumir toda a RAM. Em produção, sempre define limites.
Capabilities (Linux)
# Remover todas e adicionar só necessárias:
docker run -d --cap-drop ALL --cap-add NET_BIND_SERVICE app
# Ver capabilities atuais:
docker inspect --format="{{.HostConfig.CapAdd}}" app
docker inspect --format="{{.HostConfig.CapDrop}}" app
# Perigoso: dar todas (equivalente a root):
docker run -d --privileged app # EVITAR!--cap-drop ALL remove privilégios Linux. --cap-add adiciona só o necessário (ex: NET_BIND_SERVICE para porta 80). --privileged dá acesso total — nunca em produção. Princípio do menor privilégio.
Isolamento de rede
# Sem rede externa (só interna): docker network create --internal rede-isolada docker run -d --network rede-isolada db # Sem acesso a internet: docker run -d --network none app # DNS custom: docker run -d --dns 8.8.8.8 --dns-search empresa.local app # Hostname custom: docker run -d --hostname meu-servidor app
--internal cria rede sem acesso externo (sem gateway). --network none isola totalmente. --dns define servidor DNS. --hostname personaliza nome interno. Combina para segurança em camadas.
Limitar CPU
# Limitar a 1.5 CPUs: docker run -d --cpus 1.5 minha-app # Percentagem de 1 CPU: docker run -d --cpus 0.5 app # 50% de 1 core # CPUs específicos: docker run -d --cpuset-cpus "0,1" app # Peso relativo (shares): docker run -d --cpu-shares 512 app # padrão: 1024
--cpus limita CPUs (aceita decimais). --cpuset-cpus fixa a cores específicos. --cpu-shares define prioridade relativa. Evita que um contentor monopolize CPU. Monitoriza com docker stats.
Secrets (sem env vars)
# Mau: secrets em env (visível em inspect): docker run -e DB_PASS=secret app # Bom: montar como ficheiro: echo "secret" > /tmp/db_pass.txt docker run -d --mount type=bind,source=/tmp/db_pass.txt,target=/run/secrets/db_pass,readonly app # Docker Swarm secrets: docker secret create db_pass ./db_pass.txt docker service create --secret db_pass minha-app # Compose secrets: # secrets: # db_pass: # file: ./db_pass.txt
Evita secrets em ENV (visíveis em inspect e logs). Monta como ficheiro read-only em /run/secrets/. Em Swarm, usa docker secret (encriptado em trânsito e repouso). Em Compose, secção secrets.
PID e IPC isolation
# Isolar PID namespace (padrão): docker run -d minha-app # já isolado # Partilhar PID do host (debug): docker run -d --pid host diagnostic-tool # IPC isolation: docker run -d --ipc private minha-app # Partilhar memória (apps que usam shared memory): docker run -d --ipc host minha-app # UTS (hostname isolado por padrão): docker run -d --hostname app1 minha-app
Docker isola PID, IPC, UTS, mount e network por padrão (namespaces). --pid host remove isolamento (só debug). --ipc host para apps com shared memory. Mantém isolamento em produção.
Utilizador non-root
# No Dockerfile:
# RUN adduser -D appuser
# USER appuser
# No run (override):
docker run -d --user 1000:1000 minha-app
# Verificar user atual:
docker exec app whoami
docker exec app id
# Ver user configurado na imagem:
docker inspect --format="{{.Config.User}}" minha-appCorrer como root é risco de segurança. Define USER no Dockerfile ou --user no run. Se um exploit acontecer, o atacante fica limitado ao user. Sempre usa non-root em produção.
Rootless Docker
# Instalar rootless: curl -fsSL https://get.docker.com/rootless | sh # Iniciar daemon: export DOCKER_HOST=unix:///run/user/1000/docker.sock dockerd-rootless.sh & # Verificar: docker info | grep -i rootless # Vantagens: # • Daemon não corre como root # • Exploit não dá acesso ao host
Rootless Docker corre o daemon como user normal (sem root). Mesmo com exploit, atacante não ganha root no host. Usa user namespaces. Limitações: sem portas <1024, sem algumas redes. Recomendado para segurança.
Read-only filesystem
# Sistema de ficheiros read-only: docker run -d --read-only minha-app # Com tmpfs para escrita temporária: docker run -d --read-only --tmpfs /tmp --tmpfs /var/run app # Volume para dados persistentes: docker run -d --read-only -v dados:/data app # Verificar: docker exec app touch /teste # "Read-only file system"
--read-only torna o filesystem imutável. Previne malware de modificar ficheiros. Usa --tmpfs para dirs que precisam de escrita (/tmp, /var/run). Combina com volumes para dados. Segurança máxima.
Security options
# No-new-privileges (impede escalation): docker run -d --security-opt no-new-privileges:true app # AppArmor profile: docker run -d --security-opt apparmor=meu-perfil app # SELinux label: docker run -d --security-opt label:type:svirt_custom_t app # Seccomp profile custom: docker run -d --security-opt seccomp=perfil.json app
no-new-privileges impede setuid/escalation. AppArmor/SELinux aplicam MAC (mandatory access control). seccomp filtra syscalls. Docker já aplica perfis padrão — personaliza para apps sensíveis.