오늘 알게 된 건데, 의외로 모르는 분 꽤 많더라. Pod의 graceful shutdown 늘리려고 PreStop hook에 sleep 박는 그 패턴 말이다. 우리도 한참 그렇게 썼는데, 사실 1.29부터는 그럴 필요가 없어졌다.
기존 방식의 함정
전형적인 zero-downtime 배포 트릭이다. Service에서 endpoint가 빠지는 동안에도 잠깐 트래픽이 계속 들어오니까, SIGTERM 받기 전에 몇 초 버텨주자는 거다.
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "sleep 10"]
근데 이거 distroless 이미지 쓰는 순간 깨진다. sh도 없고 sleep도 없으니까. 우리 팀은 작년에 보안팀이 모든 이미지를 distroless로 밀어붙이면서 이게 죄다 터졌다. CrashLoopBackOff까진 아니어도 PreStop 실패 로그가 매 deploy마다 쏟아졌고, 결국 BusyBox sidecar를 끼우거나 base 이미지를 alpine으로 되돌리거나 했다. 멘탈 잡고 보면 둘 다 별로다.
그래서 sleep action
KEP-3960. 1.29에서 alpha, 1.30부터 default enable, 그리고 1.34에서 드디어 GA 도장 찍혔다. 중간에 한 번 1.32에서 GA 시도했다가 테스트 flake 때문에 되돌려진 사연도 있다.
쓰는 법은 간단하다.
lifecycle:
preStop:
sleep:
seconds: 10
이게 전부다. 컨테이너 안에 sleep 바이너리가 있든 없든 상관없다. kubelet이 직접 타이머를 들고 있다가 SIGTERM 보내기 전에 그 시간만큼만 기다린다. 프로세스 fork도 없고, shell escape 걱정도 없고, 메모리도 안 먹는다.
한 가지 주의할 점
terminationGracePeriodSeconds보다 PreStop sleep이 길면 의미가 없다. kubelet은 grace period가 만료되면 PreStop이 아직 끝나지 않았어도 SIGKILL을 때린다. 그러니까 sleep 10초를 박을 거면 grace period는 최소 15~20초는 줘야 한다. 우리는 기본값 30초로 두고 sleep은 10~15초로 잡는다.
그리고 1.32부터는 seconds: 0도 허용된다 (PodLifecycleSleepActionAllowZero feature gate). 이건 처음 봤을 때 "이걸 왜?" 싶었는데, 조건부로 PreStop을 끼워야 하는 Helm chart 같은 데서 if 분기 안 타고 0초로 깔아두는 용도라고 한다. 솔직히 우리 팀은 아직 쓸 일이 없었다.
마이그레이션
기존 exec: sleep 패턴 다 찾으려면:
kubectl get pods -A -o json | jq '.items[] | select(.spec.containers[].lifecycle.preStop.exec.command // [] | tostring | contains("sleep")) | .metadata.namespace + "/" + .metadata.name'
찾아서 yaml만 바꾸면 끝이다. 다운타임 없이 롤링 업데이트로 처리하면 된다.
배포 파이프라인 점검할 때 같이 들여다보시길.
'IT > Kubernets' 카테고리의 다른 글
| Cilium vs Calico, 2026년에 새 클러스터를 만든다면 (0) | 2026.06.24 |
|---|---|
| kubectl debug, --copy-to 쓰지 마세요 (production에서는) (0) | 2026.06.22 |
| kubectl kuberc, 이제 alias 셸에 박지 말자 (0) | 2026.06.20 |
| Gateway API v1.5 ListenerSet으로 멀티팀 Gateway 정리하기 (0) | 2026.06.18 |
| Cilium kube-proxy replacement, 내부에서는 무슨 일이 벌어지나 (0) | 2026.06.17 |