Jhonatan Pinheiro
Carregando página...
Jhonatan Pinheiro
Carregando página...
Jhonatan Pinheiro
Carregando página...
Containers, imagens, Dockerfile com multi-stage, volumes, redes, Compose e o que muda em produção: 150 comandos com exemplo pronto para copiar.
Docker resolve um problema velho: "na minha máquina funciona". Ele empacota a aplicação com tudo de que ela precisa — runtime, bibliotecas, configuração — em uma imagem que roda igual em qualquer lugar.
Estes 150 comandos vão do primeiro docker run ao diagnóstico de container que morre em produção. Os exemplos usam a CLI moderna (docker container ...) mencionando o atalho clássico quando ele é o mais usado.
Container não é máquina virtual. Ele compartilha o kernel do host e isola só o que precisa (processos, rede, sistema de arquivos). É por isso que sobe em milissegundos e pesa megabytes em vez de gigabytes.
Verificar a instalação, subir o primeiro container e entender o que aconteceu.
Confirma que o cliente conversa com o daemon.
docker version
docker infoBaixa a imagem, cria o container, executa e sai. O "olá mundo" do Docker.
docker run hello-worldCria e inicia um container a partir de uma imagem.
docker run nginx-d (detached) devolve o terminal e deixa o container rodando.
docker run -d nginx-p host:container expõe o serviço na sua máquina.
docker run -d -p 8080:80 nginx
# acesse http://localhost:8080Sem --name, o Docker inventa um nome aleatório e você perde tempo procurando.
docker run -d --name meu-nginx -p 8080:80 nginx-it conecta seu terminal ao container — indispensável para shell.
docker run -it ubuntu bash--rm apaga o container assim que ele para. Ideal para teste rápido.
docker run --rm -it alpine shA forma padrão de configurar aplicação em container.
docker run -d -e POSTGRES_PASSWORD=senha -e POSTGRES_DB=loja postgres:16Evita expor segredo no histórico do shell.
docker run --env-file .env -d minha-appO comando que você mais vai digitar.
docker ps-a inclui os parados — onde costuma estar o que falhou.
docker ps -aDeixa a lista legível em terminal estreito.
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"stop envia SIGTERM e espera; start religa o mesmo container.
docker stop meu-nginx
docker start meu-nginxAtalho para stop + start.
docker restart meu-nginxSIGKILL imediato, sem encerramento gracioso. Último recurso.
docker kill meu-nginxApaga o container parado; -f força mesmo em execução.
docker rm meu-nginx
docker rm -f meu-nginxA primeira coisa a fazer quando algo não funciona.
docker logs meu-nginx-f segue a saída; --tail limita o histórico inicial.
docker logs -f --tail 100 meu-nginxAbre um shell dentro do container em execução.
docker exec -it meu-nginx bash
docker exec -it meu-alpine sh # imagens Alpine não têm bashA imagem é o molde; o container é a instância em execução.
Mostra o que está baixado e quanto ocupa.
docker images
docker image lsTraz do registro sem executar.
docker pull postgres:16Nunca use latest em produção: ela muda sem aviso e quebra o que funcionava.
docker pull node:20-alpine
docker pull node:20.11.1-alpine3.19 # ainda mais específicoProcura no Docker Hub pela linha de comando.
docker search postgresLê o Dockerfile do diretório e gera a imagem com a tag informada.
docker build -t minha-app:1.0 .Quando há mais de um (produção, desenvolvimento, teste).
docker build -f Dockerfile.prod -t minha-app:prod .Força refazer todas as camadas — útil quando o cache está mascarando um problema.
docker build --no-cache -t minha-app:1.0 .ARG parametriza o build (não confundir com ENV, que vale em execução).
docker build --build-arg NODE_VERSION=20 -t minha-app .A mesma imagem pode ter vários nomes.
docker tag minha-app:1.0 usuario/minha-app:latestPublica no Docker Hub ou em registro privado.
docker login
docker push usuario/minha-app:1.0O nome da imagem carrega o endereço do registro.
docker tag minha-app registry.empresa.com/time/minha-app:1.0
docker push registry.empresa.com/time/minha-app:1.0Só sai se nenhum container depender dela.
docker rmi minha-app:1.0
docker image rm -f minha-app:1.0Mostra cada camada e seu tamanho — é assim que se descobre o que engordou a imagem.
docker history minha-app:1.0Metadados completos: variáveis, entrypoint, portas, camadas.
docker inspect minha-app:1.0Move imagem entre máquinas sem registro, por arquivo.
docker save -o minha-app.tar minha-app:1.0
docker load -i minha-app.tarCamadas órfãs de builds antigos, que só ocupam disco.
docker images -f "dangling=true"
docker image prune-a remove todas as que não têm container associado. Libera muito espaço.
docker image prune -aAlpine tem ~5 MB; distroless não tem nem shell. Menos superfície, menos vulnerabilidade.
# 1.1 GB
FROM node:20
# 130 MB
FROM node:20-alpine
# ~50 MB, sem shell nem gerenciador de pacotes
FROM gcr.io/distroless/nodejs20-debian12Verifica CVEs conhecidas nas dependências da imagem.
docker scout cves minha-app:1.0
trivy image minha-app:1.0Compara o tamanho das imagens para achar a gorda.
docker images --format "{{.Repository}}:{{.Tag}}\t{{.Size}}" | sort -k2 -hCria um container temporário só para copiar algo de dentro.
id=$(docker create minha-app:1.0)
docker cp $id:/app/dist ./dist
docker rm $idGera a mesma imagem para amd64 e arm64 — necessário para Apple Silicon e Graviton.
docker buildx build --platform linux/amd64,linux/arm64 -t usuario/app:1.0 --push .Inspecionar, copiar, limitar e entender o que está acontecendo lá dentro.
JSON completo: rede, volumes, variáveis, estado, código de saída.
docker inspect meu-nginxCom template Go, você pega só o que precisa.
docker inspect -f '{{.State.Status}}' meu-nginx
docker inspect -f '{{.NetworkSettings.IPAddress}}' meu-nginxCPU, memória, rede e I/O de cada container — o top do Docker.
docker stats
docker stats --no-streamO que está rodando lá dentro, visto do host.
docker top meu-nginxMove arquivos entre host e container nos dois sentidos.
docker cp ./config.json meu-nginx:/etc/app/config.json
docker cp meu-nginx:/var/log/nginx/error.log ./error.logLista arquivos criados (A), alterados (C) e removidos (D) desde a imagem.
docker diff meu-nginxSem limite, um container pode derrubar o host inteiro.
docker run -d --memory 512m --memory-swap 512m minha-app--cpus define quantos núcleos o container pode usar.
docker run -d --cpus 1.5 minha-appFaz o container voltar sozinho depois de falha ou reboot.
docker run -d --restart unless-stopped minha-app
# opções: no | on-failure | on-failure:3 | always | unless-stoppedO Docker passa a saber se o container está saudável, não só no ar.
docker run -d --health-cmd "curl -f http://localhost/health || exit 1" \
--health-interval 30s --health-retries 3 minha-appMostra o resultado das últimas verificações.
docker inspect -f '{{.State.Health.Status}}' minha-appDiz por que o container morreu — 137 é OOM/kill, 1 é erro da aplicação.
docker inspect -f '{{.State.ExitCode}}' minha-app
docker ps -a --filter "exited=137"Entra no container com um usuário específico (útil para depurar permissão).
docker exec -u root -it minha-app shContainer rodando como root é risco desnecessário.
docker run -d --user 1000:1000 minha-appSem recriar nada.
docker rename nome-antigo nome-novoCongela os processos sem encerrar o container.
docker pause minha-app
docker unpause minha-appBloqueia até a saída e devolve o código — útil em script.
docker wait minha-appFluxo ao vivo de tudo que acontece: criação, morte, health, rede.
docker events --filter container=minha-appDescobre para onde o container está mapeado.
docker port meu-nginxLimpeza rotineira.
docker container pruneEmergência ou fim de expediente.
docker stop $(docker ps -q)Congela o estado atual como imagem. Serve para depuração — nunca como forma de construir imagem.
docker commit minha-app minha-app:debugA receita da imagem. Cada instrução vira uma camada — e a ordem importa muito.
Imagem base. Primeira instrução de todo Dockerfile.
FROM node:20-alpineDefine o diretório de trabalho e o cria se não existir. Melhor que RUN cd.
WORKDIR /appCopia do contexto de build para dentro da imagem.
COPY package.json package-lock.json ./
COPY src ./srcComo o COPY, mas também baixa URL e descompacta tar. Prefira COPY: menos surpresa.
ADD https://exemplo.com/arquivo.tar.gz /tmp/Executa um comando no build e congela o resultado em uma camada.
RUN npm ci --omit=devCada RUN é uma camada. Junte comandos relacionados e limpe na mesma linha.
RUN apt-get update \
&& apt-get install -y --no-install-recommends curl \
&& rm -rf /var/lib/apt/lists/*O truque que mais acelera build: copie o manifesto e instale antes de copiar o código.
COPY package*.json ./
RUN npm ci # só refaz quando as dependências mudam
COPY . . # muda a cada commit, mas é a última camadaVariável de ambiente disponível no build e na execução.
ENV NODE_ENV=production PORT=3000Variável só do build. Não fica na imagem final — mas aparece no histórico, então nada de segredo.
ARG NODE_VERSION=20
FROM node:${NODE_VERSION}-alpineDocumenta a porta usada. Não publica nada sozinho — quem publica é o -p.
EXPOSE 3000Comando padrão do container, substituível na linha de comando.
CMD ["node", "server.js"]Comando fixo. Combinado com CMD, vira "programa + argumentos padrão".
ENTRYPOINT ["node"]
CMD ["server.js"]Use sempre a forma exec (JSON): na forma shell o processo vira filho do /bin/sh e não recebe os sinais.
CMD ["node", "server.js"] # exec — recebe SIGTERM
CMD node server.js # shell — não recebeTroca o usuário. Tudo depois disso roda sem root.
RUN addgroup -S app && adduser -S app -G app
USER appMarca um diretório como ponto de montagem de dado persistente.
VOLUME ["/var/lib/postgresql/data"]Ensina o Docker a verificar se a aplicação está mesmo respondendo.
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
CMD wget -qO- http://localhost:3000/health || exit 1Metadados da imagem: versão, autor, origem do código.
LABEL org.opencontainers.image.source="https://github.com/usuario/projeto"Reduz o contexto de build. Sem ele, você manda node_modules e .git para o daemon a cada build.
node_modules
.git
.env
dist
*.logCompila em uma etapa e copia só o resultado para a imagem final. Corta centenas de MB.
FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:20-alpine
WORKDIR /app
COPY --from=build /app/dist ./dist
COPY --from=build /app/node_modules ./node_modules
USER node
CMD ["node", "dist/server.js"]--from aceita qualquer imagem, não só um estágio anterior.
COPY --from=nginx:alpine /etc/nginx/nginx.conf /etc/nginx/nginx.confMantém o cache do gerenciador entre builds, sem inchar a imagem.
RUN --mount=type=cache,target=/root/.npm npm ciUsa a credencial durante o build sem gravá-la em nenhuma camada.
RUN --mount=type=secret,id=npmrc,target=/root/.npmrc npm ci
# docker build --secret id=npmrc,src=$HOME/.npmrc .Container é descartável; dado não pode ser. É para isso que existem os volumes.
Volume nomeado, gerenciado pelo Docker.
docker volume create dados-postgresMostra onde o volume vive no host.
docker volume ls
docker volume inspect dados-postgresO dado sobrevive à remoção do container.
docker run -d -v dados-postgres:/var/lib/postgresql/data postgres:16Monta uma pasta do host. É o que dá hot reload no desenvolvimento.
docker run -d -v $(pwd)/src:/app/src minha-appMais explícita que -v e recomendada em scripts.
docker run -d --mount type=bind,source=$(pwd)/src,target=/app/src minha-appContainer que não deveria escrever, não escreve.
docker run -d -v $(pwd)/config:/app/config:ro minha-appVolume para dado de produção (o Docker gerencia); bind mount para código em desenvolvimento.
# produção
docker run -v dados:/var/lib/mysql mysql:8
# desenvolvimento
docker run -v $(pwd):/app node:20 npm run devSistema de arquivos em memória: some quando o container para. Bom para dado sensível.
docker run -d --tmpfs /tmp:rw,size=64m minha-appContainer temporário que empacota o conteúdo do volume.
docker run --rm -v dados-postgres:/dados -v $(pwd):/backup alpine \
tar czf /backup/dados-postgres.tar.gz -C /dados .Caminho inverso do backup.
docker run --rm -v dados-postgres:/dados -v $(pwd):/backup alpine \
tar xzf /backup/dados-postgres.tar.gz -C /dadosVolume não some junto com o container — é preciso apagar de propósito.
docker volume rm dados-postgresCuidado: isso apaga dado de verdade. Confira a lista antes.
docker volume ls -f dangling=true
docker volume prune-v apaga os volumes anônimos daquele container.
docker rm -v minha-appErro clássico no Linux: o UID de dentro não é o mesmo de fora.
docker run -d --user $(id -u):$(id -g) -v $(pwd):/app minha-appComo os containers conversam entre si e com o mundo.
Toda instalação já vem com bridge, host e none.
docker network lsRede própria do projeto: os containers se enxergam pelo nome.
docker network create minha-redeNa criação ou depois, com o container já rodando.
docker run -d --network minha-rede --name api minha-app
docker network connect minha-rede outro-containerDentro da rede, o nome do container é o host. Não use IP.
# de dentro do container "api":
curl http://banco:5432
# em vez de http://172.18.0.3:5432Mostra a faixa de IP e quem está conectado.
docker network inspect minha-redeRede em uso não pode ser removida.
docker network disconnect minha-rede api
docker network rm minha-redeSem isolamento de rede: o container usa a pilha do host. Rápido, porém sem isolamento (Linux).
docker run -d --network host nginxIsolamento total. Bom para processar dado não confiável.
docker run --rm --network none alpine ip addrExpõe só no localhost, não na rede inteira.
docker run -d -p 127.0.0.1:8080:80 nginxTambém dá para expor intervalos e outro protocolo.
docker run -d -p 8000-8010:8000-8010 minha-app
docker run -d -p 53:53/udp meu-dnshost.docker.internal resolve para a máquina hospedeira.
curl http://host.docker.internal:5432Quando você realmente precisa dele.
docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' apiContainer descartável só para diagnosticar a rede.
docker run --rm --network minha-rede alpine ping -c 3 banco
docker run --rm --network minha-rede nicolaka/netshoot nslookup apiLimpeza de rotina.
docker network pruneVários containers descritos em um arquivo e subidos com um comando.
Descreve serviços, portas, variáveis e dependências.
services:
api:
build: .
ports:
- "3000:3000"
environment:
DATABASE_URL: postgres://app:senha@banco:5432/loja
depends_on:
banco:
condition: service_healthy
banco:
image: postgres:16-alpine
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: senha
POSTGRES_DB: loja
volumes:
- dados:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app"]
interval: 5s
retries: 5
volumes:
dados:-d em segundo plano; sem ele, os logs ficam no terminal.
docker compose up -dForça rebuild das imagens antes de subir.
docker compose up -d --builddown remove containers e redes; com -v, apaga também os volumes.
docker compose down
docker compose down -v # apaga os dados!Status de cada serviço da stack.
docker compose psTodos os serviços juntos, ou um específico.
docker compose logs -f
docker compose logs -f api --tail 100exec no container em execução; run cria um novo.
docker compose exec api sh
docker compose run --rm api npm run migrateSem mexer no resto da stack.
docker compose restart apiSobe várias instâncias do mesmo serviço.
docker compose up -d --scale worker=3Mostra a configuração final, com variáveis já resolvidas.
docker compose configUm base e um por ambiente — o segundo sobrescreve o primeiro.
docker compose -f compose.yaml -f compose.prod.yaml up -dO Compose lê o .env do diretório automaticamente.
# .env
POSTGRES_PASSWORD=senha-forte
# compose.yaml
environment:
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}Serviços que só sobem quando você pede — como ferramentas de desenvolvimento.
services:
adminer:
image: adminer
profiles: ["dev"]
# docker compose --profile dev up -dSem condition, o Compose só espera o container iniciar, não ficar pronto.
depends_on:
banco:
condition: service_healthyEvita que um serviço consuma a máquina toda.
deploy:
resources:
limits:
cpus: "1.5"
memory: 512MMantém os containers para religar depois.
docker compose stop
docker compose startAtualiza as imagens antes de subir.
docker compose pullstats filtrado pelos containers do projeto.
docker compose top
docker stats $(docker compose ps -q)O que separa um container que roda na sua máquina de um que aguenta produção.
Mostra quanto imagens, containers, volumes e cache estão ocupando.
docker system df
docker system df -v-a também remove imagens sem container. Confira antes — --volumes apaga dados.
docker system prune
docker system prune -a
docker system prune -a --volumesLog de container cresce sem limite e enche o disco do servidor. Este é o erro nº 1 em produção.
# /etc/docker/daemon.json
{
"log-driver": "json-file",
"log-opts": { "max-size": "10m", "max-file": "3" }
}Container root + volume no host = risco real de comprometer a máquina.
USER node # no Dockerfile
docker run --user 1000:1000 minha-appSe a aplicação não precisa escrever, não deixe.
docker run -d --read-only --tmpfs /tmp minha-appRemove privilégios do kernel que a aplicação não usa.
docker run -d --cap-drop ALL --cap-add NET_BIND_SERVICE nginxBloqueia binário com setuid ganhando privilégio dentro do container.
docker run -d --security-opt no-new-privileges minha-appEquivale a dar root do host ao container. Só em casos muito específicos e conscientes.
# evite
docker run --privileged minha-appMontar /var/run/docker.sock dá controle total do host a quem estiver no container.
# perigoso
docker run -v /var/run/docker.sock:/var/run/docker.sock minha-appO Docker manda SIGTERM e espera 10s antes do SIGKILL. Sua aplicação precisa tratar o sinal.
docker stop -t 30 minha-app--init insere um init mínimo que recolhe processos órfãos.
docker run -d --init minha-appVeja o log da execução anterior para achar a causa.
docker ps -a
docker logs --tail 200 minha-app
docker inspect -f '{{.State.ExitCode}} {{.State.Error}}' minha-appExit 137 quase sempre é falta de memória.
docker inspect -f '{{.State.OOMKilled}}' minha-app
docker stats --no-stream minha-appDistroless não tem shell: use um container auxiliar no mesmo namespace.
docker run --rm -it --pid container:minha-app --network container:minha-app \
nicolaka/netshoot shRode como produção antes de publicar: sem bind mount, com as variáveis reais.
docker run --rm -p 3000:3000 --env-file .env.production minha-app:1.0Publique com tag de versão e faça o deploy pelo digest — garantia de que é exatamente aquela imagem.
docker push usuario/app:1.4.2
docker inspect --format='{{index .RepoDigests 0}}' usuario/app:1.4.2Build e push dentro do pipeline, com cache entre execuções.
docker buildx build --push \
--cache-from type=registry,ref=usuario/app:cache \
--cache-to type=registry,ref=usuario/app:cache,mode=max \
-t usuario/app:$GIT_SHA .Compose resolve um servidor. Vários nós, escala automática e rolling update pedem orquestrador.
# Compose: 1 host, stack simples, time pequeno
# Kubernetes: múltiplos nós, autoscaling, deploy sem downtime