目录
[为什么需要 Init 容器?](#为什么需要 Init 容器?)
[二、临时容器 Ephemeral Containers](#二、临时容器 Ephemeral Containers)
一、init初始化容器
1、Init容器与普通容器区别(重点)
-
Init容器总是运行到成功为止。####
-
每个Init容器都必须在下一个Init容器启动之前成功完成。(依顺序执行)#####
如果Pod的Init容器失败,Kubenetes会不断地重启该Pod,直到Init容器成功为止。然而,如果Pod对应的restartPolicy为Never,它不会重新启动
2、Init容器的优势
-
可以包含并运行实用工具。出于安全考虑,不建议在应用程序容器镜像中包含这些实用工具的。
-
可以包含实用工具和定制代码来安装,但是不能出现在应用程序容器镜像中。例如:创建镜像没有必要FROM另一个镜像,只需要在安装过程中使用类似sed、awk、python或dig这样的工具。
-
应用程序容器镜像可以分离出创建和部署的角色,而没有必要联合它们构建一个单独的镜像。
-
Init容器使用LinuxNamespace,所以相对应用程序容器来说具有不同的文件系统视图。因此,Init容器能够具有Secret的权限,而应用程序容器则不能。
-
它们必须在应用程序容器启动之前运行完成,而应用程序容器是并行运行的,所以Init容器能够提供一种简单的阻塞或延迟应用程序容器的启动方法,直到满足先决条件。
3、Init容器应用场景
-
等待其他模块Ready:可以用来解决服务之间的依赖问题,比如我们有一个 Web服务,该服务又依赖于另外一个数据库服务,但是在我们启动这个 Web服务的时候我们并不能保证依赖的这个数据库服务就已经启动起来了,所以可能会出现一段时间内 Web服务连接数据库异常。要解决这个问题的话我们就可以在 Web 服务的 Pod 中使用一个init Container,在这个初始化容器中去检查数据库是否已经准备好了,准备好了过后初始化容器就结束退出,然后我们的主容器 Web服务被启动起来,这个时候去连接数据库就不会有问题了。
-
做初始化配置:比如集群里检测所有已经存在的成员节点,为主容器准备好集群的配置信息,这样主容器起来后就能用这个配置信息加入集群。
-
其它场景:如将 pod 注册到一个中央数据库、配置中心等。
核心概念:
Init 容器是在 Pod 的主容器(应用容器)启动之前,必须运行并成功完成 的一个或多个特殊容器。它们的主要目的是为 Pod 的主应用执行初始化任务。
为什么需要 Init 容器?
常规的应用容器通常专注于运行应用程序本身。然而,在应用启动之前,往往需要一些准备工作:
-
等待依赖服务就绪: 例如,等待数据库、消息队列或其他微服务启动完成并可以接受连接。
-
初始化数据/配置:
-
从远端(如 Git 仓库、S3 存储桶)下载配置文件或应用代码。
-
动态生成配置文件(基于环境变量、ConfigMap、Secret)。
-
初始化数据库(运行 Schema 迁移、创建初始用户、加载基础数据)。
-
解密由主容器使用的加密文件(Secret 通常以明文挂载,Init 容器可以处理更复杂的解密逻辑)。
-
-
设置文件权限/所有权: 确保挂载的卷(如共享卷)具有主容器所需正确的权限和所有权。
-
注册/发现: 在服务注册中心注册 Pod(虽然通常由 sidecar 处理,但 Init 容器也可以做)。
-
执行一次性的安装脚本。
-
安全限制: 将敏感操作(如解密密钥)隔离在 Init 容器中,主容器不需要具备这些高权限。
关键特性和工作原理:
-
运行在主容器之前: 这是最核心的特性。Pod 启动时,首先会按顺序运行所有 Init 容器。
-
顺序执行: Init 容器在 Pod 的
spec.initContainers
字段中定义,它们会严格按照定义的顺序依次执行 。只有当前一个 Init 容器**成功退出(Exit Code 0)**后,下一个 Init 容器才会启动。 -
独立运行: 每个 Init 容器都是独立运行的。它们不会并行执行(除非使用特定特性如
restartPolicy
和activeDeadlineSeconds
控制失败重试)。 -
成功退出是必要条件: 所有 定义的 Init 容器都必须运行并成功退出(Exit Code 0)。如果任何一个 Init 容器失败(非零退出码),整个 Pod 会根据其
restartPolicy
重启(默认Always
会重启 Pod,OnFailure
也会重启 Pod 以重试 Init 容器)。只有所有 Init 容器都成功完成,Pod 才会开始启动主容器。 -
资源隔离:
-
镜像: Init 容器可以使用与主容器完全不同的镜像,通常包含初始化所需的工具(如
curl
,wget
,git
,mysql-client
,awscli
,vault
等),而主容器只需要包含运行应用的最小依赖。 -
资源请求/限制: Init 容器可以单独设置
resources.requests
和resources.limits
。这非常重要,因为初始化任务可能需要不同的 CPU/内存资源(可能比主容器多或少)。Kubernetes 调度器在调度 Pod 时,会考虑 所有容器(包括 Init 容器)的资源请求总和。 -
安全上下文: Init 容器可以拥有自己独立的
securityContext
(如runAsUser
,privileged
),允许执行需要更高权限的初始化操作,而主容器可以保持较低权限。
-
-
与主容器共享资源:
-
Volume: Init 容器可以挂载与应用容器相同的 Volume(
emptyDir
,configMap
,secret
,persistentVolumeClaim
等)。这是 Init 容器执行初始化(如下载文件、生成配置)并将结果传递给主容器的关键机制。例如,Init 容器下载配置文件到共享的emptyDir
,主容器启动时从该目录读取配置。 -
网络: Init 容器共享 Pod 的网络命名空间(Network Namespace),它们拥有相同的 IP 地址和网络配置。这意味着 Init 容器可以通过
localhost
访问同一 Pod 内稍后启动的其他容器(虽然通常 Init 容器是为主容器准备环境,而不是与其他容器通信)。
-
-
重启策略:
-
如果 Init 容器失败退出,整个 Pod 会重启(根据
restartPolicy
)。 -
重启后,所有 Init 容器会重新执行 (从头开始按顺序执行)。这意味着 Init 容器中的操作必须是幂等的(多次执行效果相同),因为失败重启后它们会再次运行。
-
-
生命周期钩子: Init 容器可以看作是一种特殊的 Pod 级别的启动前钩子(Pre-Start Hook) ,但它比容器级别的
lifecycle.postStart
钩子更强大、更结构化,因为它支持多个步骤、独立镜像和资源限制。 -
与 Sidecar 的区别:
-
Init 容器: 在主容器启动前运行 ,必须成功退出 。用于一次性的初始化任务。
-
Sidecar 容器: 与主容器并行运行 ,通常在整个 Pod 生命周期内持续运行。用于提供持续的服务,如日志收集、监控代理、服务网格代理等。
-
Init容器使用案例
测试
创建init容器yaml文件
cpp
[root@k8s-master~]# cat init-test.yaml
apiVersion: v1 # API 版本
kind: Pod # 资源类型:Pod
metadata: # 元数据
name: init-demo # Pod 名称
labels: # 标签
demo: init-demo # 标签键值对
spec: # 规格定义
containers: # 主容器列表
- name: init-demo # 主容器名称
image: busybox:1.28 # 容器镜像
imagePullPolicy: IfNotPresent # 镜像拉取策略:如果本地不存在则拉取
command: ['sh', '-c', 'echo The app is running! && sleep 3600'] # 容器启动命令:打印消息并休眠 3600 秒
initContainers: # 初始化容器列表
- name: init-demo1 # 第一个初始化容器名称
image: busybox:1.28 # 容器镜像
imagePullPolicy: IfNotPresent # 镜像拉取策略
command: ['sh', '-c', 'until nslookup myservice; do echo waiting for myservice; sleep 2; done;'] # 命令:持续尝试解析 myservice 的 DNS,直到成功,每次尝试间隔 2 秒并打印等待信息
- name: init-demo2 # 第二个初始化容器名称
image: busybox:1.28 # 容器镜像
imagePullPolicy: IfNotPresent # 镜像拉取策略
command: ['sh', '-c', 'until nslookup mysql; do echo waiting for mysql; sleep 2; done;'] # 命令:持续尝试解析 mysql 的 DNS,直到成功,每次尝试间隔 2 秒并打印等待信息
提交资源清单
cpp
[root@k8s-master pvc]# kubectl apply -f init-test.yaml
pod/init-demo created
查看init容器
这是init容器是没有起来的
cpp
[root@k8s-master pvc]# kubectl get pod init-demo
NAME READY STATUS RESTARTS AGE
init-demo 0/1 Init:0/2 0 87s
查看详细信息
cpp
[root@k8s-master pvc]# kubectl describe pod init-demo
Name: init-demo
Namespace: default
Priority: 0
Service Account: default
Node: k8s-node1/192.168.158.34
Start Time: Fri, 22 Aug 2025 22:12:26 +0800
Labels: demo=init-demo
-----------------
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 102s default-scheduler Successfully assigned default/init-demo to k8s-node1
Normal Pulled 102s kubelet Container image "busybox" already present on machine
Normal Created 101s kubelet Created container init-demo1
Normal Started 101s kubelet Started container init-demo1
事件:
类型 原因 时长 来源 消息
---- ------ ---- ---- -------
Normal Scheduled 102秒 default-scheduler 成功将 default/init-demo Pod 调度到节点 k8s-node1
Normal Pulled 102秒 kubelet 容器镜像 "busybox" 已存在于机器上
Normal Created 101秒 kubelet 已创建容器 init-demo1
Normal Started 101秒 kubelet 已启动容器 init-demo1
查看日志
cpp
[root@k8s-master pvc]# kubectl logs init-demo -c init-demo1
Server: 10.96.0.10
Address: 10.96.0.10:53
** server can't find myservice.default.svc.cluster.local: NXDOMAIN
** server can't find myservice.cluster.local: NXDOMAIN
** server can't find myservice.default.svc.cluster.local: NXDOMAIN
** server can't find myservice.svc.cluster.local: NXDOMAIN
** server can't find myservice.svc.cluster.local: NXDOMAIN
** server can't find myservice.cluster.local: NXDOMAIN
waiting for myservice
Server: 10.96.0.10
Address: 10.96.0.10:53
** server can't find myservice.default.svc.cluster.local: NXDOMAIN
** server can't find myservice.cluster.local: NXDOMAIN
** server can't find myservice.cluster.local: NXDOMAIN
** server can't find myservice.default.svc.cluster.local: NXDOMAIN
** server can't find myservice.svc.cluster.local: NXDOMAIN
** server can't find myservice.svc.cluster.local: NXDOMAIN
翻译
cpp
服务器: 10.96.0.10
地址: 10.96.0.10:53
** 服务器找不到 myservice.default.svc.cluster.local: NXDOMAIN (域名不存在)
** 服务器找不到 myservice.cluster.local: NXDOMAIN (域名不存在)
** 服务器找不到 myservice.default.svc.cluster.local: NXDOMAIN (域名不存在)
** 服务器找不到 myservice.svc.cluster.local: NXDOMAIN (域名不存在)
** 服务器找不到 myservice.svc.cluster.local: NXDOMAIN (域名不存在)
** 服务器找不到 myservice.cluster.local: NXDOMAIN (域名不存在)
正在等待 myservice (waiting for myservice)
服务器: 10.96.0.10
地址: 10.96.0.10:53
所以,创建主容器和初始化容器,没有创建server服务的时候,它会一直重启,直到成功为止
正确流程
编辑yaml文件
cpp
[root@k8s-master01 ~]# cat init-test.yaml
apiVersion: v1
kind: Pod
metadata:
name: init-demo
labels:
demo: init-demo
spec:
containers:
- name: init-demo
image: busybox:1.28
imagePullPolicy: IfNotPresent
command: ['sh', '-c', 'echo The app is running! && sleep 3600']
initContainers:
- name: init-demo1
image: busybox:1.28
imagePullPolicy: IfNotPresent
command: ['sh', '-c', 'until nslookup myservice; do echo waiting for myservice;sleep 2; done;']
- name: init-demo2
image: busybox:1.28
imagePullPolicy: IfNotPresent
command: ['sh', '-c', 'until nslookup mysql; do echo waiting for mysql; sleep 2;done;']
---
apiVersion: v1
kind: Service
metadata:
name: myservice
spec:
ports:
- port: 5566
targetPort: 6655
protocol: TCP
---
apiVersion: v1
kind: Service
metadata:
name: mysql
spec:
ports:
- port: 8899
targetPort: 9988
protocol: TCP
提交资源清单
cpp
[root@k8s-master pvc]# kubectl apply -f init-test.yaml
pod/init-demo created
service/myservice created
service/mysql created
查看pod,正常启用
cpp
[root@k8s-master pvc]# kubectl get pods
NAME READY STATUS RESTARTS AGE
init-demo 0/1 Init:0/2 0 6s
[root@k8s-master pvc]# kubectl get pod init-demo
NAME READY STATUS RESTARTS AGE
init-demo 1/1 Running 0 19s
查看server服务
cpp
[root@k8s-master pvc]# kubectl get service
myservice ClusterIP 10.105.86.95 <none> 5566/TCP 5m32s
mysql ClusterIP 10.97.198.46 <none> 8899/TCP 5m32s
查看初始化容器
cpp
[root@k8s-master pvc]# kubectl logs init-demo -c init-demo1
Server: 10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local
Name: myservice
Address 1: 10.105.86.95 myservice.default.svc.cluster.local
[root@k8s-master pvc]# kubectl logs init-demo -c init-demo2
Server: 10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local
Name: mysql
Address 1: 10.97.198.46 mysql.default.svc.cluster.local
二、临时容器 Ephemeral Containers
1、概述
临时容器与其他容器的不同之处在于,它们缺少对资源或执行的保证 ,并且永远不会自动重启,因此不适用于构建应用程序。临时容器使用与常规容器相同的 Container.Spec字段进行描述,但许多字段是不允许使用的。
-
临时容器没有端口配置,因此像 ports,livenessProbe,readinessProbe 这样的字段是不允许的。
-
Pod 资源分配是不可变的,因此 resources 配置是不允许的。
临时容器是使用 API 中的一种特殊的 ephemeralcontainers处理器进行创建的, 而不是直接添加到 pod.spec段,因此无法使用 kubectl edit来添加一个临时容器。
与常规容器一样,将临时容器添加到 Pod 后,将不能更改或删除临时容器。
2、用途
-
当由于容器崩溃或容器镜像不包含调试工具而导致 kubectl exec 无用时, 临时容器对于交互式故障排查很有用。
-
尤其是,Distroless 镜像 允许用户部署最小的容器镜像,从而减少攻击面并减少故障和漏洞的暴露。 由于 distroless镜像不包含 Shell 或任何的调试工具,因此很难单独使用 kubectl exec 命令进行故障排查。
-
使用临时容器时,启用 进程名称空间共享 很有帮助,可以查看其他容器中的进程。
什么是distroless镜像
K8s distroless 是一个 Kubernetes 发行版,它专注于提供轻量级的 Kubernetes 环境,删除了许多默认的工具和组件,以减小 Kubernetes 的存储空间和资源占用。
K8s distroless 使用 Go 语言编写,并使用原生 Kubernetes API 进行集群管理。它删除了许多 Kubernetes 中的默认工具,例如 kubectl、kube-proxy、kubelet 等,并提供了自己的命令行工具来管理集群。
K8s distroless 的主要特点包括:
-
轻量化:K8s distroless 删除了许多默认的工具和组件,以减小 Kubernetes 的存储空间和资源占用。
-
易于部署:K8s distroless 可以轻松地在各种平台上部署,包括云端、本地和容器环境中。
-
高效能:K8s distroless 使用原生 Kubernetes API 进行集群管理,提供了高效的资源利用和管理。
-
安全性:K8s distroless 提供了高度的安全性,包括自动更新、自动补丁和严格的安全策略。
K8s distroless 适合那些想要使用 Kubernetes 进行容器编排,但不想安装大量默认工具和组件的用户。它提供了一种简单、高效和安全的 Kubernetes 解决方案。
核心概念:
临时容器是一种特殊的容器类型,它允许你将一个容器临时注入 到一个已经运行 的 Pod 中。这个容器与 Pod 中的常规容器(包括 Init 容器)共存,共享 Pod 的资源(如网络、存储卷),但其生命周期是独立的,并且不是为了运行应用程序的主逻辑而设计的 。它的主要目的是进行交互式故障排除和诊断。
为什么需要临时容器?
传统的容器调试方法存在局限性:
-
容器镜像缺少工具: 生产环境的应用容器通常基于精简镜像构建,缺少
curl
,ping
,nslookup
,telnet
,tcpdump
,strace
,dig
等调试工具。 -
无法修改运行中的 Pod: Kubernetes Pod 的规范(
.spec
)在创建后通常是不可变的(除了少数字段如activeDeadlineSeconds
)。你不能直接向一个运行中的 Pod 添加新的常规容器。 -
容器崩溃或无法启动: 如果主容器崩溃或无法启动(例如,因为配置错误、依赖缺失),你无法进入容器内部进行诊断。
-
避免重建 Pod: 重建 Pod 可能导致问题状态丢失(如崩溃瞬间的内存状态)、中断服务或改变调度位置,这对于诊断间歇性问题或生产环境问题非常不利。
临时容器解决了这些问题:
-
运行时注入: 可以在 Pod 运行时动态添加容器。
-
独立镜像: 使用包含所需诊断工具的独立镜像(如
busybox
,nicolaka/netshoot
,ubuntu
)。 -
共享环境: 共享 Pod 的网络命名空间、进程命名空间(可选)和存储卷,让你能在问题发生的实际上下文中进行诊断。
-
不影响主容器: 诊断操作不会影响 Pod 中其他容器的运行状态。
-
临时性: 容器是临时的,当它退出后,其状态不会被保留,也不会被 Pod 重启。它不会成为 Pod 规范的一部分。
关键特性和工作原理:
-
不可变 Pod Spec 的例外: 临时容器是唯一可以在 Pod 创建后添加到
pod.spec
中的容器类型。这是通过一个特殊的 API 端点实现的。 -
API 对象: 临时容器在 API 中被表示为
EphemeralContainer
类型,嵌套在 Pod 的ephemeralContainers
字段下。 -
添加方式: 使用
kubectl debug
命令(推荐)或直接调用 Kubernetes API 添加临时容器。 -
共享资源:
-
网络 (Network Namespace): 默认共享 Pod 的网络栈。临时容器拥有相同的 IP 地址,可以通过
localhost
访问 Pod 内的其他容器。 -
进程 (Process Namespace Sharing - 可选): 通过
shareProcessNamespace: true
在 Pod 级别启用(需要在 Pod 创建时设置)。启用后,临时容器可以看到 Pod 中所有其他容器的进程。这对于诊断进程相关问题(如查看进程列表、发送信号)至关重要。 -
存储卷 (Volumes): 可以挂载 Pod 中定义的任何存储卷(
emptyDir
,configMap
,secret
,persistentVolumeClaim
等),用于检查日志文件、配置文件或数据文件。 -
资源限制: 可以单独设置临时容器的资源请求和限制 (
resources.requests/limits
)。
-
-
生命周期:
-
临时容器由用户显式启动(例如,通过
kubectl debug
)。 -
当临时容器中的主进程退出时,容器就结束了。
-
临时容器不会自动重启 (无论 Pod 的
restartPolicy
是什么)。 -
临时容器的状态(日志、退出码)会被保留,直到 Pod 被删除。
-
-
与
kubectl exec
的区别:-
kubectl exec
: 在现有容器中执行命令。要求容器是运行状态且包含所需的 Shell/命令。 -
kubectl debug
(使用临时容器):向 Pod 中添加一个全新的容器(使用你选择的工具镜像),然后通常在该新容器中执行命令(如启动 Shell)。即使主容器崩溃或没有 Shell 也能使用。
-
临时容器案例
创建一个部署nginx的pod
cpp
[root@k8s-master01 ~]# cat pod-nginx.yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx-test
namespace: default
labels:
app: nginx
spec:
containers:
- name: nginx
ports:
- containerPort: 80
image: nginx
imagePullPolicy: IfNotPresent
提交资源清单
cpp
[root@k8s-master pvc]# kubectl apply -f pod-nginx.yaml
pod/nginx-test created
查看pod
cpp
[root@k8s-master pvc]# kubectl get pod nginx-test
NAME READY STATUS RESTARTS AGE
nginx-test 1/1 Running 0 53s
创建临时容器
cpp
#nginx-test是pod名 busybox:1.28是镜像名 nginx是容器名
[root@k8s-master ~]# kubectl debug -it nginx-test --image=busybox:1.28 --target=nginx
Targeting container "nginx". If you don't see processes from this container it may be because the container runtime doesn't support this feature.
Defaulting debug container name to debugger-dcppg.
If you don't see a command prompt, try pressing enter.
/ #
临时容器里可执行多种命令

查看nginx-test这个pod是否已经有临时容器
cpp
[root@k8s-master pvc]# kubectl describe pod nginx-test
cpp
ephemeralContainers是临时容器定义
debugger-dcppg 是容器名称


cpp
事件:
类型 原因 时长 来源 消息
---- ------ ---- ---- -------
Normal Scheduled 5分51秒 default-scheduler 成功将 default/nginx-test Pod 调度到节点 k8s-node2
Normal Pulled 5分38秒 kubelet 容器镜像 "nginx" 已存在于机器上
Normal Created 5分38秒 kubelet 已创建容器 nginx
Normal Started 5分38秒 kubelet 已启动容器 nginx
Normal Pulled 2分35秒 kubelet 容器镜像 "busybox:1.28" 已存在于机器上
Normal Created 2分35秒 kubelet 已创建容器 debugger-dcppg
Normal Started 2分35秒 kubelet 已启动容器 debugger-dcppg
查看临时容器的详细信息
执行下方命令行
cpp
#可查看详细信息
kubectl get pod pod名 -o yaml
cpp
readOnly: true # 只读模式
dnsPolicy: ClusterFirst # DNS 策略:优先使用集群 DNS
enableServiceLinks: true # 启用服务链接(将服务信息注入为环境变量)
ephemeralContainers: # 临时容器定义
- image: busybox:1.28 # 容器镜像
imagePullPolicy: IfNotPresent # 镜像拉取策略:如果本地不存在则拉取
name: debugger-dcppg # 容器名称
resources: {} # 资源请求与限制(此处为空,使用默认值)
stdin: true # 开启标准输入 (STDIN)
targetContainerName: nginx # 目标容器名称(可能用于进程命名空间共享等)
terminationMessagePath: /dev/termination-log # 终止消息写入路径
terminationMessagePolicy: File # 终止消息策略:从文件读取
tty: true # 分配伪终端 (TTY)
nodeName: k8s-node2 # 节点名称:指定 Pod 调度到 k8s-node2 节点
preemptionPolicy: PreemptLowerPriority # 抢占策略:抢占较低优先级 Pod
priority: 0 # Pod 优先级(数值)
restartPolicy: Always # 重启策略:容器退出时总是重启
schedulerName: default-scheduler # 调度器名称:使用默认调度器
securityContext: {} # 安全上下文配置(此处为空,使用默认值)
serviceAccount: default # 服务账户(已弃用,推荐使用 serviceAccountName)
serviceAccountName: default # 服务账户名称:使用 default 服务账户

cpp
关键点说明:
临时容器 (ephemeralContainers):
定义了一个名为 debugger-dcppg 的临时容器。
使用 busybox:1.28 镜像。
开启了 stdin (标准输入) 和 tty (伪终端),表明这是一个用于交互式调试的容器(例如,通过 kubectl debug 命令启动 shell)。
指定了 targetContainerName: nginx,这通常表示该临时容器希望与名为 nginx 的容器共享某些资源(最典型的是进程命名空间 shareProcessNamespace,但该配置在 Pod 级别定义,此处仅指定目标)。
配置了终止消息的写入路径和读取策略。
定义了一个用于调试的临时容器 debugger-dcppg,该容器使用 busybox 镜像并配置了交互式终端 (stdin: true, tty: true),且目标指向 nginx 容器。Pod 使用默认的服务账户 (default),并配置了在容器退出时总是重启的策略 (restartPolicy: Always)。