Docker Compose depends_on이 제대로 동작하지 않는 이유와 해결법

 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까지 조합해야 완전히 해결됐다. 이 글에서는 원인과 해결 방법을 정리한다.


depends_on에 대한 오해

많은 사람이 depends_on을 이렇게 이해한다.

postgres 준비 완료
↓
api 시작

실제 동작은 이렇다.

postgres 프로세스 시작
↓
api 시작

depends_on컨테이너 시작 순서만 보장한다. 서비스가 실제로 사용 가능한 상태인지는 확인하지 않는다.

아래 설정이 바로 문제의 원인이다.

yaml
# 잘못된 구성 - 여전히 연결 실패 가능
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이 실제로 접속 가능한 상태인지 확인할 수 있다.

yaml
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 추가:

yaml
healthcheck:
  test: ["CMD-SHELL", "pg_isready -U shorts"]
  interval: 5s
  timeout: 5s
  retries: 5

pg_isready는 PostgreSQL이 실제로 접속 가능한 상태인지 확인하는 명령어다. 5초마다 확인하고, 5번 실패하면 unhealthy 상태로 표시된다.

depends_on에 condition 추가:

yaml
depends_on:
  postgres:
    condition: service_healthy

service_healthypostgres의 healthcheck가 통과된 후에만 api를 시작하도록 한다. 단순히 컨테이너가 시작됐는지가 아니라 실제 서비스 상태를 확인한다.


참고: condition: service_healthy는 Docker Compose V2 기준으로 사용 가능하다. 구버전 Compose를 사용 중이라면 동작 방식이 다를 수 있으므로 버전을 먼저 확인하는 것이 좋다.

docker compose version


실제 적용 사례

쇼츠 자동화 시스템에서 postgres, redis, shorts-api 세 개 컨테이너를 운영하고 있었다.

처음에는 단순히 depends_on: - postgres만 설정했다. 시스템을 처음 올릴 때는 문제가 없었는데, NAS 재부팅 후 재시작할 때 간헐적으로 api가 DB 연결에 실패했다. 재시작할 때마다 직접 확인하고 수동으로 컨테이너를 다시 올려야 했다.

healthcheckcondition: service_healthy를 적용한 후에는 NAS 재부팅 후에도 모든 컨테이너가 자동으로 정상 상태로 올라온다.


healthcheck 상태 확인

bash
# 컨테이너 상태 확인 (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만으로는 부족한 경우가 많다. 운영 환경에서는 세 가지를 함께 사용하는 것을 권장한다.

yaml
services:
  api:
    depends_on:
      postgres:
        condition: service_healthy
    restart: unless-stopped  # 실패 시 자동 재시작
    # 애플리케이션 레벨 재시도 로직도 추가 권장

restart: unless-stopped는 컨테이너가 비정상 종료될 때 자동으로 재시작한다. healthcheck와 함께 사용하면 대부분의 시작 순서 문제를 커버할 수 있다.

healthcheck를 적용해도 네트워크 지연이나 일시적인 문제는 발생할 수 있다. 운영 환경에서는 FastAPI 시작 시 데이터베이스 연결을 여러 번 재시도하도록 구현하는 것이 좋다.

healthcheck는 첫 번째 방어선, 애플리케이션 재시도 로직은 두 번째 방어선이다.


마무리

depends_on은 컨테이너 시작 순서만 보장한다. 서비스가 실제로 사용 가능한 상태인지 확인하려면 healthcheckcondition: service_healthy를 함께 사용해야 한다.

depends_on만 사용
→ 컨테이너 시작 순서만 보장
→ 간헐적 연결 실패 발생

depends_on + healthcheck + condition: service_healthy
→ 실제 서비스 준비 상태 확인
→ 안정적인 시작 보장

NAS 재부팅처럼 모든 컨테이너가 동시에 시작되는 환경에서 특히 중요하다.


관련 글

댓글

이 블로그의 인기 게시물

UGREEN DXP4800PLUS NAS로 유튜브 쇼츠 자동화 시스템 구축기 (Docker · FastAPI · PostgreSQL · Edge-TTS)

YouTube Data API 쿼터 초과를 피하는 방법 (700개 쇼츠 수집 사례)

FastAPI app.mount 사용 후 API가 404가 되는 이유와 해결 방법