배포 중에 사용자가 결제를 진행하고 있었다면, 그 요청은 어떻게 되는 걸까요?

Kubernetes에서 롤링 업데이트를 하거나 kill 명령으로 서버를 멈출 때, 진행 중인 HTTP 요청이 있을 수 있습니다. Graceful Shutdown이 없으면 이 요청은 중간에 끊어집니다 — 결제, 데이터 저장, 파일 업로드 등이 반쯤 완료된 채로.

개념 정의

Graceful Shutdown 은 서버 종료 신호(SIGTERM)를 받았을 때, 새 요청은 거부 하면서 진행 중인 요청은 완료될 때까지 기다린 후 종료하는 방식입니다.

설정

YAML
# application.yml
server:
  shutdown: graceful  # 기본값은 immediate (즉시 종료)

spring:
  lifecycle:
    timeout-per-shutdown-phase: 30s  # 최대 대기 시간 (기본 30초)

이 두 줄이면 됩니다. 설정하면 다음과 같이 동작합니다.

동작 흐름

  1. **SIGTERM 수신 **: JVM이 종료 신호를 받습니다 (Kubernetes의 preStop, kill 명령 등).
  2. ** 새 요청 거부 **: 서블릿 컨테이너가 새 커넥션을 받지 않습니다. 클라이언트는 503을 받습니다.
  3. ** 진행 중인 요청 대기 **: 이미 처리 중인 요청은 계속 진행합니다.
  4. ** 타임아웃 **: timeout-per-shutdown-phase 안에 끝나지 않으면 강제 종료됩니다.

Kubernetes와 함께 쓸 때

Kubernetes에서 Pod을 종료할 때는 SIGTERM → 유예 기간 → SIGKILL 순서입니다.

단계KubernetesSpring Boot
1preStop 훅 실행
2SIGTERM 전송Graceful Shutdown 시작
3terminationGracePeriodSeconds 대기timeout-per-shutdown-phase 대기
4SIGKILL (강제 종료)프로세스 즉시 종료

Spring Boot의 timeout-per-shutdown-phase는 Kubernetes의 terminationGracePeriodSeconds보다 ** 짧게** 설정해야 합니다. 그래야 Spring이 먼저 정리를 마치고, Kubernetes가 SIGKILL을 보내기 전에 정상 종료됩니다.

YAML
# 예: K8s 60초, Spring 30초
# Pod spec
terminationGracePeriodSeconds: 60

# application.yml
spring.lifecycle.timeout-per-shutdown-phase: 30s

주의할 점

1. Graceful Shutdown은 기본값이 꺼져 있다

Spring Boot의 기본 server.shutdownimmediate입니다. 명시적으로 graceful로 설정하지 않으면 진행 중인 요청이 끊어집니다. 많은 프로젝트가 이 설정 없이 프로덕션에 올라가 있습니다.

2. @Async 작업은 Graceful Shutdown 대상이 아니다

Graceful Shutdown이 기다리는 건 HTTP 요청 뿐입니다. @Async로 실행된 비동기 작업이나 @Scheduled 작업은 별도로 처리해야 합니다.

JAVA
@Bean
public TaskExecutor taskExecutor() {
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    executor.setWaitForTasksToCompleteOnShutdown(true);  // ← 이 설정
    executor.setAwaitTerminationSeconds(30);
    return executor;
}

3. 로드밸런서가 즉시 트래픽을 끊지 않는다

Kubernetes에서 Pod이 종료 중이어도, Service의 엔드포인트에서 제거되기까지 수 초의 지연 이 있습니다. 이 사이에 새 요청이 종료 중인 Pod으로 들어올 수 있습니다. preStop 훅에 sleep 5를 넣어 엔드포인트 제거를 기다리는 패턴이 흔합니다.

YAML
lifecycle:
  preStop:
    exec:
      command: ["sleep", "5"]

정리

항목설정 안 함 (immediate)설정함 (graceful)
진행 중 요청즉시 끊김완료까지 대기
새 요청받다가 갑자기 끊김503 거부
배포 중 에러발생 가능안전
설정없음server.shutdown: graceful

프로덕션에서 server.shutdown: graceful은 선택이 아니라 필수 입니다. 특히 Kubernetes 환경에서는 terminationGracePeriodSecondstimeout-per-shutdown-phase의 관계를 반드시 맞춰야 합니다.

댓글 로딩 중...