안전하게 순서대로 띄우기: Health Check
DB가 완전히 켜질 때까지 백엔드 서버가 기다리도록, Health Check 기능과 depends_on을 활용하여 실행 순서를 맞추는 법을 배웁니다.
우리는 종종 여러 개의 서비스를 동시에 실행합니다. 이때 백엔드 서버가 데이터베이스보다 먼저 실행되어 연결에 실패하고 종료되는 오류를 겪곤 합니다. Docker Compose가 컨테이너를 시작하는 것과 애플리케이션이 '진짜' 준비된 것은 다르기 때문입니다. 이번 에피소드에서는 서비스 간의 올바른 실행 순서를 보장하는 Health Check와 depends_on 설정에 대해 배웁니다.
컨테이너의 시작과 애플리케이션의 준비
`docker-compose up` 명령어를 실행하면 도커는 파일에 정의된 컨테이너들을 빠르게 띄우기 시작합니다. 이때 발생하는 흔한 문제가 바로 실행 타이밍의 불일치입니다.
데이터베이스 컨테이너(예: PostgreSQL)는 켜지면서 데이터 초기화, 권한 설정 등 내부적으로 준비할 시간이 필요합니다. 반면 상대적으로 가벼운 API 서버는 즉시 켜져서 데이터베이스 연결을 시도합니다. 아직 데이터베이스가 준비되지 않은 상태에서 연결을 시도한 API 서버는 `Connection Refused` 에러를 뱉으며 쓰러지고 맙니다.
컨테이너가 '실행 중(Running)'이라는 상태가, 내부의 애플리케이션이 '요청을 받을 준비가 됨(Ready)'을 의미하지는 않기 때문입니다.
단순한 순서 제어: depends_on
이 문제를 해결하기 위한 첫 번째 접근법은 `depends_on` 옵션입니다. 이 옵션을 사용하면 A 컨테이너가 B 컨테이너보다 먼저 실행되도록 순서를 강제할 수 있습니다.
services:
api:
image: my-api
depends_on:
- db
db:
image: postgres이 설정은 도커 엔진에게 "api를 실행하기 전에 db 컨테이너를 먼저 실행해"라고 지시합니다. 하지만 이 방법에는 치명적인 한계가 있습니다. 도커는 데이터베이스 컨테이너 프로세스가 켜지는 순간까지만 기다릴 뿐, 데이터베이스가 완전히 초기화되어 연결을 받을 수 있는 상태인지는 확인하지 않습니다. 여전히 동일한 에러가 발생할 가능성이 큽니다.
타이밍 문제의 원인과 결과
DB 초기화보다 API 서버가 빨리 시작됨
→DB 연결 실패로 API 컨테이너 비정상 종료
단순 depends_on 사용
→컨테이너만 띄워질 뿐, 애플리케이션 레벨의 준비 상태는 확인 안 됨
애플리케이션 레벨의 상태 확인: Health Check
이 근본적인 문제를 해결하려면 도커가 컨테이너 내부의 애플리케이션이 정말 준비되었는지 주기적으로 찔러보는 메커니즘이 필요합니다. 이것이 바로 Health Check(건강 상태 확인)입니다.
Health Check는 우리가 지정한 명령어를 컨테이너 내부에서 주기적으로 실행하여 그 결과를 확인합니다. 명령어가 성공하면 컨테이너는 `healthy` 상태가 되고, 실패하면 `unhealthy` 상태가 됩니다.
데이터베이스의 Health Check 설정 예시를 살펴보겠습니다.
services:
db:
image: postgres
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: password
healthcheck:
test: ["CMD-SHELL", "pg_isready -U user"]
interval: 5s
timeout: 5s
retries: 5이 설정은 5초마다(`interval`), 최대 5초간 대기하며(`timeout`), `pg_isready`라는 명령어를 실행해 DB가 준비되었는지 확인합니다. 5번 연속 실패하면(`retries`) 해당 컨테이너는 문제가 있다고 판단합니다.
Health Check와 depends_on의 결합
이제 Health Check를 통해 데이터베이스가 `healthy` 상태인지 알 수 있게 되었습니다. 이를 API 서버의 `depends_on` 옵션과 결합하면 완벽한 실행 순서 제어가 가능해집니다.
services:
api:
image: my-api
depends_on:
db:
condition: service_healthy
db:
image: postgres
healthcheck:
test: ["CMD-SHELL", "pg_isready -U user"]
interval: 5s`condition: service_healthy`라는 특별한 조건을 추가했습니다. 이제 도커는 단순하게 데이터베이스 컨테이너를 시작하는 것에 그치지 않고, Health Check를 통과해 DB가 완벽하게 통신을 받을 준비가 될 때까지 기다렸다가 API 서버를 띄우게 됩니다.
주요 서비스별 Health Check 명령어
서비스마다 자신이 정상 동작하는지 확인하는 명령어가 다릅니다.
- PostgreSQL: `pg_isready -U 사용자이름`
- MySQL / MariaDB: `mysqladmin ping -h localhost`
- Redis: `redis-cli ping`
- 웹서버 / API 서버: `curl -f http://localhost:8080/health || exit 1`
컨테이너 자가 치유
Health Check의 또 다른 장점은 런타임 환경에서의 안정성 확보입니다. 만약 운영 중에 애플리케이션 내부에 심각한 버그가 발생하여 프로세스는 살아있지만 더 이상 요청을 처리할 수 없는 교착 상태(Deadlock)에 빠졌다고 가정해 봅시다.
도커 자체는 프로세스가 켜져 있으니 문제가 없다고 판단하겠지만, Health Check가 설정되어 있다면 설정된 명령어가 실패하여 상태가 `unhealthy`로 변경됩니다. 이 상태는 외부의 모니터링 시스템이나 오케스트레이션 도구(예: Docker Swarm, Kubernetes)에 전달되어, 비정상 컨테이너를 강제로 재시작시키는 등 자동 복구(Auto Healing) 프로세스를 작동시키는 트리거가 됩니다.
개념 퀴즈
퀴즈 답을 맞춰야 학습 완료가 됩니다.다음 중 컨테이너 실행 순서를 제어하고, 선행 애플리케이션이 완전히 준비될 때까지 기다리도록 만드는 가장 올바른 설정은 무엇인가요?
3줄 요약
- 1컨테이너가 실행되었다는 것이 내부 애플리케이션이 통신 준비를 마쳤다는 의미는 아닙니다.
- 2Health Check는 컨테이너 내부에 주기적으로 테스트 명령을 보내 애플리케이션의 실제 구동 상태를 파악합니다.
- 3depends_on과 service_healthy 조건을 결합하면 의존성 있는 서비스들의 완벽한 실행 순서를 보장할 수 있습니다.