Init 컨테이너 추가하기

KubernetesBeginner
지금 연습하기

소개

Init 컨테이너는 Pod 내의 메인 애플리케이션 컨테이너가 시작되기 전에 실행됩니다. 각 Init 컨테이너는 다음 Init 컨테이너나 일반 컨테이너가 시작되기 전에 성공적으로 완료되어야 합니다. 이를 통해 Init 컨테이너는 파일 준비, 의존성 확인 또는 애플리케이션과 함께 계속 실행될 필요가 없는 간단한 설정 작업을 수행하는 데 유용하게 사용됩니다.

이번 실습에서는 공유 emptyDir 볼륨에 시작 파일을 작성하는 Init 컨테이너를 추가하고, 애플리케이션 컨테이너가 해당 파일을 읽을 수 있는지 확인하며, Init 컨테이너의 완료 상태와 로그를 검사한 뒤, 동일한 패턴을 Deployment 템플릿에 적용해 보겠습니다.

Init 컨테이너를 위한 네임스페이스 준비

이 단계에서는 Init 컨테이너 실습을 위한 깨끗한 네임스페이스를 준비합니다.

/home/labex/project 디렉토리에서 시작합니다. 이 디렉토리에 Pod 및 Deployment 매니페스트를 작성하게 됩니다.

mkdir -p /home/labex/project
cd /home/labex/project

Kubernetes API 서버에 접근 가능한지 확인합니다. VM 이 막 시작되어 노드가 아직 준비되지 않았다면, 몇 초 기다린 후 명령어를 다시 실행하세요.

kubectl get nodes --request-timeout=5s

예상 출력:

NAME         STATUS   ROLES           AGE   VERSION
labex-v135   Ready    control-plane   ...   v1.35...

Init 컨테이너는 Pod 내부에서 실행되므로, 워크로드를 생성하기 전에 클러스터 네트워킹 Pod 가 준비 (Ready) 상태가 될 때까지 기다립니다.

kubectl wait --namespace kube-system --for=condition=Ready pod -l k8s-app=calico-node --timeout=120s

예상 출력:

pod/calico-node-... condition met

ckad-prep-init라는 이름의 네임스페이스를 생성합니다:

kubectl create namespace ckad-prep-init

이 네임스페이스를 현재 컨텍스트의 네임스페이스로 설정합니다:

kubectl config set-context --current --namespace=ckad-prep-init

현재 네임스페이스를 확인합니다:

kubectl config view --minify --output 'jsonpath={..namespace}'; echo

예상 출력:

ckad-prep-init

앱 시작 전 공유 데이터 준비

이 단계에서는 Init 컨테이너가 애플리케이션 컨테이너 시작 전에 파일을 준비하는 Pod 를 생성합니다.

emptyDir 볼륨은 Pod 가 노드에 할당될 때 생성되며, 동일한 Pod 내의 컨테이너들이 공유합니다. Init 컨테이너와 앱 컨테이너 모두 이 볼륨을 /work 경로에 마운트합니다. Init 컨테이너는 /work/startup.txt 파일을 작성한 후 종료됩니다. Init 컨테이너가 성공적으로 종료된 후에만 Kubernetes 가 앱 컨테이너를 시작합니다.

init-file-pod.yaml 파일을 생성합니다:

cat > init-file-pod.yaml <<'EOF'
apiVersion: v1
kind: Pod
metadata:
  name: init-file-demo
  labels:
    app: init-file-demo
spec:
  terminationGracePeriodSeconds: 0
  volumes:
  - name: shared-work
    emptyDir: {}
  initContainers:
  - name: prepare-startup-file
    image: busybox:1.36
    command:
    - /bin/sh
    - -c
    - 'echo init-started; echo prepared-by-init > /work/startup.txt; echo init-finished'
    volumeMounts:
    - name: shared-work
      mountPath: /work
  containers:
  - name: app
    image: busybox:1.36
    command:
    - /bin/sh
    - -c
    - 'echo app-started; cat /work/startup.txt; sleep 3600'
    volumeMounts:
    - name: shared-work
      mountPath: /work
EOF

매니페스트를 적용합니다:

kubectl apply -f init-file-pod.yaml

예상 출력:

pod/init-file-demo created

Pod 가 준비 (Ready) 상태가 될 때까지 기다립니다:

kubectl wait --for=condition=Ready pod/init-file-demo --timeout=120s

예상 출력:

pod/init-file-demo condition met

앱 컨테이너 내부에서 파일을 읽어봅니다. 이는 Init 컨테이너가 앱 시작 전에 공유 볼륨에 데이터를 성공적으로 준비했음을 증명합니다.

kubectl exec init-file-demo -c app -- cat /work/startup.txt

예상 출력:

prepared-by-init

Init 컨테이너 상태 및 로그 검사

이 단계에서는 Init 컨테이너가 앱 컨테이너 실행 전에 완료되었다는 증거를 확인합니다.

Init 컨테이너는 .status.initContainerStatuses 아래에 나타나며, 일반 컨테이너는 .status.containerStatuses 아래에 나타납니다. Init 컨테이너는 성공적으로 종료되었으므로 Completed 상태여야 합니다.

kubectl get pod init-file-demo -o 'jsonpath={.status.initContainerStatuses[0].name}{" "}{.status.initContainerStatuses[0].state.terminated.reason}{"\n"}'

예상 출력:

prepare-startup-file Completed

이제 일반 애플리케이션 컨테이너의 상태를 확인합니다. 준비 (Ready) 상태이며 실행 중이어야 합니다.

kubectl get pod init-file-demo -o 'jsonpath={.status.containerStatuses[0].name}{" "}{.status.containerStatuses[0].ready}{" "}{.status.containerStatuses[0].state.running.startedAt}{"\n"}'

예상 출력:

app true ...

Init 컨테이너의 로그를 읽습니다. Init 컨테이너는 이미 종료되었지만, kubectl logs -c를 통해 로그를 확인할 수 있습니다.

kubectl logs init-file-demo -c prepare-startup-file

예상 출력:

init-started
init-finished

앱 컨테이너의 로그를 읽습니다. 앱은 Init 컨테이너가 파일을 작성한 후에 시작되므로, 로그에 준비된 값이 포함되어 있습니다.

kubectl logs init-file-demo -c app

예상 출력:

app-started
prepared-by-init

Deployment 에 Init 컨테이너 추가하기

이 단계에서는 Deployment 템플릿 내에서 동일한 Init 컨테이너 패턴을 사용합니다.

애플리케이션 워크로드의 경우, 일반적으로 독립적인 Pod 를 생성하는 대신 컨트롤러의 Pod 템플릿에 Init 컨테이너를 배치합니다. Deployment 가 Pod 를 생성하면, Kubernetes 는 해당 템플릿에서 생성된 각 Pod 에 대해 Init 컨테이너를 먼저 실행합니다.

init-deployment.yaml 파일을 생성합니다:

cat > init-deployment.yaml <<'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
  name: init-web
spec:
  replicas: 1
  selector:
    matchLabels:
      app: init-web
  template:
    metadata:
      labels:
        app: init-web
    spec:
      terminationGracePeriodSeconds: 0
      volumes:
      - name: web-content
        emptyDir: {}
      initContainers:
      - name: render-index
        image: busybox:1.36
        command:
        - /bin/sh
        - -c
        - 'echo deployment-init > /site/index.txt'
        volumeMounts:
        - name: web-content
          mountPath: /site
      containers:
      - name: web
        image: busybox:1.36
        command:
        - /bin/sh
        - -c
        - 'cat /site/index.txt; sleep 3600'
        volumeMounts:
        - name: web-content
          mountPath: /site
EOF

Deployment 를 적용합니다:

kubectl apply -f init-deployment.yaml

예상 출력:

deployment.apps/init-web created

Deployment 가 사용 가능 (Available) 상태가 될 때까지 기다립니다:

kubectl wait --for=condition=Available deployment/init-web --timeout=120s

예상 출력:

deployment.apps/init-web condition met

Deployment 에 의해 생성된 Pod 를 찾습니다:

web_pod="$(kubectl get pods -l app=init-web -o 'jsonpath={.items[0].metadata.name}')"
echo "$web_pod"

예상 출력 (생성된 Pod 이름):

init-web-...

해당 Pod 에서 앱 컨테이너의 로그를 읽습니다:

kubectl logs "$web_pod" -c web

예상 출력:

deployment-init

Deployment 가 생성한 Pod 내부에서 Init 컨테이너의 완료 상태를 확인합니다:

kubectl get pod "$web_pod" -o 'jsonpath={.status.initContainerStatuses[0].name}{" "}{.status.initContainerStatuses[0].state.terminated.reason}{"\n"}'

예상 출력:

render-index Completed

요약

Pod 와 Deployment 템플릿에 Init 컨테이너를 추가하는 방법을 실습했습니다. emptyDir 볼륨을 사용하여 준비된 파일을 앱 컨테이너와 공유하고, 완료된 Init 컨테이너의 상태를 검사하며, Init 및 앱 로그를 읽고, 동일한 패턴이 컨트롤러로 관리되는 애플리케이션 Pod 에서도 작동함을 확인했습니다.