docker ps에서 컨테이너가 Up이라고 떠도 그 안의 앱이 실제로 요청을 받을 수 있는 상태라는 보장은 없습니다. 프로세스는 떠 있지만 DB 연결이 끊겨 500만 뱉는 경우가 흔하죠. HEALTHCHECK는 컨테이너 안에서 주기적으로 명령을 실행해 "정말 정상인지"를 도커가 직접 판단하게 만듭니다.
1단계 — 현재 헬스 상태를 본다
로컬 터미널
# STATUS 컬럼에 (healthy)/(unhealthy) 표시
docker ps
# 헬스체크 실행 이력과 마지막 출력
docker inspect --format '{{json .State.Health}}' <container>
inspect의 Health.Log에 최근 검사 결과와 명령 출력이 그대로 남아, unhealthy 원인을 바로 볼 수 있습니다.
2단계 — HEALTHCHECK 작성
Dockerfile
FROM node:20-slim
# ... 앱 빌드 ...
HEALTHCHECK --interval=10s --timeout=3s --start-period=30s --retries=3 \
CMD curl -fsS http://localhost:3000/health || exit 1
CMD ["node", "dist/main.js"]
핵심 옵션은 정해져 있습니다.
| 옵션 | 의미 | 자주 틀리는 점 |
|---|---|---|
--interval | 검사 주기 | 너무 짧으면 부하, 보통 10~30s |
--timeout | 명령 제한 시간 | 앱 응답보다 짧으면 오탐 |
--start-period | 기동 유예 | 누락하면 시작 중에 unhealthy 처리 |
--retries | 연속 실패 허용 | 1이면 일시적 끊김에도 unhealthy |
--start-period를 빼먹으면 앱이 아직 뜨는 중인데 unhealthy로 찍혀, 오케스트레이터가 멀쩡한 컨테이너를 계속 죽입니다.
unhealthy 원인별 해결
- 헬스 엔드포인트가 없음 —
/health같은 경량 라우트를 앱에 추가합니다. 메인 페이지로 검사하면 DB 부하까지 끌고 옵니다. - 이미지에 curl이 없음 — slim/alpine엔 curl이 없을 수 있습니다.
wget -q -O- ...또는 앱 자체 헬스 바이너리를 씁니다. - timeout이 너무 짧음 — 콜드 스타트 응답이 timeout을 넘겨 오탐. timeout을 늘리거나 start-period를 줍니다.
Compose에서는 depends_on의 condition: service_healthy로 의존 서비스가 healthy가 될 때까지 기동을 대기시킬 수 있습니다.
YAML
services:
app:
depends_on:
db:
condition: service_healthy
체크리스트
로컬 터미널
docker ps # healthy 여부
docker inspect --format '{{json .State.Health}}' <c> # 실패 로그
docker exec <c> curl -fsS localhost:3000/health # 안에서 직접 검사
컨테이너 안에서 직접 헬스 명령을 쳐 보면, 문제가 앱인지 헬스체크 설정인지 바로 갈립니다.
HEALTHCHECK를 붙여 일부러 unhealthy를 만들고 로그로 원인을 좁히는 실습은 도커 트랙에서 무료로 해볼 수 있습니다.