Conceptly
← 전체 목록
💓

Health Check

runtime컨테이너 내부 애플리케이션의 실제 작동 상태를 검증하는 메커니즘

Health Check는 컨테이너 안에서 실행 중인 애플리케이션이 실제로 정상 동작하는지를 주기적으로 검사하는 메커니즘입니다. Docker는 프로세스가 살아 있는지만 봅니다. 하지만 프로세스가 살아 있어도 데드락에 걸렸거나, DB 연결이 끊겼거나, 메모리가 부족해 요청을 처리하지 못하는 경우는 흔합니다. Health Check는 이런 상황을 감지합니다.

아키텍처 다이어그램

🔄 프로세스 다이어그램

점선 애니메이션은 데이터 또는 요청의 흐름 방향을 나타냅니다

왜 필요한가요?

Docker는 기본적으로 컨테이너의 메인 프로세스가 종료됐는지만 확인합니다. 프로세스가 돌고 있으면 running 상태로 보이지만, 애플리케이션이 실제로 요청을 받아 처리할 수 있는지는 알 수 없습니다. 웹 서버 프로세스는 떠 있는데 500 에러만 반환하는 상황, Node.js가 이벤트 루프를 돌고 있지만 DB 커넥션 풀이 고갈된 상황이 대표적입니다. 이런 반쯤 죽은 상태를 감지하지 못하면 사용자는 장애를 겪는데 모니터링에는 정상으로 보입니다. 오케스트레이터가 재시작이나 트래픽 전환을 판단하려면 프로세스 생존 여부를 넘어 애플리케이션 수준의 건강 신호가 필요합니다.

왜 이런 방식이 등장했나요?

컨테이너 하나를 수동으로 관리하던 시절에는 로그를 보고 직접 판단하면 됐습니다. 그러나 컨테이너 오케스트레이션이 보편화되면서, 수십~수백 개 컨테이너의 상태를 사람이 일일이 확인하는 것은 불가능해졌습니다. Kubernetes의 liveness probe, readiness probe 개념이 등장한 것도 같은 맥락입니다. Docker의 HEALTHCHECK는 이미지 자체에 건강 검사 로직을 내장시켜, 이미지를 만든 사람이 '이 애플리케이션은 이 조건을 만족하면 정상'이라는 기준을 정의할 수 있게 했습니다. 오케스트레이터는 이 신호를 읽어 재시작, 트래픽 제외 같은 자동 조치를 취합니다.

내부적으로 어떻게 동작하나요?

헬스 체크는 세 가지 상태를 거칩니다. 컨테이너가 처음 시작되면 `starting` 상태입니다. `start-period`로 설정한 시간 동안은 검사가 실패해도 unhealthy로 전환되지 않습니다. 애플리케이션이 초기화되는 시간을 주기 위한 유예 기간입니다. 유예 기간이 지나면 `interval`마다 지정한 CMD를 실행합니다. 명령어가 exit 0을 반환하면 `healthy` 상태가 됩니다. exit 1을 반환하거나 `timeout` 안에 응답하지 않으면 실패로 기록됩니다. 연속 실패 횟수가 `retries`에 도달하면 `unhealthy` 상태로 전환됩니다. 한 번 실패했다고 바로 unhealthy가 되지 않는 이유는 일시적인 부하나 네트워크 지연을 감안하기 위해서입니다. unhealthy 상태는 Docker 자체가 자동으로 컨테이너를 재시작하지는 않지만, Docker Swarm이나 Compose의 restart policy, 또는 외부 오케스트레이터가 이 상태를 읽어 후속 조치를 취합니다.

Dockerfile로 보면

HEALTHCHECK 명령어 구조

HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
  CMD curl -f http://localhost:8080/health || exit 1

interval마다 CMD를 실행하고, exit 0이면 healthy, exit 1이 retries만큼 연속되면 unhealthy로 전환됩니다. start-period는 컨테이너 초기화 시간을 고려한 유예 기간입니다.

Compose에서 의존성 순서 제어

services:
  db:
    image: postgres:16
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 10s
      retries: 5
  api:
    depends_on:
      db:
        condition: service_healthy

db 서비스가 healthy가 된 후에야 api 서비스가 시작됩니다. depends_on만으로는 프로세스 시작 순서만 보장하고, condition: service_healthy를 써야 실제 준비 상태를 기다립니다.

경계와 구분

Docker의 HEALTHCHECK와 Kubernetes의 probe는 둘 다 애플리케이션의 실제 상태를 검사한다는 점에서 같습니다. 차이는 세분화 수준에 있습니다. Docker HEALTHCHECK는 healthy/unhealthy 이분법입니다. Kubernetes는 liveness probe(프로세스를 재시작해야 하는가), readiness probe(트래픽을 받을 준비가 됐는가), startup probe(초기화 중인가)로 세 가지 질문을 분리합니다. Docker 환경에서 단일 컨테이너나 Compose로 운영할 때는 HEALTHCHECK로 충분하고, Kubernetes 환경으로 넘어가면 세 가지 probe를 각각 설정해 더 정밀한 생명주기 관리를 합니다.

언제 쓰나요?

프로덕션에서 HEALTHCHECK를 설정할 때 가장 중요한 결정은 무엇을 검사할지입니다. 단순히 포트가 열려 있는지 확인하는 것과, 실제로 요청을 처리할 수 있는지 확인하는 것은 다릅니다. 웹 서버라면 `/health` 엔드포인트가 DB 연결과 핵심 의존성까지 확인하도록 만드는 편이 유용합니다. 다만 헬스 체크 자체가 무거우면 interval마다 부하를 주므로 가벼운 검사를 유지해야 합니다. start-period와 retries 값은 애플리케이션의 실제 초기화 시간에 맞춰 조정해야 합니다. Java 애플리케이션처럼 기동에 수십 초가 걸리는 경우 start-period가 너무 짧으면 매번 unhealthy로 판정됩니다. 반대로 retries를 너무 높게 잡으면 실제 장애 상황에서 감지가 늦어집니다.

프로덕션 안정성배포 안전망서비스 의존성로드 밸런서 연동