1. 概述

本文将深入介绍 Kubernetes 中的 Init 容器(Initialization Containers),它们在 Pod 生命周期中扮演初始化角色,用于完成前置任务,确保主容器在启动前具备所需环境和资源。

2. Kubernetes 中的 Init 容器

在 Kubernetes 中,一个 Pod 可以包含多个容器,这些容器共同协作完成一个整体功能。其中,容器分为两种类型:

  • 主容器(Main Containers):运行应用的核心逻辑。
  • 初始化容器(Init Containers):在主容器启动前运行,用于完成前置任务。

在 Pod 的 YAML 定义中,主容器通过 containers 字段定义,而初始化容器则通过 initContainers 字段定义。以下是一个包含 Init 容器和主容器的 Pod 示例:

# static-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: static-web
spec:
  initContainers:
    - name: init-create-dir
      image: alpine
      command: ['sh', '-c', 'sleep 5 && echo "creating dir" && mkdir -p /opt/log']
      volumeMounts:
        - name: data
          mountPath: /opt
  containers:
    - name: app
      image: alpine
      command: ['sh', '-c', 'echo "app is running" && sleep infinity']
      volumeMounts:
        - name: data
          mountPath: /opt
  volumes:
    - name: data
      emptyDir: {}

✅ 说明:

  • init-create-dir 是一个初始化容器,用于创建目录。
  • app 是主容器,依赖于初始化容器创建的目录。
  • 两者共享同一个 emptyDir 类型的卷。

2.1. Init 容器的作用

Init 容器常用于执行前置检查或配置任务,例如:

  • 等待某个依赖服务启动完成(如数据库、Redis)。
  • 创建共享卷中的目录或文件。
  • 配置网络规则(如 Istio 使用 Init 容器设置 iptables)。

⚠️ 关键点:Init 容器在主容器之前运行,且必须全部成功完成,主容器才会启动。

2.2. Init 容器的优势

  • 安全性提升:可将初始化所需工具(如 curlnsenter)放在 Init 容器中,避免污染主容器镜像。
  • 权限控制更灵活:Init 容器可以拥有更高的权限(如 CAP_NET_ADMIN),但只在初始化阶段使用,降低主容器的安全风险。
  • 逻辑解耦:将初始化逻辑与主应用逻辑分离,使主容器更轻量、专注业务。

3. Init 容器的特点

3.1. 执行顺序

  • 按定义顺序串行执行:多个 Init 容器按 YAML 中的顺序依次运行。
  • 失败影响整个启动流程:任一 Init 容器失败,Pod 的启动流程将根据 restartPolicy 决定是否重试。

3.2. 不支持的字段

Init 容器不支持以下字段:

  • livenessProbe
  • readinessProbe
  • startupProbe
  • lifecycle

设置这些字段会导致 Pod 创建失败,报错如下:

error: failed to create containerd task: failed to create shim task: OCI runtime create failed: invalid lifecycle configuration: unknown

⚠️ 原因:这些字段用于运行时健康检查,而 Init 容器只运行一次,无法支持。

3.3. 重启策略

  • restartPolicy 设置为 AlwaysOnFailure 时,Init 容器的重启策略始终为 OnFailure
  • Never:失败后不重启,Pod 状态变为 Failed

4. Init 容器的生命周期

4.1. 初始化阶段启动

  1. 用户提交 Pod 定义。
  2. kube-scheduler 将 Pod 调度到节点。
  3. kubelet 创建并启动第一个 Init 容器。
  4. kubelet 检查其退出码,决定是否继续执行下一个 Init 容器。

4.2. Init 容器失败

  • 非零退出码:表示初始化失败。
  • 根据 restartPolicy
    • Never:Pod 状态变为 Init:Error
    • OnFailureAlways:尝试重启整个 Init 容器列表,最多达到重试上限后变为 Init:CrashLoopBackOff

⚠️ 建议:确保 Init 容器逻辑是幂等的(Idempotent),避免因重试导致副作用。

4.3. Init 容器成功

  • 零退出码:表示该 Init 容器执行成功。
  • Pod 状态变为 Init:N/M(N 为已成功容器数,M 为总数)。
  • 所有 Init 容器完成后,Pod 状态变为 PodInitializing,主容器开始启动。

5. Init 容器生命周期演示

5.1. 示例 Pod 定义

# static-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: static-web
spec:
  initContainers:
    - name: init-create-dir
      image: alpine
      command: ['sh', '-c', 'sleep 5 && echo "creating dir"']
    - name: init-create-config
      image: alpine
      command: ['sh', '-c', 'sleep 5 && echo "creating file"']
  containers:
    - name: app
      image: alpine
      command: ['sh', '-c', 'echo "app is running" && sleep infinity']
    - name: log-transformer
      image: alpine
      command: ['sh', '-c', 'echo "transforming log" && sleep infinity']

5.2. 创建 Pod

kubectl apply -f static-pod.yaml
pod/static-web created

5.3. 查看状态

使用 kubectl describe pod static-web 查看状态变化:

初始状态

Init Containers:
  init-create-dir:
    State: Waiting
      Reason: PodInitializing

第一个 Init 容器运行中

init-create-dir:
  State: Running
    Started: Sun, 21 May 2023 11:32:29 +0800
init-create-config:
  State: Waiting
    Reason: PodInitializing

第一个 Init 容器完成

init-create-dir:
  State: Terminated
    Exit Code: 0
init-create-config:
  State: Running
    Started: Sun, 21 May 2023 11:32:29 +0800

所有 Init 容器完成,主容器启动

Conditions:
  Type              Status
  Initialized       True
Containers:
  app:
    State: Running
  log-transformer:
    State: Running

6. 小结

  • Init 容器用于在主容器启动前完成初始化任务。
  • 所有 Init 容器必须按顺序执行成功,主容器才会启动。
  • 支持共享卷和网络命名空间,适合用于配置网络、创建目录等。
  • 不支持健康检查字段,避免配置错误。
  • 合理使用 Init 容器可以提升 Pod 的安全性和可维护性。

最佳实践建议

  • 将初始化逻辑与主容器解耦。
  • Init 容器应尽量轻量,避免引入复杂依赖。
  • 确保 Init 容器逻辑幂等,避免重试导致副作用。
  • 使用 kubectl describe 监控 Init 容器状态,便于排查问题。

原始标题:Init Containers in Kubernetes