Init 容器
此页面提供了关于 init 容器的概述:这些是在 Pod 中在应用程序容器之前运行的专用容器。Init 容器可以包含应用程序镜像中不存在的实用程序或设置脚本。
您可以在 Pod 规范的 containers 数组(描述应用程序容器)旁边指定 init 容器。
在 Kubernetes 中,sidecar 容器 是在主应用程序容器启动之前启动并持续运行的容器。本文介绍的是 init 容器:在 Pod 初始化期间运行至完成的容器。
理解 init 容器
一个 Pod 可以有多个运行应用程序的容器,但它也可以有一个或多个 init 容器,这些容器在应用程序容器启动之前运行。
Init 容器与常规容器完全相同,除了
- Init 容器始终运行至完成。
- 每个 init 容器必须成功完成,下一个才能开始。
如果 Pod 的 init 容器失败,kubelet 会反复重启该 init 容器,直到它成功为止。但是,如果 Pod 具有 restartPolicy 设置为 Never,并且 init 容器在 Pod 启动期间失败,Kubernetes 会将整个 Pod 视为失败。
要在 Pod 中指定 init 容器,请将 initContainers 字段添加到 Pod 规范 中,作为 container 项目的数组(类似于 app containers 字段及其内容)。有关更多详细信息,请参阅 API 参考中的 Container。
init 容器的状态在 .status.initContainerStatuses 字段中作为容器状态的数组返回(类似于 .status.containerStatuses 字段)。
与常规容器的区别
Init 容器支持应用程序容器的所有字段和功能,包括资源限制、卷 和安全设置。但是,init 容器的资源请求和限制的处理方式不同,如 容器内的资源共享 中所述。
常规 init 容器(换句话说:不包括 sidecar 容器)不支持 lifecycle、livenessProbe、readinessProbe 或 startupProbe 字段。Init 容器必须运行至完成,然后 Pod 才能准备就绪;sidecar 容器在 Pod 的生命周期内持续运行,并且确实支持某些探测。有关更多详细信息,请参阅 sidecar 容器。
如果您为 Pod 指定了多个 init 容器,kubelet 会按 Pod 规范中出现的顺序依次运行每个 init 容器。每个 init 容器必须成功完成,下一个才能运行。当所有 init 容器都运行至完成时,kubelet 会初始化 Pod 的应用程序容器并像往常一样运行它们。
与 sidecar 容器的区别
Init 容器运行并完成其任务,然后才启动主应用程序容器。与 sidecar 容器 不同,init 容器不会持续与主容器一起运行。
Init 容器按顺序运行至完成,并且只有在所有 init 容器都成功完成之后,才会启动主容器。
init 容器不支持 lifecycle、livenessProbe、readinessProbe 或 startupProbe,而 sidecar 容器支持所有这些 探测 以控制其生命周期。
Init 容器与主应用程序容器共享相同的资源(CPU、内存、网络),但它们不直接相互交互。但是,它们可以使用共享卷进行数据交换。
使用 init 容器
由于 init 容器具有与应用程序容器不同的镜像,因此它们在启动相关代码方面具有一些优势
- Init 容器可以包含应用程序镜像中不存在的实用程序或自定义代码,用于设置。例如,无需使用
sed、awk、python或dig等工具进行设置,就无需从另一个镜像构建镜像。 - 应用程序镜像构建者和部署者角色可以独立工作,而无需共同构建单个应用程序镜像。
- Init 容器可以使用与同一 Pod 中的应用程序容器不同的文件系统视图。因此,可以授予它们访问 Secrets,而应用程序容器无法访问。
- 由于 init 容器在任何应用程序容器启动之前运行至完成,因此 init 容器提供了一种机制,可以阻止或延迟应用程序容器启动,直到满足一组先决条件。满足先决条件后,Pod 中的所有应用程序容器都可以并行启动。
- Init 容器可以安全地运行会使应用程序容器镜像不那么安全的实用程序或自定义代码。通过将不必要的工具分开,您可以限制应用程序容器镜像的攻击面。
示例
以下是一些使用 init 容器的想法
使用 shell 一行命令等待 Service 创建,例如
for i in {1..100}; do sleep 1; if nslookup myservice; then exit 0; fi; done; exit 1使用来自 downward API 的命令将此 Pod 注册到远程服务器,例如
curl -X POST http://$MANAGEMENT_SERVICE_HOST:$MANAGEMENT_SERVICE_PORT/register -d 'instance=$(<POD_NAME>)&ip=$(<POD_IP>)'使用命令等待一段时间,然后再启动应用程序容器,例如
sleep 60将 Git 存储库克隆到 Volume
将值放入配置文件,并运行模板工具以动态生成主应用程序容器的配置文件。例如,将
POD_IP值放入配置中,并使用 Jinja 生成主应用程序配置文件。
正在使用 init 容器
此示例定义了一个简单的 Pod,其中包含两个 init 容器。第一个等待 myservice,第二个等待 mydb。一旦两个 init 容器都完成,Pod 就会从其 spec 部分运行应用程序容器。
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
labels:
app.kubernetes.io/name: MyApp
spec:
containers:
- name: myapp-container
image: busybox:1.28
command: ['sh', '-c', 'echo The app is running! && sleep 3600']
initContainers:
- name: init-myservice
image: busybox:1.28
command: ['sh', '-c', "until nslookup myservice.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for myservice; sleep 2; done"]
- name: init-mydb
image: busybox:1.28
command: ['sh', '-c', "until nslookup mydb.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for mydb; sleep 2; done"]
您可以通过运行以下命令来启动此 Pod
kubectl apply -f myapp.yaml
输出类似于此
pod/myapp-pod created
并使用以下命令检查其状态
kubectl get -f myapp.yaml
输出类似于此
NAME READY STATUS RESTARTS AGE
myapp-pod 0/1 Init:0/2 0 6m
或者获取更多详细信息
kubectl describe -f myapp.yaml
输出类似于此
Name: myapp-pod
Namespace: default
[...]
Labels: app.kubernetes.io/name=MyApp
Status: Pending
[...]
Init Containers:
init-myservice:
[...]
State: Running
[...]
init-mydb:
[...]
State: Waiting
Reason: PodInitializing
Ready: False
[...]
Containers:
myapp-container:
[...]
State: Waiting
Reason: PodInitializing
Ready: False
[...]
Events:
FirstSeen LastSeen Count From SubObjectPath Type Reason Message
--------- -------- ----- ---- ------------- -------- ------ -------
16s 16s 1 {default-scheduler } Normal Scheduled Successfully assigned myapp-pod to 172.17.4.201
16s 16s 1 {kubelet 172.17.4.201} spec.initContainers{init-myservice} Normal Pulling pulling image "busybox"
13s 13s 1 {kubelet 172.17.4.201} spec.initContainers{init-myservice} Normal Pulled Successfully pulled image "busybox"
13s 13s 1 {kubelet 172.17.4.201} spec.initContainers{init-myservice} Normal Created Created container init-myservice
13s 13s 1 {kubelet 172.17.4.201} spec.initContainers{init-myservice} Normal Started Started container init-myservice
要查看此 Pod 中 init 容器的日志,请运行
kubectl logs myapp-pod -c init-myservice # Inspect the first init container
kubectl logs myapp-pod -c init-mydb # Inspect the second init container
此时,这些 init 容器将等待发现 Services,名为 mydb 和 myservice。
以下是您可以用来使这些 Services 出现的配置
---
apiVersion: v1
kind: Service
metadata:
name: myservice
spec:
ports:
- protocol: TCP
port: 80
targetPort: 9376
---
apiVersion: v1
kind: Service
metadata:
name: mydb
spec:
ports:
- protocol: TCP
port: 80
targetPort: 9377
要创建 mydb 和 myservice 服务
kubectl apply -f services.yaml
输出类似于此
service/myservice created
service/mydb created
然后您将看到这些 init 容器完成,并且 myapp-pod Pod 移动到 Running 状态
kubectl get -f myapp.yaml
输出类似于此
NAME READY STATUS RESTARTS AGE
myapp-pod 1/1 Running 0 9m
这个简单的示例应该为您提供一些灵感,以便创建您自己的 init 容器。下一步 包含指向更详细示例的链接。
详细行为
在 Pod 启动期间,kubelet 会延迟运行 init 容器,直到网络和存储准备就绪。然后 kubelet 会按 Pod 规范中出现的顺序运行 Pod 的 init 容器。
每个 init 容器必须成功退出后,下一个容器才会启动。如果容器由于运行时问题而无法启动或以失败状态退出,则会根据 Pod 的 restartPolicy 进行重试。但是,如果 Pod 的 restartPolicy 设置为 Always,则 init 容器将使用 restartPolicy OnFailure。
Pod 在所有 init 容器都成功完成之前,无法进入 Ready 状态。init 容器上的端口不会在 Service 下聚合。正在初始化的 Pod 处于 Pending 状态,但应该将条件 Initialized 设置为 false。
如果 Pod 重启,或被重启,所有 init 容器都必须再次执行。
对 init 容器 spec 的更改仅限于容器镜像字段。直接更改 init 容器的 image 字段不会重启 Pod 或触发其重新创建。如果 Pod 尚未启动,则该更改可能会影响 Pod 的启动方式。
对于 pod 模板,通常可以更改 init 容器的任何字段;进行更改的影响取决于 pod 模板的使用位置。
由于 init 容器可以被重启、重试或重新执行,因此 init 容器代码应该是幂等的。特别是,写入任何 emptyDir 卷的代码应该为可能已经存在输出文件做好准备。
Init 容器具有 app 容器的所有字段。但是,Kubernetes 禁止使用 readinessProbe,因为 init 容器无法定义与完成不同的就绪状态。此项在验证期间强制执行。
使用 Pod 上的 activeDeadlineSeconds 来防止 init 容器永远失败。活动期限包括 init 容器。但是,建议仅在团队将应用程序作为 Job 部署时才使用 activeDeadlineSeconds,因为 activeDeadlineSeconds 即使在 initContainer 完成后也会产生影响。如果设置了,已经正确运行的 Pod 将被 activeDeadlineSeconds 杀死。
Pod 中每个 app 容器和 init 容器的名称必须唯一;对于任何使用相同名称的容器,将抛出验证错误。
容器内的资源共享
鉴于 init、sidecar 和 app 容器的执行顺序,以下资源使用规则适用
- 所有 init 容器上定义的任何特定资源请求或限制的最高值是有效的 init 请求/限制。如果任何资源没有指定资源限制,则将其视为最高限制。
- Pod 的有效请求/限制 是以下两者中的较高者:
- 所有 app 容器对资源的请求/限制之和
- 资源的有效 init 请求/限制
- 调度基于有效的请求/限制进行,这意味着 init 容器可以为初始化保留在 Pod 生命周期期间未使用的资源。
- Pod 的有效 QoS 层级 的 QoS (服务质量) 层级与 init 容器和 app 容器相同。
基于有效的 Pod 请求和限制应用配额和限制。
Init 容器和 Linux cgroups
在 Linux 上,Pod 级别控制组 (cgroups) 的资源分配基于有效的 Pod 请求和限制,与调度器相同。
Pod 重启原因
Pod 可以重启,导致 init 容器重新执行,原因如下:
- Pod 基础设施容器被重启。这不常见,必须由具有节点 root 访问权限的人员完成。
- 当
restartPolicy设置为 Always 时,Pod 中的所有容器都被终止,强制重启,并且由于 垃圾回收,init 容器完成记录丢失。
当 init 容器镜像更改时,或者由于垃圾回收导致 init 容器完成记录丢失时,Pod 将不会重启。这适用于 Kubernetes v1.20 及更高版本。如果您使用的是早期版本的 Kubernetes,请参阅您正在使用的版本的文档。
接下来
了解更多信息
- 创建具有 init 容器的 Pod.
- 调试 init 容器.
- 关于 kubelet 和 kubectl 的概述。
- 探测类型:liveness、readiness、startup probe。
- Sidecar 容器.