Docker healthcheck와 depends_on 이해하기
NOTE
Docker Engine 27.4.0, Docker Compose 2.39.2 기준으로 작성되었습니다.
Healthcheck의 역할은 간단하다. 지정한 Healthcheck 명령을 실행하고 종료 코드(Exit Code)에 따라 상태를 구분한다. 종료 코드 0은 성공, 나머지는 실패로 처리된다. 이 상태는 컨테이너의 플래그로 지정된다.
Docker Compose에서의 Healthcheck 문법은 아래와 같다.
services:
redis:
image: redis:8
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 3s
timeout: 5s
retries: 1
start_period: 30s
start_interval: 3s다음은 Healthcheck 옵션이다.
| 옵션 | 설명 | 필수 | 기본값 |
|---|---|---|---|
| interval | test 실행 주기 | N | 30s |
| timeout | test 실행 시간 제한 | N | 30s |
| start_period | 컨테이너가 시작되고 나서 설정된 시간 동안 test 실패 시 실패 횟수가 증가하지 않는다. test가 성공하면 해당 옵션은 꺼진다. 자세한 건 아래의 Healthcheck 흐름 참고. | N | 0s |
| start_interval1 | 시작 기간 중 test 실행 주기 | N | 5s |
| retries | 얼마나 연속적으로 실패를 허용할 것인지에 대한 옵션. 값이 5면 연속적으로 5번 실패 시 unhealthy상태로 판단한다. | N | 3 |
CAUTION
interval이 너무 짧으면 리소스 부담이 있을 수 있으니 적절히 조절해서 사용해야 합니다.
옵션마다 컨테이너 실행 후 Healthcheck가 어떤 흐름으로 동작하는지 알아본다.
healthcheck:
test: ["CMD", "redis-cli", "ping"]
#interval: 30s
#timeout: 30s
#retries: 3
#start_period: 0s
#start_interval: 5s흐름에서 괄호 안에 있는 것은 docker container inspect ... 명령 실행 시 나오는 State.Health.Status값이다.
성공시 흐름
0초: 컨테이너 시작(starting)
...
30초: 1차 test 실행(healthy)
...
60초: 2차 test 실행(healthy)실패시 흐름
0초: 컨테이너 시작(starting)
...
30초: 1차 test 실행(starting)
...
60초: 2차 test 실행(starting)
...
90초: 3차 test 실행(unhealthy)healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 5s
retries: 3
start_period: 30s
start_interval: 3s성공시 흐름
0초: 컨테이너 시작(starting)
...
3초: 1차 test 실행(healthy)
...
8초: 2차 test 실행(healthy)
...
13초: 3차 test 실행(healthy)여기서 주목해야 할 것은 1차 test 실행과 2차 test 실행 간격이 5초라는 것이다. start_period가 30초로 설정되어 있지만 처음 1차 test가 성공하고 나서 interval 주기로 동작하게 된다. 또한 1차 test 이후에 실패 시 실패 카운트가 증가할 것이다.
실패시 흐름
0초: 컨테이너 시작(starting)
...
3초: 1차 test 실행(starting)
...
6초: 2차 test 실행(starting)
...
9초: 3차 test 실행(starting)
...
12초: 4차 test 실행(starting)
.........
30초: test 실행(starting)
...
35초: test 실행(starting)
...
40초: test 실행(starting)
...
45초: test 실행(unhealthy)단독으로 특별한 기능을 하지 않는다. 하지만 아래처럼 활용할 수 있다.
- 플래그 값을 참조하는 모니터링 툴을 사용
- 반복적으로 플래그 값을 검사해서
unhealthy면 재시작하는 스크립트를 작성[^2] - Docker Compose
depends_on과 함께 사용
또한 healthcheck와 restart정책은 서로 연관이 없다.
Docker Compose의 depends_on은 서비스 시작, 중지 순서를 제어할 수 있다. 아래의 Docker Compose 스크립트를 예시로 들면
services:
server:
image: hello-world:latest
depends_on:
- postgres
- redis
postgres:
image: postgres:18
environment:
- POSTGRES_USER=admin
- POSTGRES_PASSWORD=admin
- POSTGRES_DB=app
redis:
image: redis:8먼저 server 서비스(hello-world:latest)는 postgres, redis 서비스에 의존하고 있다고 가정한다. 그러므로 이 서비스들의 시작 순서가
- postgres 컨테이너와 redis 컨테이너가 시작됨
- server 컨테이너 시작
으로 동작해야 할 것이다. 만약 depends_on을 지정하지 않았다면 세 서비스 모두 거의 동시에 시작될 것이다.
depends_on은 시작 순서를 제어하는 것이지 컨테이너의 프로세스가 정상적으로 시작되었고, 요청을 받아들일(혹은 보낼) 준비되었다는 게 아니다. 이러한 특징 때문에 depends_on이 지정되지 않았더라도 postgres, redis가 프로세스 실행시간이 시간이 짧고 server는 길다면 정상적으로 동작할 것이다.
만약 postgres 컨테이너가 실행되었지만, 프로세스 실행이 오래 걸려 준비가 안 된 상태라고 가정하자. 이 경우 server 앱이 정상적으로 동작하지 않을 것이다.
이 문제를 해결해 주는 것이 depends_on.condition과 healthcheck다.
depends_on.condition은 해당 서비스가 어떤 상태가 되었을 때 시작할 것인지에 대한 조건이다. depends_on.condition에서 사용할 수 있는 값은 다음 세 가지 값이다.
service_started: 서비스가 시작됨(기본값)service_healthy: healthcheck로 인한 컨테이너 플래그가healthy임service_completed_successfully: 서비스가 성공적으로 종료되었음(보통 마이그레이션같은 일회성 서비스에서 사용됨)
여기서 service_healthy를 사용해 문제를 해결할 수 있다.
services:
server:
image: hello-world:latest
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
postgres:
image: postgres:18
command: ["bash", "-c", "sleep 10 && exec docker-entrypoint.sh postgres"] # 10초 지연 후 서버 시작
environment:
- POSTGRES_USER=admin
- POSTGRES_PASSWORD=admin
- POSTGRES_DB=app
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
interval: 1m
timeout: 5s
retries: 3
start_period: 30s
start_interval: 3s
redis:
image: redis:8
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 1m
timeout: 5s
retries: 3
start_period: 30s
start_interval: 3spostgres와 redis의 healthcheck로 컨테이너가 준비되었는지 확인하고 healthy 플래그를 설정한다. server는 이 플래그 기반으로 둘 다 healthy가 되어야 컨테이너가 시작된다.

TIP
Healthcheck가 depends_on에서만 쓰인다면 interval은 길게 하고 start_period와 start_interval을 설정하세요.
Docker Compose 2.20.2부터 사용 가능 ↩
Plorence
주로 TypeScript와 C#을 사용하며 겪었던 문제나 잘 알려지지 않은 내용들을 다루는 기술 블로그입니다.