docker compose up을 했더니 앱 컨테이너가 "DB 연결 실패"로 죽는 경험, 한 번쯤 있을 겁니다. depends_on으로 DB를 먼저 적었는데도 말이죠. 핵심 오해는 여기 있습니다. depends_on은 "시작 순서"만 정할 뿐, 의존 서비스가 "준비 완료"되기를 기다리지 않습니다. DB 컨테이너가 시작되자마자 앱이 뜨는데, 그 시점에 PostgreSQL은 아직 초기화 중이라 접속을 못 받습니다.
기본 depends_on의 한계
services:
app:
build: .
depends_on:
- db
db:
image: postgres:16
이 설정은 "db 컨테이너를 app보다 먼저 띄워라"까지만 합니다. db 안의 Postgres 프로세스가 접속을 받을 준비가 됐는지는 보지 않습니다. 그래서 앱이 너무 빨리 떠서 다음 에러를 만납니다.
could not connect to server: Connection refused
Is the server running on host "db" (172.18.0.2) and accepting
TCP connections on port 5432?
해결 — healthcheck + condition
준비 완료를 기다리게 하려면 의존 서비스에 healthcheck를 달고, depends_on에서 condition: service_healthy를 요구합니다.
services:
app:
build: .
depends_on:
db:
condition: service_healthy
db:
image: postgres:16
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 3s
retries: 5
이제 pg_isready가 성공해 db가 healthy로 바뀐 뒤에야 app이 시작됩니다.
condition 종류
| condition | 의미 |
|---|---|
service_started | 컨테이너가 시작되면 (기본값, 준비 보장 안 함) |
service_healthy | healthcheck가 통과하면 |
service_completed_successfully | 의존 서비스가 정상 종료하면 (마이그레이션 등) |
그래도 앱 쪽 재시도는 필요하다
healthcheck를 걸어도 운영 중 DB가 잠깐 끊기면 앱은 다시 실패합니다. compose의 기동 순서는 "첫 부팅"만 다뤄주므로, 앱 코드에 접속 재시도 로직을 넣는 게 정석입니다. 컨테이너 자체를 다시 살리려면 restart: on-failure도 함께 둡니다.
체크리스트
docker compose config # depends_on·condition 파싱 확인
docker compose ps # STATUS의 (healthy) 표시 확인
docker inspect <컨테이너> --format '{{.State.Health.Status}}'
docker compose logs db # pg_isready 통과 시점 확인
docker compose ps에서 db가 Up (healthy)로 보이면 condition이 제대로 동작하는 것입니다.
healthcheck와 의존 순서를 직접 만들어 앱이 DB를 기다리게 하는 실습은 도커 트랙에서 무료로 할 수 있습니다.