参考:https://kubernetes.io/docs/concepts/workloads/pods/init-containers/
在kubernetes中,一个pod可以包含多个容器,其中的init container,顾名思义主要负责初始化工作,一个pod也可以包含多个init container。后文统一用"初始化容器"表示"init container"。初始化容器也是容器,在pod的定义中,如果将"containers"改成"initContainers",那么这个数组内定义的所有容器就都是初始化容器,定义初始化容器与普通应用容器的语法相同。但是,初始化容器不支持readiness类型探针,这个很容易理解,初始化容器主要负责初始化工作,在它没有运行完成之前pod一定处于未就绪状态(unready),所以readiness类型探针一定会诊断失败,没有意义。另外初始化容器在资源请求与限制方面与普通的应用容器有细微差别,后文详细描述。
初始化容器先于普通应用容器执行,如果pod有多个初始化容器,那么它们按定义的顺序逐个执行,并且前一个必需执行成功后一个才可以执行,所有的初始化容器执行成功后,普通应用容器才可以执行。当初始化容器执行失败时,如果restart policy是OnFailure或者Always,那么会重复执行失败的初始化容器一直到成功,如果restart policy是Never,则不会重启失败的初始化容器。如果初始化容器执行成功,那么无论restart policy是什么,即使是Always,也不会再次被重启。
初始化容器的状态可以通过pod的.status.initContainerStatuses字段查看,它的值是一个数组,分别表示每个初始化容器的状态。数组中每个成员的结果与表示普通应用容器状态的结构一样,字段含义相同。
在开发一个程序时,通常初化代码与主体业务代码放置在同一个程序中。为什么kubernetes提供初始化容器这种功能,将初始化工作从普通容器中隔离出来?这种特性有什么用呢?初始化容器与普通容器有各自独立的image,本质上是将初始化逻辑与主体业务逻辑分离并放置在不同的image中,以下是初始化容器的主要用处:
以下是如何使用初始化容器的一些想法、idea:
以下是具备示例:
Pod有两个初始化容器,一个等待myservice被创建,第二个等待mydb,当两个服务都创建后,应用容器才会启动。注意,文件中涉及初始化容器的语法适用于1.8及以后的版本。
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
labels:
app: myapp
spec:
containers:
- name: myapp-container
image: busybox
command: ['sh', '-c', 'echo The app is running! && sleep 3600']
initContainers:
- name: init-myservice
image: busybox
command: ['sh', '-c', 'until nslookup myservice; do echo waiting for myservice; sleep 2; done;']
- name: init-mydb
image: busybox
command: ['sh', '-c', 'until nslookup mydb; do echo waiting for mydb; sleep 2; done;']
以下是myservice与mydb:
kind: Service
apiVersion: v1
metadata:
name: myservice
spec:
ports:
- protocol: TCP
port: 80
targetPort: 9376
---
kind: Service
apiVersion: v1
metadata:
name: mydb
spec:
ports:
- protocol: TCP
port: 80
targetPort: 9377
启动与调试pod:
$ kubectl create -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=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 with docker id 5ced34a04634; Security:[seccomp=unconfined]
13s 13s 1 {kubelet 172.17.4.201} spec.initContainers{init-myservice} Normal Started Started container with docker id 5ced34a04634
$ kubectl logs myapp-pod -c init-myservice # Inspect the first init container
$ kubectl logs myapp-pod -c init-mydb # Inspect the second init container
由以上输出可以看出,pod中唯一的应用容器没有到达"ready"阶段,状态显示需要成功执行两个初始化容器,但目前执行成功的个数是0。进一步确认,pod处于peding状态,初始化容器1已经成功运行,但是无法成功结束,所以整个pod被阻塞在了pending状态。通过kubectl logs可分别查看两个初始化容器的日志。
创建myservice与mydb服务,再确认pod状态:
$ kubectl create -f services.yaml
service "myservice" created
service "mydb" created
$ kubectl get -f myapp.yaml
NAME READY STATUS RESTARTS AGE
myapp-pod 1/1 Running 0 9m
可以看到,pod完全处于ready阶段,状态显示为running,表明两个初始化容器已经成功结束。
初始化容器先于普通应用容器启动,如果有多个则按定义顺序逐个启动,并且前一个必需成功后才可以启动后一下,所有的都成功后才可以启动应用容器。关于初始化容器与restart policy,前文已描述。需要注册的是,网络与volume的初始化要先于初始化容器,或者说pod的pause根容器先于初始化容器启动。
当初始化容器正在运行时,整个pod处于pending阶段,状态为Initializing。如果整个pod被restart,则所有容器包括初始化容器也要再运行一次。
对初始化容器的变更仅限于image字段,其它字段不可以再行更改。变更初始化容器的image字段会导致整个pod重新启动。
初始化容器可以被重启、重复运行多次。因些注意解决"幂等"问题,如初始化容器第一次运行时创建文件并写入内容,那么第二次运行时文件已经存在并且已经有内容,要注意解决这类问题。
以几下几条规则: