
백엔드
Node.js 컨테이너, 왜 깔끔하게 안 죽을까? (feat. Graceful shutdown)
두줄요약
Node.js 컨테이너가 종료 시그널을 받아도 바로 안 죽는 원인을 PID 1과 이벤트 루프로 분석했습니다. dumb-init과 shutdown 훅, K8s 종료 설정을 함께 조정해 graceful shutdown을 맞췄습니다.
문제 상황
- Node.js 배치 컨슈머가 종료 시그널을 받아도 SIGKILL까지 버티는 현상
- 배치가 일부만 반영된 채 끝난 듯 보이는 간헐적 장애
- 종료 훅만으로 해결되지 않는 컨테이너 종료 이슈
원인 분석
- Linux PID 1의 특수한 시그널 처리와 자식 프로세스 관리 문제
- Node.js 이벤트 루프에 남아 있는 비동기 작업으로 인한 종료 지연
- Promise.race 타임아웃 이후에도 취소되지 않는 배치 작업
해결 방법
- dumb-init을 PID 1로 두어 시그널 전달과 좀비 프로세스 정리 보장
- NestJS shutdown hook과 타임아웃으로 종료 대기 범위 제한
- terminationGracePeriodSeconds를 앱 타임아웃보다 길게 설정
주의할 점
- AbortController로 모든 비동기 루프를 중단하는 방식은 복잡도와 정합성 위험 증가
- return 시점과 프로세스 종료 시점은 다름
- 애플리케이션 타임아웃과 Kubernetes 종료 정책의 동기화 필요
