Pular para o conteúdo
Cursos

DevClub

LógicaFront-endBack-endMobile

IA Club

IA na prática
Estudar programaçãoEstudar IA
LiçãoIntermediáriocódigo testado

Docker Compose: subir API, rede e volume com um comando

Crie um compose.yaml para uma API Node, use variável obrigatória, healthcheck, rede e volume e diagnostique a configuração antes de iniciar.

Rodolfo Mori5 min de leitura

Docker Compose lê um arquivo compose.yaml e cria os serviços, redes e volumes de uma aplicação como um único projeto. Nesta lição você vai subir uma API Node, esperar seu healthcheck, chamar a API por nome em outro contêiner e preservar um volume depois do down.

Esta é a sexta etapa da trilha Docker. Ela junta o que já foi provado em redes Docker e volumes Docker; Compose automatiza essas peças, mas não muda o mecanismo delas.

A ficha de abertura organiza todas as equipes

Imagine a abertura de uma feira de bairro. A ficha do evento diz quais barracas existem, qual depósito usam, em que circuito elétrico entram e qual inspeção uma barraca precisa passar antes de outra começar o atendimento. A ficha é o compose.yaml; cada barraca é um serviço; o circuito é a rede; o depósito é o volume; a inspeção é o healthcheck.

Voltando aos termos técnicos: um projeto Compose agrupa recursos com um nome. Cada service vira um ou mais contêineres. Compose cria uma rede padrão e registra os nomes dos serviços no DNS. Volumes declarados no topo recebem nomes associados ao projeto.

A analogia não significa que Compose executa a lógica da aplicação. Ele cria e coordena recursos. Quem responde HTTP é o Node; quem decide se uma rota está correta é seu código.

A aplicação deixa duas provas visíveis

Crie server.js. Além de responder HTTP, ele grava no volume a mensagem recebida por variável de ambiente:

js
import { createServer } from 'node:http';
import { writeFileSync } from 'node:fs';

const port = 3000;
const mensagem = process.env.MENSAGEM;

writeFileSync('/dados/ultima-mensagem.txt', `${mensagem}\n`);

const server = createServer((request, response) => {
  const body = request.url === '/health'
    ? { status: 'ok' }
    : { servico: 'cantina', mensagem, node: process.version };

  response.writeHead(200, {
    'content-type': 'application/json; charset=utf-8',
  });
  response.end(JSON.stringify(body));
});

server.listen(port, '0.0.0.0', () => {
  console.log(`API pronta na porta ${port}; mensagem gravada no volume`);
});

O endpoint /health responde sem depender do arquivo. Em projeto real, o healthcheck deve verificar aquilo que define prontidão sem provocar alteração de estado a cada consulta.

O package.json mantém o comando explícito:

json
{
  "name": "cantina-compose",
  "private": true,
  "type": "module",
  "scripts": {
    "start": "node server.js"
  }
}

A imagem prepara o ponto de montagem

O Dockerfile cria /dados, entrega a pasta ao usuário sem privilégio e inicia a API:

text
FROM node:24-alpine
WORKDIR /app
COPY --chown=node:node package.json server.js ./
RUN mkdir /dados && chown node:node /dados
USER node
EXPOSE 3000
CMD ["npm", "start"]

Quando um volume vazio é montado nesse caminho, a aplicação node consegue gravar sem voltar a rodar como root. A lógica de cada instrução foi construída na lição de Dockerfile para Node.

Exclua arquivos que não pertencem ao contexto:

text
node_modules
.git
.env

O .env fica fora da imagem. Isso evita cópia acidental; não transforma o arquivo local em cofre.

compose.yaml descreve o conjunto

Salve este arquivo ao lado dos anteriores:

text
name: cantina-devclub

services:
  api:
    build: .
    environment:
      MENSAGEM: ${MENSAGEM:?defina MENSAGEM}
    ports:
      - "127.0.0.1:3077:3000"
    volumes:
      - historico:/dados
    healthcheck:
      test:
        - CMD
        - node
        - -e
        - fetch('http://127.0.0.1:3000/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))
      interval: 2s
      timeout: 1s
      retries: 10

  verificador:
    image: node:24-alpine
    depends_on:
      api:
        condition: service_healthy
    command:
      - node
      - -e
      - fetch('http://api:3000').then(r=>r.text()).then(console.log)

volumes:
  historico:

api é construída localmente. verificador usa uma imagem pronta e chama http://api:3000: o nome do serviço vira hostname na rede padrão. Ele não usa a porta 3077, porque essa é a entrada do host; entre serviços vale a porta 3000 do contêiner.

depends_on com condition: service_healthy faz o verificador esperar o healthcheck da API. Essa espera organiza a inicialização, mas uma aplicação de produção ainda precisa reconectar se a dependência cair depois.

Não existe campo version no topo. Compose 5 usa a Compose Specification atual; os antigos formatos 2 e 3 foram incorporados.

Erro real: variável obrigatória ausente

Antes de iniciar, peça ao Compose para resolver e validar a configuração:

bash
docker compose config
error while interpolating services.api.environment.MENSAGEM: required variable MENSAGEM is missing a value: defina MENSAGEM

O operador ${MENSAGEM:?defina MENSAGEM} é interpolação obrigatória. Em português simples, Compose se recusa a montar o projeto se a variável estiver ausente ou vazia. O erro acontece antes de criar imagem, rede ou contêiner.

Use um valor público de laboratório e liste os serviços resolvidos:

bash
MENSAGEM='pedidos abertos' docker compose config --services
api verificador

Para configurações sensíveis, evite colar a saída completa de docker compose config em tickets: ela pode conter valores interpolados. A ideia de separar configuração está detalhada em variáveis de ambiente no Node.

up constrói, cria e inicia na ordem necessária

Forneça a mesma variável ao comando que sobe o projeto:

bash
MENSAGEM='pedidos abertos' docker compose up --build -d
Image cantina-devclub-api Built Network cantina-devclub_default Created Volume cantina-devclub_historico Created Container cantina-devclub-api-1 Started Container cantina-devclub-api-1 Healthy Container cantina-devclub-verificador-1 Started

As linhas internas do cache de build foram omitidas acima; as seis linhas são da execução real e mostram o ciclo do projeto. --build atualiza a imagem da API. -d devolve o terminal depois de iniciar os serviços.

Espere um instante e leia os estados sem misturar outros projetos:

bash
MENSAGEM='pedidos abertos' docker compose ps -a --format '{{.Service}} | {{.State}} | {{.Health}}'
api | running | healthy verificador | exited |

verificador encerrar não é falha. Seu trabalho era fazer uma requisição e sair com sucesso. api continua rodando e saudável.

Três observações comprovam rede, porta e volume

Do host, chame a porta publicada:

bash
curl -sS http://127.0.0.1:3077
{"servico":"cantina","mensagem":"pedidos abertos","node":"v24.19.0"}

O log do verificador mostra que outro contêiner recebeu o mesmo JSON pelo nome api, sem usar a porta do host:

bash
MENSAGEM='pedidos abertos' docker compose logs --no-color verificador
verificador-1 | {"servico":"cantina","mensagem":"pedidos abertos","node":"v24.19.0"}

Por fim, leia o arquivo montado sem descobrir nome de contêiner manualmente:

bash
MENSAGEM='pedidos abertos' docker compose exec api node -e "console.log(require('node:fs').readFileSync('/dados/ultima-mensagem.txt','utf8').trim())"
pedidos abertos

docker compose exec api resolve qual contêiner implementa o serviço. O texto veio de /dados, caminho sustentado pelo volume historico.

logs, ps e config formam o primeiro diagnóstico

Quando up não entrega a aplicação, siga uma ordem que preserve evidência:

  1. docker compose config encontra interpolação e estrutura inválida antes do runtime;
  2. docker compose ps -a mostra processo encerrado e estado do healthcheck;
  3. docker compose logs api mostra o que o processo principal explicou;
  4. docker compose exec api ... serve para uma inspeção específica quando os logs não bastam.

Não comece com down -v, rebuild sem cache e limpeza global. Apagar estado pode esconder a causa e destruir o único dado que permitiria reproduzir o problema.

down remove execução, não o dado nomeado

Pare o projeto usando o mesmo valor de interpolação:

bash
MENSAGEM='pedidos abertos' docker compose down
Container cantina-devclub-verificador-1 Removed Container cantina-devclub-api-1 Removed Network cantina-devclub_default Removed

Compose remove os contêineres e a rede padrão. O volume continua:

bash
docker volume ls --filter label=com.docker.compose.project=cantina-devclub --format '{{.Name}}'
cantina-devclub_historico

docker compose down -v incluiria volumes declarados e apagaria esses dados. Use -v somente quando o ambiente for descartável e o alvo estiver conferido.

Missão: mudar configuração sem mudar a imagem

Suba o mesmo projeto com MENSAGEM='turno da tarde', sem editar server.js nem o Dockerfile. O critério de sucesso tem quatro partes: api fica healthy; o host recebe turno da tarde; o log do verificador recebe o mesmo valor pelo DNS interno; o arquivo no volume também contém a nova mensagem.

Depois execute down sem -v e confirme que o volume ainda aparece. Esse teste separa três responsabilidades: a imagem traz o programa, a variável configura a execução e o volume guarda dado fora do contêiner.

Você fechou o circuito da trilha. O próximo passo profissional é preparar o deploy de uma API Node, adicionando registro, segredos, observação e atualização. O guia de Docker permanece como mapa para revisar qualquer peça.

  • docker
  • docker compose
  • compose yaml
  • healthcheck
  • node
  • ambiente local

Perguntas frequentes

O arquivo ainda precisa declarar version no topo?
Não nas versões atuais. Compose usa a Compose Specification. O campo version legado é apenas informativo e pode gerar aviso de obsolescência.
docker compose up recria tudo em toda execução?
Compose compara a configuração e reaproveita o que não mudou. Ele recria os contêineres afetados quando imagem ou configuração muda, mantendo redes e volumes compatíveis com o projeto.
depends_on garante que o banco está pronto?
A forma curta garante ordem de início, não prontidão. Para esperar um serviço funcional, defina healthcheck e use a condição service_healthy; a aplicação ainda deve tolerar falhas e reconectar.
Posso guardar senha no arquivo .env do Compose?
Em desenvolvimento, um .env ignorado pelo Git ajuda a fornecer valores, mas variável de ambiente não é cofre. Em produção use o mecanismo de segredos da plataforma e evite imprimir a configuração resolvida.

Dúvidas e comentários

Travou em algum passo? Pergunte aqui — a equipe e outros alunos respondem.

Todo o código deste artigo foi executado em Docker Engine 29.5.3, Docker Compose 5.1.4 e Node 24.19.0 (node:24-alpine) no macOS ARM64, e as saídas exibidas são as reais — como produzimos este conteúdo.

Fontes consultadas

  1. Docker Docs — Docker Compose — docs.docker.com
  2. Docker Docs — Compose file reference — docs.docker.com
  3. Docker Docs — Networking in Compose — docs.docker.com
  4. Docker Docs — Control startup order — docs.docker.com
  5. Docker Docs — Variable interpolation — docs.docker.com

Continue por aqui