이닛 컨테이너
이닛 컨테이너 (Init Containers)
이 페이지는 파드(Pod)에서 앱 컨테이너보다 먼저 실행되는 특수 컨테이너인 이닛 컨테이너(init containers)에 대한 개요를 제공해요. 이닛 컨테이너는 앱 이미지에 존재하지 않는 유틸리티나 설정 스크립트를 포함할 수 있어요.
파드 스펙의 containers 배열(앱 컨테이너를 설명함)과 함께 이닛 컨테이너를 지정할 수 있어요.
쿠버네티스에서 사이드카 컨테이너(sidecar container)는 주요 애플리케이션 컨테이너보다 먼저 시작되고 계속 실행되는 컨테이너예요. 이 문서는 이닛 컨테이너, 즉 파드 초기화 중에 완료될 때까지 실행되는 컨테이너에 관한 것이에요.
출처: 문서
본문
이닛 컨테이너 이해하기
파드는 그 안에서 앱을 실행하는 여러 컨테이너를 가질 수 있지만, 앱 컨테이너가 시작되기 전에 실행되는 하나 이상의 이닛 컨테이너도 가질 수 있어요.
이닛 컨테이너는 다음을 제외하면 일반 컨테이너와 정확히 같아요:
- 이닛 컨테이너는 항상 완료될 때까지 실행돼요.
- 각 이닛 컨테이너는 다음 것이 시작되기 전에 성공적으로 완료되어야 해요.
파드의 이닛 컨테이너가 실패하면 kubelet은 성공할 때까지 그 이닛 컨테이너를 반복적으로 다시 시작해요. 하지만 파드의 restartPolicy가 Never이고 이닛 컨테이너가 그 파드의 시작 동안 실패하면, 쿠버네티스는 전체 파드를 실패한 것으로 취급해요.
파드에 이닛 컨테이너를 지정하려면 파드 스펙에 initContainers 필드를 컨테이너 항목 배열로 추가해요 (앱 컨테이너 필드 및 그 내용과 유사). 자세한 내용은 API 참조의 Container를 참조하세요.
이닛 컨테이너의 상태는 .status.initContainerStatuses 필드에 컨테이너 상태 배열로 반환돼요 (.status.containerStatuses 필드와 유사).
일반 컨테이너와의 차이
이닛 컨테이너는 리소스 한도, 볼륨, 보안 설정을 포함해 앱 컨테이너의 모든 필드와 기능을 지원해요. 하지만 이닛 컨테이너의 리소스 요청과 한도는 '컨테이너 내 리소스 공유'에 문서화된 대로 다르게 처리돼요.
일반 이닛 컨테이너(즉, 사이드카 컨테이너 제외)는 lifecycle, livenessProbe, readinessProbe, startupProbe 필드를 지원하지 않아요. 이닛 컨테이너는 파드가 준비될 수 있기 전에 완료되어 실행해야 해요; 사이드카 컨테이너는 파드 수명 동안 계속 실행되며 일부 프로브를 지원해요. 사이드카 컨테이너에 대한 자세한 내용은 사이드카 컨테이너를 참조하세요.
파드에 여러 이닛 컨테이너를 지정하면 kubelet이 각 이닛 컨테이너를 순차적으로 실행해요. 각 이닛 컨테이너는 다음 것이 실행될 수 있기 전에 성공해야 해요. 모든 이닛 컨테이너가 완료될 때까지 실행되면 kubelet이 파드의 애플리케이션 컨테이너를 초기화하고 평소처럼 실행해요.
사이드카 컨테이너와의 차이
이닛 컨테이너는 주요 애플리케이션 컨테이너가 시작되기 전에 실행되고 그 작업을 완료해요. 사이드카 컨테이너와 달리, 이닛 컨테이너는 주요 컨테이너와 함께 지속적으로 실행되지 않아요.
이닛 컨테이너는 순차적으로 완료까지 실행되며, 모든 이닛 컨테이너가 성공적으로 완료될 때까지 주요 컨테이너는 시작되지 않아요.
이닛 컨테이너는 lifecycle, livenessProbe, readinessProbe, startupProbe를 지원하지 않는 반면, 사이드카 컨테이너는 수명 주기를 제어하기 위해 이러한 프로브를 모두 지원해요.
이닛 컨테이너는 주요 애플리케이션 컨테이너와 동일한 리소스(CPU, 메모리, 네트워크)를 공유하지만 직접 상호작용하지는 않아요. 그러나 데이터 교환을 위해 공유 볼륨을 사용할 수는 있어요.
이닛 컨테이너 사용하기
이닛 컨테이너는 앱 컨테이너와 별도의 이미지를 가지므로, 시작 관련 코드에 몇 가지 이점이 있어요:
- 이닛 컨테이너는 앱 이미지에 없는 설정용 유틸리티나 사용자 지정 코드를 포함할 수 있어요. 예를 들어, 설정 중에
sed,awk,python,dig같은 도구를 사용하려고 이미지를 다른 이미지로부터 만들 필요가 없어요. - 애플리케이션 이미지 빌더와 배포자 역할이 단일 앱 이미지를 공동으로 만들 필요 없이 독립적으로 작업할 수 있어요.
- 이닛 컨테이너는 같은 파드의 앱 컨테이너와 다른 파일시스템 뷰로 실행될 수 있어요. 결과적으로 앱 컨테이너가 접근할 수 없는 Secret에 접근할 수 있게 부여될 수 있어요.
- 이닛 컨테이너는 어떤 앱 컨테이너가 시작되기 전에 완료까지 실행되므로, 일련의 사전 조건이 충족될 때까지 앱 컨테이너 시작을 차단하거나 지연하는 메커니즘을 제공해요. 사전 조건이 충족되면 파드의 모든 앱 컨테이너가 병렬로 시작될 수 있어요.
- 이닛 컨테이너는 앱 컨테이너 이미지를 덜 안전하게 만들 유틸리티나 사용자 지정 코드를 안전하게 실행할 수 있어요. 불필요한 도구를 분리함으로써 앱 컨테이너 이미지의 공격 표면을 제한할 수 있어요.
예시
이닛 컨테이너를 사용하는 몇 가지 아이디어가 있어요:
- 셸 원라이너 명령으로 Service가 생성될 때까지 기다리기, 예:
for i in {1..100}; do sleep 1; if nslookup myservice; then exit 0; fi; done; exit 1 - downward API로 이 파드를 원격 서버에 등록하기, 예:
curl -X POST http://$MANAGEMENT_SERVICE_HOST:$MANAGEMENT_SERVICE_PORT/register -d 'instance=$(<POD_NAME>)&ip=$(<POD_IP>)' sleep 60같은 명령으로 앱 컨테이너를 시작하기 전에 잠시 기다리기- Git 저장소를 볼륨(Volume)에 복제하기
- 값을 구성 파일에 넣고 템플릿 도구를 실행해 주요 앱 컨테이너용 구성 파일을 동적으로 생성하기. 예를 들어, 구성에 POD_IP 값을 넣고 Jinja를 사용해 주요 앱 구성 파일을 생성하기.
사용 중인 이닛 컨테이너
이 예시는 두 개의 이닛 컨테이너가 있는 단순한 파드를 정의해요. 첫 번째는 myservice를 기다리고, 두 번째는 mydb를 기다려요. 두 이닛 컨테이너가 모두 완료되면 파드는 spec 섹션에서 앱 컨테이너를 실행해요.
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 apply -f myapp.yaml
출력은 다음과 비슷해요:
pod/myapp-pod created
그리고 상태를 확인해요:
kubectl get -f myapp.yaml
출력은 다음과 비슷해요:
NAME READY STATUS RESTARTS AGE
myapp-pod 0/1 Init:0/2 0 6m
또는 더 자세히 보려면:
kubectl describe -f myapp.yaml
출력은 다음과 비슷해요:
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 init-myservice
13s 13s 1 {kubelet 172.17.4.201} spec.initContainers{init-myservice} Normal Started Started container init-myservice
이 파드의 이닛 컨테이너 로그를 보려면 다음을 실행해요:
kubectl logs myapp-pod -c init-myservice # Inspect the first init container
kubectl logs myapp-pod -c init-mydb # Inspect the second init container
이 시점에서 그 이닛 컨테이너들은 mydb와 myservice라는 Service를 발견하기를 기다릴 거예요.
그 Service들이 나타나게 하는 데 사용할 수 있는 구성은 다음과 같아요:
---
apiVersion: v1
kind: Service
metadata:
name: myservice
spec:
ports:
- protocol: TCP
port: 80
targetPort: 9376
---
apiVersion: v1
kind: Service
metadata:
name: mydb
spec:
ports:
- protocol: TCP
port: 80
targetPort: 9377
mydb와 myservice service를 만들려면:
kubectl apply -f services.yaml
출력은 다음과 비슷해요:
service/myservice created
service/mydb created
그러면 그 이닛 컨테이너들이 완료되고 myapp-pod 파드가 Running 상태로 이동하는 것을 볼 수 있어요:
kubectl get -f myapp.yaml
출력은 다음과 비슷해요:
NAME READY STATUS RESTARTS AGE
myapp-pod 1/1 Running 0 9m
이 간단한 예시는 여러분이 자신의 이닛 컨테이너를 만드는 데 몇 가지 영감을 제공해야 해요. What's next에는 더 자세한 예시에 대한 링크가 포함돼 있어요.
자세한 동작
파드 시작 중에 kubelet은 네트워킹과 저장소가 준비될 때까지 이닛 컨테이너 실행을 지연해요. 그런 다음 kubelet은 파드의 이닛 컨테이너를 파드 spec에 나타나는 순서대로 실행해요.
각 이닛 컨테이너는 다음 컨테이너가 시작되기 전에 성공적으로 종료해야 해요. 컨테이너가 런타임 때문에 시작에 실패하거나 실패로 종료되면, 파드 restartPolicy에 따라 재시도돼요. 하지만 파드 restartPolicy가 Always로 설정되면 이닛 컨테이너는 restartPolicy OnFailure를 사용해요.
모든 이닛 컨테이너가 성공할 때까지 파드는 Ready일 수 없어요. 이닛 컨테이너의 포트는 Service 아래에 집계되지 않아요. 초기화 중인 파드는 Pending 상태이지만 Initialized 조건이 false로 설정되어야 해요.
파드가 재시작되거나 다시 시작되면 모든 이닛 컨테이너가 다시 실행되어야 해요.
이닛 컨테이너 spec에 대한 변경은 컨테이너 이미지 필드로 제한돼요. 이닛 컨테이너의 이미지를 직접 변경해도 파드가 재시작되거나 재생성이 트리거되지 않아요. 파드가 아직 시작되지 않았다면, 그 변경이 파드가 부팅되는 방식에 영향을 줄 수 있어요.
파드 템플릿의 경우 일반적으로 이닛 컨테이너에 대해 어떤 필드든 변경할 수 있어요; 그 변경의 영향은 파드 템플릿이 사용되는 위치에 따라 달라져요.
이닛 컨테이너는 재시작, 재시도, 재실행될 수 있으므로 이닛 컨테이너 코드는 멱등적(idempotent)이어야 해요. 특히 어떤 emptyDir 볼륨에든 쓰는 코드는 출력 파일이 이미 존재할 가능성에 대비해야 해요.
이닛 컨테이너는 앱 컨테이너의 모든 필드를 가져요. 그러나 쿠버네티스는 이닛 컨테이너가 완료와 구별되는 readiness를 정의할 수 없으므로 readinessProbe 사용을 금지해요. 이는 검증 중에 강제돼요.
이닛 컨테이너가 영원히 실패하는 것을 방지하려면 파드에 activeDeadlineSeconds를 사용해요. 활성 데드라인(active deadline)은 이닛 컨테이너를 포함해요. 그러나 activeDeadlineSeconds는 이닛 컨테이너가 완료된 후에도 효과가 있으므로, 팀이 애플리케이션을 Job으로 배포하는 경우에만 activeDeadlineSeconds를 사용하는 것이 권장돼요. 이미 올바르게 실행 중인 파드는 설정하면 activeDeadlineSeconds에 의해 종료될 것이기 때문이에요.
파드의 각 앱 및 이닛 컨테이너의 이름은 고유해야 해요; 다른 컨테이너와 이름을 공유하는 컨테이너에 대해서는 검증 오류가 발생해요.
컨테이너 내 리소스 공유
이닛, 사이드카, 앱 컨테이너의 실행 순서를 고려할 때 리소스 사용에 대해 다음 규칙이 적용돼요:
- 모든 이닛 컨테이너에 정의된 특정 리소스 요청 또는 한도의 최고값이 유효 이닛 요청/한도(effective init request/limit)예요. 어떤 리소스에 리소스 한도가 지정되지 않으면 최고 한도로 간주돼요.
- 파드의 리소스에 대한 유효 요청/한도는 다음 중 더 높은 값이에요: 리소스에 대한 모든 앱 컨테이너 요청/한도의 합, 리소스에 대한 유효 이닛 요청/한도.
- 스케줄링은 유효 요청/한도에 기반해 수행되며, 이는 이닛 컨테이너가 파드 수명 동안 사용되지 않는 초기화용 리소스를 예약할 수 있음을 의미해요.
- 파드의 유효 QoS(서비스 품질) 계층은 이닛 컨테이너와 앱 컨테이너 모두에 대한 QoS 계층이에요.
쿼터와 한도는 유효 파드 요청과 한도에 기반해 적용돼요.
이닛 컨테이너와 Linux cgroups
Linux에서 파드 레벨 제어 그룹(cgroups)에 대한 리소스 할당은 스케줄러와 동일하게 유효 파드 요청과 한도에 기반해요.
파드 재시작 이유
다음 이유로 파드가 재시작되어 이닛 컨테이너가 재실행될 수 있어요:
- 파드 인프라스트럭처 컨테이너가 재시작됨. 이것은 드물며 노드에 대한 루트 접근 권한이 있는 누군가가 해야 해요.
restartPolicy가 Always로 설정된 동안 파드의 모든 컨테이너가 종료되어 재시작을 강제하고, 가비지 컬렉션으로 인해 이닛 컨테이너 완료 기록이 손실됨.
이닛 컨테이너 이미지가 변경되거나 이닛 컨테이너 완료 기록이 가비지 컬렉션으로 손실될 때 파드는 재시작되지 않아요. 이는 쿠버네티스 v1.20 이상에 적용돼요. 이전 버전의 쿠버네티스를 사용 중이라면 사용 중인 버전의 문서를 참조하세요.
더 알아보기 (Learn more)
다음에 대해 더 배워 보세요:
- 이닛 컨테이너가 있는 파드 만들기.
- 이닛 컨테이너 디버그하기.
- kubelet과 kubectl 개요.
- 프로브 유형: liveness, readiness, startup probe.
- 사이드카 컨테이너.