초기화 컨테이너

초기화 컨테이너 (init-containers)

이 페이지는 init 컨테이너에 대한 개요를 제공해요. init 컨테이너는 Pod 안에서 앱 컨테이너보다 먼저 실행되는 특수 목적 컨테이너예요. init 컨테이너는 앱 이미지에 없는 유틸리티나 셋업 스크립트를 포함할 수 있어요.

containers 배열(앱 컨테이너를 설명하는) 옆에 Pod 사양에서 init 컨테이너를 지정할 수 있어요.

Kubernetes에서 사이드카 컨테이너는 메인 애플리케이션 컨테이너보다 먼저 시작되고 계속 실행되는 컨테이너예요. 이 문서는 init 컨테이너, 즉 Pod 초기화 중에 완료까지 실행되는 컨테이너에 관한 것이에요.

init 컨테이너 이해하기

Pod는 그 안에서 앱을 실행하는 여러 컨테이너를 가질 수 있지만, 앱 컨테이너가 시작되기 전에 실행되는 하나 이상의 init 컨테이너를 가질 수도 있어요.

init 컨테이너는 일반 컨테이너와 정확히 같지만, 다음이 다릅니다.

  • init 컨테이너는 항상 완료까지 실행돼요.
  • 각 init 컨테이너는 다음이 시작되기 전에 성공적으로 완료되어야 해요. Pod의 init 컨테이너가 실패하면 kubelet은 성공할 때까지 그 init 컨테이너를 반복해서 재시작해요. 단, Pod의 restartPolicy가 Never이고 init 컨테이너가 Pod 시작 중 실패하면 Kubernetes는 전체 Pod를 실패로 취급해요.

Pod에 init 컨테이너를 지정하려면 initContainers 필드를 Pod 사양container 항목의 배열로 추가해요(앱 containers 필드 및 그 내용과 유사). 자세한 내용은 API 레퍼런스의 Container를 참고하세요.

init 컨테이너의 상태는 .status.initContainerStatuses 필드에 컨테이너 상태 배열로 반환돼요(.status.containerStatuses 필드와 유사).

일반 컨테이너와의 차이

init 컨테이너는 리소스 제한, 볼륨, 보안 설정을 포함한 앱 컨테이너의 모든 필드와 기능을 지원해요. 단, init 컨테이너의 리소스 요청과 제한은 '컨테이너 내 리소스 공유'에 문서화된 대로 다르게 처리돼요.

일반 init 컨테이너(다시 말해 사이드카 컨테이너를 제외한)는 lifecycle, livenessProbe, readinessProbe, startupProbe 필드를 지원하지 않아요. init 컨테이너는 Pod가 ready가 되기 전에 완료까지 실행되어야 해요. 사이드카 컨테이너는 Pod의 수명 동안 계속 실행되며 일부 프로브를 지원해요. 사이드카 컨테이너에 대한 자세한 내용은 사이드카 컨테이너를 참고하세요.

Pod에 여러 init 컨테이너를 지정하면 kubelet은 각 init 컨테이너를 순차적으로 실행해요. 각 init 컨테이너는 다음이 실행되기 전에 성공해야 해요. 모든 init 컨테이너가 완료까지 실행되면 kubelet은 Pod의 애플리케이션 컨테이너를 초기화하고 평소처럼 실행해요.

사이드카 컨테이너와의 차이

init 컨테이너는 메인 애플리케이션 컨테이너가 시작되기 전에 자신의 작업을 실행하고 완료해요. 사이드카 컨테이너와 달리 init 컨테이너는 메인 컨테이너와 함께 계속 실행되지 않아요.

init 컨테이너는 순차적으로 완료까지 실행되고, 모든 init 컨테이너가 성공적으로 완료될 때까지 메인 컨테이너는 시작되지 않아요.

init 컨테이너는 lifecycle, livenessProbe, readinessProbe, startupProbe를 지원하지 않는 반면, 사이드카 컨테이너는 수명주기를 제어하기 위한 이러한 프로브를 모두 지원해요.

init 컨테이너는 메인 애플리케이션 컨테이너와 동일한 리소스(CPU, 메모리, 네트워크)를 공유하지만 직접 상호작용하지는 않아요. 다만 데이터 교환을 위해 공유 볼륨을 사용할 수 있어요.

init 컨테이너 사용하기

init 컨테이너는 앱 컨테이너와 별도의 이미지를 가지므로, 시작 관련 코드에 몇 가지 장점이 있어요.

  • init 컨테이너는 앱 이미지에 없는 셋업용 유틸리티나 사용자 코드를 포함할 수 있어요. 예를 들어 셋업 중에 sed, awk, python, dig 같은 도구를 쓰려고 이미지를 다른 이미지 FROM으로 만들 필요가 없어요.
  • 애플리케이션 이미지 빌더와 디플로이어 역할은 단일 앱 이미지를 공동으로 빌드할 필요 없이 독립적으로 동작할 수 있어요.
  • init 컨테이너는 같은 Pod의 앱 컨테이너와 다른 파일시스템 관점으로 실행될 수 있어요. 결과적으로 앱 컨테이너가 접근할 수 없는 Secret에 접근 권한을 줄 수 있어요.
  • init 컨테이너는 앱 컨테이너가 시작되기 전에 완료까지 실행되므로, 사전 조건 집합이 충족될 때까지 앱 컨테이너 시작을 차단하거나 지연시키는 메커니즘을 제공해요. 사전 조건이 충족되면 Pod의 모든 앱 컨테이너가 병렬로 시작될 수 있어요.
  • init 컨테이너는 앱 컨테이너 이미지를 덜 안전하게 만들 수 있는 유틸리티나 사용자 코드를 안전하게 실행할 수 있어요. 불필요한 도구를 분리하면 앱 컨테이너 이미지의 공격 표면을 제한할 수 있죠.

예시

init 컨테이너를 사용하는 몇 가지 아이디어를 볼게요.

  • 셸 원라이너 커맨드로 Service가 생성될 때까지 기다리기:
for i in {1..100}; do sleep 1; if nslookup myservice; then exit 0; fi; done; exit 1
  • downward API에서 커맨드로 이 Pod를 원격 서버에 등록하기:
curl -X POST http://$MANAGEMENT_SERVICE_HOST:$MANAGEMENT_SERVICE_PORT/register -d 'instance=$(<POD_NAME>)&ip=$(<POD_IP>)'
  • 앱 컨테이너를 시작하기 전에 잠시 기다리기:
sleep 60
  • Git 저장소를 볼륨에 클론하기
  • 구성 파일에 값을 넣고 템플릿 도구를 실행해서 메인 앱 컨테이너의 구성 파일을 동적으로 생성하기. 예를 들어 POD_IP 값을 구성에 넣고 Jinja로 메인 앱 구성 파일을 생성해요.

init 컨테이너 사용 예시

이 예시는 두 개의 init 컨테이너를 가진 간단한 Pod를 정의해요. 첫 번째는 myservice를 기다리고, 두 번째는 mydb를 기다려요. 두 init 컨테이너가 완료되면 Pod는 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"]

이 Pod를 시작하려면 다음을 실행해요.

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

이 시점에 init 컨테이너들은 mydbmyservice라는 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

mydbmyservice Service를 만들려면:

kubectl apply -f services.yaml

init 컨테이너가 완료되고 myapp-pod가 Running 상태로 이동하는 것을 확인할 수 있을 거예요.

상세 동작

Pod 시작 중에 kubelet은 네트워킹과 스토리지가 준비될 때까지 init 컨테이너 실행을 지연시켜요. 그런 다음 kubelet은 Pod spec에 나타나는 순서대로 Pod의 init 컨테이너를 실행해요.

각 init 컨테이너는 다음 컨테이너가 시작되기 전에 성공적으로 종료되어야 해요. 컨테이너가 런타임 때문에 시작에 실패하거나 실패로 종료되면 Pod restartPolicy에 따라 재시도돼요. 단, Pod restartPolicy가 Always로 설정되면 init 컨테이너는 restartPolicy OnFailure를 사용해요.

모든 init 컨테이너가 성공하기 전까지 Pod는 Ready가 될 수 없어요. init 컨테이너의 포트는 Service 아래에 집계되지 않아요. 초기화 중인 Pod는 Pending 상태이지만 Initialized 조건이 false로 설정되어 있어야 해요.

Pod가 재시작되면 모든 init 컨테이너가 다시 실행되어야 해요.

pod 템플릿의 경우 보통 init 컨테이너의 어떤 필드든 변경할 수 있어요. 변경의 영향은 pod 템플릿이 사용되는 위치에 따라 달라요.

init 컨테이너는 재시작, 재시도, 재실행될 수 있으므로 init 컨테이너 코드는 **멱등적(idempotent)**이어야 해요. 특히 emptyDir 볼륨에 쓰는 코드는 출력 파일이 이미 존재할 가능성에 대비해야 해요.

init 컨테이너는 앱 컨테이너의 모든 필드를 가져요. 그러나 Kubernetes는 readinessProbe 사용을 금지하는데, init 컨테이너는 완료와 구분되는 readiness를 정의할 수 없기 때문이에요. 이는 검증 중에 강제돼요.

init 컨테이너가 영원히 실패하는 것을 막으려면 Pod에 activeDeadlineSeconds를 사용해요. 활성 데드라인에는 init 컨테이너가 포함돼요. 다만 activeDeadlineSeconds는 initContainer가 끝난 후에도 영향을 주므로 팀이 애플리케이션을 Job으로 배포할 때만 사용하는 것을 권장해요. 설정하면 이미 올바르게 실행 중인 Pod가 activeDeadlineSeconds에 의해 죽을 수 있으니까요.

Pod의 각 앱 및 init 컨테이너의 이름은 고유해야 해요. 다른 컨테이너와 이름을 공유하면 검증 오류가 발생해요.

init, 사이드카, 앱 컨테이너의 실행 순서를 고려하면 리소스 사용에 다음 규칙이 적용돼요.

  • 모든 init 컨테이너에 정의된 특정 리소스 요청 또는 제한 중 가장 높은 값이 유효 init 요청/제한이에요. 어떤 리소스에 리소스 제한이 지정되지 않으면 가장 높은 제한으로 간주돼요.
  • 리소스에 대한 Pod의 유효 요청/제한은 다음 중 더 높은 값이에요.
    • 해당 리소스에 대한 모든 앱 컨테이너 요청/제한의 합
    • 해당 리소스에 대한 유효 init 요청/제한
  • 스케줄링은 유효 요청/제한에 기반해 수행돼요. 즉 init 컨테이너는 Pod 수명 동안 사용되지 않는 초기화용 리소스를 예약할 수 있어요.
  • Pod의 유효 QoS 계층은 init 컨테이너와 앱 컨테이너 모두에 대한 QoS 계층과 같아요. 할당량과 제한은 유효 Pod 요청과 제한에 기반해 적용돼요.

init 컨테이너와 Linux cgroups

Linux에서 Pod 수준 cgroup에 대한 리소스 할당은 스케줄러와 마찬가지로 유효 Pod 요청과 제한에 기반해요.

Pod 재시작 이유

다음 이유로 Pod가 재시작되어 init 컨테이너가 재실행될 수 있어요.

  • Pod 인프라 컨테이너가 재시작된 경우. 이는 흔하지 않으며 노드에 대한 루트 접근 권한을 가진 사람이 해야 해요.
  • restartPolicy가 Always로 설정된 동안 Pod의 모든 컨테이너가 종료되어 재시작을 강제하고, 가비지 컬렉션으로 인해 init 컨테이너 완료 기록이 손실된 경우.

Pod는 init 컨테이너 이미지가 변경되거나 init 컨테이너 완료 기록이 가비지 컬렉션으로 손실되어도 재시작되지 않아요. 이것은 Kubernetes v1.20 이상에 적용돼요. 이전 버전의 Kubernetes를 사용한다면 사용 중인 버전의 문서를 참고하세요.

다음 내용

다음에 대해 더 알아보세요.