초기화 컨테이너
쿠버네티스 공식문서를 확인하며 초기화 컨테이너에 대해서 기억해야 하는 부분을 기록한다.
- 초기화 컨테이너는 파드의 앱 컨테이너들이 실행되기 전에 실행되는 특수한 컨테이너로 앱 이미지에는 없는 유틸리티 또는 설정 스크립트 등을 포함할 수 있다.
- 초기화 컨테이너는
containers
배열에 과 나란히 파드 스펙에 명시할 수 있다. - 초기화 컨테이너는 일반 컨테이너와 비교하여 아래와 같은 차이가 있다.
- 초기화 컨테이너는 항상 완료를 목표로 실행된다.
- 각 초기화 컨테이너는 다음 초기화 컨테이너가 시작되기 전에 성공적으로 완료되어야 한다.
- 파드의 초기화 컨테이너가 실패하면, kubelet은 초기화 컨테이너가 성공할 때까지 반복적으로 재시작한다.
- 만약 파드의
restartPolicy
를 Never로 설정하고, 해당 파드를 시작하는 동안 초기화 컨테이너가 실패하면, 쿠버네티스는 전체 파드를 실패한 것으로 처리한다. - 컨테이너를 초기화 컨테이너로 지정하기 위해서는, 파드 스펙에
initContainers
필드를container
항목들의 배열로서 추가한다. - 초기화 컨테이너의 상태는 컨테이너 상태의 배열로
.status.initContainerStatuses
필드에 반환된다.
[정리]
초기화 컨테이너는 애플리케이션 컨테이너를 실행시키기 위한 특수한 컨테이너로 애플리케이션 컨테이너가 실행되기 전에 성공적으로 완료되는 것을 목표로 한다. 여러 초기화 컨테이너가 존재할 수 있으며 애플리케이션 컨테이너를 실행시키기 위해 순차적으로 실행된다.
일반적인 컨테이너와의 차이점
- 초기화 컨테이너는 앱 컨테이너의 리소스 상한, 볼륨, 보안 세팅을 포함한 모든 필드와 기능을 지원하지만 초기화 컨테이너를 위한 리소스 요청량과 상한은 다르게 처리된다.
- 초기화 컨테이너는
lifecycle
,livenessProbe
,readinessProbe
또는startupProbe
를 지원하지 않는다. 파드의 준비 상태가 되기 전에 완료를 목표로 실행되어야 하기 때문이다. - 다수의 초기화 컨테이너가 파드에 지정되어 있다면, kubelet은 한 번에 하나씩 실행한다. 각 초기화 컨테이너는 다음 컨테이너를 실행하기 전에 꼭 성공해야 한다. 모든 컨테이너들이 실행 완료되었을 때, kubelet은 파드의 애플리케이션 컨테이너들을 초기화하고 평소와 같이 실행한다.
[정리]
초기화 컨테이너는 일반 컨테이너와 대부분의 필드를 공유하지만 리소스 요청 및 사용량은 다르게 처리된다. 컨테이너의 목적에 따라 라이프사이클, 일부 프로브와 같은 특정 기능은 제공하지 않는다.
초기화 컨테이너 사용하기
- 초기화 컨테이너는 앱 컨테이너와는 별도의 이미지를 가지고 있기 때문에, 시동(start-up)에 관련된 코드로서 몇 가지 이점을 가진다.
- 앱 이미지에는 없는 셋업을 위한 유틸리티 또는 맞춤 코드를 포함할 수 있다. FROM에서 다른 이미지를 명시하고 셋업을 위한 작업을 진행할 수 있다는 것을 의미한다.
- 애플리케이션 이미지 빌더와 디플로이 역할은 독립적으로 동작될 수 있어서 공동의 단일 앱 이미지 형태로 빌드될 필요가 없다.
- 앱 컨테이너들은 병렬로 실행되는 반면, 초기화 컨테이너들은 어떠한 앱 컨테이너라도 시작되기 전에 실행 완료되어야 하므로, 초기화 컨테이너는 사전 조건들이 충족될 때까지 앱 컨테이너가 시동되는 것을 막거나 지연시키는 간편한 방법을 제공한다.
- 초기화 컨테이너는 앱 컨테이너 이미지의 보안성을 떨어뜨릴 수도 있는 유틸리티 혹은 커스텀 코드를 안전하게 실행할 수 있다. 불필요한 툴들을 분리한 채로 유지함으로써 앱 컨테이너 이미지의 공격에 대한 노출을 제한할 수 있다.
- 아래와 같은 Shell 커맨드로 서비스가 생성될 때까지 기다릴 수 있다.
for i in {1..100}; do sleep 1; if dig myservice; then exit 0; fi; done; exit 1
- 아래와 같은 커맨드로, 다운워드 API (Downward API)를 통한 원격 서버에 해당 파드를 등록할 수 있다.
curl -X POST http://$MANAGEMENT_SERVICE_HOST:$MANAGEMENT_SERVICE_PORT/register -d 'instance=$(<POD_NAME>)&ip=$(<POD_IP>)'
- 아래와 같은 커맨드로 앱 컨테이너가 시작되기 전에 일정 시간을 기다리도록 할 수 있다.
sleep 60
- 이외에도 깃 저장소를 볼륨안에 복사, 설정 파일에 값을 지정하고 메인 앱 컨테이너를 위한 설정 파일들을 동적으로 생성과 같은 작업을 진행할 수 있다.
[정리]
초기화 컨테이너는 앱 컨테이너와는 별도의 이미지를 가져있기 때문에 몇가지 이점을 가지고 있다. 특히 초기화 컨테이너가 전부 완료되기 전에 앱 컨테이너가 실행되는 것을 제어해야 하기 때문에 이를 위한 여러가지 간편한 방법을 제공한다.
사용 중인 초기화 컨테이너
- v1.5 기준으로 아래와 같은 yaml 파일이 있을 때 처음으로
myservice
를 기다리고 두 번째는mydb
를 기다릴 것이다. 두 컨테이너들이 완료되면 파드가 시작된다.
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
labels:
app.kubernetes.io/name: MyApp
spec:
containers:
- name: myapp-container
image: busybox:1.28
command: ['sh', '-c', 'echo The app is running! && sleep 3600']
initContainers:
- name: init-myservice
image: busybox:1.28
command: ['sh', '-c', "until nslookup myservice.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for myservice; sleep 2; done"]
- name: init-mydb
image: busybox:1.28
command: ['sh', '-c', "until nslookup mydb.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for mydb; sleep 2; done"]
kubectl describe -f myapp.yaml
커맨드를 입력하여 파드가 실행된 순서를 확인해보면 아래와 같다.mydb
및myservice
서비스를 시작하고 초기화 컨테이너가 완료되면myapp-pod
가 생성된다.
Name: myapp-pod
Namespace: default
[...]
Labels: app.kubernetes.io/name=MyApp
Status: Pending
[...]
Init Containers:
init-myservice:
[...]
State: Running
[...]
init-mydb:
[...]
State: Waiting
Reason: PodInitializing
Ready: False
[...]
Containers:
myapp-container:
[...]
State: Waiting
Reason: PodInitializing
Ready: False
[...]
Events:
FirstSeen LastSeen Count From SubObjectPath Type Reason Message
--------- -------- ----- ---- ------------- -------- ------ -------
16s 16s 1 {default-scheduler } Normal Scheduled Successfully assigned myapp-pod to 172.17.4.201
16s 16s 1 {kubelet 172.17.4.201} spec.initContainers{init-myservice} Normal Pulling pulling image "busybox"
13s 13s 1 {kubelet 172.17.4.201} spec.initContainers{init-myservice} Normal Pulled Successfully pulled image "busybox"
13s 13s 1 {kubelet 172.17.4.201} spec.initContainers{init-myservice} Normal Created Created container with docker id 5ced34a04634; Security:[seccomp=unconfined]
13s 13s 1 {kubelet 172.17.4.201} spec.initContainers{init-myservice} Normal Started Started container with docker id 5ced34a04634
자세한 동작
- 파드 시작 시에 kubelet은 네트워크와 스토리지가 준비될 때까지 초기화 컨테이너의 실행을 지연시키고, 파드 사양에 나와있는 순서대로 파드의 초기화 컨테이너를 실행한다.
- 각 초기화 컨테이너는 다음 컨테이너가 시작되기 전에 성공적으로 종료되어야 하며, 런타임 문제나 실패 상태로 종료되는 문제로 인하여 초기화 컨테이너가 실패한다면, 초기화 컨테이너는 파드의
restartPolicy
에 따라서 재시도 된다. - 만약, 파드의
restartPolicy
가 항상(Always)로 설정된 경우, 해당 초기화 컨테이너는restartPolicy
를 실패 시(OnFailure)로 사용한다. - 파드는 모든 초기화 컨테이너가 성공되기 전까지
Ready
될 수 없다. 초기화 컨테이너의 포트는 서비스 하에 합쳐지지 않고 초기화 중인 파드는Pending
상태이지만Initialized
가 거짓이 되는 조건을 가져야 한다. - 파드가 재시작되는 경우에 모든 초기화 컨테이너도 반드시 다시 실행된다.
- 초기화 컨테이너 스펙 변경은 컨테이너 이미지 필드에서만 한정적으로 가능하며 초기화 컨테이너 이미지 필드를 변경하는 것은 파드를 재시작하는 것과 같다.
- 초기화 컨테이너는 재시작되거나, 재시도 또는 재실행 될 수 있기 때문에 초기화 컨테이너 멱등성(idempotent)을 유지해야 한다. 특히,
EmptyDirs
에 있는 파일에 쓰기를 수행하는 코드는 출력 파일이 이미 존재할 가능성에 대비해야 한다. - 초기화 컨테이너는 앱 컨테이너의 모든 필드를 가지고 있지만
**readinessProbe
를 사용하는 것을 금지한다. 초기화 컨테이너가 **완료 상태와 준비성을 구분해서 정의할 수 없기 때문이다. - 초기화 컨테이너들이 실패를 영원히 지속하는 상황을 방지하기 위해서 파드의
activeDeadlineSeconds
를 사용하고 Active deadline은 초기화 컨테이너를 포함한다. activeDeadlineSeconds
는 초기화 컨테이너가 완료된 이후에도 영향을 주기 때문에 애플리케이션을 잡(job)으로 배포된 경우에만 사용되는 것이 추천된다. 이미 정상적으로 동작하고 있는 파드도activeDeadlineSeconds
를 설정한 경우 종료될 수 있다.
[정리]
kubelet은 초기화 컨테이너가 시작되기 전에 네트워크와 스토리지 준비를 하고 준비가 완료되면 순서대로 실행시킨다. 순서대로라고 하면 사용자가 의도한 순서를 의미한다. 트랜잭션과 같이 모든 초기화 컨테이너가 성공적으로 실행되어야 애플리케이션 컨테이너가 실행될 수 있고 파드가 재실행되는 경우에도 모든 초기화 컨테이너는 정해진 순서대로 재실행된다. 초기화 컨테이너는 몇 번이고 재실행될 수 있기 때문에 항상 같은 결과를 반환해야 한다.
리소스
- 초기화 컨테이너에게 명령과 실행이 주어진 경우, 리소스 사용에 대해 아래의 규칙이 적용된다.
- 모든 컨테이너에 정의된 특정 리소스 요청량과 상한 중 가장 높은 것은 유효 초기화 요청량과 상한이다. 리소스 제한이 지정되지 않은 리소스는 이 유효 초기화 요청량과 상한을 가장 높은 요청량 상한으로 간주한다.
- 리소스를 위한 파드의 유효한 초기화 요청량과 상한은 아래보다 더 높다.
- 모든 앱 컨테이너의 리소스에 대한 요청량과 상한의 합계
- 리소스에 대한 유효한 초기화 요청량과 상한
- 스케줄링은 유효한 요청과 상한에 따라 이루어진다. 즉, 초기화 컨테이너는 파드의 삶에서 사용되지 않는 초기화를 위한 리소스를 예약할 수 있다.
- 파드의 유효한 QoS 계층에서 QoS(서비스의 품질) 계층은 초기화 컨테이너들과 앱 컨테이너들의 QoS 계층과 같다.
- 쿼터 및 상한은 유효한 파드의 요청량 및 상한에 따라 적용된다.
- 파드 레벨 cgroup은 유효한 파드 요청량 및 상한을 기반으로 하고 이것은 스케줄러와 같다.
파드 재시작 이유
- 파드는 아래와 같은 사유로, 초기화 컨테이너들의 재실행을 일으키는 재시작을 수행할 수 있다.
- 파드 인프라스트럭처 컨테이너가 재시작된 상황. 이것은 일반적인 상황이 아니며 노드에 대해서 root 접근 권한을 가진 누군가에 의해서 수행됐을 것이다.
- 초기화 컨테이너의 완료 기록이 가비지 수집 때문에 유실된 상태에서,
restartPolicy
가 Always로 설정된 파드의 모든 컨테이너가 종료되어 모든 컨테이너를 재시작해야 하는 상황
- 초기화 컨테이너 이미지가 변경되거나 초기화 컨테이너의 완료 기록이 가비지 수집 때문에 유실된 상태이면 파드는 재시작되지 않는다.
[정리]
초기화 컨테이너의 경우 애플리케이션 컨테이너와 다르게 리소스 사용 규칙이 적용되기 때문에 이 부분을 인지해야 한다. 파드는 일반적인 상황이 아니면 노드에 대해서 root 접근 권한을 가진 누군가에 의해 재실행되거나 가비지 수집에 의해 초기화 컨테이너 완료기록이 유실된 상태로 restartPolicy
가 Always로 설정되어 재시작해야 하는 상황에서 재시작을 수행할 수 있다.
참고 자료
'Infrastructure > Kubernetes' 카테고리의 다른 글
[Pod] 임시(Ephemeral) 컨테이너 (0) | 2022.10.18 |
---|---|
[Pod] 중단(Disruption) (0) | 2022.10.18 |
[Pod] 라이프사이클 (0) | 2022.10.18 |
[Pod] 개념 (0) | 2022.10.14 |
[워크로드] 개념 (0) | 2022.10.14 |