Skip to main content
CI/CD8 min read

Corrigindo Erros de Conexão do Docker Compose em CI/CD

Passei 4 horas depurando erros de 'Conexão recusada' no Jenkins. Aqui está o que aprendi sobre redes Docker em pipelines de CI.

Part ofCloud & Infrastructure->
By Jason TeixeiraJanuary 5, 2024
DockerJenkinsCI/CDTroubleshooting
Share:
On this page

Imagine: sua configuração do Docker Compose funciona perfeitamente na sua máquina local. Você envia para CI e, de repente, todos os testes de integração falham com Connection refused.

O contêiner do banco de dados está "rodando". O contêiner da API está "saudável". O processo de teste inicia. Então ele não consegue se conectar ao serviço necessário.

Essa falha parece aleatória até você lembrar de uma coisa: a rede Docker local e a rede Docker de CI não são o mesmo ambiente.

A configuração local te engana

Na sua máquina, você pode se conectar ao Postgres em localhost:5432.

Dentro de uma rede do Compose, outro contêiner geralmente deve se conectar a postgres:5432, onde postgres é o nome do serviço.

Em CI, o executor de testes pode estar:

  • dentro da rede do Compose
  • fora da rede do Compose no host
  • dentro de um contêiner de serviço de CI
  • dentro de um executor Docker aninhado

Esses quatro casos usam hostnames diferentes.

É por isso que uma string de conexão pode estar "correta" localmente e errada no pipeline.

Primeiro, identifique onde o processo de teste roda

Antes de alterar portas, faça uma pergunta:

O comando de teste está rodando dentro de um serviço do Compose ou no host de CI?

Se os testes rodam dentro do Compose:

code panelTXT
DATABASE_URL=postgres://user:pass@postgres:5432/app

Se os testes rodam no host de CI e o Compose publicou a porta:

code panelTXT
DATABASE_URL=postgres://user:pass@127.0.0.1:5432/app

Se os testes rodam em um contêiner de CI separado, nenhum dos dois pode funcionar até que a rede de serviços da plataforma de CI seja configurada.

Decisão de rede de CIcompose -> testes
Serviço ComposeRedeExecutor de testesBanco de dados

O hostname correto depende de onde o executor de testes está. Nomes de serviço funcionam dentro da rede do Compose. Portas publicadas em localhost funcionam a partir do host.

Não confie em depends_on como prontidão

depends_on pode controlar a ordem de inicialização. Ele não garante que Postgres, Redis ou seu aplicativo esteja pronto para aceitar conexões.

A versão ruim comum:

code panelYAML
services:
  api:
    depends_on:
      - postgres

Isso significa apenas que o contêiner postgres inicia antes de api. Não significa que as migrações rodaram. Não significa que TCP está pronto. Não significa que o banco de dados aceitou autenticação.

Use health checks ou um script de espera explícito.

code panelYAML
services:
  postgres:
    image: postgres:16
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U app"]
      interval: 5s
      timeout: 5s
      retries: 12

  api:
    depends_on:
      postgres:
        condition: service_healthy

Isso ainda não resolve todas as plataformas de CI, mas remove a condição de corrida mais comum.

Verifique as quatro classes de falha

Quando vejo Connection refused, trabalho nesta ordem.

A verificação TCP é simples, mas útil:

code panelBASH
node -e "require('net').connect(5432, process.env.DB_HOST).on('connect', () => { console.log('ok'); process.exit(0) }).on('error', e => { console.error(e.message); process.exit(1) })"

Se isso falhar, sua suíte de testes do aplicativo não é o que deve ser depurado ainda.

Use strings de conexão diferentes para limites diferentes

Um padrão limpo é tornar o limite explícito:

code panelENV
DATABASE_URL_INTERNAL=postgres://app:app@postgres:5432/app
DATABASE_URL_HOST=postgres://app:app@127.0.0.1:5432/app

Então seu job de CI escolhe a correta com base em onde o comando roda.

Isso é menos mágico do que tentar fazer uma URL funcionar em todos os lugares.

Mantenha migrações separadas da prontidão

Um banco de dados pode estar saudável antes do esquema estar pronto.

Se seu aplicativo precisa de migrações, torne isso uma etapa explícita do pipeline:

code panelBASH
docker compose up -d postgres
docker compose run --rm migrate
docker compose run --rm test

Ou execute testes dentro de um serviço que aguarda ambos:

  • saúde do banco de dados
  • migrações concluídas
  • dados de seed carregados

Caso contrário, você obtém uma classe pior de falha: erros de teste intermitentes que parecem bugs do aplicativo, mas são na verdade condições de corrida de configuração.

A saída de depuração que quero em toda falha de CI

Não despeje segredos. Imprima a forma do ambiente.

Saída útil:

  • Serviços do Docker Compose e status
  • logs do contêiner para a dependência
  • host e porta resolvidos, com senha oculta
  • nomes de rede
  • status do health-check
  • status da migração

Exemplo:

code panelBASH
docker compose ps
docker compose logs --tail=80 postgres
docker network ls

O objetivo é tornar a próxima falha diagnosticável em uma única passagem.

A lição de produção

A dor de rede de CI é uma prévia da dor de integração em produção.

Se seus testes dependem de esperança, suas implantações provavelmente também. Torne os limites de serviço explícitos. Adicione health checks. Separe prontidão de migrações. Registre os fatos corretos.

É assim que você transforma "funciona na minha máquina" em algo que um pipeline pode provar.

Reader route

article -> proof -> offer

ReadClusterProofScope

cluster

Cloud & Infrastructure

intent

CI/CD

route

next step

What to do with this

Turn the note into a build path.

If this topic maps to a real business problem, keep reading the cluster, study the academy path, or route the work into a scoped engagement.

Jason Teixeira
Written by
Jason Teixeira
Founder, Sage Ideas Studio · Principal Engineer
livebuild 5d6c8652026-08-05 06:00Z
// solo studio// no analytics resold// every commit human-reviewed