컨테이너 라이프사이클 훅
컨테이너 라이프사이클 훅 (Container Lifecycle Hooks)
이 페이지는 kubelet이 관리하는 컨테이너가 컨테이너 라이프사이클 훅 프레임워크를 사용해 관리 수명주기 중의 이벤트로 트리거되는 코드를 실행하는 방법을 설명해요.
출처: 문서
본문
개요 (Overview)
Angular 같은 많은 프로그래밍 언어 프레임워크가 컴포넌트 라이프사이클 훅을 갖는 것과 유사하게, Kubernetes는 컨테이너에 라이프사이클 훅을 제공해요. 훅 덕분에 컨테이너는 관리 수명주기 중의 이벤트를 인지하고, 해당 라이프사이클 훅이 실행될 때 핸들러로 구현된 코드를 실행할 수 있어요.
컨테이너 훅 (Container hooks)
컨테이너에 노출되는 훅은 두 가지예요.
PostStart
이 훅은 컨테이너가 생성된 직후 실행돼요. 컨테이너의 ENTRYPOINT(주 프로세스)와 동시에 실행되므로, 훅은 주 프로세스가 시작되기 전, 도중, 후에 실행될 수 있어요. 핸들러에는 매개변수가 전달되지 않아요.
PreStop
이 훅은 liveness/startup 프로브 실패, 선점, 리소스 경합 등의 API 요청이나 관리 이벤트로 컨테이너가 종료되기 직전에 호출돼요. 컨테이너가 이미 terminated 또는 completed 상태이면 PreStop 훅 호출은 실패하고, 컨테이너를 중지하는 TERM 신호를 보내기 전에 훅이 완료되어야 해요. 파드의 종료 유예 기간(termination grace period) 카운트다운은 PreStop 훅이 실행되기 전에 시작되므로, 핸들러의 결과와 무관하게 컨테이너는 결국 파드의 종료 유예 기간 안에 종료돼요. 핸들러에는 매개변수가 전달되지 않아요.
종료 동작에 대한 더 자세한 설명은 파드 종료에서 찾을 수 있어요.
StopSignal
StopSignal 수명주기는 컨테이너가 중지될 때 보낼 정지 신호를 정의하는 데 사용할 수 있어요. 이것을 설정하면 컨테이너 이미지에 정의된 STOPSIGNAL 지시를 덮어써요.
커스텀 정지 신호를 사용한 종료 동작의 더 자세한 설명은 정지 신호에서 찾을 수 있어요.
훅 핸들러 구현 (Hook handler implementations)
컨테이너는 훅에 대한 핸들러를 구현하고 등록해 훅에 접근할 수 있어요. 컨테이너에 구현할 수 있는 훅 핸들러 유형은 세 가지예요.
- Exec — 컨테이너의 cgroups와 네임스페이스 안에서
pre-stop.sh같은 특정 명령을 실행해요. 명령이 소비하는 리소스는 컨테이너에 계산돼요. - HTTP — 컨테이너의 특정 엔드포인트에 HTTP 요청을 실행해요.
- Sleep — 지정된 시간 동안 컨테이너를 일시 중지해요.
훅 핸들러 실행 (Hook handler execution)
컨테이너 라이프사이클 관리 훅이 호출되면 Kubernetes 관리 시스템은 훅 액션에 따라 핸들러를 실행해요. httpGet, tcpSocket(사용 중단됨), sleep은 kubelet 프로세스가 실행하고, exec는 컨테이너에서 실행돼요.
PostStart 훅 핸들러 호출은 컨테이너가 생성될 때 시작돼요. 즉 컨테이너 ENTRYPOINT와 PostStart 훅이 동시에 트리거되는 거예요. (따라서 PostStart에 HTTP 훅을 사용하는 것은 일반적으로 의미가 없어요. 훅이 실행될 때 컨테이너의 프로세스가 완전히 시작되었으리라는 보장이 없기 때문이에요.) PostStart 훅 실행이 너무 오래 걸리거나 멈추면(hang), 컨테이너가 실행(running) 상태로 전환되는 것을 막을 수 있어요.
PreStop 훅은 컨테이너 중지 신호와 비동기적으로 실행되지 않아요. TERM 신호를 보내기 전에 훅이 실행을 완료해야 해요. PreStop 훅이 실행 중에 멈추면, 파드의 phase는 Terminating이 되고 terminationGracePeriodSeconds가 만료된 후 파드가 죽을 때까지 그 상태에 남아요. 이 유예 기간은 PreStop 훅이 실행되고 컨테이너가 정상적으로 중지되는 데 걸리는 전체 시간에 적용돼요. 예를 들어 terminationGracePeriodSeconds가 60이고, 훅이 완료되는 데 55초, 신호를 받은 후 컨테이너가 정상 중지되는 데 10초가 걸린다면, 이 두 가지 일이 일어나는 총 시간(55+10)보다 terminationGracePeriodSeconds가 작으므로 컨테이너는 정상적으로 중지되기 전에 죽게 돼요.
PostStart 또는 PreStop 훅 중 하나라도 실패하면 그 컨테이너를 종료해요.
사용자는 훅 핸들러를 최대한 가볍게 만들어야 해요. 다만 컨테이너를 중지하기 전에 상태를 저장하는 경우처럼 오래 실행되는 명령이 의미가 있는 경우도 있어요.
훅 전달 보장 (Hook delivery guarantees)
훅 전달은 최소 한 번(at least once) 을 의도하며, 이는 PostStart나 PreStop 같은 주어진 이벤트에 대해 훅이 여러 번 호출될 수 있음을 의미해요. 올바르게 처리하는 것은 훅 구현의 몫이에요.
일반적으로 단일 전달만 이루어져요. 예를 들어 HTTP 훅 수신기가 다운되어 트래픽을 받을 수 없으면 재전송을 시도하지 않아요. 다만 드물게 이중 전달이 발생할 수 있어요. 예를 들어 kubelet이 훅을 보내는 도중에 재시작되면, kubelet이 다시 올라온 후 훅이 재전송될 수 있어요.
훅 핸들러 디버깅 (Debugging Hook handlers)
훅 핸들러의 로그는 파드 이벤트에 노출되지 않아요. 핸들러가 어떤 이유로 실패하면 이벤트를 브로드캐스트해요. PostStart의 경우 FailedPostStartHook 이벤트, PreStop의 경우 FailedPreStopHook 이벤트예요.
직접 실패한 FailedPostStartHook 이벤트를 만들어 보려면 lifecycle-events.yaml 파일을 수정해 postStart 명령을 "badcommand"로 바꾸고 적용해요. kubectl describe pod lifecycle-demo를 실행했을 때 볼 수 있는 결과 이벤트의 예시 출력은 다음과 같아요.
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 7s default-scheduler Successfully assigned default/lifecycle-demo to ip-XXX-XXX-XX-XX.us-east-2...
Normal Pulled 6s kubelet Successfully pulled image "nginx" in 229.604315ms
Normal Pulling 4s (x2 over 6s) kubelet Pulling image "nginx"
Normal Created 4s (x2 over 5s) kubelet Created container lifecycle-demo-container
Normal Started 4s (x2 over 5s) kubelet Started container lifecycle-demo-container
Warning FailedPostStartHook 4s (x2 over 5s) kubelet Exec lifecycle hook ([badcommand]) for Container "lifecycle-demo-container" in Pod "lifecycle-demo_default(30229739-9651-4e5a-9a32-a8f1688862db)" failed - error: command 'badcommand' exited with 126: , message: "OCI runtime exec failed: exec failed: container_linux.go:380: starting container process caused: exec: \"badcommand\": executable file not found in $PATH: unknown\r\n"
Normal Killing 4s (x2 over 5s) kubelet FailedPostStartHook
Normal Pulled 4s kubelet Successfully pulled image "nginx" in 215.66395ms
Warning BackOff 2s (x2 over 3s) kubelet Back-off restarting failed container
다음 단계
- 컨테이너 환경 더 알아보기
- 컨테이너 라이프사이클 이벤트에 핸들러 연결 실습 경험 쌓기