컨테이너가 실행 중이라고 해서 서비스가 정상이라는 보장은 없다. 프로세스는 살아있지만 데이터베이스 연결이 끊겼거나, HTTP 엔드포인트가 500을 반환하는 상황이 실제로 발생한다. Docker Health Check는 이런 상황을 감지해 컨테이너 상태를 healthy / unhealthy로 분류한다.
Dockerfile에 HEALTHCHECK 명령어를 추가하면 Docker 데몬이 주기적으로 해당 명령을 실행해 컨테이너 상태를 판단한다.
FROM node:20-alpine
WORKDIR /app
COPY . .
RUN npm ci --omit=dev
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD wget -qO- http://localhost:3000/health || exit 1
CMD ["node", "server.js"]
--interval: 헬스 체크 실행 간격 (기본값 30s) / --timeout: 명령 타임아웃 (기본값 30s) / --start-period: 컨테이너 시작 후 첫 체크까지 대기 시간 (기본값 0s) / --retries: 연속 실패 횟수가 이 값을 넘으면 unhealthy 처리 (기본값 3)
헬스 체크 명령의 종료 코드가 0이면 healthy, 1이면 unhealthy로 판단한다. 2는 예약된 값으로 사용하지 않는다.
컨테이너 시작
│
▼
[starting] ◄─── start-period 동안 실패해도 무시
│
│ start-period 경과 후 첫 성공
▼
[healthy]
│
│ retries 횟수 연속 실패
▼
[unhealthy]
│
│ 다시 성공
▼
[healthy]
starting 상태는 컨테이너가 초기화되는 동안 불필요한 경보를 막기 위해 존재한다. --start-period를 애플리케이션 부팅 시간보다 충분히 크게 설정해야 한다.
docker inspect --format='{{json .State.Health}}' <container_id> | jq
출력 예시:
{
"Status": "healthy",
"FailingStreak": 0,
"Log": [
{
"Start": "2027-01-18T09:00:00Z",
"End": "2027-01-18T09:00:00.123Z",
"ExitCode": 0,
"Output": "OK"
}
]
}
Log 배열에는 최근 5개의 체크 결과가 저장된다. ExitCode와 Output으로 실패 원인을 빠르게 파악할 수 있다.
이미지에 HEALTHCHECK가 없거나 값을 재정의하고 싶을 때는 docker run 플래그를 사용한다.
docker run \
--health-cmd="curl -f http://localhost:8080/ping || exit 1" \
--health-interval=15s \
--health-timeout=3s \
--health-retries=2 \
my-image
반대로 이미지에 정의된 헬스 체크를 비활성화하려면 --no-healthcheck 플래그를 사용한다.
| 옵션 | 기본값 | 권장 범위 | 설명 |
|---|---|---|---|
--interval | 30s | 10s–60s | 체크 실행 주기 |
--timeout | 30s | 2s–10s | 단일 명령 타임아웃 |
--start-period | 0s | 앱 부팅 시간 이상 | 초기화 유예 기간 |
--retries | 3 | 2–5 | unhealthy 판정 연속 실패 횟수 |
--timeout이 --interval보다 크면 의미가 없다. 타임아웃은 항상 인터벌보다 짧게 설정한다.
헬스 체크 명령이 호출하는 엔드포인트는 단순해야 한다. 외부 의존성(DB, 캐시 등) 확인을 포함할 경우, 해당 의존성 장애가 컨테이너 재시작을 유발한다는 점을 의도적으로 선택해야 한다.
// Express 예시
app.get('/health', (_req, res) => {
res.status(200).json({ status: 'ok' });
});
의존성 확인이 필요하다면 /health/live(프로세스 생존)와 /health/ready(트래픽 수용 가능)를 분리하는 패턴이 Kubernetes의 livenessProbe / readinessProbe와도 자연스럽게 맞는다.