Contents
see List컨테이너가 시작됐다고 서비스가 준비된 것은 아니다
Docker Compose로 API, 데이터베이스, 캐시를 함께 실행할 때 가장 흔한 오해는 컨테이너의 실행 상태와 애플리케이션의 준비 상태가 같다는 생각입니다. 데이터베이스 컨테이너가 실행 중이어도 초기화 스크립트가 끝나지 않았거나 연결을 받을 준비가 되지 않았을 수 있습니다. 이때 API가 먼저 접속을 시도하면 시작 직후 연결 오류가 발생하고, 재시작 정책에 따라 로그만 반복되는 상황이 만들어집니다.
해결의 핵심은 각 서비스가 외부 요청을 받을 수 있는 기준을 healthcheck로 정의하고, 의존 서비스는 그 검사 결과가 정상일 때 시작하도록 구성하는 것입니다. 단순히 start 순서를 정하는 것보다 실제 준비 상태를 기준으로 삼아야 개발 환경과 CI 환경의 간헐적 실패를 줄일 수 있습니다.
healthcheck에 넣어야 할 검사 기준
healthcheck는 프로세스가 존재하는지보다 서비스가 제공하는 기능을 확인해야 합니다. PostgreSQL은 pg_isready로 접속 수락 여부를 확인하고, HTTP API는 인증 없이 접근 가능한 health 엔드포인트가 200 응답을 주는지 검사하는 방식이 적절합니다. 검사 명령은 빠르게 끝나야 하며, 외부 네트워크나 불안정한 의존성을 다시 호출하는 작업은 피하는 편이 좋습니다.
- test: 컨테이너 내부에서 실행할 검사 명령입니다.
- interval: 검사 주기입니다. 너무 짧으면 로그와 부하가 늘어납니다.
- timeout: 한 번의 검사에 허용하는 최대 시간입니다.
- retries: 연속 실패를 비정상으로 판단할 횟수입니다.
- start_period: 초기 기동 동안의 실패를 유예하는 시간입니다.
데이터베이스는 이미지와 데이터 양에 따라 초기 기동 시간이 달라질 수 있습니다. 특히 볼륨 마이그레이션이나 초기 SQL 실행이 있다면 start_period를 너무 짧게 두지 말고 실제 기동 로그를 측정해 정해야 합니다.
PostgreSQL과 API를 연결하는 Compose 예제
services:
db:
image: postgres:16
environment:
POSTGRES_DB: app
POSTGRES_USER: app
POSTGRES_PASSWORD: change-me-locally
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app -d app"]
interval: 5s
timeout: 3s
retries: 10
start_period: 10s
api:
build: ./api
environment:
DATABASE_URL: postgres://app:change-me-locally@db:5432/app
depends_on:
db:
condition: service_healthy
healthcheck:
test: ["CMD-SHELL", "wget -q -O - http://localhost:8080/health | grep -q 'UP'"]
interval: 10s
timeout: 3s
retries: 6
start_period: 20s예제에서 api는 db 컨테이너가 단순히 생성된 시점이 아니라 healthy 상태가 된 뒤에 시작합니다. API 이미지에 wget이나 grep이 없다면 해당 도구를 설치하기보다 애플리케이션 런타임에 이미 있는 도구, 또는 작은 healthcheck 전용 엔드포인트를 활용하는 방법을 검토해야 합니다. 셸 명령이 이미지마다 다르면 로컬에서는 통과하지만 CI에서 실패할 수 있으므로 실제 이미지에서 직접 실행해 확인합니다.
depends_on만으로 해결되지 않는 경우
depends_on의 health 조건은 Compose가 컨테이너를 시작하는 순간을 조절합니다. 하지만 운영 중 데이터베이스가 일시적으로 끊기거나 API가 배포 후 재시작하면 애플리케이션 자체의 재시도 전략도 필요합니다. 데이터베이스 연결 풀에는 연결 시간 제한과 재시도 간격을 설정하고, 요청 처리에서는 무한 재시도를 피해야 합니다. Compose의 시작 순서를 장애 복구 장치로 오해하면 문제가 더 오래 숨겨질 수 있습니다.
또한 API health 엔드포인트는 용도를 분리하는 것이 좋습니다. liveness는 프로세스가 교착 상태인지 확인하고, readiness는 트래픽을 받을 준비가 됐는지 확인합니다. 데이터베이스 연결 실패를 readiness 실패로 표현할지는 서비스 특성에 따라 결정합니다. 모든 하위 시스템을 검사하면 짧은 외부 장애에도 서비스 전체가 비정상처럼 보일 수 있으므로, 사용자 요청에 필수적인 의존성만 포함합니다.
개발 환경과 CI에서 확인하는 방법
구성을 추가한 뒤에는 컨테이너 목록과 health 상태를 함께 확인해야 합니다. 단순히 docker compose up 명령이 성공했다는 사실만으로 API가 준비됐다고 판단하지 않습니다. 테스트 실행 전 API의 health 응답을 대기시키고, 실패 시에는 각 서비스의 로그를 수집하면 원인 파악 시간이 줄어듭니다.
docker compose up -d --build
docker compose ps
docker compose logs --tail=100 db api
curl --fail --retry 12 --retry-delay 2 http://localhost:8080/healthCI에서는 테스트가 끝난 뒤 docker compose down -v를 실행할지 신중히 결정합니다. 독립적인 일회성 실행이라면 이전 볼륨의 데이터가 테스트 결과에 영향을 주지 않도록 정리하는 편이 안전합니다. 반대로 디버깅 목적의 실패 환경은 즉시 삭제하지 않고 로그와 상태를 보존해야 합니다.
배포 전 체크리스트
- healthcheck가 프로세스 존재가 아니라 실제 요청 가능 상태를 검사하는지 확인합니다.
- 검사 명령이 해당 컨테이너 이미지에 실제로 포함되어 있는지 확인합니다.
- 초기화 시간을 반영해 timeout, retries, start_period를 측정값으로 설정합니다.
- 시작 순서와 별도로 애플리케이션의 연결 제한 및 재시도 정책을 점검합니다.
- CI에서 health 상태, 실패 로그, 테스트용 데이터 초기화를 함께 검증합니다.
healthcheck는 Compose 파일의 부가 설정이 아니라 서비스 계약입니다. 준비 기준을 명확히 기록하고 자동 검사로 고정하면, 로컬 개발과 CI에서 재현하기 어려운 기동 실패를 줄일 수 있습니다.
tool
| No | 작성일 | Title |
|---|---|---|
| 3464 | 2026. 09. 11. | Docker Compose에서 서비스 준비 상태를 보장하는 방법: healthcheck와 의존성 설정 실전 가이드 |
| 3432 | 2026. 09. 02. | Git 훅으로 커밋 품질을 자동 검증하는 방법: Husky와 lint-staged 실전 구성 |
| 3400 | 2026. 08. 25. | Playwright CI 테스트가 가끔 실패할 때: flaky test 줄이는 실전 점검 가이드 |
| 3368 | 2026. 08. 17. | Docker 이미지 용량과 보안 위험을 함께 줄이는 멀티 스테이지 빌드 실전 가이드 |
| 3336 | 2026. 08. 08. | GitHub Actions 빌드 시간을 줄이는 방법: 의존성 캐시와 변경 경로 분리 실전 가이드 |
| 3304 | 2026. 07. 31. | Git worktree로 여러 기능 브랜치를 동시에 작업하는 실전 가이드 |
| 3273 | 2026. 07. 23. | Docker 이미지 용량과 보안 함께 줄이기: 멀티 스테이지 빌드 실전 가이드 |
| 3244 | 2026. 07. 15. | Git pre-commit 훅으로 코드 품질 검사 자동화하기: Husky와 lint-staged 실전 설정 |
| 3227 | 2026. 07. 11. | QK Alice Duo v1.0.6 맥북 잠자기 개선 펌웨어 — DarkWake LED·오입력 차단, 실제 키 입력 시 복구 (4개 구성 세트) |
| 3226 | 2026. 07. 11. | QK Alice Duo v1.0.6 Firmware Fixes for Mac Background Wake-Ups (DarkWake) |