[Kubernetes] 배포할 때마다 500에러? Graceful Shutdown으로 무중단 배포 완성하기

도니·2026년 1월 23일

1. 문제의 시작

"Graceful Shutdown을 안전한게 구성하려면 어떻게 해야 하나요?" 라는 질문을 받았을 때, 머릿속에서는 SIGTERM, preStop 같은 키워드가 둥둥 떠다녔지만, 정작 논리 정연하게 설명하지 못했다. 안다고 착각했던 지식을 진짜 내 것으로 만들기 위해, 그리고 서비스의 무중단 배포 방법을 알기 위해 Graceful Shutdown의 원리와 설정 방법을 확실히 정리해 본다.

2. Graceful Shutdown이란?

Graceful Shutdown은 말 그대로 "우아한 종료"다. 애플리케이션이 종료 신호를 받았을 때, 하던 작업(진행 중인 API 요청, DB트랜잭션 등)을 강제로 끊지 않고 안전하게 마무리한 뒤 종료 하는 것을 말한다. 이게 안되면 배포(Rolling Update) 때마다 사용자는 "500 Internal Server Error"를 겪게 된다.

3. Kubernetes의 Pod 종료 메커니즘

쿠버네티스가 Pod를 종료할 때의 순서를 이해하는 것이 핵심이다.

  1. Terminating 상태 변경: API 서버가 Pod를 삭제 상태로 마킹한다.
  2. 동시 시작 (중요!)
    • 트래픽 차단: Endpoint 리소스에서 해당 Pod IP가 제거된다 (Service -> Pod 연결 해제)
    • SIGTERM 전송: Kubelet이 컨테이너에 SIGTERM 시그널을 보낸다.
  3. 대기 (Grace Period): 설정된 시간 (terminationGracePeriodSeconds, 기본 30초)만큼 기다린다.
  4. SIGKILL (강제 종료): 시간이 지나도 안꺼지면 SIGKILL을 날려 강제 종료한다.

문제점: 트래픽 차단(Endpoint 갱신)이 전파되는 속도보다 SIGTERM을 받고 앱이 종료되는 속도가 더 빠를 때 발생한다. 로드밸런서는 아직 Pod가 살아있다고 생각해서 요청을 보내는데, Pod는 이미 문을 닫아버린 상황이 생기는 것이다.

4. 해결 방법: 안전장치 2가지

이를 방지하기 위해 두 가지 설정이 필수적이다.

① 애플리케이션 레벨: SIGTERM 핸들링
애플리케이션 코드는 SIGTERM 신호를 받으면 즉시 꺼지지 않고, 새로운 요청은 거절하되(Readiness Probe Fail), 이미 들어온 요청은 다 처리할 때까지 기다려야 한다.

② 인프라 레벨: preStop Hook (핵심)
애플리케이션이 종료 준비를 하기도 전에 트래픽이 들어오는 것을 막기 위해, SIGTERM을 받기 전 잠시 대기하는 시간을 번다

lifecycle:
  preStop:
    exec:
      command: ["/bin/sh", "-c", "sleep 10"]

이렇게 sleep 10을 주면, kubelet은 종료 신호를 보내기 전에 10초를 기다린다. 이 10초 동안 로드밸런서(service/ingress)의 Endpoint 갱신이 완료되어 트래픽이 더 이상 이 Pod로 오지 않게 된다. 그 후 안전하게 종료 절차를 밟는다.

5. 결론 및 요약

안전한 종료를 위해서는 다음 3박자가 맞아야한다.

  1. preStop Hook: 로드밸런서 갱신 시간 벌어주기 (sleep)
  2. SIGTERM 핸들링: 앱 내부에서 기존 작업 마무리 로직 구현
  3. terminationGracePeriodSeconds: 위 작업들이 충분히 끝날 떄까지 기다려주는 넉넉한 시간 설정

단순히 "설정값을 넣는다"가 아니라, "트래픽이 끊기는 시점과 앱이 꺼지는 시점의 차이(Race Condition)를 없애는 것"이 본질임을 잊지 말자.

✅추가로 생각해 볼 수 있는 질문

  • "만약 terminationGracePeriodSeconds가 preStop 시간보다 짧으면 어떻게 되나요?"
  • "Nginx나 Spring Boot에서는 각각 어떻게 설정을 적용하나요?"
profile
세상만사에 호기심

0개의 댓글