본문으로 건너뛰기
개발 머꼬
개발 노트Docker
hohyeon.dev25

컨테이너가 떴다고 표시됐는데 실제로는 요청을 못 받던 상황 — HEALTHCHECK

  • #DevOps
  • #Docker
  • #Engineering Note

문제 발생

새 버전 컨테이너를 배포한 직후 docker ps로 보면 "Up" 상태였지만, 실제로는 애플리케이션이 아직 초기화(DB 커넥션 풀 준비, 캐시 워밍업 등) 중이라 그 시점에 들어온 요청들이 연결 거부로 실패했습니다.

원인 분석

Docker(그리고 이를 이어받는 오케스트레이션 도구들)가 기본으로 판단하는 컨테이너의 "살아있음"은 컨테이너의 메인 프로세스가 아직 종료되지 않았는가뿐입니다. 이건 애플리케이션이 실제로 요청을 처리할 준비가 됐는가와는 다른 질문입니다 — Node.js 프로세스가 시작되긴 했지만 아직 리스닝 포트를 열기 전이거나, DB 연결을 맺는 중이거나, 초기 캐시를 채우는 중일 수 있습니다. 이 "프로세스는 살아있지만 아직 준비 안 됨" 구간을 Docker의 기본 상태 확인은 구분하지 못합니다.

해결 방안

Dockerfile의 HEALTHCHECK 명령으로, "컨테이너가 살아있다"는 것을 애플리케이션 자신의 기준(보통 /health 같은 엔드포인트가 200을 반환하는지)으로 직접 정의합니다.

HEALTHCHECK --interval=10s --timeout=3s --start-period=15s --retries=3 \
  CMD curl -f http://localhost:3000/health || exit 1
  • --start-period: 컨테이너가 막 시작된 뒤 이 기간 동안의 헬스체크 실패는 컨테이너를 즉시 unhealthy로 표시하지 않습니다 — 초기화에 시간이 걸리는 애플리케이션을 위한 유예 기간입니다.
  • --interval/--timeout/--retries: 얼마나 자주, 얼마나 기다렸다가, 몇 번 연속 실패해야 unhealthy로 판정할지를 정합니다.
  1. docker ps의 상태 컬럼에 (healthy)/(unhealthy)가 표시되어, 단순히 "떠 있는지"가 아니라 "실제로 응답 가능한지"를 한눈에 구분할 수 있게 됩니다.
  2. docker-compose.yml에서 depends_oncondition: service_healthy를 함께 쓰면, 의존하는 서비스가 단순히 시작된 것이 아니라 healthy 상태가 될 때까지 기다렸다가 다음 서비스를 시작하게 만들 수 있습니다 — 예를 들어 애플리케이션 컨테이너가 DB 컨테이너의 헬스체크를 기다리게 하는 데 흔히 쓰입니다.
  3. Kubernetes 환경이라면 이 개념이 liveness probe(살아있는지)와 readiness probe(요청을 받을 준비가 됐는지)로 더 세분화되어 있습니다 — 둘의 역할이 다르므로(readiness 실패는 트래픽만 안 보내고, liveness 실패는 컨테이너를 재시작합니다), Docker의 단일 HEALTHCHECK보다 더 정교한 제어가 가능합니다.
  4. 헬스체크 엔드포인트 자체는 가볍게 만듭니다 — DB 쿼리처럼 무거운 검사를 매 헬스체크마다 실행하면, 헬스체크 트래픽 자체가 부하를 유발할 수 있습니다.

공식 문서

마지막 수정

좋아요북마크

댓글0

아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.