[k8s] Graceful Shutdown을 위한 preStop, terminationGracePeriodSeconds 설정
Graceful Shutdown (우아한 종료) 이란?
현재 들어온 요청을 모두 수행하고 우아하게 종료된다는 개념이다. 수행 중이던 요청을 미처 처리하지 못하고 종료되면 리소스가 서버에 좀비처럼 남아있거나 데이터 손실을 일으킬 수 있다.
- kube-proxy나 Ingress controller 등은 엔드포인트 변경에 대해 알림을 받기까지 다소 시간이 걸립니다. 따라서 트래픽은 종료된 것으로 표시가 되어도 여전히 포드(Pod)로 트래픽이 흐를 수 있습니다. 앱은 새로운 요청 수락을 중지되고 모든 연결이 종료되면 종료돼야 합니다. 이를 위해서 앱 spec에서 'lifecycle.preStop'을 설정하는 것을 고려합니다.
- Pod가 종료 전 데이터베이스와의 연결을 종료하고 트랜잭션을 완료하여 종료해야 하는 상황.
어플리케이션에서 Graceful Shutdown 로직을 구현하면 좋겠지만 그렇지 않았을 때 preStop hook, terminationGracePeriodSeconds을 설정하여 우아한 종료를 할 수 있다.
Pod 종료 순서
- 사용자 또는 관리자가 Pod를 삭제하도록 kubectl, API 서버, 또는 그 외 다른 방법으로 요청합니다.
- API 서버는 요청을 받고 etcd에 저장된 해당 Pod의 상태를 `Terminating`으로 변경합니다.
- Kubelet은 주기적으로 API 서버에게 상태를 확인하고 `Terminating` 상태의 변경사항을 감지합니다.
- Kubelet은 컨테이너에 `preStop`훅이 있다면 해당 훅을 실행합니다.
- `preStop` 훅이 완료되면, Kubelet은 각 컨테이너에게 `SIGTERM` 시그널을 전송하여 정상적인 종료 절차를 밟을 것을 요청합니다.
- 각 컨테이너는 종료 기간(terminationGracePeriodSeconds) 동안 정리 작업을 수행하고 종료됩니다.
- 만약 종료 기간 이내에 종료되지 않는다면, Kubelet은 컨테이너에게 `SIGKILL` 시그널을 전송하여 강제로 종료시킬 것입니다.
- 컨테이너가 모두 종료되면, Kubelet은 API 서버에게 Pod 종료 상태를 보고합니다.
terminationGracePeriodSeconds 이란?
kubernetes에서 Pod를 안전하게 종료하기 위해 사용하는 설정 값으로, kubelet이 SIGTERM 신호을 보낸 후부터 완전히 종료될 때까지 기다리는 시간(초)이다. 해당 시간이 끝나면 SIGKILL 신호를 보내 Pod를 종료한다.
preStop 이란?
kubectl delete pod 또는 관리 이벤트 등과 같은 종료 명령을 받으면 바로 실행된다.
어플리케이션에서 Graceful Shutdown 로직을 구현하지 않았을 때 Pod의 Yaml파일에서 간단히 설정할 수 있는 방법이다.
terminationGracePeriodSeconds만으로는 Graceful한 종료를 보장 할 수 없기 때문에 preStop을 추가로 제공한다.
terminationGracePeriodSeconds은 preStop과 병렬이기 때문에 최소한 preStop 소요 시간보다 더 길게 설정해야 한다.
예를들어, preStop hook이 60초 동안 실행되는데 terminationGracePeriodSeconds을 30초로 설정하면 30초 후 SIGKILL 신호가 전달되어 컨테이너가 강제 종료되기 때문이다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx
spec:
selector:
matchLabels:
run: my-nginx
replicas: 2
minReadySeconds: 5
template:
metadata:
labels:
run: my-nginx
spec:
containers:
- name: my-nginx
image: nginx
ports:
- containerPort: 80
lifecycle:
preStop:
exec:
command: ["/usr/sbin/nginx","-s","quit"]
# command: ["/bin/sh", "-c", "sleep 40"]
readinessGates:
- conditionType: target-health.alb.ingress.k8s.aws/ingress-app_my-nginx_80
terminationGracePeriodSeconds: 60 # 60초 후 SIGKILL을 날려서 컨테이너를 중지 시킨다.
위의 코드에서 보면 nginx container 가 종료되기 전에 preStop 훅을 설정해서 nginx의 정상 종료를 시도한다.
terminationGracePeriodSeconds는 디폴트가 30초이다. 컨테이너의 정상 종료 시간 확보를 위해서 적절한 시간을 설정해야 한다.
추가로, AWS ALB는 idle timeout 시간이 디폴트가 60초이다. 그러므로 AWS ALB를 사용한다면 terminationGracePeriodSeconds을 60 이상으로 설정해야 504 에러를 피할 수 있다.
공식문서에서도 애플리케이션의 유휴 시간제한을 로드 밸런서에 대해 구성된 유휴 시간제한보다 크게 구성하는 것이 좋다고 말하고 있다.
참고
https://kubernetes.io/ko/docs/concepts/workloads/pods/pod-lifecycle/#pod-termination
https://merrily-code.tistory.com/275