결론부터
컨테이너에서 실행되는 프로세스가 완료된 후 애플리케이션이 정상적으로 종료되는 Graceful Shutdown을 위해서는, 쿠버네티스 파드 설정의 preStop Hook과 terminationGracePeriodSeconds 옵션을 통해서 할 수 있다.
파드가 종료될 때 실행중인 프로세스의 문제
Kubernetes 환경에서 배포, liveness probe의 이상 감지 등 여러 가지 이유로 파드가 종료 상태로 변경될 때, 컨테이너에서 진행 중인 프로세스가 모두 완료된 후 종료되도록 설정이 필요하다. 파드가 종료될 때 kubelet이 컨테이너에 SIGTERM 신호를 보내게 되는데, SIGTERM 신호를 막는 설정이 없다면 진행 중인 프로세스와 무관하게 애플리케이션 서버는 종료되는 문제가 생긴다.
이런 상황을 방지하고 Graceful Shutdown(직역하면 우아한 종료)이 되기 위해 설정을 하게 되는데, 먼저 애플리케이션에서 가장 오래 걸리는 프로세스의 최대 실행시간을 계산한다. 예를 들어 스프링부트에서 가장 오래걸리는 배치나 API 서비스가 있고, 50초 이내에 끝난다고 보장된다면, 50초를 최대 실행시간으로 계산한다.
preStop Hook
애플리케이션이 하던 일을 모두 마치고 종료하도록 하기 위해서는, 애플리케이션에서 Graceful Shutdown을 설정하거나 쿠버네티스 파드 설정에서 preStop 훅을 이용한다. 애플리케이션에서 설정한다면 SIGTERM 명령어가 들어와도 진행 중인 작업이 다 끝나고 종료되도록 시간을 설정하면 된다. 애플리케이션에서 설정이 불가하거나 다른 이유로 쿠버네티스에서 설정이 필요한 경우에는, 컨테이너 라이프사이클 훅 중 preStop을 이용한다. preStop Hook은 파드가 종료를 요청받고 컨테이너가 종료되기 전 실행되는 훅인데, 파드가 종료되기 전 컨테이너에 원하는 커맨드를 실행할 수 있다. 애플리케이션이 일찍 종료되지 않도록 커맨드 부분에 애플리케이션에서 가장 길게 실행되는 프로세스의 완료가 보장되는 최대 시간보다 길게 유휴시간을 갖도록 설정한다. 이 시간을 길게 설정할수록 안정성은 좋아지나 다운되는 시간이 길어지는 트레이드 오프가 생긴다.
lifecycle:
preStop:
exec:
command:
- sleep
- "50"
terminationGracePeriodSeconds
만약 애플리케이션에서 설정한 Graceful Shutdown 시간 또는 preStop 훅에서 설정한 유휴시간이 30초 이상일 경우 추가로 설정해야 할 부분이 있는데, 파드 종료 시작 후 30초가 넘게 되면 파드는 terminationGracePeriodSeconds 설정의 기본값(30초)으로 인해 30초 만에 SIGKILL 신호로 즉시 종료된다. terminationGracePeriodSeconds 옵션은 preStop 훅과 병렬로 진행되며, 따라서 preStop에서 설정한 시간보다 길게 설정해야 한다. 앞서 50초로 preStop을 설정했다면, preStop 이후 애플리케이션이 SIGTERM 신호로 애플리케이션이 종료되는 시간까지 고려해서 terminationGracePeriodSeconds을 세팅한다. 예를 들어 애플리케이션이 정상적으로 종료되는데 걸리는 시간이 5초라면 terminationGracePeriodSeconds 옵션을 50초보다 긴 55초로 설정하는 등의 전략을 사용할 수 있다. 여러 가지 값들을 설정해 보고 파드를 종료하는 테스트를 통해 최적화하는 것이 좋다.