概述
Init Container就是用来做初始化工作的容器,可以是一个或者多个,如果有多个的话,这些容器会按定义的顺序依次执行,只有所有的Init Container执行完后,主容器才会被启动。我们知道一个Pod里面的所有容器是共享数据卷和网络命名空间的,所以Init Container里面产生的数据可以被主容器使用到的。 Init Container与应用容器本质上是一样的,但他们是仅运行一次就结束的任务,并且必须在成功执行完后,系统才能继续执行下一个容器。
一个pod完整的生命周期如下:
从上面这张图我们可以直观的看到PostStart和PreStop包括liveness和readiness是属于主容器的生命周期范围内的,而Init Container是独立于主容器之外的,当然他们都属于Pod的生命周期范畴之内的。
另外我们可以看到上面我们的Pod右边还有一个infra的容器,这是一个什么容器呢?我们可以在集群环境中去查看任意一个Pod对应的运行的Docker容器,我们可以发现每一个Pod下面都包含了一个pause-amd64的镜像,这个就是我们的infra镜像,我们知道Pod下面的所有容器是共享同一个网络命名空间的,这个镜像就是来做这个事情的,所以每一个Pod当中都会包含一个这个镜像,其实就是我们之前了解到的pause容器。
应用场景
在很多应用场景中,应用在启动之前都需要进行初始化操作,如:
- 等待其他关联服务正确运行(例如数据库或某个后台服务)
- 基于环境变量或配置模板生成服务所需配置文件
- 从远程数据库获取本地所需配置,或者将自身注册到某个中央数据库中
- 下载相关依赖包,或者对统进行一些预配置操作
示例1
我们先来测试使用initcontainer创建一个服务依赖的场景的Pod:
##引入了外部mysql
[root@k8s-m1 k8s-total]# cat mysql-svc.yml
kind: Endpoints
apiVersion: v1
metadata:
name: mysql-production
subsets:
- addresses:
- ip: 192.168.2.142
ports:
- port: 3306
---
apiVersion: v1
kind: Service
metadata:
name: mysql-production
spec:
ports:
- port: 3306
##部署nginx
[root@k8s-m1 k8s-total]# cat my-nginx.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx
labels:
app: nginx
spec:
selector:
matchLabels:
tier: frontend
replicas: 1
template:
metadata:
labels:
tier: frontend
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
ports:
- port: 80
selector:
tier: frontend
##有初始化容器的pod
apiVersion: v1
kind: Pod
metadata:
name: init-pod-1
labels:
app: init
spec:
containers:
- name: main-container
image: busybox
command: ['sh', '-c', 'echo The app is running! && sleep 3600']
initContainers:
- name: init-service
image: busybox
command: ['sh', '-c', 'until nslookup nginx-service; do echo waiting for nginx-service; sleep 2; done;']
- name: init-mysql
image: busybox
command: ['sh', '-c', 'until nslookup mysql-production; do echo waiting for mysql; sleep 2; done;']
#部署
[root@k8s-m1 k8s-init-container]# kubectl apply -f mysql-svc.yml -f my-nginx.yml -f init-pod-1.yml
#查看
[root@k8s-m1 k8s-total]# kubectl get ep,po
NAME ENDPOINTS AGE
endpoints/nginx-service 10.244.42.159:80 10m
endpoints/mysql-production 192.168.2.142:3306 10m
NAME READY STATUS RESTARTS AGE
pod/init-pod-1 1/1 Running 0 10m
pod/my-nginx-7ff446c4f4-tbtpc 1/1 Running 1 10m
我们可以 describe 下看看详细信息:
[root@k8s-m1 k8s-total]# kubectl describe pod init-pod-1
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 13m default-scheduler Successfully assigned default/init-pod-1 to k8s-m1
Normal Pulling 13m kubelet Pulling image "busybox"
Normal Pulled 13m kubelet Successfully pulled image "busybox" in 15.979657153s
Normal Created 13m kubelet Created container init-service
Normal Started 13m kubelet Started container init-service
Normal Pulling 12m kubelet Pulling image "busybox"
Normal Pulled 12m kubelet Successfully pulled image "busybox" in 15.995762509s
Normal Created 12m kubelet Created container init-mysql
Normal Started 12m kubelet Started container init-mysql
Normal Pulling 12m kubelet Pulling image "busybox"
Normal Pulled 12m kubelet Successfully pulled image "busybox" in 15.976887445s
Normal Created 12m kubelet Created container main-container
可以看到,我们在Pod启动过程中,初始化容器会按顺序在网络和数据卷初始化之后启动。每个容器必须在下一个容器启动之前成功退出。如果由于运行时或失败退出,导致容器启动失败,它会根据Pod的restartPolicy指定的策略进行重试。 然而,如果 Pod 的 restartPolicy 设置为 Always,Init 容器失败时会使用 RestartPolicy 策略。
在所有的初始化容器没有成功之前,Pod将不会变成 Ready状态。正在初始化中的Pod处于Pending状态,但应该会将条件Initializing设置为 true。
示例2
接下来我们测试使用initcontainer创建一个做初始化配置工作的Pod:
[root@k8s-m1 k8s-init-container]# cat init-pod-2.yaml
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
initContainers:
- name: install
image: busybox
command:
- wget
- "-O"
- "/work-dir/index.html"
- http://www.baidu.com
volumeMounts:
- name: workdir
mountPath: "/work-dir"
volumes:
- name: workdir
emptyDir: {}
我们可以看到这里又出现了volumes,spec.volumes指的是Pod中的卷,spec.containers.volumeMounts,是将指定的卷 mount 到容器指定的位置,相当于Docker里面的-v 宿主机目录:容器目录,我们前面用到过hostPath,我们这里使用的是emptyDir{},这个就相当于一个共享卷,是一个临时的目录,生命周期等同于Pod的生命周期。
初始化容器执行完,会下载一个 html 文件映射到emptyDir{},而主容器也是和 spec.volumes 里的 emptyDir{} 进行映射,所以nginx容器的/usr/share/nginx/html目录下会映射 index.html 文件。
我们来创建下该Pod,然后验证nginx容器是否运行:
[root@k8s-m1 k8s-init-container]# kubectl apply -f init-pod-2.yaml
pod/init-demo created
[root@k8s-m1 k8s-init-container]# kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
init-demo 1/1 Running 0 60s 10.244.42.155 k8s-m1 <none> <none>
[root@k8s-m1 k8s-init-container]# curl 10.244.42.155
正常效果我们可以看到有百度相关的信息,就可以证明我们上面的初始化的工作正常完成。
更多关于kubernetes的知识分享,请前往博客主页。编写过程中,难免出现差错,敬请指出