Kubernetes Init Container:启动阶段、失败重试与探针边界
有些应用启动前需要生成配置文件、等待依赖服务被发现,或者执行初始化脚本。把这些动作放进应用入口脚本,会让应用镜像同时包含初始化工具和业务程序,也需要在入口脚本中处理等待和失败重试。
Kubernetes 提供了 initContainers 字段来表达这类「启动前必须完成的工作」。本文先说明它在 Pod 生命周期中的位置,再回答三个容易混淆的问题:失败后会发生什么、普通 init container 是否支持 liveness/readiness/startup probe,以及它和原生 sidecar 到底有什么区别。
文中的源码分析基于本地 Kubernetes 仓库的 commit c028ba348dbaea5e8b0df94b2581e70d687a77c8,实验文件固定在 k8sdev 0bb95c0c。源码和文档的具体版本可能继续变化,文章中的结论以这两个提交为准。
普通 init container 的启动和失败处理规则如下:
- 普通 init container 在 Pod 被调度到节点、Pod sandbox 的网络和存储准备好之后运行;多个 init container 严格按照
spec.initContainers的顺序执行。 - 只有进程以退出码
0结束,kubelet 才会启动下一个 init container;全部普通 init container 成功后,应用容器才会启动。 - 普通 init container 失败时,
restartPolicy: Always或OnFailure会让 kubelet 重试;restartPolicy: Never不会重试,Pod 最终进入Failed,应用容器不会启动。 - 普通 init container 不支持
lifecycle、livenessProbe、readinessProbe和startupProbe。它的「就绪」条件不是探针,而是成功退出。 - init 阶段会阻止 Pod 进入可服务状态,但 init container 的端口不会自动成为 Service 的后端。应用容器启动后,Pod 是否接收流量仍由应用容器的 readiness 状态决定。
- 当前 Kubernetes 还支持把
initContainers中设置了容器级restartPolicy: Always的条目作为原生 sidecar。它会持续运行并支持探针,不应再按普通 init container 理解。
init container 解决什么问题
普通容器描述的是「Pod 运行期间持续提供服务的进程」。init container 描述的是「应用启动前必须完成的一次性步骤」。两者都使用 Container 的大部分字段,也都可以使用独立镜像、环境变量、Secret、ConfigMap 和 volume;区别在于 kubelet 对它们的启动顺序和完成条件不同。
常见的使用场景有三类:
- 使用一个很小的工具镜像生成配置、模板或证书,再通过共享 volume 交给应用容器。
- 在应用启动前等待一个外部前置条件,例如等待 Service 的 DNS 名称出现,或等待某个初始化任务完成。
- 把只在初始化阶段需要的工具放在独立镜像里,不把
curl、dig、模板工具和调试脚本长期放进应用镜像。
它适合表达「不满足这个前置条件,应用就不应该启动」。如果条件只影响流量接收、不应该导致应用被重启,就应该使用应用容器的 readiness probe,而不是让 init container 无限等待。
它在 Pod 的哪个阶段触发
创建 Pod 对象时,API Server 只保存 spec,不会直接在 API Server 所在节点执行 init container。典型链路是:
flowchart LR
API["创建 Pod 对象"] --> Schedule["Scheduler 选择节点"]
Schedule --> Kubelet["节点 kubelet 接收 Pod"]
Kubelet --> Sandbox["创建 Pod sandbox\n准备网络和存储"]
Sandbox --> Init1["init-1\n按顺序运行"]
Init1 --> Init2["init-2\n前一个成功后才运行"]
Init2 --> App["启动应用容器"]
App --> Ready["应用容器探针\n决定 Pod Ready"]
Init1 -. "退出非 0" .-> Retry["按重启策略重试\n或结束 Pod"]
Retry --> Init1
Kubernetes 官方文档把 init container 描述为在应用容器之前运行、并且必须运行到完成的容器。源码中,kubelet 的 runtime manager 在创建 Pod sandbox 时把第一个 init container 放入 InitContainersToStart;后续同步时,computeInitContainerActions 根据前一个 init container 的状态决定是重试当前容器,还是启动下一个容器。
这个顺序有两个直接结果:
- init container 还在运行时,应用容器不会进入正常启动流程。 应用容器的状态通常会显示为
Waiting,原因可能是PodInitializing;Pod phase 通常仍是Pending。 - init container 是串行的。 即使两个初始化动作互不依赖,也不能让普通 init container 并行执行。若它们确实需要并行,应合并脚本,或者重新设计成应用容器/sidecar 之间的显式协作。
多个 init container 的最小结构如下:
| |
init container 失败后会怎样
普通 init container 的成功标准是进程终止且退出码为 0。它不需要、也不能通过 readiness probe 来声明「准备好了」。如果进程退出码非 0,或者容器因为镜像拉取、创建或运行时错误没有正常启动,kubelet 会把初始化视为未完成。
Always 和 OnFailure:继续重试
Pod 的 restartPolicy 默认是 Always。对于普通 init container,成功退出后不会因为 Always 被重新启动;Always 在 init 阶段实际表现为「失败时继续重试」。OnFailure 也会重试失败的 init container。重试之间会有 kubelet 的退避时间,因此一个持续失败的 Pod 不会无间隔地创建容器。
在重试期间,后面的 init container 和应用容器都不会启动。常见的观察结果是:
| |
这里的 0/1 不是「应用容器 readiness probe 失败」,而是应用容器还没有进入运行阶段。应优先查看 init container 的日志和 initContainerStatuses:
| |
Never:Pod 进入 Failed
如果 Pod 使用 restartPolicy: Never,普通 init container 的失败不会在同一个 Pod 中重试。kubelet 会停止初始化流程,Pod phase 进入 Failed,应用容器不会启动。官方文档也特别强调了这个差异:init container 失败时,Never 会把整个 Pod 视为失败。
这时 init container 的终止状态仍然保留在 .status.initContainerStatuses 中,退出码、reason 和 message 是排查失败原因的首要证据。一次性初始化任务可以使用这种语义,但如果它属于 Job,通常还要结合 Job 的 backoffLimit 和任务级结果来设计,而不是只依赖裸 Pod。
重试与重新执行要求幂等
init container 可能因为非 0 退出、节点故障、Pod 被重新创建或 Pod sandbox 重建而再次执行。因此初始化脚本应当可以安全地重复运行:
- 创建目录前使用
mkdir -p,写文件时明确覆盖或原子替换策略。 - 数据库迁移使用版本表、事务或唯一约束,避免重复执行产生破坏性结果。
- 向外部系统注册时使用幂等 key,或者在执行前检查已有记录。
- 等待依赖时设置合理的超时和失败信息,不要让一个永远无法满足的 DNS、证书或远端 API 把 Pod 永久卡在 Pending。
如果需要给整个 Pod 的初始化设置上限,可以使用 activeDeadlineSeconds。它包含 init 阶段的时间,但它是 Pod 的总活动期限;Pod 已经正常运行后仍可能受到这个字段影响。对一次性任务可考虑使用 Job 的时间限制,对长期运行的 Deployment 则要谨慎设置。
普通 init container 不支持探针和生命周期钩子
这些限制由 API Server 在接收 Pod 配置时检查。
普通 init container 不支持以下字段:
| 字段 | 普通 init container | 应用容器中的作用 |
|---|---|---|
lifecycle | 不允许 | 启动或停止时执行钩子 |
livenessProbe | 不允许 | 失败后让 kubelet 重启容器 |
readinessProbe | 不允许 | 失败时从 Service 后端摘除 |
startupProbe | 不允许 | 成功前暂停 liveness/readiness,并给慢启动容器更长时间 |
源码中的 validateInitContainers 会逐项拒绝这些字段,因此下面的 Pod 配置会在提交时得到校验错误:
| |
普通 init container 的完成状态由进程退出码表达。kubelet 在生成状态时,会把「已终止且退出码为 0」的普通 init container 视为完成,并据此生成 PodInitialized=True;只要还有 init container 未完成,应用容器就不会被正常检查和启动。
初始化完成、可以接收流量和需要重启容器,分别使用不同的判断:
- 初始化是否完成:看 init container 是否成功退出,决定能否继续创建后续容器。
- 是否接收流量:看应用容器的 readiness probe 和 Pod readiness gate。
- 是否重启容器:看应用容器的 liveness probe,失败时重启该应用容器。应用容器在同一个 Pod 中被 liveness 重启,通常不会重新执行已经完成的普通 init container。
因此,01-shared-volume.yaml 里的 readiness/liveness probe 都配置在 nginx 上,而不是配置在 generate-index 上:init 负责生成页面并退出,nginx 负责回答「页面是否已经能服务」以及「服务进程是否仍然存活」。
当前版本的例外:原生 sidecar
从支持 sidecar container 的 Kubernetes 版本开始,initContainers 中的条目可以设置容器级 restartPolicy: Always。这种条目仍然写在 initContainers 数组里,但语义已经不是普通的一次性 init container:
- 它会在 init 序列中启动,但启动后持续运行,不等待它退出才启动后续容器。
- 如果配置了
startupProbe,需要先通过 startup probe,后续 init 容器才会继续。 - 它可以使用 liveness/readiness/startup probe,并在应用容器运行期间继续作为 sidecar 工作。
- Pod 终止时,sidecar 的停止顺序也与普通 init container 不同。
当前本地源码的 API 定义和校验逻辑都明确区分了这条路径:只有 restartPolicy: Always 的 init 条目才进入 sidecar 的探针校验分支。文章后面的实验全部是普通 init container,不依赖 sidecar 特性。
Pod Ready、Service 流量与 init container 端口
「init container 成功」只表示 Pod 具备启动应用容器的条件,不表示应用已经可以接收流量。完整状态至少要区分三个字段:
| 状态 | 含义 | init 阶段的典型值 |
|---|---|---|
status.phase | Pod 的粗粒度阶段 | Pending |
status.conditions[Initialized] | 所有 init container 是否完成 | False |
status.conditions[Ready] | 所有应用容器和 readiness gate 是否满足 | False |
全部普通 init container 成功后,kubelet 才启动应用容器。应用容器没有 readiness probe 时,运行中的容器通常会被视为 ready;配置了 readiness probe 后,只有探针成功,Pod 才会进入 Ready=True,Service 才会把它作为可用后端。readiness 失败会摘除 Service 流量,但不会像 liveness 失败那样重启容器。
init container 的端口也不会被聚合到 Pod 的 Service 端点。它们是启动阶段的临时容器,不是 Service 的长期服务进程。需要被 Service 访问的端口,必须由应用容器提供,并由应用容器的 readiness 状态保护。
最常见的使用方式:共享 volume
init container 与应用容器共享 Pod 的 volume,是最容易验证也最实用的模式。下面的流程是:
generate-index把页面写入emptyDir。- init container 以退出码
0结束。 - kubelet 启动 nginx,并把同一个
emptyDir挂载到 nginx 文档根目录。 - nginx 的 readiness probe 请求
/index.html,成功后 Pod 才 ready。
实验文件使用的关键结构如下:
| |
这里的 emptyDir 只适合 Pod 生命周期内的数据交换。它不是持久化存储;如果初始化结果必须跨 Pod 重建保留,应使用 PVC、ConfigMap、Secret 或外部存储,并仍然考虑重复执行和并发更新。
等待依赖服务:可以用,但要有边界
等待依赖是官方文档给出的典型用法。init container 可以循环执行 DNS 查询,直到目标 Service 出现,然后以成功退出结束。它的好处是应用容器不需要内置一段「启动前先探测依赖」的特殊逻辑。
但等待脚本要避免三个问题:
- 无限等待:依赖名称写错或依赖永远不会创建时,Pod 会一直 Pending。应配合超时、重试次数或
activeDeadlineSeconds。 - 把可恢复依赖当成硬前置条件:如果应用启动后可以优雅地等待数据库恢复,就让应用容器启动,并用 readiness 控制流量;只有确实不能启动的依赖才适合放进 init container。
- 把 DNS 可解析当成服务可用:DNS 只证明 Service 名称存在,不证明后端有 Endpoint、端口可连接或业务已经完成迁移。需要更强的条件时,应在 init 脚本里做有超时的 TCP/HTTP/业务检查。
开源组件中的真实用法
前面的例子是应用自己定义 init container。很多开源组件也大量使用同一机制,但它们等待或准备的对象更具体:可能是 Pod 网络规则、节点上的 CNI 文件、存储设备状态,或者共享 volume 中的 Secret 文件。
这些组件有一个共同点:初始化动作必须发生在主进程启动之前,而且失败时宁可让 Pod 停在 init 阶段,也不能让主进程以一个不完整的环境启动。下面几个例子分别对应不同的使用方式。
Istio:在 Envoy 启动前设置流量转发规则
在 Istio 的 sidecar 模式中,注入器会向业务 Pod 加入 istio-init 和 istio-proxy。istio-init 在 Pod 的网络命名空间中配置 iptables,把进出 Pod 的 TCP 流量重定向到 Envoy。这样,应用从第一个连接开始就经过 sidecar,而不会出现「应用已经启动,但早期流量绕过代理」的窗口。
这不是一个 readiness 检查。istio-init 的成功含义是「网络转发规则已经写入」,因此它完成后应用容器和 Envoy 才继续启动。如果配置规则失败,应用不会进入正常运行阶段,排查时要看 istio-init 的日志和退出状态。
这个方案需要修改网络命名空间,因此传统 sidecar 安装通常要求 NET_ADMIN、NET_RAW 能力以及较宽松的 Pod Security 设置。Istio 官方文档明确说明,使用 Istio CNI 可以把这部分节点级操作移出业务 Pod,从而避免给 istio-init 添加这些能力;应用要求 也说明了这一边界。使用 Ambient 模式时,数据面的组成又不同,不能假定每个加入 Istio 的 Pod 都会有普通的 istio-init。
这个例子说明:init container 不只用于生成文件,也可以用来完成「应用启动前必须存在的网络环境」。
Linkerd:用 init container 把 TCP 流量接入代理
Linkerd 的注入器在启用 Pod 注入后,会加入代理和用于启动配置的 init container。普通 init 路径中的 linkerd-init 使用 iptables,把 Pod 的 TCP 流量导向 linkerd-proxy;代理随后负责透明代理、指标采集和 mTLS 等运行期间的工作。Linkerd 的架构文档对这一过程有直接说明。
init container 负责一次性的 iptables 配置,代理容器持续处理流量。如果只重启代理,通常不需要重新执行已经完成的普通 init;如果 Pod 被重新创建,网络命名空间也重新创建,init container 会再次执行。
需要留意版本和配置差异。Linkerd 2.20 起默认使用 Kubernetes native sidecar 部署 proxy,旧的普通 init 路径仍然存在;启用 Linkerd CNI 后,也可以由 CNI 插件完成这部分网络规则配置。因此,在集群中看到 proxy-init、linkerd-init 或 native sidecar 时,应结合 Linkerd 版本和注入结果判断,不能只凭容器名称推断它的生命周期。
Longhorn:在驱动部署前等待 Manager 服务可用
Longhorn 的 longhorn-driver-deployer 是一个很典型的「等待依赖服务」案例。Longhorn 的官方 Helm 模板中包含名为 wait-longhorn-manager 的 init container,它循环访问 http://longhorn-backend:9500/v1,直到返回 HTTP 200;只有这个 init container 成功退出,主容器才会运行 longhorn-manager deploy-driver。
它和应用里的 readiness probe 有一个重要区别:readiness 失败时,进程通常已经在运行,只是暂时不接收流量;Longhorn 这里则把 Manager 可用作为驱动部署器的硬前置条件,主进程在依赖满足前根本不会启动。实现可以直接查看 Longhorn 的 Deployment 模板。
这类写法的代价也很明显:如果 Service 名称、端口或 Manager 本身有问题,Pod 会长时间停在 Init:0/1。当前模板的等待命令是持续轮询,因此部署时应结合 Pod 事件、init container 日志和上层 Deployment 的超时策略观察,不能只看到「Pod 还在初始化」就判断为 kubelet 故障。
Cilium:把节点准备工作从 Agent 主进程中拆出来
Cilium 的 Agent DaemonSet 还展示了另一类用法:init container 并不是为某个业务 Pod 准备环境,而是为节点上的网络组件准备文件。Cilium Helm 模板中的 install-cni-binaries 会执行 /install-plugin.sh,并把宿主机的 CNI 二进制目录挂载到容器内的 /host/opt/cni/bin,将所需插件安装到节点。
模板注释直接说明了这样做的目的:把 CNI 二进制安装放在 init container 中,从而避免让长期运行的 Agent 主容器持有可写的宿主机目录挂载。初始化完成后,Agent 主容器可以使用已经准备好的 CNI 文件;如果安装步骤失败,节点上的 CNI 配置就不完整,Agent 不应被当成已经正常工作。
具体配置见 Cilium 的 Agent DaemonSet 模板。这里的 init container 会写入宿主机目录,配置或权限错误可能影响整个节点的网络插件。
Rook/Ceph:按顺序激活 OSD 和准备存储目录
Rook/Ceph 中的 init container 更接近「多步存储初始化」。Rook 在生成 Ceph OSD Pod 时,可以根据配置加入多个 init container,例如生成配置、复制二进制、激活 OSD、扩展存储、更新 CephX key,以及修正 Ceph 数据目录权限。主 ceph-osd 进程要等这些步骤按顺序完成后才启动。
这些 init container 分别准备磁盘或 PVC、配置和 key、目录权限。任意一步失败,OSD Pod 都会停在初始化阶段,主进程不会在准备工作尚未完成时启动。Rook 的 OSD Pod 生成代码展示了这些步骤的组合逻辑,官方排障文档也提醒需要分别查看各个 init container 的日志。
把配置生成、设备激活、权限修复拆成独立命名的 init container 后,可以分别查看每一步的日志和退出状态,定位失败位置。
OpenBao Agent Injector:在应用启动前把 Secret 写入共享 volume
OpenBao Agent Injector 可以通过 Pod 注入机制添加一个 init container,用 Agent 身份认证并把请求的 Secret 渲染到共享 volume。应用容器只需要挂载同一个目录,在启动时读取已经生成的文件,不需要把 OpenBao 客户端、认证逻辑和模板渲染工具放进业务镜像。
OpenBao 的注入代码用 PrePopulate 控制是否加入预填充 init container,用 InitFirst 控制是否把它放在其他 init container 之前。如果其他初始化步骤也需要读取 Secret,就需要先运行 OpenBao Agent。对应实现见 OpenBao Kubernetes 注入器源码。
预填充 init container 只保证「Pod 启动时」Secret 已经存在。如果 Secret 需要在运行期间自动刷新,通常还要使用持续运行的 Agent sidecar,并让应用支持重新读取文件;不能把一次性 init container 当成 Secret rotation 机制。
把这些案例放在一起,可以看到 init container 的边界:
- Istio 和 Linkerd 在启动前准备网络规则。
- Longhorn 在启动驱动部署器前等待 Manager 服务。
- Cilium 在节点上安装 CNI 文件。
- Rook/Ceph 按顺序完成存储设备、配置和权限初始化。
- OpenBao 在应用启动前准备共享 volume 中的 Secret。
如果动作需要在应用运行期间持续执行,应该考虑 sidecar;如果动作只影响是否接收流量,应该考虑 readiness probe;只有「完成之前不允许主容器启动」的工作,才适合放进普通 init container。
init container 的资源会影响调度
init container 虽然只在启动阶段运行,但它的资源请求不会被调度器忽略。对每种资源,Pod 的有效请求大致取:
| |
因此,一个需要 2 CPU 做初始化、但应用长期只需要 200m CPU 的 Pod,在调度时仍可能按 2 CPU 的有效请求占用节点资源。资源限制也会按类似规则参与 Pod 的 QoS 和 cgroup 配置。
这 2 CPU 是调度时需要满足的资源请求,并不表示 init container 会一直使用 2 CPU。初始化工具的请求设置过高时,即使应用运行所需的资源很少,Pod 也可能因节点无法满足初始化请求而保持 Pending。
动手验证
本次实验文件固定在 k8sdev 0bb95c0c,包含以下路径:
| 文件 | 观察目标 |
|---|---|
01-shared-volume.yaml | init 生成页面,应用容器通过共享 emptyDir 提供页面 |
02-failure-retry.yaml | restartPolicy: Always 下失败 init 的重试和退避 |
03-failure-never.yaml | restartPolicy: Never 下 Pod 进入 Failed |
04-wait-for-service.yaml | Service 尚未存在时,Pod 停在 init 阶段 |
05-dependency-service.yaml | 创建 Service 后解除 DNS 等待 |
先准备一个能访问 Kubernetes API 的测试集群,再获取固定实验文件:
| |
观察成功初始化
| |
预期现象是:generate-index 先进入 Terminated 且退出码为 0,nginx 才会创建并进入运行状态。nginx 的 readiness probe 成功后,Pod 的 READY 从 0/1 变成 1/1。
观察失败重试
| |
重点看 Init:CrashLoopBackOff、事件中的重启退避和 .status.initContainerStatuses。不要只看 Pod 的 RESTARTS 摘要列,排查 init 时应直接查询 init container 的状态数组。
观察 restartPolicy: Never
| |
这里的结果应为 Failed 和 42,而 app 容器没有启动记录。
观察等待依赖
| |
这个实验只证明 Service 名称能被 DNS 解析,不证明后端业务已经健康。真实场景中应根据依赖协议增加连接超时和业务级检查。
实验结束后,只删除这组实验对象:
| |
从 kubelet 源码看这条链路
1. runtime manager 只放行一个初始化步骤
computePodActions 在需要创建新的 Pod sandbox 时,如果 Pod 存在 init container,就把 InitContainersToStart 设置为第一个索引,然后返回。此时不会同时把普通应用容器放进 ContainersToStart。
当 sandbox 已经存在时,runtime manager 调用 computeInitContainerActions 检查初始化进度。这个函数会从 init 列表尾部向前扫描,找到仍需要处理的最小状态集合;这样即使较早完成的容器状态已经被 runtime 垃圾回收,也不会盲目重跑全部初始化步骤。
对普通 init container,核心分支是:
Running:保持当前 init,应用容器仍不进入启动流程。Exited且退出码为0:当前 init 完成,启动下一个;最后一个完成后返回「Pod 已初始化」。Exited且退出码非0:如果允许重启,把当前 init 重新加入InitContainersToStart;否则设置KillPod=true,初始化流程结束。Unknown或创建状态异常:按失败或需重建处理,具体动作仍受重启策略影响。
一旦 computeInitContainerActions 返回未初始化,computePodActions 会直接返回,不再检查普通应用容器。这就是「init 没有完成时,应用容器不会被启动或按普通容器逻辑处理」的源码依据。
2. API 校验决定普通 init 是否能使用探针
validateInitContainers 先判断 init container 是否设置了容器级 restartPolicy: Always:
- 没有设置时,
lifecycle、livenessProbe、readinessProbe、startupProbe都会生成Forbidden错误。 - 设置为
Always时,校验转入 restartable init / sidecar 分支,这些探针字段可以被校验和使用。
API 类型定义也把这条限制写在 PodSpec.InitContainers 的注释中:普通 init container 不允许生命周期动作和三类探针,资源请求则会参与有效 Pod 请求计算。可以对照 staging/src/k8s.io/api/core/v1/types.go 和容器级 restart policy 的定义 types.go。
3. Initialized 和 Ready 是两条状态判断
kubelet 生成 Pod 状态时,会把 init container 状态和应用容器状态分别写入 status.initContainerStatuses、status.containerStatuses。普通 init container 只有成功终止时才被视为完成;这一点在 prober_manager.go 的状态更新逻辑和 generate.go 的 condition 生成逻辑中都能看到。
GeneratePodInitializedCondition 判断所有 init container 是否完成;GeneratePodReadyCondition 则先检查所有应用容器是否 ready,再检查 readinessGates。因此 Initialized=True 不是 Ready=True 的替代字段:它只说明启动前置步骤完成,不代表应用已经能接收流量。
Pod phase 的判断位于 getPhase:普通 init 尚未完成时会增加 pending 计数,失败且不可重启时才返回 PodFailed。这解释了为什么一个 init 反复失败的 Pod 常见状态是 Pending,而 restartPolicy: Never 的同类 Pod 最终是 Failed。
什么时候不应该使用 init container
以下需求通常不适合普通 init container:
- 需要和应用一起长期运行的日志代理、代理服务器、文件同步器或服务网格组件。它们是 sidecar 场景。
- 应用已经可以启动,只是暂时不应该接收流量。使用应用容器的 readiness probe。
- 需要在每次应用容器重启前都执行的动作。普通 init container 不会随着应用容器的单次重启重新执行,应使用应用自身的启动逻辑、生命周期钩子或 sidecar 协作。
- 不能安全重试、且会造成不可逆外部副作用的脚本。先补充幂等和恢复设计,再放进 init 阶段。
- 没有超时边界的远端依赖等待。否则故障会表现为大量长期 Pending 的 Pod,且应用容器日志为空。
反过来,如果动作确实是「应用启动前必须完成」,并且可以重复执行、失败可诊断,那么 init container 往往比把逻辑塞进应用入口脚本更清楚。它把启动门槛直接写进 Pod spec,状态也会通过 initContainerStatuses、事件和 Initialized condition 暴露出来。
总结
普通 init container 是 Pod 启动阶段的串行前置步骤:kubelet 准备 sandbox 后按顺序运行它们,成功退出才继续;失败时按 Pod 的重启策略重试或结束 Pod。它没有 liveness/readiness/startup probe,成功退出本身就是完成信号,流量接收控制仍然属于应用容器 readiness。
使用时抓住四个边界即可:
- 把启动前的一次性准备放进 init,把长期运行的协作进程放进 sidecar。
- 把生成结果放进共享 volume 或明确的外部对象,并让脚本幂等。
- 用
initContainerStatuses、事件和退出码判断初始化问题,不要只看应用容器日志。 - 区分
Initialized、Running和Ready;Pod 已初始化不等于应用已经可以接收流量。
进一步阅读:Init Containers、Configure Pod Initialization、Pod Lifecycle、Liveness, Readiness, and Startup Probes 和 Sidecar Containers。