핵심 요약
배포·스케일 조정으로 서버 프로세스를 종료할 때, 그냥 죽이면 처리 중이던 요청이 중간에 끊겨 사용자가 에러를 본다. 우아한 종료는 종료 신호(SIGTERM)를 받으면 ① 새 요청은 거부하고 ② 진행 중인 요청은 끝까지 처리한 뒤 ③ 정리하고 종료하는 것이다.
1. 왜 필요한가
쿠버네티스·PM2·systemd 등은 종료 시 프로세스에 먼저 SIGTERM을 보내 "정리할 시간"을 준다. 이 신호를 무시하면 잠시 후 SIGKILL로 강제 종료되어 진행 중 작업이 날아간다. SIGTERM을 받아 마무리 로직을 넣는 게 핵심이다.
2. 기본 구현 (Node.js 예)
const server = app.listen(3000)
function shutdown() {
console.log('SIGTERM 수신, 종료 시작')
// 1) 새 연결 거부 + 진행 중 요청은 마무리
server.close(async () => {
// 2) DB·큐 등 자원 정리
await db.close()
console.log('정상 종료')
process.exit(0)
})
// 3) 너무 오래 걸리면 강제 종료(무한 대기 방지)
setTimeout(() => process.exit(1), 10000).unref()
}
process.on('SIGTERM', shutdown)
process.on('SIGINT', shutdown) // Ctrl+C
3. 놓치기 쉬운 것
- 헬스체크 연동 — 종료 시작 시 헬스체크를 먼저 실패로 바꿔, 로드밸런서가 새 트래픽을 안 보내게 한다(그 다음 기존 요청 마무리).
- keep-alive 연결 — 유휴 연결이
server.close를 막을 수 있다. 타임아웃으로 강제 종료 안전망을 둔다. - 백그라운드 작업 — 처리 중인 큐 작업·크론도 안전하게 멈추거나 완료를 기다린다.
4. 쿠버네티스에서
- SIGTERM 후
terminationGracePeriodSeconds(기본 30초) 동안 정리 시간을 준다 — 마무리 로직이 이보다 오래 걸리면 잘린다. preStop훅으로 잠깐 대기해, 엔드포인트에서 파드가 빠지는 시간을 확보하는 패턴도 흔하다.
자주 묻는 질문
SIGTERM과 SIGKILL 차이가 뭔가요?
SIGTERM은 "정리하고 종료해달라"는 정중한 요청이라 앱이 가로채 마무리 작업을 할 수 있습니다. SIGKILL은 즉시 강제 종료라 가로챌 수 없습니다. 오케스트레이터는 보통 SIGTERM을 먼저 보내고, 유예 시간 안에 안 죽으면 SIGKILL을 보냅니다.
종료 처리가 유예 시간보다 오래 걸리면요?
강제 종료(SIGKILL)되어 진행 중 작업이 잘립니다. 그래서 마무리 로직에 자체 타임아웃을 두고, 오래 걸리는 작업은 트랜잭션·멱등 처리로 중단돼도 안전하게 설계해야 합니다. 유예 시간도 실제 소요에 맞게 늘리세요.
헬스체크를 먼저 실패시키는 이유는요?
로드밸런서가 이 서버를 "정상"으로 아는 동안엔 새 요청을 계속 보냅니다. 종료를 시작하면 헬스체크를 먼저 실패로 바꿔 신규 트래픽을 끊고, 이미 들어온 요청만 마무리하면 끊김 없는 배포가 됩니다.

댓글 0