Pod 的 init Containers


在 Kubernetes 中,Init Containers(初始化容器) 是在 Pod 的主容器(应用容器)启动之前运行的专用容器,用于完成主容器启动前需要的初始化工作。它们为应用容器提供了隔离的初始化环境,确保主容器启动时所有依赖条件都已满足。

核心作用

Init Containers 的主要任务是为应用容器准备运行环境,常见场景包括:

  • 等待其他服务(如数据库、API 服务)就绪
  • 下载或配置应用所需的配置文件、密钥等
  • 初始化数据库表结构或数据迁移
  • 设置文件系统权限、环境变量等
  • 执行前置命令(如解压资源包、验证依赖)

关键特点

1.执行顺序

一个 Pod 可以定义多个 Init Containers,它们按 定义顺序串行执行(前一个完成后,下一个才启动),全部成功后才会启动主容器。

2.运行特性

  • 每个 Init Container 必须 成功退出(退出码为 0),否则 Kubernetes 会不断重启该 Pod 直到 Init Container 成功。
  • 仅在 Pod 首次启动或因 spec 变更导致重启时执行,主容器重启不会触发 Init Containers 重新运行。

3.资源共享

  • 与主容器共享 Pod 的网络命名空间(可访问同一网络)和存储卷(可传递文件/配置)。
  • 不共享进程命名空间,与主容器完全隔离。

4.资源限制

  • 可以设置资源请求(requests)和限制(limits),但调度时会取 Init Containers 和主容器的资源需求最大值(避免资源不足)。

与主容器(App Containers)的区别

特性 Init Containers 主容器(App Containers)
启动时机 主容器启动前 Init Containers 全部完成后
运行次数 仅执行一次(成功后退出) 持续运行(除非崩溃或被终止)
失败处理 重启 Pod 直到成功 根据 restartPolicy 策略处理
生命周期钩子 不支持(lifecycle 配置无效) 支持(如 postStartpreStop

使用示例

以下是一个包含 Init Containers 的 Pod 配置示例: 该 Pod 先通过 Init Container 等待数据库服务就绪,再启动主应用容器。

apiVersion: v1
kind: Pod
metadata:
  name: app-with-init
spec:
  initContainers:
  - name: wait-for-db  # 第一个 Init Container:等待数据库服务
    image: busybox:1.35
    command: ['sh', '-c', 'until nslookup db-service; do echo waiting for db; sleep 2; done;']
  - name: fetch-config  # 第二个 Init Container:下载配置文件
    image: curlimages/curl
    command: ['sh', '-c', 'curl -o /config/config.yaml http://config-server/config;']
    volumeMounts:
    - name: config-volume
      mountPath: /config
  containers:
  - name: app-main  # 主应用容器
    image: my-app:v1
    volumeMounts:
    - name: config-volume
      mountPath: /app/config
  volumes:
  - name: config-volume
    emptyDir: {}

说明
1. wait-for-db 容器通过 nslookup 检查 db-service 是否可解析(服务就绪),未就绪则循环等待。
2. fetch-config 容器从配置服务器下载文件到共享卷 config-volume
3. 主容器 app-main 从共享卷读取配置文件并启动。

注意事项

  • 避免在 Init Containers 中执行耗时过长的操作,以免阻塞 Pod 启动。
  • 若 Init Containers 依赖外部服务,需做好超时控制(如设置 activeDeadlineSeconds)。
  • 资源需求需合理配置:Init Containers 的资源限制不应低于其实际需求,否则可能因资源不足失败。
  • 若需跳过 Init Containers 执行(如调试),可通过修改 Pod spec 临时移除,但生产环境不建议。

通过 Init Containers,Kubernetes 提供了一种优雅的方式处理应用启动前的依赖和初始化逻辑,确保主容器在最佳状态下运行。

原理

Pod能够具有多个容器,应用运行在容器里面,但是它也可能有一个或多个先于应用容器启动的Init容器,Init容器与普通的容器非常像,除了如下两点:

  • Init容器总是运行到成功完成为止
  • 每个Init容器都必须在下一个Init容器启动之前成功完成

如果Pod的Init容器失败, Kubernetes 会不断地重启该Pod,直到Init容器成功为止。然而,如果Pod对应的restartPolicy为Never,它不会重新启动。

initContainers示例:

apiVersion: v1
kind: Pod
metadata:
  name: initc-demo
  labels:
    app: myapp
spec:
  containers:
  - name: myapp-container
    image: docker.io/busybox
    command: ['sh', '-c', 'echo The app is running! && sleep 3600']
  initContainers:
  - name: init-myservice
    image: docker.io/busybox
    command: ['sh', '-c', 'until nslookup myservice; do echo waiting for myservice; sleep 2; done;']
  - name: init-mydb
    image: busybox
    command: ['sh', '-c', 'until nslookup mydb; do echo waiting for mydb; sleep 2; done;']

我们创建模版资源后查看结果为:

29.png

我们查看一下日志:

30.png

这个时候我们可以看到因为解析不成功,所以初始化程序卡住了,那么先创建满足第一个解析的service资源:

kind: Service
apiVersion: v1
metadata:
  name: myservice
spec:
  ports:
  - protocol: TCP
    port: 80
    targetPort: 9376

创建完成后我们在查看一下 Pod 的状态:

31.png

第一个 init 初始化程序已经成功,这是因为,我们创建名为“myservice”的 SVC 的数据会写到我们内部的DNS(coreDNS) 上,因为可以正常的解析了,所以第一个 init 初始化程序完成,同理我们加入第二个 init 初始化程序的 SVC 后查看 Pod 状态:

kind: Service
apiVersion: v1
metadata:
  name: mydb
spec:
  ports:
  - protocol: TCP
    port: 80
    targetPort: 9377

32.png

这个时候我们可以看到,第二个 init 初始化程序已经完成,我们的主容器 Pod 开始初始化,最后成功开始运行。

initContainers特殊说明

1、在 Pod 启动过程中,Init 容器会按顺序在网络和数据卷初始化之后启动。每个容器必须在下一个容器启动之前成功退出。

2、如果由于运行时或失败退出,将导致容器启动失败,它会根据 Pod 的 restartPolicy 指定的策略进行重试。然而,如果 Pod 的 restartPolicy 设置为 Always , Init 容器失败时会使用 RestartPolicy 策略。

3、在所有的Init容器没有成功之前,Pod 将不会变成 Ready 状态。Init 容器的端口将不会在 Service 中进行聚集。正在初始化中的 Pod 处于 Pending 状态,但会将 Initializing 状态设置为 true。

4、如果 Pod 重启,所有 Init 容器必须重新执行。

5、对 Init 容器 spec 的修改被限制在容器 image 字段, 修改其他字段都不会生效。 更改 Init 容器的 image字段,等价于重启该 Pod。

6、Init 容器具有应用容器的所有字段。除了 readinessProbe , 因为Init容器无法定义不同于完成 (completion) 的就绪 (readiness) 之外的其他状态。这会在验证过程中强制执行。

7、在 Pod 中的每个 app 和 Init 容器的名称必须唯一,与任何其它容器共享同个名称,会在验证时抛出错误。