← 학습 카테고리

Learn

Kubernetes

24개 모듈 · 현재 10번째

Kubernetes 모듈 10/24 k8s-learn-10

Graceful Termination — SIGTERM과 트래픽 제거는 동시에 일어나지 않는다

Kubernetes PodDisruptionBudget 및 Graceful Termination 공식 문서 종합 (웹 리서치, 2026) — Kubernetes 공식 문서(concepts/workloads/pods/pod-lifecycle, concepts/workloads/pods/disruptions) Kubernetes 공식문서 Pod Lifecycle — Termination of Pods

이 모듈을 다 읽으면

  • terminationGracePeriodSeconds가 SIGTERM 이후 preStop 훅까지 포함한 '전체 예산'이라는 것을 설명할 수 있다
  • Pod가 Service 엔드포인트에서 빠지는 것과 컨테이너 종료가 비동기로 동시에 일어나 롤링 업데이트 중 요청 실패가 생길 수 있는 이유를 설명할 수 있다

k8s-learn-02에서 다룬 롤링 업데이트가 무중단을 '보장'하는 것처럼 보이지만, 실제로는 Pod 종료 시점의 타이밍 문제로 요청이 실패할 수 있다는 것을 전제로, Pod 종료 시퀀스와 preStop 훅으로 이를 완화하는 방법을 정리한다.

종료 시퀀스 — SIGTERM → preStop 훅 → 그래도 안 끝나면 강제 종료

Pod가 종료될 때 kubelet은 먼저 컨테이너에 SIGTERM 신호를 보낸다. 컨테이너에 preStop 훅이 정의돼 있으면(그리고 terminationGracePeriodSeconds가 0이 아니면), kubelet은 그 훅을 실행한다. 이 SIGTERM 처리와 preStop 훅 실행 전부가 terminationGracePeriodSeconds(기본 30초) 안에 끝나야 한다 — 이 값은 SIGTERM만의 시간이 아니라 preStop까지 포함한 '전체 예산'이다. 이 시간이 다 지나도 컨테이너가 안 끝나면 그때 강제로 종료된다.

핵심 포인트

  • 종료 순서: SIGTERM 전송 → (있으면) preStop 훅 실행 → terminationGracePeriodSeconds(기본 30초) 안에 안 끝나면 강제 종료
  • terminationGracePeriodSeconds는 SIGTERM만이 아니라 preStop 훅까지 포함한 전체 예산 — preStop이 30초를 쓰고 앱 자체 처리에도 30초가 필요하면 이 값을 60초 이상으로 늘려야 함

왜 롤링 업데이트 중 요청이 간헐적으로 실패하는가 — 종료와 엔드포인트 제거의 비동기 경합

Pod가 Service의 엔드포인트 목록에서 빠지는 것과, 컨테이너에 SIGTERM이 전달돼 종료가 시작되는 것은 서로 완전히 동기화된 하나의 원자적 절차가 아니라 병렬적·비동기적으로 진행된다. 그 결과 SIGTERM을 받은 직후에도 짧은 시간 동안은 여전히 그 Pod로 트래픽이 들어올 수 있다 — kube-proxy(k8s-learn-01)가 라우팅 규칙을 갱신하는 데 걸리는 시간과, kubelet이 컨테이너 종료를 시작하는 타이밍이 정확히 맞아떨어진다는 보장이 없기 때문이다. 이것이 무중단 배포를 목표로 한 롤링 업데이트에서도 요청이 간헐적으로 실패하는 근본 원인이다.

핵심 포인트

  • Pod의 Service 엔드포인트 제거와 컨테이너 SIGTERM 처리는 원자적으로 동기화돼 있지 않고 병렬·비동기로 진행됨
  • SIGTERM 직후에도 kube-proxy의 라우팅 규칙 갱신이 끝나기 전까지는 짧게 트래픽이 계속 들어올 수 있음 — 이것이 롤링 업데이트 중 요청 실패의 근본 원인

완화책 — preStop에 짧은 sleep을 넣어 엔드포인트 전파를 기다린다

이 경합을 완전히 없앨 수는 없지만, 실무에서 흔히 쓰는 완화책은 preStop 훅에 몇 초짜리 sleep(예: 5~10초)을 넣는 것이다 — 컨테이너는 SIGTERM을 받고도 이 sleep 동안은 계속 살아서 요청을 받을 수 있는 상태를 유지하고, 그 사이 kube-proxy의 엔드포인트 제거가 클러스터 전체에 전파될 시간을 벌어준다. 애플리케이션 자체도 SIGTERM을 받으면 새 요청은 그만 받되 이미 들어온 요청은 마저 처리하고 끝내는(graceful shutdown) 로직을 구현하는 것이 이상적이며, 이 경우 preStop sleep 시간과 애플리케이션의 자체 드레인 시간을 합친 만큼 terminationGracePeriodSeconds를 넉넉히 잡아야 한다.

핵심 포인트

  • 완화책: preStop 훅에 짧은 sleep(예: 5~10초)을 넣어 그동안 컨테이너가 계속 요청을 받게 하고, kube-proxy의 엔드포인트 제거 전파 시간을 벌어줌
  • 애플리케이션도 SIGTERM 수신 시 새 요청 거부+기존 요청 완료 후 종료(graceful shutdown)를 구현하는 것이 이상적 — 이 경우 preStop sleep+앱 드레인 시간의 합만큼 terminationGracePeriodSeconds를 넉넉히 설정