您现在的位置是:首页 >技术交流 >【云原生|Kubernetes】08-Pod中的Init容器网站首页技术交流
【云原生|Kubernetes】08-Pod中的Init容器
【云原生Kubernetes】08-Pod中的Init容器
文章目录
简介
Init 容器是一种特殊容器,在 Pod内的应用容器启动之前运行。Init 容器可以包括一些应用镜像中不存在的实用工具和安装脚本。
你可以在 Pod 的spec中与用来描述应用容器的
containers
数组平行的位置指定 Init 容器。containers: - name: myapp-container image: busybox:1.28 initContainers: - name: init-myservice image: busybox:1.28
- Init 容器的状态在
status.initContainerStatuses
字段中以容器状态数组的格式返回 (类似status.containerStatuses
字段)。
理解 Init 容器
- 每个 Pod中可以包含多个容器, 应用运行在这些容器里面,同时 Pod 也可以有一个或多个先于应用容器启动的 Init 容器。
Init 容器与普通的容器非常像,除了如下两点:
- 它们总是运行到完成。
- 每个都必须在下一个启动之前成功完成。
-
如果 Pod 的 Init 容器失败,kubelet 会不断地重启该 Init 容器直到该容器成功为止。 然而,如果 Pod 对应的
restartPolicy
值为 “Never”,并且 Pod 的 Init 容器失败, 则 Kubernetes 会将整个 Pod 状态设置为失败。 -
如果为一个 Pod 指定了多个 Init 容器,这些容器会按顺序逐个运行。 每个 Init 容器必须运行成功,下一个才能够运行。当所有的 Init 容器运行完成时, Kubernetes 才会为 Pod 初始化应用容器并像平常一样运行。
与普通容器的不同之处:
- Init 容器支持应用容器的全部字段和特性,包括资源限制、数据卷和安全设置。 然而,Init 容器对资源请求和限制的处理稍有不同;
- Init 容器不支持
lifecycle
、livenessProbe
、readinessProbe
和startupProbe
, 因为它们必须在 Pod 就绪之前运行完成。
Init容器使用场景
因为 Init 容器具有与应用容器分离的单独镜像,其启动相关代码具有如下优势:
-
Init 容器可以包含一些安装过程中应用容器中不存在的实用工具或个性化代码。 例如,没有必要仅为了在安装过程中使用类似
sed
、awk
、python
或dig
这样的工具而去FROM
一个镜像来生成一个新的镜像。 -
应用镜像的创建者和部署者可以各自独立工作,而没有必要联合构建一个单独的应用镜像。
-
与同一 Pod 中的多个应用容器相比,Init 容器能以不同的文件系统视图运行。因此,Init 容器可以被赋予访问应用容器不能访问的 Secret 的权限。
-
由于 Init 容器必须在应用容器启动之前运行完成,因此 Init 容器提供了一种机制来阻塞或延迟应用容器的启动,直到满足了一组先决条件。 一旦前置条件满足,Pod 内的所有的应用容器会并行启动。
-
Init 容器可以安全地运行实用程序或自定义代码,而在其他方式下运行这些实用程序或自定义代码可能会降低应用容器镜像的安全性。 通过将不必要的工具分开,你可以限制应用容器镜像的被攻击范围。
创建包含Init容器的Pod
- 应用容器和 Init 容器共享了一个卷。Init 容器将共享卷挂载到了
/work-dir
目录,应用容器将共享卷挂载到了/usr/share/nginx/html
目录。 Init 容器执行完下载index.html的指令后就终止;实现nginx Pod的展示文件,在启动前自动在线下载。
apiVersion: v1
kind: Pod
metadata:
name: init-demo
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
volumeMounts:
- name: workdir
mountPath: /usr/share/nginx/html
# 这些容器在 Pod 初始化期间运行
initContainers:
- name: install
image: busybox:1.28
command:
- wget
- "-O"
- "/work-dir/index.html"
- http://info.cern.ch
volumeMounts:
- name: workdir
mountPath: "/work-dir"
dnsPolicy: Default
volumes:
- name: workdir
emptyDir: {}
- Init 容器将共享卷挂载到了
/work-dir
目录,应用容器将共享卷挂载到了/usr/share/nginx/html
目录。 Init 容器执行完下面的命令就终止:wget -O /work-dir/index.html http://info.cern.ch
- 请注意 Init 容器在 nginx 服务器的根目录写入
index.html
。- 创建Pod
kubectl apply -f init-containers.yaml
- 检查 init-demo Pod运行正常:
[root@master pod]# kubectl get pods init-demo NAME READY STATUS RESTARTS AGE init-demo 1/1 Running 0 7m34s [root@master pod]#
- 查看Pod中initcontainer的日志
[root@master pod]# kubectl logs init-demo -c install Connecting to info.cern.ch (188.184.21.108:80) index.html 100% |*******************************| 646 0:00:00 ETA [root@master pod]#
- 通过 shell 进入 init-demo Pod 中的 nginx 容器,在 shell 中,发送个 GET 请求到 nginx 服务器,结果表明 nginx 正在为 Init 容器编写的 web 页面服务:
[root@master pod]# kubectl exec -it init-demo -- /bin/bash Defaulted container "nginx" out of: nginx, install (init) root@init-demo:/# root@init-demo:/# curl localhost <html><head></head><body><header> <title>http://info.cern.ch</title> </header> <h1>http://info.cern.ch - home of the first website</h1> <p>From here you can:</p> <ul> <li><a href="http://info.cern.ch/hypertext/WWW/TheProject.html">Browse the first website</a></li> <li><a href="http://line-mode.cern.ch/www/hypertext/WWW/TheProject.html">Browse the first website using the line-mode browser simulator</a></li> <li><a href="http://home.web.cern.ch/topics/birth-web">Learn about the birth of the web</a></li> <li><a href="http://home.web.cern.ch/about">Learn about CERN, the physics laboratory where the web was born</a></li> </ul> </body></html> root@init-demo:/# exit
- 定义了一个具有 2 个 Init 容器的简单 Pod。 第一个等待
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 two-init.yaml
- 检查其Pod状态:
[root@master pod]# kubectl get pods myapp-pod
NAME READY STATUS RESTARTS AGE
myapp-pod 0/1 Init:0/2 0 2s
[root@master pod]#
- 如需查看 Pod 内 Init 容器的日志,请执行:
kubectl logs myapp-pod -c init-myservice # 查看第一个 Init 容器
kubectl logs myapp-pod -c init-mydb # 查看第二个 Init 容器
创建这些 Service 的配置文件
---
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
- 这样你将能看到这些 Init 容器执行完毕,随后
my-app
的 Pod 进入Running
状态:
[root@master pod]# kubectl get pods myapp-pod
NAME READY STATUS RESTARTS AGE
myapp-pod 1/1 Running 0 15m
[root@master pod]#
具体行为
- 在 Pod 启动过程中,每个 Init 容器会在网络和数据卷初始化(即pause容器)之后按顺序启动。 kubelet 运行依据 Init 容器在 Pod 规约中的出现顺序依次运行之。
- 每个 Init 容器成功退出后才会启动下一个 Init 容器。 如果某容器因为容器运行时的原因无法启动,或以错误状态退出,kubelet 会根据 Pod 的
restartPolicy
策略进行重试。 然而,如果 Pod 的restartPolicy
设置为 “Always”,Init 容器失败时会使用restartPolicy
的 “OnFailure” 策略。- 在所有的 Init 容器没有成功之前,Pod 将不会变成
Ready
状态。 Init 容器的端口将不会在 Service 中进行聚集。正在初始化中的 Pod 处于Pending
状态, 但会将状况Initializing
设置为 false。- 如果 Pod 重启,所有 Init 容器必须重新执行。对 Init 容器规约的修改仅限于容器的
image
字段。 更改 Init 容器的image
字段,等同于重启该 Pod。- 因为 Init 容器可能会被重启、重试或者重新执行,所以 Init 容器的代码应该是幂等的。 特别地,基于
emptyDirs
写文件的代码,应该对输出文件可能已经存在做好准备。- nit 容器具有应用容器的所有字段。然而 Kubernetes 禁止使用
readinessProbe
, 因为 Init 容器不能定义不同于完成态(Completion)的就绪态(Readiness)。 Kubernetes 会在校验时强制执行此检查。- 在 Pod 上使用
activeDeadlineSeconds
和在容器上使用livenessProbe
可以避免 Init 容器一直重复失败。activeDeadlineSeconds
时间包含了 Init 容器启动的时间。 但建议仅在团队将其应用程序部署为 Job 时才使用activeDeadlineSeconds
, 因为activeDeadlineSeconds
在 Init 容器结束后仍有效果。 如果你设置了activeDeadlineSeconds
,已经在正常运行的 Pod 会被杀死。- 在 Pod 中的每个应用容器和 Init 容器的名称必须唯一; 与任何其它容器共享同一个名称,会在校验时抛出错误。
资源
在给定的 Init 容器执行顺序下,资源使用适用于如下规则:
- 所有 Init 容器上定义的任何特定资源的 limit 或 request 的最大值,作为 Pod 有效初始 request/limit。 如果任何资源没有指定资源限制,这被视为最高限制。
- Pod 对资源的有效 limit/request是如下两者中的较大者:
- 所有应用容器对某个资源的 limit/request 之和;
- 对某个资源的有效初始 limit/request。
- 基于有效 limit/request 完成调度,这意味着 Init 容器能够为初始化过程预留资源, 这些资源在 Pod 生命周期过程中并没有被使用。
- Pod 的 有效 QoS 层,与 Init 容器和应用容器的一样。
Pod 重启的原因
Pod 重启会导致 Init 容器重新执行,主要有如下几个原因:
- Pod 的基础设施容器 (译者注:如
pause
容器) 被重启。这种情况不多见, 必须由具备 root 权限访问节点的人员来完成。- 当
restartPolicy
设置为Always
,Pod 中所有容器会终止而强制重启。 由于垃圾收集机制的原因,Init 容器的完成记录将会丢失。当 Init 容器的镜像发生改变或者 Init 容器的完成记录因为垃圾收集等原因被丢失时, Pod 不会被重启。这一行为适用于 Kubernetes v1.20 及更新版本。
调试Init容器
- 检查 Init 容器的状态
- 显示你的 Pod 的状态
kubectl get pod <pod-name>
如,状态 Init:1/2 表明两个 Init 容器中的一个已经成功完成:
NAME READY STATUS RESTARTS AGE
<pod-name> 0/1 Init:1/2 0 7s
- 获取 Init 容器详情
- Init 容器运行的更多详情
kubectl describe pod <pod-name>
- 与 Pod 名称一起传递 Init 容器名称,以访问容器的日志。
kubectl logs <pod-name> -c <init-container-2>
理解 Pod 的状态
以 Init:
开头的 Pod 状态汇总了 Init 容器执行的状态。 下表介绍调试 Init 容器时可能看到的一些状态值示例。
状态 | 含义 |
---|---|
Init:N/M | Pod 包含 M 个 Init 容器,其中 N 个已经运行完成。 |
Init:Error | Init 容器已执行失败。 |
Init:CrashLoopBackOff | Init 容器执行总是失败。 |
Pending | Pod 还没有开始执行 Init 容器。正在被调度 |
PodInitializingor Running | Pod 已经完成执行 Init 容器。 |