Docker Compose에서 depends_on을 설정했는데도 FastAPI 컨테이너가 PostgreSQL 연결에 실패했습니다.
분명히 postgres가 먼저 시작되도록 설정했는데 왜 이런 문제가 발생했을까요? 로그를 보니 다음과 같은 오류가 찍혀 있었습니다.
could not connect to server: Connection refused
Is the server running on host "postgres" and accepting
TCP/IP connections on port 5432?
UGREEN DXP4800PLUS NAS에서 쇼츠 자동화 시스템을 운영하면서 이 문제를 겪었습니다. depends_on을 추가해도 해결이 안 됐고, 결국 healthcheck까지 조합해 Compose 시작 시의 순서 문제를 해결했습니다. 이 글에서는 원인과 해결 방법을 정리합니다.
depends_on에 대한 오해
많은 사람이 depends_on을 이렇게 이해합니다.
postgres 준비 완료
↓
api 시작
짧은 구문의 실제 동작은 다음과 같습니다.
postgres 프로세스 시작
↓
api 시작
depends_on은 컨테이너 시작 순서만 정합니다. 짧은 구문은 서비스가 실제로 사용 가능한 상태인지 확인하지 않습니다.
아래 설정은 연결 실패가 발생할 수 있는 구성입니다.
# 잘못된 구성 - 여전히 연결 실패 가능
services:
api:
depends_on:
- postgres
postgres:
image: postgres:15-alpine
postgres 컨테이너가 시작됐다고 해서 PostgreSQL이 접속 가능한 상태라는 의미는 아닙니다.
왜 이런 문제가 발생하는가
PostgreSQL은 컨테이너가 시작된 후 실제로 접속 가능한 상태가 되기까지 여러 단계를 거칩니다.
컨테이너 시작
↓
PostgreSQL 프로세스 시작
↓
데이터베이스 초기화
↓
WAL 복구 (재시작 시)
↓
접속 가능 상태
이 과정이 수 초에서 수십 초까지 걸릴 수 있습니다. depends_on만 사용하면 이 대기 시간 없이 api가 바로 DB 연결을 시도합니다.
실제 실행 결과의 로그 패턴:
shorts-api | sqlalchemy.exc.OperationalError: could not connect to server
shorts-api | connection refused
shorts-postgres | database system is starting up
shorts-postgres | database system was shut down at ...
shorts-postgres | database system is ready to accept connections
api가 연결을 시도하는 시점에 postgres는 아직 초기화 중인 상태입니다.
해결 방법: healthcheck + condition
healthcheck를 사용하면 PostgreSQL이 실제로 접속 가능한 상태인지 확인할 수 있습니다.
services:
postgres:
image: postgres:15-alpine
container_name: shorts-postgres
environment:
POSTGRES_DB: shorts
POSTGRES_USER: shorts
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -U shorts"]
interval: 5s
timeout: 5s
retries: 5
api:
image: python:3.11-slim
container_name: shorts-api
depends_on:
postgres:
condition: service_healthy
restart: unless-stopped
핵심은 두 가지입니다.
postgres에 healthcheck 추가:
healthcheck:
test: ["CMD-SHELL", "pg_isready -U shorts"]
interval: 5s
timeout: 5s
retries: 5
pg_isready는 PostgreSQL이 실제로 접속 가능한 상태인지 확인하는 명령어입니다. 5초마다 확인하고, 5번 실패하면 unhealthy 상태로 표시됩니다.
depends_on에 condition 추가:
depends_on:
postgres:
condition: service_healthy
service_healthy는 docker compose up에서 서비스를 생성할 때 postgres의 healthcheck가 통과된 후 api를 생성·시작하도록 기다립니다. 단순한 컨테이너 실행 상태보다 서비스 준비 상태를 확인하는 용도입니다.
참고: condition: service_healthy는 Docker Compose V2 기준으로 사용할 수 있습니다. 구버전 Compose를 사용 중이라면 동작 방식이 다를 수 있으므로 버전을 먼저 확인하는 것이 좋습니다.
docker compose version
실제 적용 사례
쇼츠 자동화 시스템에서 postgres, redis, shorts-api 세 개 컨테이너를 운영하고 있었습니다.
처음에는 단순히 depends_on: - postgres만 설정했습니다. 시스템을 처음 올릴 때는 문제가 없었는데, NAS 재부팅 후 재시작할 때 간헐적으로 api가 DB 연결에 실패했습니다. 재시작할 때마다 직접 확인하고 수동으로 컨테이너를 다시 올려야 했습니다.
healthcheck와 condition: service_healthy를 적용한 뒤 docker compose up으로 서비스를 생성·재생성할 때는 PostgreSQL이 healthy가 된 뒤 API가 시작되도록 순서를 제어할 수 있습니다. 그러나 NAS 재부팅 뒤 Docker 엔진의 restart: unless-stopped 정책으로 컨테이너가 자동 재시작되는 상황에서는 Compose가 depends_on 조건을 다시 평가해 의존 서비스를 순서대로 재시작한다고 보장할 수 없습니다. Docker 공식 startup order 안내도 service_healthy를 Compose 시작 시 준비 상태 제어로 설명합니다. 애플리케이션에도 DB 연결 재시도와 실패 복구를 두어야 합니다.
healthcheck 상태 확인
# 컨테이너 상태 확인 (healthy 표시 여부)
docker ps
# 특정 컨테이너 healthcheck 상세 확인
docker inspect shorts-postgres | grep -A 10 Health
docker ps 결과에서 (healthy) 표시가 있으면 정상입니다.
shorts-postgres Up 5 days (healthy) 5432/tcp
추가 팁
depends_on은 Compose가 서비스를 생성·재생성할 때의 순서, healthcheck는 준비 상태 판정, restart 정책은 컨테이너 런타임의 재시작을 각각 담당합니다. 세 기능의 역할을 분리하고 API 자체에도 DB 연결 재시도·백오프를 두는 편이 안전합니다. Compose long syntax의 restart: true는 명시적인 Compose 작업으로 의존 서비스를 갱신·재시작할 때 종속 서비스를 함께 재시작하는 옵션이며, 컨테이너 런타임의 자동 재시작까지 뜻하지 않습니다.
services:
api:
depends_on:
postgres:
condition: service_healthy
restart: unless-stopped # 실패 시 자동 재시작
# 애플리케이션 레벨 재시도 로직도 추가 권장
restart: unless-stopped는 컨테이너가 비정상 종료될 때 자동으로 재시작합니다. healthcheck는 상태를 보여줄 뿐 런타임 재시작 순서를 제어하지 않습니다. Compose로 서비스를 생성·재생성할 때의 시작 순서는 depends_on과 service_healthy가 담당하고, Docker 엔진의 자동 재시작은 restart 정책이 각 컨테이너에 별도로 적용합니다.
healthcheck를 적용해도 네트워크 지연이나 일시적인 문제는 발생할 수 있습니다. 운영 환경에서는 FastAPI 시작 시 데이터베이스 연결을 여러 번 재시도하도록 구현하는 것이 좋습니다.
healthcheck는 첫 번째 방어선, 애플리케이션 재시도 로직은 두 번째 방어선입니다.
마무리
depends_on의 짧은 구문은 컨테이너 시작 순서만 정합니다. 서비스가 실제로 사용 가능한 상태인지 확인하려면 healthcheck와 condition: service_healthy를 함께 사용해야 합니다.
depends_on만 사용
→ 컨테이너 시작 순서만 보장
→ 간헐적 연결 실패 발생
docker compose up 시 depends_on + healthcheck + condition: service_healthy
→ 의존 서비스가 healthy인지 확인한 뒤 API 시작
Docker 엔진 런타임 자동 재시작
→ restart 정책이 컨테이너별로 적용
→ 의존 서비스 재시작 순서는 보장하지 않음
→ 애플리케이션 DB 재시도 필요
NAS 재부팅 뒤에는 restart 정책으로 컨테이너가 각각 다시 올라올 수 있으므로 service_healthy만 믿지 말고 애플리케이션의 DB 연결 재시도·backoff를 함께 두어야 합니다.
