添加初始化容器 (Init Containers)

KubernetesBeginner
立即练习

介绍

初始化容器(Init Container)在 Pod 中的主应用容器启动之前运行。每个初始化容器必须成功完成,Kubernetes 才会启动下一个初始化容器或任何常规容器。这使得初始化容器非常适合用于准备文件、检查依赖项或执行一些不应在应用运行期间持续存在的简单设置任务。

在本实验中,你将添加一个初始化容器,它会将启动文件写入共享的 emptyDir 卷中,确认应用容器能够读取该文件,检查初始化容器的完成状态和日志,然后将相同的模式应用于 Deployment 模板中。

准备初始化容器命名空间

在此步骤中,你将准备一个干净的命名空间,用于练习初始化容器。

首先进入 /home/labex/project 目录。你将在此目录中编写 Pod 和 Deployment 的清单文件。

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

检查 Kubernetes API 服务器是否可访问。如果虚拟机刚启动且节点尚未就绪,请等待几秒钟后再运行该命令。

kubectl get nodes --request-timeout=5s

预期输出包含:

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

初始化容器在 Pod 内部运行,因此在创建工作负载之前,请确保集群网络 Pod 已就绪。

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

在应用启动前准备共享数据

在此步骤中,你将创建一个 Pod,其中的初始化容器会在应用容器启动前准备好一个文件。

emptyDir 卷在 Pod 被分配到节点时创建,并由同一 Pod 中的容器共享。初始化容器和应用容器都将此卷挂载到 /work 目录。初始化容器写入 /work/startup.txt,然后退出。只有在它成功退出后,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 就绪:

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

预期输出:

pod/init-file-demo condition met

从应用容器内部读取文件。这证明了初始化容器在应用启动前已在共享卷上准备好了数据。

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

预期输出:

prepared-by-init

检查初始化容器的状态和日志

在此步骤中,你将检查初始化容器在应用容器运行前已完成的证据。

初始化容器显示在 .status.initContainerStatuses 下,而常规容器显示在 .status.containerStatuses 下。初始化容器应该有一个 Completed 原因,因为它已成功退出。

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

预期输出:

prepare-startup-file Completed

现在检查常规应用容器的状态。它应该是就绪且正在运行的。

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 ...

读取初始化容器的日志。即使初始化容器已经完成,其日志仍然可以通过 kubectl logs -c 查看。

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

预期输出:

init-started
init-finished

读取应用容器的日志。应用在初始化容器写入文件后启动,因此其日志中包含了准备好的值。

kubectl logs init-file-demo -c app

预期输出包含:

app-started
prepared-by-init

将初始化容器添加到 Deployment

在此步骤中,你将在 Deployment 模板中使用相同的初始化容器模式。

对于应用工作负载,通常将初始化容器放置在控制器的 Pod 模板中,而不是创建独立的 Pod。当 Deployment 创建 Pod 时,Kubernetes 会为从该模板创建的每个 Pod 首先运行初始化容器。

创建 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 可用:

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 中初始化容器的完成状态:

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

预期输出:

render-index Completed

总结

你已成功将初始化容器添加到 Pod 和 Deployment 模板中。你使用了 emptyDir 卷与应用容器共享准备好的文件,检查了已完成的初始化容器状态,读取了初始化容器和应用容器的日志,并确认了相同的模式同样适用于由控制器管理的应用 Pod。