初始化容器
此页面提供了对 init 容器的概述:在 Pod 中的应用程序容器之前运行的专用容器。init 容器可以包含应用程序映像中不存在的实用程序或设置脚本。
您可以在 Pod 规范中与 containers
数组(描述应用程序容器)一起指定 init 容器。
在 Kubernetes 中,边车容器 是在主应用程序容器之前启动并继续运行的容器。本文档介绍的是 init 容器:在 Pod 初始化期间运行至完成的容器。
了解 init 容器
一个 Pod 可以拥有多个运行应用程序的容器,但它也可以拥有一个或多个 init 容器,它们在应用程序容器启动之前运行。
init 容器与普通容器完全相同,除了
- init 容器始终运行至完成。
- 每个 init 容器必须成功完成才能启动下一个容器。
如果 Pod 的 init 容器失败,kubelet 会重复重启该 init 容器,直到它成功。但是,如果 Pod 的 restartPolicy
为 Never,并且 init 容器在该 Pod 启动期间失败,则 Kubernetes 会将整个 Pod 视为失败。
要为 Pod 指定 init 容器,请将 initContainers
字段添加到 Pod 规范 中,作为 container
项目的数组(类似于应用程序 containers
字段及其内容)。有关详细信息,请参见 API 参考中的 Container。
init 容器的状态以 .status.initContainerStatuses
字段的形式返回,作为容器状态的数组(类似于 .status.containerStatuses
字段)。
与普通容器的区别
init 容器支持应用程序容器的所有字段和功能,包括资源限制、卷 和安全设置。但是,init 容器的资源请求和限制的处理方式不同,如 容器内的资源共享 中所述。
普通 init 容器(换句话说:不包括边车容器)不支持 lifecycle
、livenessProbe
、readinessProbe
或 startupProbe
字段。init 容器必须运行至完成,Pod 才能变为就绪状态;边车容器在 Pod 的生命周期内继续运行,并且确实支持一些探针。有关边车容器的更多详细信息,请参见 边车容器。
如果为 Pod 指定了多个 init 容器,kubelet 会按顺序运行每个 init 容器。每个 init 容器必须成功才能运行下一个容器。当所有 init 容器都运行至完成时,kubelet 会初始化 Pod 的应用程序容器并照常运行它们。
与边车容器的区别
init 容器在主应用程序容器启动之前运行并完成其任务。与 边车容器 不同,init 容器不会与主容器一起持续运行。
init 容器按顺序运行至完成,并且主容器只有在所有 init 容器都成功完成之后才能启动。
init 容器不支持 lifecycle
、livenessProbe
、readinessProbe
或 startupProbe
,而边车容器支持所有这些 探针 来控制其生命周期。
init 容器与主应用程序容器共享相同的资源(CPU、内存、网络),但不会直接与它们交互。但是,它们可以使用共享卷来进行数据交换。
使用 init 容器
由于 init 容器具有与应用程序容器不同的映像,因此它们在与启动相关的代码方面具有一些优势
- init 容器可以包含应用程序映像中不存在的用于设置的实用程序或自定义代码。例如,无需将映像
FROM
另一个映像,只需在设置期间使用sed
、awk
、python
或dig
等工具。 - 应用程序映像构建器和部署器角色可以独立工作,而无需共同构建单个应用程序映像。
- init 容器可以使用与同一 Pod 中的应用程序容器不同的文件系统视图运行。因此,可以授予它们访问应用程序容器无法访问的 秘密。
- 由于 init 容器在任何应用程序容器启动之前都运行至完成,因此 init 容器提供了一种机制,可以阻止或延迟应用程序容器启动,直到满足一组先决条件。一旦满足先决条件,Pod 中的所有应用程序容器就可以并行启动。
- init 容器可以安全地运行原本会降低应用程序容器映像安全性的实用程序或自定义代码。通过将不必要的工具分开,您可以限制应用程序容器映像的攻击面。
示例
以下是使用 init 容器的一些想法
等待 服务 创建,使用类似于以下的 shell 单行命令
for i in {1..100}; do sleep 1; if nslookup myservice; then exit 0; fi; done; exit 1
使用类似于以下的命令将此 Pod 注册到远程服务器
curl -X POST http://$MANAGEMENT_SERVICE_HOST:$MANAGEMENT_SERVICE_PORT/register -d 'instance=$(<POD_NAME>)&ip=$(<POD_IP>)'
在启动应用程序容器之前等待一段时间,使用类似于以下的命令
sleep 60
将 Git 存储库克隆到 卷 中
将值放入配置文件,并运行模板工具以动态生成主应用程序容器的配置文件。例如,将
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 容器将等待发现名为 mydb
和 myservice
的 服务。
以下配置可用于使这些服务出现
---
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 容器上的端口不会在服务下聚合。正在初始化的 Pod 处于 Pending
状态,但应将 Initialized
条件设置为 false。
如果 Pod 重启 或被重启,则所有 init 容器都必须重新执行。
对 init 容器规范的更改仅限于容器映像字段。更改 init 容器映像字段等同于重启 Pod。
由于 init 容器可以重启、重试或重新执行,因此 init 容器代码应是幂等的。特别是,写入 EmptyDirs
上文件的代码应做好已经存在输出文件的准备。
init 容器具有应用程序容器的所有字段。但是,Kubernetes 禁止使用 readinessProbe
,因为 init 容器无法定义与完成不同的就绪状态。这在验证期间强制执行。
使用 Pod 上的 activeDeadlineSeconds
来防止 init 容器无限期地失败。活动截止日期包括 init 容器。但是,建议仅在团队将应用程序部署为作业时使用 activeDeadlineSeconds
,因为 activeDeadlineSeconds
即使在 initContainer 完成后也会生效。如果设置了 activeDeadlineSeconds
,则已经正确运行的 Pod 将会被杀死。
Pod 中每个应用程序和 init 容器的名称必须是唯一的;如果任何容器与其他容器共享名称,则会抛出验证错误。
容器内的资源共享
鉴于 init、边车和应用程序容器的执行顺序,资源使用将适用以下规则
- 所有初始化容器上定义的任何特定资源请求或限制中的最高者是有效初始化请求/限制。如果任何资源没有指定资源限制,则视为最高限制。
- Pod 的有效请求/限制对于某个资源来说是以下两者中较高的:
- 所有应用程序容器对该资源的请求/限制之和
- 该资源的有效初始化请求/限制
- 调度是基于有效的请求/限制进行的,这意味着初始化容器可以为初始化保留在 Pod 生命周期中未使用的资源。
- Pod 的有效 QoS 等级的 QoS (服务质量) 等级与初始化容器和应用程序容器的 QoS 等级相同。
配额和限制是根据有效的 Pod 请求和限制应用的。
初始化容器和 Linux cgroups
在 Linux 上,Pod 级控制组 (cgroups) 的资源分配是根据有效的 Pod 请求和限制进行的,与调度程序相同。
Pod 重启原因
Pod 可能会重启,导致重新执行初始化容器,原因如下:
- Pod 基础设施容器重新启动。这并不常见,需要具有节点 root 访问权限的人员才能执行。
- 当 Pod 中的所有容器在
restartPolicy
设置为 Always 时终止,强制重启,并且初始化容器完成记录由于垃圾回收而丢失。
当初始化容器镜像更改或初始化容器完成记录由于垃圾回收而丢失时,Pod 不会重新启动。这适用于 Kubernetes v1.20 及更高版本。如果您使用的是更早版本的 Kubernetes,请参阅您所用版本的文档。
下一步
了解以下内容的更多信息:
- 创建包含初始化容器的 Pod.
- 调试初始化容器.
- 对kubelet 和 kubectl 的概述。
- 探针类型:存活探针、就绪探针、启动探针。
- Sidecar 容器.