graceful하게 pod를 종료하려면 큰 틀에서 두 가지 조건을 만족해야 한다.
1) 신규 트래픽이 종료 중인 파드로 전달되면 안된다.
2) 이미 들어온 트래픽이 처리가 될 때까지 파드는 종료되면 안된다.
신규 트래픽이 종료 중인 파드로 전달되면 안된다.

신규 트래픽이 노드로 들어오면 kube-proxy가 iptables로 기록해둔 경로를 따라 service에서 endpoint 중 하나로 로드밸런싱한다. ( 원리 : https://lordofkangs.tistory.com/709 ) 이때 endpoint는 service에게 3가지 상태를 공유한다.
- ready : 트래픽을 받을 준비가 되었는가?
- serving : 요청 처리가 가능한가?
- terminating : 파드가 삭제중인가?
# 세 가지 플래그 확인하는 방법
kubectl get endpointslices -o json -l kubernetes.io/service-name=[서비스명]
파드가 종료되면 endpoint의 ready는 true에서 false로 변경되고 service는 ready가 false인 endpoint로 트래픽을 전달하지 않아 종료중인 파드에게 트래픽을 전달하지 않는다.
이미 들어온 트래픽이 처리가 될 때까지 파드는 종료되면 안된다.
신규 트래픽이 노드로 들어오면 kube-proxy가 iptables로 기록해둔 경로를 따라 파드로 전달하여 세션이 생성되지만 세션의 상태는 Linux의 conntrack 모듈로 관리된다. 한마디로 [ ClientIP - PodIP ]로 TCP 연결 상태를 추적 관리하므로, 파드가 종료 명령을 받아 endpoint의 ready 플래그가 false여도 파드 상태만 정상 상태로 지연시킬 수 있다면 종료 명령 받기전 생성된 세션은 트래픽이 처리될 때까지 유지할 수 있다는 것이다.
이를 증명하기 위해 한 가지 테스트를 해보자.
Nginx Deployment 메니페스트
1) preStop 설정 추가
2) terminationGracePeriodSeconds 설정 추가
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
terminationGracePeriodSeconds: 300
containers:
- name: nginx
image: nginx:latest
lifecycle:
preStop:
exec:
command:
- /bin/sh
- -c
- |
echo "[$(date)] preStop start"
sleep 240
echo "[$(date)] preStop end"
ports:
- containerPort: 80
Nginx Service 메니페스트
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: 80
Client 파드 메니페스트
apiVersion: v1
kind: Pod
metadata:
name: curl-client
namespace: test
spec:
restartPolicy: Never
containers:
- name: curl
image: curlimages/curl:8.8.0
command:
- /bin/sh
- -c
- |
echo "curl client ready"
sleep infinity
Client가 nginx 파드에 있는 파일을 다운로드 하여 TCP 세션을 유지하는 상황을 가정해보자.
우선 nginx 파드 내부로 exec하여 파일 하나를 설치한다.
dd if=/dev/zero of=/usr/share/nginx/html/1g.bin bs=1M count=1024
그리고 client 파드 내부로 exec하여 curl 명령어로 해당 파일을 다운로드 하여 세션을 유지한다.
curl http://nginx-service.test/1g.bin --limit-rate 100k -o /dev/null
아래 명령어로 client 파드와 nginx 파드의 ip와 nginx 파드가 떠있는 노드를 확인한다.
kubectl get pods -n test -o wide
nginx 파드가 떠있는 node 내부로 접속하여 아래 명령어를 실행한다.
# 10.233.111.16 : nginx 파드 ip
conntrack -L -p tcp | grep 10.233.111.16
출력 결과
src=10.233.111.16 : nginx 파드
dst=10.233.111.56 : client 파드
tcp 6 299 ESTABLISHED src=10.233.111.56 dst=10.233.16.192 sport=36916 dport=80 packets=911 bytes=47539 src=10.233.111.16 dst=10.233.111.56 sport=80 dport=36916 packets=5236 bytes=16801505 [ASSURED] mark=0 use=1
최초에 10.233.111.56(Client Pod)에서 10.233.16.192(NGINX Service)으로 요청이 유입된다. kube-proxy의 iptables 규칙에 의해 Service IP는 10.233.111.16(NGINX Pod)으로 DNAT된다. 이후 Client Pod(10.233.111.56)와 NGINX Pod(10.233.111.16) 사이의 TCP 연결이 ESTABLISHED 상태로 생성되며 해당 연결 정보는 Linux conntrack 테이블에 기록되어 관리된다.
여기서 파드가 종료명령을 받으면 어떻게 될까?
kubectl get endpointslices -o json -l kubernetes.io/service-name=nginx-service
"conditions": {
"ready": false,
"serving": true,
"terminating": true
},
endpoint는 ready가 false로 변경되며 신규 트래픽 유입이 막힌다.
파드는 preStop 설정에 의해 종료명령을 받더라도 프로세스 종료작업을 진행하지 않는다.
# 10.233.111.16 : nginx 파드 ip
conntrack -L -p tcp | grep 10.233.111.16
출력결과
tcp 6 299 ESTABLISHED src=10.233.111.56 dst=10.233.16.192 sport=36916 dport=80 packets=2731 bytes=142179 src=10.233.111.16 dst=10.233.111.56 sport=80 dport=36916 packets=15669 bytes=47039597 [ASSURED] mark=0 use=1
conntrack으로 확인해보면 파드가 종료명령을 받았는데도 아직 TCP 세션은 ESTABLISHED 상태로 유지됨을 확인할 수 있다. TCP 세션은 client 와 파드 사이의 연결로, 파드만 유지된다면 세션은 끊기지 않기 때문이다.
그러므로 파드를 안전하게 종료하려면
파드에 신규 트래픽 유입을 막고
파드의 모든 세션이 자발적으로 종료될때까지 종료를 지연시키는 것이 중요하다.
1) 파드에 preStop 설정 추가
2) 파드에 terminationGracePeriodSeconds 설정 추가
pod가 종료 명령을 받으면 내부적으로 SIGTERM 명령을 받아 안전하게 프로세스를 종료하는 작업을 진행한다.
preStop은 SIGTERM 명령이 들어오는 시간을 지연시켜준다. 요청이 들어오면 프로세스가 살아 있어야 하기에, 종료시키지 않고 유지시켜주는 작업이다. preStop으로 설정된 시간이 지나가면 SIGTERM 명령이 들어온다. 그러면 프로세스 정리작업이 시작된다. 프로세스가 정리되는데 시간이 오래 걸리고 서비스가 Stateful해서 프로세스가 안전하게 정리되어야 한다면 terminationGracePeriodSeconds 시간을 길게 잡아야 한다.
terminationGracePeriodSeconds 가 120초이고 preStop 60초이면 pod 종료 명령이 들어오고 60초 동안은 SIGTERM이 내부에서실행되지 않는다. 60초가 지나면 SIGTERM명령이 실행되고 프로세스 종료작업이 진행된다. 그리고 60초가 흐르면 파드는 종료된다.
참고자료
https://kubernetes.io/docs/tutorials/services/pods-and-endpoint-termination-flow/
'Ops > Kubernetes' 카테고리의 다른 글
| K8s readonly kubeconfig 파일 생성하기 (0) | 2026.05.14 |
|---|---|
| k8s Secret을 Git으로 관리하는 방법 (2) | 2026.04.30 |
| 영구적인 Service Account Token 만들기 (0) | 2026.02.04 |
| node-core-dns 란? (0) | 2025.12.31 |
| [이슈] Network Policy - failed to get status by proxying to the pod, you might lack permissions to get pods/proxy (0) | 2025.12.08 |