深入理解Pod-初始化容器

1、什么是Init Container?

Init Container 就是⽤来做初始化⼯作的容器,可以是⼀个或者多个。如果有多个的话,这些容器会按定义的顺序依次执行,只有所有的 Init Container 执行完后,主容器才会被启动。我们知道⼀个 Pod ⾥⾯的所有容器是共享数据卷和网络命名空间的,所以 Init Container ⾥⾯产生的数据可以被主容器使⽤到的。

其实, Init Container 和前面讲的钩⼦函数有点类似,只是钩子函数是在容器执⾏前来做⼀些⼯作。从直观的⻆度看上去的话,初始化容器的确有点像 PreStart ,但是钩⼦函数和我们的 Init Container 是处在不同的阶段的,我们可以通过下⾯的图来了解下:

img

从上⾯这张图我们可以直观的看到 PostStart 和 PreStop 函数包括 liveness 和 readiness 探针是属于主容器生命周期范围内的,而 Init Container 是独⽴于主容器之外的,当然他们都属于 Pod 的⽣命周期范畴之内的,这就是 Init Container 和钩子函数之类的区别。

从上面的图我们可以看到 Pod 右边还有⼀个 infra 的容器,这是⼀个什么容器呢?我们可以在集群环境中去查看下⼈任意⼀个 Pod 对应的运⾏的 Docker 容器,我们可以发现每⼀个 Pod 下⾯都包含了⼀个 pause-amd64 的镜像,这个就是我们的 infra 镜像,我们知道 Pod 下⾯的所有容器是共享同⼀个⽹络命名空间的,这个镜像就是来做这个事情的,所以每⼀个 Pod 当中都会包含⼀个这个镜像。

注:很多同学最开始 Pod 启动不起来就是因为这个 infra 镜像没有被拉下来,因为默认该镜像是需要到⾕歌服务器上拉取的,所以需要提前拉取到节点上⾯。

2、Init Container应用场景

Init Container 主要是来做初始化容器⼯作的,那么他有哪些应⽤场景呢?

  • 等待其他模块Ready:这个可以⽤来解决服务之间的依赖问题,例如我们有⼀个 Web 服务,该服务⼜依赖于另外⼀个数据库服务,但是在我们启动这个 Web 服务的时候我们并不能保证依赖的这个数据库服务就已经启动起来了,所以可能会出现⼀段时间内 Web 服务连接数据库异常。要解决这个问题的话我们就可以在 Web 服务的 Pod 中使⽤⼀个 Init Container,在这个初始化容器中去检查数据库是否已经准备好了,准备好了过后初始化容器就结束退出,然后我们的主容器 Web 服务被启动起来,这个时候去连接数据库就不会有问题了。
  • 初始化配置:例如集群⾥检测所有已经存在的成员节点,为主容器准备好集群的配置信息,这样主容器起来后就能⽤这个配置信息加⼊集群。
  • 其它场景:例如将 pod 注册到⼀个中央数据库、配置中⼼等。

等待其他模块Ready

我们还是通过示例的方式先来给⼤家演示下服务依赖的场景下初始化容器的使⽤方法,Pod 的定义方法如下:

apiVersion: v1
kind: Pod
metadata:
  name: init-pod
  labels:
    app: init
spec:
  containers:
  - name: main-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;']

通过 kubectl get pods 我们可以看到 REDY 0/1 表示还有1个主容器没有准备好;Init:0/2 表示有两个初始化容器,都还没有准备好。

img

执行kubectl describe pod init-pod 我们可以看到该Pod的详情:main-container主容器的状态还是waiting等待状态,这是因为PodInitializing还在初始化。

img

init-mydb初始化容器还在等待初始化是因为上面的 init-myservice 容器还没有completed完成初始化、他就会一直处于等待状态,这里我们可以先来创建service,创建好了以后 init-myservice 检测成功之后就会退出,系统继续执行init-mydb初始化容器。

img

Service 对应的Yaml内容:

kind: Service
apiVersion: v1
metadata:
  name: myservice
spec:
  ports:
  - protocol: TCP
    port: 80
    targetPort: 6376
---
kind: Service
apiVersion: v1
metadata:
  name: mydb
spec:
  ports:
  - protocol: TCP
    port: 80
    targetPort: 6377

我们可以先创建上⾯的 Pod ,然后查看下 Pod 的状态,然后再创建下⾯的 Service ,对比下前后状
态:

img

这里我们看到Pod创建成功以后、init-myservice首先完成初始化、然后就是init-mydb完成初始化,最后是我们创建的main-container主进程:

img

最终我们可以看到Pod已经成功启动了,这样做的目的可以解决服务依赖的问题。

img

我们在 Pod 启动过程中,初始化容器会按顺序在⽹络和数据卷初始化之后启动。每个容器必须在下⼀个容器启动之前成功退出。如果由于运⾏时或失败退出,导致容器启动失败,它会根据 Pod 的 restartPolicy 指定的策略进⾏重试。 然而,如果 Pod 的 restartPolicy 设置为 Always,Init 容器失败时会使用 RestartPolicy 策略。

在所有的初始化容器没有成功之前, Pod 将不会变成 Ready 状态。正在初始化中的 Pod 处于 Pending 状态,但应该会将条件 Initializing 设置为 true。

初始化配置使用场景

接下来我们再来尝试创建⼀个做初始化配置⼯作的 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
  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: {}

注: spec.volumes 指的是 Pod 中的卷, spec.containers.volumeMounts ,是将指定的卷 mount 到容器指定的位置,相当于docker⾥⾯的 -v 宿主机⽬录:容器目录 ,我们前面用到过 hostPath ,我们这⾥使⽤的是 emptyDir{ } ,这个就相当于⼀个共享卷,是⼀个临时的⽬录,生命周期等同于 Pod 的生命周期。

初始化容器执⾏完,会下载⼀个 html ⽂件映射到emptyDir{ },而主容器也是和 spec.volumes ⾥的emptyDir{ } 进⾏映射,所以 nginx容器的 /usr/share/nginx/html`⽬录下会映射 index.html ⽂件。

我们通过 kubectl apply -f initconfig.yaml 命令创建该Pod,创建完成以后我们来验证nginx容器是否正常运行。我们通过 kubectl exec -it init-demo — /bin/bash 命令进入到nginx容器的shell当中,在shell里面,直接查看index.html文件的内容,可以看到、我们已经正常拿到百度的相关内容。说明上面Pod的初始化工作已经完成了。

img

以上就是我们初始化容器的使用方法,到这里我们就把 Pod 的整个⽣命周期当中的几个主要阶段讲完了:第⼀个是容器的两个钩子函数: PostStart 和 PreStop ,还有就是容器健康检查的两个探针: liveness probe 和 readiness probe ,以及 Init Container 。

推荐文章