환경변수와 라이프사이클 훅(Hook)
쿠버네티스 공식문서를 확인하며 환경변수와 라이프사이클 훅(Hook)에 대해서 기억해야 하는 부분을 기록한다.
환경변수
컨테이너 환경
- 컨테이너 환경은 컨테이너에 몇 가지 중요한 리소스를 제공한다.
- 하나의 이미지와 하나 이상의 볼륨이 결합된 파일 시스템
- 컨테이너 자신에 대한 정보
- 클러스터 내의 다른 오브젝트에 대한 정보
컨테이너 정보
- 컨테이너의 “호스트네임”은 컨테이너가 동작 중인 파드의 이름과 같다.
**hostname
커맨드 또는 libc의gethostname
함수 호출을 통해서 구할 수** 있다. - 파드 이름과 네임스페이스는 다운워드(Downward) API를 통해 환경 변수로 구할 수 있다.
- 컨테이너 이미지에 정적으로 명시된 환경 변수와 마찬가지로, 파드 정의에서의 사용자 정의 환경 변수도 컨테이너가 사용할 수 있다.
클러스터 정보
- 컨테이너가 생성될 때 실행 중이던 모든 서비스의 목록은 환경 변수로 해당 컨테이너에서 사용할 수 있다.
- 서비스 목록은 새로운 컨테이너의 파드 및 쿠버네티스 컨트롤 플레인 서비스와 동일한 네임스페이스 내에 있는 서비스로 한정된다.
- bar라는 이름의 컨테이너에 매핑되는 foo라는 이름의 서비스에 대해서는, 아래의 형태로 변수가 정의된다.
FOO_SERVICE_HOST=<서비스가 동작 중인 호스트>
FOO_SERVICE_PORT=<서비스가 동작 중인 포트>
- 서비스에 지정된 IP 주소가 있고 DNS 애드온이 활성화된 경우, DNS를 통해서 컨테이너가 서비스를 사용할 수 있다.
[정리]
컨테이너 환경은 컨테이너에게 환경변수로 몇가지 리소스를 제공한다.
여기에는 컨테이너 정보, 클러스터 정보, 동일 네임스페이스 내에 있는 서비스 목록 등이 있다.
컨테이너 라이프사이클 훅(Hook)
- kubelet이 관리하는 컨테이너가 관리 라이프사이클 동안 이벤트에 의해 발동되는 코드를 실행하기 위해 컨테이너 라이프사이클 훅 프레임워크를 사용한다.
- Angular와 같이, 컴포넌트 라이프사이클 훅을 가진 많은 프로그래밍 언어 프레임워크와 유사하게, 쿠버네티스도 컨테이너에 라이프사이클 훅을 제공한다.
- 훅은 컨테이너가 관리 라이프사이클의 이벤트를 인지하고 상응하는 라이프사이클 훅이 실행될 때 핸들러에 구현된 코드를 실행할 수 있게 한다.
컨테이너 훅
- 컨테이너 훅은 아래와 총 두 가지가 있다.
PostStart
PostStart
훅은 컨테이너가 생성된 직후에 실행되지만, 훅이 컨테이너 엔트리포인트에 앞서서 실행된다는 보장은 없으며, 파라미터는 핸들러에 전달되지 않는다.
PreStop
PreStop
훅은 API 요청이나 활성 프로브(liveness probe) 실패, 선점, 자원 경합 등의 관리 이벤트로 인해 컨테이너가 종료되기 직전에 호출된다.- 컨테이너가 이미 terminated 또는 completed 상태인 경우에는
PreStop
훅 요청이 실패하며, 훅은 컨테이너를 중지하기 위한 TERM 신호가 보내지기 이전에 완료되어야 한다. - 파드의 그레이스 종료 기간(termination grace period)의 초읽기는
PreStop
훅이 실행되기 전에 시작되어, 핸들러의 결과에 상관없이 컨테이너가 파드의 그레이스 종료 기간 내에 결국 종료되도록 한다. 파라미터는 핸들러에 전달되지 않는다.
[정리]
라이프사이클 훅 프레임워크 컨테이너의 라이프사이클을 관리하기 위해서 사용된다. 훅은 컨테이너가 관리 라이프사이클의 이벤트를 인지하고 상응하는 라이프사이클 훅이 실행될 때 핸들러에 구현된 코드를 실행할 수 있게 한다.
PostStart
는 컨테이너 생성 직후 실행되며, PreStop
은 컨테이너가 종료되기 직전에 호출된다.
훅 핸들러 구현
- 컨테이너는 훅의 핸들러를 구현하고 등록함으로써 해당 훅에 접근할 수 있다.
- 구현될 수 있는 컨테이너의 훅 핸들러에는 두 가지 유형이 있다
- Exec: 컨테이너의 cgroups와 네임스페이스 안에서,
pre-stop.sh
와 같은, 특정 커맨드를 실행. 커맨드에 의해 소비된 리소스는 해당 컨테이너에 대해 계산된다. - HTTP: 컨테이너의 특정 엔드포인트에 대해서 HTTP 요청을 실행.
- Exec: 컨테이너의 cgroups와 네임스페이스 안에서,
훅 핸들러 실행
- 컨테이너 라이프사이클 관리 훅이 호출되면, 쿠버네티스 관리 시스템은 훅 동작에 따라 핸들러를 실행하고,
httpGet
와tcpSocket
은 kubelet 프로세스에 의해 실행되고,exec
은 컨테이너에서 실행된다. - 훅 핸들러 호출은 해당 컨테이너를 포함하고 있는 파드의 컨텍스트와 동기적으로 동작한다. 이것은
PostStart
훅에 대해서, 훅이 컨테이너 엔트리포인트와는 비동기적으로 동작함을 의미한다. 파드와 동기이기 때문에 파드 내부의 컨테이너와는 비동기로 작동할 수 밖에 없다. - 그러나, 만약 해당 훅이 너무 오래 동작하거나 어딘가에 걸려 있다면, 컨테이너는
running
상태에 이르지 못한다. PreStop
훅은 컨테이너 중지 신호에서 비동기적으로 실행되지 않는다. 훅은 TERM 신호를 보내기 전에 실행을 완료해야 한다.- 실행 중에
PreStop
훅이 중단되면, 파드의 단계는Terminating
이며terminationGracePeriodSeconds
가 만료된 후 파드가 종료될 때까지 남아 있다. 이때의 유예 기간은PreStop
훅이 실행되고 컨테이너가 정상적으로 중지되는 데 걸리는 총 시간에 적용된다. - 만약
terminationGracePeriodSeconds
가 60이고, 훅이 완료되는 데 55초가 걸리고, 컨테이너가 신호를 수신한 후 정상적으로 중지하는 데 10초가 걸린다면,terminationGracePeriodSeconds
이후 컨테이너가 정상적으로 중지되기 전에 종료된다. 이 두 가지 일이 발생하는 데 걸리는 총 시간(55 + 10)보다 적다. - 만약
PostStart
또는PreStop
훅이 실패하면, 컨테이너를 종료시킨다.
훅 전달 보장
- 훅 전달은 “한 번 이상”으로 의도되어 있는데, 이는
PostStart
또는PreStop
과 같은 특정 이벤트에 대해서, 훅이 여러 번 호출될 수 있다는 것을 의미한다. - 일반적으로 훅의 전달은 단 한 번만 이루어지기 때문에 HTTP 훅 수신기가 다운되어 트래픽을 받을 수 없는 경우에도, 재전송을 시도하지 않는다.
- 훅을 전송하는 도중에 kubelet이 재시작된다면 kubelet이 구동된 후에 해당 훅이 재전송되기 때문에 적은 확룔로 이중 전달이 발생할 수 있다.
훅 핸들러 디버깅
- 훅 핸들러의 로그는 파드 이벤트로 노출되지 않는다. 핸들러가 어떠한 이유로 실패하면, 핸들러는 이벤트를 방송한다.
PostStart
의 경우, 이것은FailedPostStartHook
이벤트이며,PreStop
의 경우, 이것은FailedPreStopHook
이벤트다.- 실패한
FailedPreStopHook
이벤트를 직접 생성하려면, lifecycle-events.yaml 파일을 수정하여postStart
명령을badcommend
로 변경하고 이를 적용한다. - 아래는
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
[정리]
컨테이너는 훅에 접근하기 위해서 훅의 핸들러를 구현해서 등록해야 한다. 훅 핸들러 호출은 컨테이너의 파드 컨텍스트와 동기적으로 작동하기 때문에 컨테이너와는 비동기적으로 동작한다. 만약 훅이 너무 오래 동작하거나 실행되지 않는다면 컨테이너는 running
상태에 도달할 수 없으며 훅이 실패하는 경우에도 컨테이너가 종료된다.
훅 전달은 한 번 이상으로 의도되어 있기 때문에 여러 번 호출될 수 있다. 하지만 일반적으로 훅의 전달은 단 한 번만 이루어지게 된다. kubelet이 재시작된다면 kubelet이 구동된 후에 해당 훅이 재전송되어 훅이 이중으로 전달될 수 있다.
훅 핸들러의 로그는 파드 이벤트로 노출되지 않기 때문에 핸들러가 어떠한 이유라 실패하면, 핸들러는 이벤트를 방송한다.
참고 자료
'Infrastructure > Kubernetes' 카테고리의 다른 글
[Pod] 개념 (0) | 2022.10.14 |
---|---|
[워크로드] 개념 (0) | 2022.10.14 |
[컨테이너] 런타임클래스 (0) | 2022.10.14 |
[컨테이너] 이미지 (0) | 2022.10.14 |
[클러스터 아키텍처] 컨테이너 런타임 인터페이스(CRI) (0) | 2022.10.13 |