Docker do Zero ao Profissional: 100 Exemplos com Dockerfile, Compose e CI/CD

Docker do Zero ao Profissional: 100 Exemplos com Dockerfile, Compose e CI/CD

O Docker empacota uma aplicação e tudo o que ela precisa dentro de um contêiner, de modo que o mesmo software roda da mesma forma no notebook, no servidor e na nuvem. Este artigo leva você do primeiro contêiner até uma configuração pensada para produção, em 100 exemplos. Você vai aprender a CLI do Docker, imagens e registros, a escrever Dockerfiles com builds em vários estágios, volumes e redes, o Docker Compose para aplicações com vários contêineres, configuração e segredos, limites de recursos e verificações de saúde, segurança de contêineres, depuração, pipelines de CI/CD e uma primeira visão de Kubernetes e Swarm. Nota de transparência: o Docker não estava instalado no computador usado para preparar este artigo, então nenhum dos 100 exemplos foi executado aqui. Cada comando e arquivo foi escrito de acordo com a sintaxe oficial do Docker e verificado por leitura. Execute cada um você mesmo antes de confiar nele.

O que você precisa

Docker Desktop no Windows ou no macOS, ou Docker Engine no Linux, com o plugin Docker Compose (o comando docker compose version deve funcionar). Um editor como o Visual Studio Code com a extensão do Docker ajuda bastante. Conhecimento básico de linha de comando é suficiente para começar. Alguns exemplos usam um projeto de exemplo em Python ou Node.js, e outros usam um registro privado, então use valores de exemplo como registry.example.com e nunca cole senhas reais em comandos.

Como usar um exemplo

Os exemplos em bash usam a CLI do Docker e o Docker Compose. Os exemplos de Dockerfile são salvos em um arquivo chamado Dockerfile na pasta do projeto. Os exemplos em YAML são salvos em compose.yaml ou no arquivo do Kubernetes indicado no texto.

Tópico 1: Primeiros passos com o Docker

Um contêiner é um processo isolado, com seu próprio sistema de arquivos e rede, criado a partir de uma imagem. Aprenda as ideias centrais, verifique a instalação e execute seus primeiros contêineres.

Imagens, contêineres e registros

Uma imagem é um modelo somente leitura formado por camadas. Um contêiner é uma instância em execução de uma imagem. Um registro, como o Docker Hub ou o Azure Container Registry, guarda as imagens para que outras máquinas possam baixá-las.

Por que os contêineres ajudam

Os contêineres reúnem a aplicação com suas bibliotecas, então ela se comporta igual em qualquer máquina. Eles iniciam em segundos e compartilham o kernel do host, por isso usam muito menos memória do que máquinas virtuais completas.

Verificando a instalação

Execute docker version para ver o cliente e o servidor. Se a linha do servidor não aparecer, o Docker Desktop ou o serviço do Docker ainda não está em execução, então inicie-o antes de qualquer comando de contêiner.

Ilustrações

Como o Docker leva uma aplicação de um Dockerfile até um contêiner em execução: o Dockerfile é construído em uma imagem, a imagem é enviada a um registro e os hosts a baixam e a executam como contêineres
Como o Docker leva uma aplicação de um Dockerfile até um contêiner em execução: o Dockerfile é construído em uma imagem, a imagem é enviada a um registro e os hosts a baixam e a executam como contêineres

Exemplo 1: Verificar se o Docker está instalado

Confirme que o cliente consegue falar com o motor do Docker.

docker --version
docker info --format "server: {{.ServerVersion}}"

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 2: Executar o hello-world

Seu primeiro contêiner, baixado e executado com um comando.

docker run hello-world

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 3: Baixar uma imagem

Obtenha a imagem sem iniciar um contêiner.

docker pull nginx:1.27

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 4: Listar imagens locais

Veja quais imagens estão guardadas neste computador.

docker images
docker image ls --filter "reference=nginx"

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 5: Inspecionar uma imagem

Leia a arquitetura e o sistema operacional da imagem.

docker image inspect nginx:1.27 --format "arch={{.Architecture}} os={{.Os}}"

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 6: Verificar o uso de disco

Veja quanto espaço imagens, contêineres e volumes ocupam.

docker system df

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Tópico 2: Executando e gerenciando contêineres

O comando docker run cria um contêiner a partir de uma imagem. Você vai publicar portas, ler logs, executar comandos dentro dos contêineres e limpar os que não são mais necessários.

Modos destacado e interativo

-d executa o contêiner em segundo plano, e -it conecta um terminal. --rm remove o contêiner quando ele para, o que combina com experimentos curtos.

Portas e nomes

-p host:contêiner publica uma porta, então 8080:80 envia a porta 8080 do host para a porta 80 do contêiner. --name dá ao contêiner um nome legível para usar em comandos posteriores.

Ciclo de vida e limpeza

Contêineres parados mantêm seus dados até serem removidos. Use docker ps -a para vê-los e docker rm para apagá-los, e faça limpezas regulares em máquinas que constroem imagens com frequência.

Exemplo 7: Executar um contêiner em segundo plano

Inicie um contêiner destacado e liste-o.

docker run -d --name web nginx:1.27
docker ps

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 8: Publicar uma porta

Acesse o servidor web do contêiner a partir do host.

docker run -d --name web-8080 -p 8080:80 nginx:1.27
curl http://localhost:8080

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 9: Iniciar um shell temporário

Use --rm para remover o contêiner ao sair.

docker run -it --rm alpine:3.20 sh

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 10: Listar contêineres

Mostre os contêineres em execução e os que pararam.

docker ps
docker ps -a --format "table {{.Names}}\t{{.Status}}\t{{.Image}}"

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 11: Ler os logs de um contêiner

Mostre as últimas linhas ou acompanhe a saída ao vivo.

docker logs --tail 50 web
docker logs -f web

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 12: Executar um comando dentro de um contêiner

Abra um shell ou rode um comando em um contêiner ativo.

docker exec -it web sh
docker exec web cat /etc/nginx/nginx.conf

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 13: Parar, iniciar e reiniciar

Controle o ciclo de vida de um contêiner.

docker stop web
docker start web
docker restart web

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 14: Remover contêineres

Apague os contêineres que você não precisa mais.

docker rm -f web web-8080
docker container prune -f

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Tópico 3: Imagens e registros

Uma imagem tem nome, tag e digest. As tags podem mudar, os digests não, então você precisa dos dois para marcar, compartilhar e reproduzir software com exatidão.

Tags e digests

latest é apenas uma tag e muda sempre que o publicador a atualiza. Um digest, como sha256 seguido de um hash longo, identifica exatamente uma imagem, e é assim que um deploy se torna reproduzível.

Enviando para um registro

Faça login, marque a imagem com o endereço completo do registro e envie-a. Outras máquinas podem então baixar a mesma imagem pelo nome. Nunca envie imagens que contenham segredos.

Lendo o histórico da imagem

docker history mostra cada camada, a instrução que a criou e o seu tamanho. Ajuda a encontrar camadas grandes demais e a confirmar que uma senha nunca foi gravada na imagem.

Exemplo 15: Dar uma tag à imagem

Dê à imagem um nome e uma versão que sejam seus.

docker tag nginx:1.27 mycompany/nginx:1.0.0
docker images mycompany/nginx

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 16: Enviar uma imagem para um registro

Envie a imagem para que outras máquinas possam baixá-la.

docker login registry.example.com
docker tag mycompany/nginx:1.0.0 registry.example.com/team/nginx:1.0.0
docker push registry.example.com/team/nginx:1.0.0

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 17: Enviar para o Azure Container Registry

Entre com a CLI do Azure e envie para o seu registro.

az acr login --name <registry-name>
docker tag mycompany/nginx:1.0.0 <registry-name>.azurecr.io/nginx:1.0.0
docker push <registry-name>.azurecr.io/nginx:1.0.0

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 18: Executar um registro local

Inicie um registro privado no seu computador para testes.

docker run -d -p 5000:5000 --restart unless-stopped --name registry registry:2
docker tag nginx:1.27 localhost:5000/nginx:1.27
docker push localhost:5000/nginx:1.27

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 19: Fixar uma imagem pelo digest

O digest é uma impressão digital que não muda, ao contrário da tag.

docker pull nginx:1.27
docker image inspect nginx:1.27 --format "{{index .RepoDigests 0}}"
docker run -d nginx@sha256:<digest-from-the-line-above>

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 20: Ler as camadas de uma imagem

Veja qual instrução criou cada camada e seu tamanho.

docker history nginx:1.27
docker history --no-trunc --format "{{.Size}}\t{{.CreatedBy}}" nginx:1.27

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Tópico 4: Escrevendo Dockerfiles

Um Dockerfile é uma receita em texto. Cada instrução cria uma camada, então a ordem e o conteúdo das instruções determinam o tamanho, a velocidade das reconstruções e o que roda quando o contêiner inicia.

Instruções principais

FROM escolhe a imagem base. COPY e RUN adicionam arquivos e comandos. WORKDIR define a pasta para as instruções seguintes. CMD e ENTRYPOINT definem o que o contêiner executa.

ARG versus ENV

ARG existe apenas enquanto a imagem é construída. ENV permanece na imagem e fica visível para o contêiner em execução. Use ARG para escolhas de build e ENV para configurações que o programa lê.

Construindo e marcando

docker build -t nome:tag . envia a pasta ao construtor. Só são usados os arquivos que o Dockerfile copia, então mantenha o contexto de build pequeno com um arquivo .dockerignore.

Exemplo 21: Um Dockerfile mínimo

A receita menor e útil: uma imagem base e um comando.

FROM alpine:3.20
CMD ["echo", "Hello from a container"]

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 22: Construir e executar a imagem

Transforme um Dockerfile em imagem e inicie-a.

docker build -t hello-image:1.0 .
docker run --rm hello-image:1.0

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 23: Dockerfile para uma aplicação Python

Instale as dependências e copie o código para uma imagem enxuta.

FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python", "app.py"]

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 24: Dockerfile para uma aplicação Node.js

Instale os pacotes de produção e exponha uma porta.

FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 25: ARG e ENV

Defina valores de construção e variáveis de ambiente de execução.

FROM alpine:3.20
ARG APP_VERSION=dev
ENV APP_VERSION=$APP_VERSION
ENV APP_PORT=8080
CMD ["sh", "-c", "echo version=$APP_VERSION port=$APP_PORT"]

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 26: Passar argumentos de construção

Mude a versão na construção sem editar o arquivo.

docker build --build-arg APP_VERSION=1.4.2 -t vars-image:1.4.2 .
docker run --rm -e APP_PORT=9000 vars-image:1.4.2

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 27: WORKDIR e COPY

Defina a pasta de trabalho e copie arquivos para a imagem.

FROM alpine:3.20
WORKDIR /srv/site
COPY ./site/ ./
# ADD can unpack archives, but COPY is clearer for plain files
RUN ls -la /srv/site

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 28: ENTRYPOINT e CMD

Escolha o programa fixo e seus argumentos padrão.

FROM alpine:3.20
RUN apk add --no-cache curl
ENTRYPOINT ["curl", "-s"]
CMD ["https://example.com"]

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Tópico 5: Boas práticas para imagens

Boas imagens são pequenas, reproduzíveis, rápidas de construir e seguras para executar. Builds em vários estágios, ordem das camadas, versões fixadas, usuários não root e verificações de saúde cobrem a maior parte da prática.

Builds em vários estágios

Um estágio de build guarda compiladores e ferramentas, e o estágio final copia apenas o resultado. A imagem final continua pequena e não contém código-fonte nem ferramentas de build que um atacante poderia usar.

Cache de camadas

O Docker reaproveita uma camada quando a instrução e as entradas não mudaram, e reconstrói tudo depois da primeira alteração. Copie os arquivos de dependências e instale-as antes de copiar o código-fonte.

Usuários, versões e verificações

Execute como um usuário numérico não root, fixe as versões das imagens base, adicione rótulos de dono e versão, e use HEALTHCHECK para que o Docker consiga informar se a aplicação realmente funciona.

Exemplo 29: Excluir arquivos com .dockerignore

Mantenha segredos, dados do Git e dependências fora da construção.

cat > .dockerignore <<'EOF'
.git
node_modules
*.log
.env
EOF
docker build -t app:1.0 .

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 30: Build em vários estágios para Go

Compile em uma imagem grande e entregue apenas o programa.

FROM golang:1.23 AS build
WORKDIR /src
COPY go.mod ./
COPY . .
RUN CGO_ENABLED=0 go build -o /out/app .

FROM gcr.io/distroless/static-debian12
COPY --from=build /out/app /app
ENTRYPOINT ["/app"]

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 31: Build em vários estágios para front-end

Gere os arquivos estáticos e sirva-os com o Nginx.

FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM nginx:1.27-alpine
COPY --from=build /app/dist /usr/share/nginx/html

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 32: Executar como usuário não root

Limite o estrago se a aplicação for atacada.

FROM python:3.12-slim
RUN useradd --create-home --uid 10001 appuser
WORKDIR /home/appuser/app
COPY --chown=appuser:appuser . .
USER appuser
CMD ["python", "app.py"]

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 33: Fixar versões das imagens base

Evite surpresas da tag latest, que muda com o tempo.

# Avoid "latest": it changes without warning
FROM python:3.12.5-slim-bookworm
# Pin system packages when the build must be reproducible
RUN apt-get update && apt-get install -y --no-install-recommends curl=8.* \
 && rm -rf /var/lib/apt/lists/*

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 34: Ordenar camadas para usar o cache

Copie os arquivos de dependências primeiro para manter a construção rápida.

FROM node:20-alpine
WORKDIR /app
# Dependencies change less often than source code, so copy them first
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
CMD ["npm", "start"]

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 35: Adicionar rótulos à imagem

Registre título, versão e responsável dentro da imagem.

FROM alpine:3.20
LABEL org.opencontainers.image.title="sample-api" \
      org.opencontainers.image.version="1.4.2" \
      org.opencontainers.image.maintainer="[email protected]"

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 36: HEALTHCHECK

Avise o Docker se a aplicação está funcionando.

FROM nginx:1.27-alpine
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
  CMD wget -qO- http://localhost/ || exit 1

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Tópico 6: Volumes e dados persistentes

Contêineres são descartáveis, mas bancos de dados e uploads não. Volumes e bind mounts mantêm os dados fora da camada gravável do contêiner, então eles sobrevivem a reinicializações e atualizações.

Volumes nomeados

Um volume nomeado é gerenciado pelo Docker e guardado fora do contêiner. Use-o com bancos de dados, porque os dados permanecem quando o contêiner é removido e você pode fazer backup com um contêiner auxiliar.

Bind mounts

Um bind mount associa uma pasta do seu computador ao contêiner. É prático durante o desenvolvimento, pois as alterações de código aparecem na hora, mas dá ao contêiner acesso direto aos arquivos do host, então use :ro quando ele só precisar ler.

Backup e dados temporários

Faça backup de um volume executando um contêiner pequeno que monta o volume e grava um arquivo tar. Use tmpfs para arquivos temporários que devem ficar na memória e desaparecer quando o contêiner parar.

Exemplo 37: Criar um volume nomeado

Guarde dados que devem sobreviver à remoção do contêiner.

docker volume create app-data
docker volume ls
docker volume inspect app-data

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 38: Volume nomeado com um banco de dados

Guarde os dados do PostgreSQL com segurança ao reiniciar o contêiner.

docker run -d --name db -e POSTGRES_PASSWORD=<strong-password> \
  -v app-data:/var/lib/postgresql/data postgres:16
docker exec db psql -U postgres -c "select 1"

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 39: Montar uma pasta do host com bind mount

Compartilhe uma pasta do seu computador durante o desenvolvimento.

docker run --rm -it -v "$(pwd)/site:/usr/share/nginx/html:ro" -p 8081:80 nginx:1.27-alpine

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 40: Montagem somente leitura

Deixe o contêiner ler a configuração sem poder alterá-la.

docker run --rm -v /srv/config:/etc/app:ro alpine:3.20 ls -l /etc/app

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 41: Fazer backup de um volume

Arquive os dados do volume em um arquivo tar com um contêiner auxiliar.

docker run --rm -v app-data:/data -v "$(pwd):/backup" alpine:3.20 \
  tar czf /backup/app-data-backup.tgz -C /data .

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 42: Restaurar um volume

Devolva os dados arquivados para um volume novo.

docker volume create app-data-restored
docker run --rm -v app-data-restored:/data -v "$(pwd):/backup" alpine:3.20 \
  tar xzf /backup/app-data-backup.tgz -C /data

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 43: tmpfs para dados temporários

Guarde arquivos temporários na memória e apague-os ao parar.

docker run --rm --tmpfs /tmp:rw,size=64m alpine:3.20 sh -c "echo scratch > /tmp/x && cat /tmp/x"

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Tópico 7: Redes de contêineres

O Docker dá a cada contêiner a sua própria rede. Redes definidas pelo usuário permitem que os contêineres se encontrem pelo nome, e a publicação de portas decide quais serviços podem ser acessados de fora.

Rede bridge e redes definidas pelo usuário

A rede bridge padrão dá aos contêineres apenas endereços IP. Uma rede bridge definida pelo usuário também oferece DNS, então um contêiner alcança outro pelo nome, por exemplo http://api ou redis://cache.

Publicando portas com segurança

Por padrão, as portas são publicadas em todas as interfaces. Associe a 127.0.0.1 para manter um banco de dados privado no seu computador. EXPOSE apenas documenta uma porta e não a abre.

Várias redes

Um contêiner pode entrar em várias redes. Coloque o banco de dados em uma rede de back-end e o proxy web em uma rede de front-end, para que só os serviços que precisam do banco consigam alcançá-lo.

Exemplo 44: Listar e inspecionar redes

Veja as redes criadas pelo Docker e suas faixas de endereços.

docker network ls
docker network inspect bridge --format "{{range .IPAM.Config}}{{.Subnet}}{{end}}"

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 45: Criar uma rede definida pelo usuário

Dê aos contêineres relacionados uma rede privada própria.

docker network create app-net
docker run -d --name api --network app-net nginx:1.27
docker run --rm --network app-net alpine:3.20 wget -qO- http://api

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 46: Encontrar contêineres pelo nome

Use os nomes dos contêineres como nomes de host na rede.

docker run -d --name cache --network app-net redis:7-alpine
docker run --rm --network app-net redis:7-alpine redis-cli -h cache ping

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 47: Conectar um contêiner a uma segunda rede

Permita que um serviço alcance duas redes separadas.

docker network create back-net
docker network connect back-net api
docker inspect api --format "{{json .NetworkSettings.Networks}}"

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 48: Publicar em apenas uma interface

Deixe o banco de dados acessível somente a partir deste computador.

docker run -d -p 127.0.0.1:5432:5432 --name pg -e POSTGRES_PASSWORD=<strong-password> postgres:16

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 49: Expose versus publish

EXPOSE apenas documenta a porta; -p a torna acessível pelo host.

FROM node:20-alpine
# EXPOSE only documents the port; it does not open it to the host
EXPOSE 3000
CMD ["node", "server.js"]

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 50: Usar a rede do host

Compartilhe a pilha de rede do host no Linux em casos especiais.

docker run --rm --network host alpine:3.20 sh -c "ip addr show eth0 | head -3"

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Tópico 8: Fundamentos do Docker Compose

O Compose descreve uma aplicação com vários contêineres em um único arquivo YAML. Um comando inicia, para e inspeciona cada serviço, rede e volume do projeto.

O arquivo compose

A chave services lista cada contêiner. Cada serviço tem uma seção image ou build, além de ports, environment, volumes e networks. O Compose cria automaticamente uma rede compartilhada para o projeto.

Comandos

docker compose up -d inicia o projeto, docker compose ps mostra o estado, docker compose logs -f acompanha a saída e docker compose down para e remove os contêineres e a rede padrão.

Substituições

compose.yaml guarda as configurações compartilhadas, e compose.override.yaml é mesclado automaticamente. Mantenha as alterações locais no arquivo de substituição, para que o arquivo principal seja o mesmo em todos os ambientes.

Exemplo 51: Um arquivo Compose mínimo

Descreva um serviço web em um único arquivo YAML.

services:
  web:
    image: nginx:1.27-alpine
    ports:
      - "8080:80"

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 52: Iniciar e parar um projeto Compose

Suba ou derrube um aplicativo com vários contêineres de uma vez.

docker compose up -d
docker compose ps
docker compose down

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 53: Construir a imagem dentro do Compose

Deixe o Compose construir a imagem a partir de um Dockerfile.

services:
  api:
    build:
      context: ./api
      dockerfile: Dockerfile
    ports:
      - "3000:3000"

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 54: Variáveis de ambiente no Compose

Defina as configurações de cada serviço no arquivo.

services:
  api:
    build: ./api
    environment:
      APP_ENV: development
      LOG_LEVEL: info

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 55: Esperar outro serviço

Use depends_on para controlar a ordem de inicialização.

services:
  api:
    build: ./api
    depends_on:
      - db
  db:
    image: postgres:16

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 56: Volumes nomeados no Compose

Mantenha os arquivos do banco entre reinicializações do projeto.

services:
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: <strong-password>
    volumes:
      - db-data:/var/lib/postgresql/data
volumes:
  db-data:

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 57: Logs, exec e comandos avulsos

Execute testes ou ferramentas dentro do projeto.

docker compose logs -f api
docker compose exec db psql -U postgres
docker compose run --rm api npm test

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 58: Arquivos de override para desenvolvimento

Separe as configurações de produção das alterações locais.

# compose.yaml holds the shared settings
services:
  api:
    build: ./api
# compose.override.yaml is merged automatically on "docker compose up"
services:
  api:
    volumes:
      - ./api:/app
    environment:
      DEBUG: "1"

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Tópico 9: Compose para aplicações reais

Aplicações reais têm uma camada web, um banco de dados, um cache e um proxy. O Compose conecta tudo, espera que cada parte esteja pronta e permite escalar as partes que precisam de mais capacidade.

Web, banco de dados e cache

Dê a cada serviço uma única função, guarde os arquivos do banco em um volume nomeado e acesse cada serviço pelo nome. Passe a string de conexão por uma variável de ambiente, em vez de fixar o nome do host no código.

Ordem de inicialização e saúde

depends_on só espera o contêiner iniciar. Adicione um healthcheck ao banco e use condition: service_healthy para que a aplicação só inicie depois que o banco aceitar conexões.

Escala e isolamento

docker compose up --scale altera o número de cópias de um serviço sem estado. Separe as redes para que o proxy público alcance a API, mas não consiga acessar o banco diretamente.

Exemplo 59: Perfis para serviços opcionais

Suba as ferramentas de administração somente quando pedir.

services:
  api:
    build: ./api
  pgadmin:
    image: dpage/pgadmin4
    profiles: ["tools"]

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 60: Iniciar um perfil

Ative um serviço opcional pela linha de comando.

docker compose up -d
docker compose --profile tools up -d

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 61: Aplicação web com PostgreSQL

Um aplicativo completo com banco de dados e dados persistentes.

services:
  web:
    build: ./web
    ports:
      - "8000:8000"
    depends_on:
      - db
  db:
    image: postgres:16
    environment:
      POSTGRES_DB: shop
      POSTGRES_USER: shop
      POSTGRES_PASSWORD: <strong-password>
    volumes:
      - pg-data:/var/lib/postgresql/data
volumes:
  pg-data:

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 62: Adicionar Redis como cache

Conecte um armazenamento chave-valor rápido pelo nome do serviço.

services:
  web:
    build: ./web
    environment:
      REDIS_URL: redis://cache:6379
  cache:
    image: redis:7-alpine
    command: ["redis-server", "--save", "", "--appendonly", "no"]

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 63: Nginx como proxy reverso

Envie o tráfego web para a aplicação por um único ponto de entrada.

services:
  proxy:
    image: nginx:1.27-alpine
    ports:
      - "80:80"
    volumes:
      - ./proxy/default.conf:/etc/nginx/conf.d/default.conf:ro
    depends_on:
      - app
  app:
    build: ./app

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 64: Esperar o banco ficar saudável

Inicie a aplicação só depois que o banco aceitar consultas.

services:
  api:
    build: ./api
    depends_on:
      db:
        condition: service_healthy
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: <strong-password>
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      retries: 10

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Tópico 10: Configuração e segredos

A configuração muda de um ambiente para outro, e segredos nunca podem acabar em uma imagem ou no Git. Use variáveis de ambiente, arquivos env e arquivos de segredo, e leia-os com segurança no seu programa.

Variáveis de ambiente e arquivos .env

O Compose lê um arquivo .env para substituir variáveis no arquivo compose. Coloque .env no .gitignore e versione um arquivo .env.example com valores de exemplo, para que outros desenvolvedores saibam o que configurar.

Segredos como arquivos

Um segredo baseado em arquivo é montado em /run/secrets dentro do contêiner. Ler de um arquivo mantém o valor fora da saída do docker inspect e da lista de processos, onde variáveis de ambiente podem vazar.

Nunca grave segredos na imagem

Um valor copiado para uma camada continua no histórico da imagem, mesmo que uma etapa posterior o apague. Passe os segredos em tempo de execução, verifique o histórico com docker history e troque qualquer segredo que já tenha sido enviado.

Exemplo 65: Escalar um serviço

Execute várias cópias de um worker.

docker compose up -d --scale worker=3
docker compose ps worker

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 66: Redes de front-end e back-end

Mantenha o banco de dados fora da rede pública.

services:
  proxy:
    image: nginx:1.27-alpine
    networks: [front]
    ports: ["80:80"]
  api:
    build: ./api
    networks: [front, back]
  db:
    image: postgres:16
    networks: [back]
networks:
  front:
  back:

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 67: Carregar variáveis do .env

Mantenha os valores locais fora do arquivo Compose.

cat > .env <<'EOF'
POSTGRES_PASSWORD=<strong-password>
APP_PORT=8000
EOF
docker compose config

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 68: Usar env_file

Carregue um grupo inteiro de configurações de um arquivo.

services:
  api:
    build: ./api
    env_file:
      - ./config/api.env

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 69: Um arquivo de segredo no Compose

Entregue a senha ao serviço sem colocá-la na imagem.

services:
  api:
    build: ./api
    secrets:
      - db_password
secrets:
  db_password:
    file: ./secrets/db_password.txt

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 70: Ler configurações em Python

Use valores padrão e converta textos do ambiente em números.

import os

db_host = os.environ.get("DB_HOST", "localhost")
db_port = int(os.environ.get("DB_PORT", "5432"))
print(f"connect to {db_host}:{db_port}")

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 71: Ler um arquivo de segredo em Python

Carregue a senha pelo caminho do segredo dentro do contêiner.

from pathlib import Path

secret_file = Path("/run/secrets/db_password")
password = secret_file.read_text(encoding="utf-8").strip()
print("password length:", len(password))

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 72: Configurações em tempo de execução

Substitua valores em uma execução e confira o histórico da imagem.

docker run --rm -e LOG_LEVEL=debug -e DB_HOST=db.internal app:1.0
docker history --no-trunc app:1.0 | grep -i password || echo "no password in the layers"

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Tópico 11: Recursos, saúde e logs

Um contêiner pode usar toda a memória e a CPU do host se você permitir. Limites, políticas de reinício, verificações de saúde e rotação de logs mantêm os serviços estáveis e impedem que o disco encha.

Limites de memória e CPU

--memory interrompe um contêiner que usa RAM demais, e --cpus limita sua parcela de tempo de processador. Defina os limites com base no uso medido por docker stats, e não por palpite.

Políticas de reinício

unless-stopped reinicia o contêiner depois de uma falha ou de uma reinicialização do host, mas não depois de você tê-lo parado manualmente. Use para serviços de longa duração e corrija a causa de falhas repetidas, em vez de depender de reinícios.

Logs e saúde

O Docker guarda a saída do contêiner no driver de log. Limite o tamanho do json-file com max-size e max-file. O estado de saúde mostra starting, healthy ou unhealthy, o que balanceadores de carga e orquestradores podem usar.

Exemplo 73: Limite de memória

Impeça um processo descontrolado de consumir toda a RAM.

docker run -d --name api-mem --memory 256m --memory-swap 256m nginx:1.27-alpine
docker stats --no-stream api-mem

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 74: Limite de CPU

Dê ao contêiner meio núcleo de processador.

docker run -d --name api-cpu --cpus 0.5 nginx:1.27-alpine

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 75: Política de reinício

Reinicie o serviço depois de uma falha ou de um reinício da máquina.

docker run -d --name worker --restart unless-stopped app:1.0
docker inspect worker --format "{{.HostConfig.RestartPolicy.Name}}"

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 76: Limitar o tamanho dos logs

Impeça que os arquivos de log lotem o disco.

docker run -d --name api-logs \
  --log-driver json-file --log-opt max-size=10m --log-opt max-file=3 nginx:1.27-alpine

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 77: Ler o estado de saúde

Veja se o contêiner está saudável, iniciando ou não saudável.

docker ps --format "table {{.Names}}\t{{.Status}}"
docker inspect --format "{{.State.Health.Status}}" api

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 78: Limpar objetos não utilizados

Remova com segurança contêineres parados e imagens órfãs.

docker container prune -f
docker image prune -f
docker volume ls -qf dangling=true

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Tópico 12: Segurança de contêineres

Um contêiner compartilha o kernel do host, então uma configuração descuidada pode dar a um atacante mais acesso do que o pretendido. Privilégio mínimo, sistemas de arquivos somente leitura, varredura e imagens base atualizadas reduzem o risco.

Privilégio mínimo

Execute como um usuário não root, remova as capacidades Linux de que não precisa com --cap-drop ALL e devolva apenas as específicas que são necessárias. Use no-new-privileges para impedir que processos ganhem mais direitos.

Sistemas de arquivos somente leitura

--read-only impede que o contêiner altere os próprios arquivos. Forneça montagens tmpfs para os poucos diretórios que precisam ser graváveis, como /tmp.

Varredura e atualizações

Verifique as imagens com o Docker Scout ou outro scanner, reconstrua regularmente para pegar imagens base corrigidas e faça o build falhar quando forem encontradas vulnerabilidades críticas. Nunca execute contêineres com --privileged sem um motivo específico.

Exemplo 79: Remover capacidades do Linux

Tire permissões que a aplicação não precisa.

docker run -d --name locked --cap-drop ALL --cap-add NET_BIND_SERVICE -p 80:80 nginx:1.27-alpine

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 80: Sistema de arquivos raiz somente leitura

Impeça o contêiner de gravar nos próprios arquivos do sistema.

docker run -d --name ro-app --read-only --tmpfs /tmp --tmpfs /var/run nginx:1.27-alpine

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 81: Executar com um usuário numérico

Escolha o ID do usuário em vez de confiar em um nome.

docker run --rm --user 10001:10001 alpine:3.20 id

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 82: Bloquear escalonamento de privilégios

Impeça que processos ganhem mais direitos.

docker run -d --name hardened --security-opt no-new-privileges:true nginx:1.27-alpine

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 83: Verificar a imagem em busca de vulnerabilidades

Encontre CVEs conhecidos nos pacotes da imagem.

docker scout cves nginx:1.27-alpine
docker scout quickview nginx:1.27-alpine

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 84: Configurações seguras no Compose

Combine as opções de reforço em um único serviço.

services:
  api:
    build: ./api
    user: "10001:10001"
    read_only: true
    cap_drop: [ALL]
    security_opt: ["no-new-privileges:true"]
    tmpfs: [/tmp]

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Tópico 13: Depuração e solução de problemas

Quando um contêiner falha, as respostas estão nos seus logs, nos dados do inspect, nos eventos e nos processos. Uma rotina sistemática encontra a maioria dos problemas em poucos minutos.

Logs, eventos e inspect

Leia primeiro o docker logs. docker events mostra o que o Docker fez e quando, e docker inspect mostra o código de saída, o ambiente, as montagens e a rede, para que você compare o contêiner em execução com o que esperava.

Verificações de recursos e processos

docker stats mostra CPU e memória por contêiner, e docker top lista os processos. Um código de saída 137 costuma significar que o kernel encerrou o processo por usar memória demais, o que você pode confirmar pelo campo OOMKilled.

Depuração de rede

Execute uma imagem de ferramentas no mesmo namespace de rede com --network container:nome e use curl, dig e ping de dentro. Isso testa a visão real que o contêiner tem da rede sem alterar nada.

Exemplo 85: Acompanhar eventos do Docker

Observe contêineres iniciando, parando e falhando em tempo real.

docker events --filter type=container --since 10m

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 86: Acompanhar o uso de recursos

Veja CPU e memória de cada contêiner.

docker stats --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}"

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 87: Listar processos de um contêiner

Veja o que está rodando lá dentro sem abrir um shell.

docker top web

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 88: Ler um campo do inspect

Obtenha o código de saída e se o kernel encerrou o processo.

docker inspect web --format "{{.State.ExitCode}} {{.State.OOMKilled}}"

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 89: Copiar arquivos para dentro e para fora

Traga um arquivo de configuração ou coloque uma correção no contêiner.

docker cp web:/etc/nginx/nginx.conf ./nginx.conf
docker cp ./nginx.conf web:/etc/nginx/nginx.conf

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 90: Depurar com uma imagem de ferramentas de rede

Compartilhe a rede de um contêiner e teste com curl e dig.

docker run --rm -it --network container:web nicolaka/netshoot
# inside the tools shell: curl -v http://localhost:80

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Tópico 14: Pipelines de CI/CD para imagens

Um pipeline constrói, testa, verifica e envia uma imagem sempre que o código muda. A automação torna cada imagem rastreável até um commit e mantém os lançamentos iguais de uma execução para a outra.

Build e push no CI

Um pipeline faz login com uma credencial de vida curta, constrói a imagem, executa os testes e a envia com uma tag que inclui o commit. Os mesmos passos funcionam no GitHub Actions e no Azure Pipelines.

Estratégia de tags

Marque cada imagem com o commit do Git para rastreabilidade, e mova uma tag de versão somente quando o lançamento for aprovado. Evite depender de latest em produção, porque ela esconde qual código está rodando.

Higiene dos runners

Runners de CI acumulam camadas e volumes. Limpe o cache de build conforme um cronograma e defina um limite de tamanho para o cache, para que a máquina de build não encha o disco e falhe de forma imprevisível.

Exemplo 91: Construir e enviar no GitHub Actions

Automatize a construção da imagem a cada envio para main.

name: build-image
on:
  push:
    branches: [main]
jobs:
  build:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
    steps:
      - uses: actions/checkout@v4
      - uses: docker/build-push-action@v6
        with:
          context: .
          push: true
          tags: ghcr.io/example/app:${{ github.sha }}

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 92: Construir uma imagem no Azure Pipelines

Use a tarefa do Docker com uma conexão de serviço.

trigger:
  - main
pool:
  vmImage: ubuntu-latest
steps:
  - task: Docker@2
    inputs:
      command: buildAndPush
      containerRegistry: my-acr-connection
      repository: sample-api
      tags: |
        $(Build.BuildId)

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 93: Marcar a imagem com o commit do Git

Rastreie cada imagem até o código-fonte exato.

SHA=$(git rev-parse --short HEAD)
docker build -t app:$SHA .
docker tag app:$SHA app:latest
echo "built app:$SHA"

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 94: Limpar o cache de build em um runner de CI

Impeça que a máquina de build fique sem disco.

docker builder prune --filter until=72h -f
docker image prune -a --filter until=168h -f

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Tópico 15: Produção e orquestração

Um único host basta para um serviço pequeno. Quando você precisa de várias máquinas, autorrecuperação e atualizações graduais, um orquestrador como Kubernetes ou Docker Swarm executa os contêineres por você.

Checklist de produção

Fixe as versões das imagens, defina limites de memória, configure as políticas de reinício, adicione verificações de saúde, associe as portas à interface pretendida, mantenha os segredos fora da imagem e envie os logs para um local centralizado.

Fundamentos do Kubernetes

Um Deployment mantém um número de réplicas em execução e faz atualizações graduais. Um Service dá às réplicas um endereço estável. A mesma imagem de contêiner roda sem alterações tanto no Compose quanto no Kubernetes.

Swarm e quando usar

O Docker Swarm usa um arquivo no estilo do Compose em uma stack e executa serviços em várias máquinas. O Kubernetes tem um ecossistema maior e é comum em serviços de nuvem gerenciados, então escolha a ferramenta que a sua equipe consegue operar.

Exemplo 95: Configurações de produção no Compose

Fixe a versão, limite a memória, reinicie e verifique a saúde.

services:
  api:
    image: registry.example.com/team/api:1.4.2
    restart: unless-stopped
    env_file: ./config/api.env
    ports:
      - "127.0.0.1:8000:8000"
    deploy:
      resources:
        limits:
          memory: 512M
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
      interval: 30s
      timeout: 3s
      retries: 3

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 96: Um Deployment do Kubernetes

Execute três cópias da mesma imagem no cluster.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: api
spec:
  replicas: 3
  selector:
    matchLabels:
      app: api
  template:
    metadata:
      labels:
        app: api
    spec:
      containers:
        - name: api
          image: registry.example.com/team/api:1.4.2
          ports:
            - containerPort: 8000
          resources:
            limits:
              memory: 512Mi
              cpu: "500m"

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 97: Um Service do Kubernetes

Dê às cópias um endereço estável dentro do cluster.

apiVersion: v1
kind: Service
metadata:
  name: api
spec:
  selector:
    app: api
  ports:
    - port: 80
      targetPort: 8000
  type: ClusterIP

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 98: Implantar uma stack do Docker Swarm

Execute os mesmos serviços em várias máquinas.

docker swarm init
docker stack deploy -c stack.yml shop
docker service ls

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 99: Verificar contêineres com o Docker SDK

Leia os estados dos contêineres a partir de código Python.

import docker

client = docker.from_env()
for c in client.containers.list():
    state = c.attrs["State"].get("Health", {}).get("Status", "no healthcheck")
    print(c.name, state)

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Exemplo 100: Um script de checklist de lançamento

Interrompa o lançamento se a imagem rodar como root ou tiver CVEs críticos.

#!/usr/bin/env bash
set -euo pipefail
IMAGE="$1"
docker image inspect "$IMAGE" > /dev/null
USER_ID=$(docker run --rm --entrypoint id "$IMAGE" -u)
[ "$USER_ID" != "0" ] || { echo "image runs as root"; exit 1; }
docker scout cves --exit-code --only-severity critical "$IMAGE"
echo "release checks passed for $IMAGE"

*Este exemplo não foi executado aqui, porque o Docker não estava instalado no computador que preparou este artigo.*

Laboratório: construir, executar e lançar uma pequena aplicação web

Reúna o artigo inteiro em um único projeto: uma pequena aplicação web, um Dockerfile com build em vários estágios, um arquivo Compose com banco de dados e volume, verificações de saúde e uma checagem antes do lançamento.

1. Crie uma pasta com uma pequena aplicação e um arquivo requirements.txt, depois escreva um Dockerfile que a execute como usuário não root e tenha um HEALTHCHECK.

2. Adicione um arquivo .dockerignore e construa a imagem com uma tag de versão, por exemplo docker build -t lab-web:1.0.0 .

3. Escreva um compose.yaml com o serviço web e um serviço PostgreSQL que use um volume nomeado e um healthcheck, e inicie-o com docker compose up -d.

4. Verifique o estado de saúde com docker compose ps e leia os logs com docker compose logs -f web.

5. Execute docker scout cves na imagem, corrija o que for possível atualizando a imagem base e reconstrua.

6. Execute a checklist de lançamento do exemplo 100 na sua imagem e interrompa o lançamento se ela falhar.

7. Pare o projeto com docker compose down e remova o volume nomeado somente quando não precisar mais dos dados.

Próximos passos

Depois do laboratório, leia a documentação oficial do Docker para o runtime que você escolheu, pratique em um projeto separado e aprenda um orquestrador em profundidade quando o seu serviço precisar de mais de uma máquina. Guarde os exemplos deste artigo em um repositório para executá-los de novo após cada atualização do Docker.

Resumo

O Docker transforma uma aplicação em uma imagem que roda igual em qualquer lugar. Você aprendeu a executar e gerenciar contêineres, fixar imagens com tags e digests, escrever Dockerfiles eficientes com builds em vários estágios, guardar dados em volumes, conectar serviços por redes, descrever a aplicação inteira com o Compose, tratar configuração e segredos com segurança, limitar recursos, reforçar contêineres, depurar de forma sistemática, automatizar builds em CI/CD e se preparar para a orquestração em produção.