Kubernetes中容器的相关
一、容器
容器镜像
在 Kubernetes 的 Pod 中使用容器镜像之前,您必须将其推送到一个镜像仓库(或者使用仓库中已经有的容器镜像)。在 Kubernetes 的 Pod 定义中定义容器时,必须指定容器所使用的镜像,容器中的 image 字段支持与 docker 命令一样的语法,包括私有镜像仓库和标签。
例如:my-registry.example.com:5000/example/web-example:v1.0.1 由如下几个部分组成:
my-registry.example.com:5000/example/web-example:v1.0.1
- my-registry.example.com:registry 地址
- :5000:registry 端口
- example:repository 名字
- web-example:image 名字
- v1.0.1:image 标签
如果使用 hub.dokcer.com Registry 中的镜像,可以省略 registry 地址和 registry 端口。
例如:nginx:latest,eipwork/kuboard
更新镜像
Kubernetes中,默认的镜像抓取策略是 IfNotPresent,使用此策略,kubelet在发现本机有镜像的情况下,不会向镜像仓库抓取镜像。如果想每次启动 Pod 时,都强制从镜像仓库抓取镜像,可以尝试如下方式:
- 设置 container 中的
imagePullPolicy为Always - 省略
imagePullPolicy字段,并使用:latesttag 的镜像 - 省略
imagePullPolicy字段和镜像的 tag - 激活 AlwaysPullImages 管理控制器
imagePullPolicy 字段和 image tag的可能取值将影响到 kubelet 如何抓取镜像:
imagePullPolicy: IfNotPresent仅在节点上没有该镜像时,从镜像仓库抓取imagePullPolicy: Always每次启动 Pod 时,从镜像仓库抓取imagePullPolicy未填写,镜像 tag 为:latest或者未填写,则同Always每次启动 Pod 时,从镜像仓库抓取imagePullPolicy未填写,镜像 tag 已填写但不是:latest,则同IfNotPresent仅在节点上没有该镜像时,从镜像仓库抓取imagePullPolicy: Never,Kubernetes 假设本地存在该镜像,并且不会尝试从镜像仓库抓取镜像
二、容器的环境变量
Kubernetes为容器提供了一系列重要的资源:
- 由镜像、一个或多个数据卷合并组成的文件系统
- 容器自身的信息
- 集群中其他重要对象的信息
集群的信息
在容器创建时,集群中所有的 Service 的连接信息将以环境变量的形式注入到容器中。例如,已创建了一个名为 Foo 的 Service,此时再创建任何容器时,该容器将包含如下环境变量:
1 | FOO_SERVICE_HOST = <Service的ClusterIP> |
三、容器生命周期
容器钩子
Kubernetes中为容器提供了两个 hook(钩子函数):
PostStart此钩子函数在容器创建后将立刻执行。但是,并不能保证该钩子函数在容器的
ENTRYPOINT之前执行。该钩子函数没有输入参数。PreStop此钩子函数在容器被 terminate(终止)之前执行,例如:
- 通过接口调用删除容器所在 Pod
- 某些管理事件的发生:健康检查失败、资源紧缺等
如果容器已经被关闭或者进入了
completed状态,preStop 钩子函数的调用将失败。该函数的执行是同步的,即,kubernetes 将在该函数完成执行之后才删除容器。该钩子函数没有输入参数。Hook handler的实现
容器只要实现并注册 hook handler 便可以使用钩子函数。Kubernetes 中,容器可以实现两种类型的 hook handler:
- Exec - 在容器的名称空进和 cgroups 中执行一个指定的命令,例如
pre-stop.sh。该命令所消耗的 CPU、内存等资源,将计入容器可以使用的资源限制。 - HTTP - 向容器的指定端口发送一个 HTTP 请求
Hook handler的执行
当容器的生命周期事件发生时,Kubernetes 在容器中执行该钩子函数注册的 handler。
对于 Pod 而言,hook handler 的调用是同步的。即,如果是
PostStarthook,容器的ENTRYPOINT和 hook 是同时出发的,然而如果 hook 执行的时间过长或者挂起了,容器将不能进入到Running状态。PreStophook 的行为与此相似。如果 hook 在执行过程中挂起了,Pod phase 将停留在Terminating的状态,并且在terminationGracePeriodSeconds超时之后,Pod被删除。如果PostStart或者PreStophook 执行失败,则 Kubernetes 将 kill(杀掉)该容器。用户应该使其 hook handler 越轻量级越好。例如,对于长时间运行的任务,在停止容器前,调用
PreStop钩子函数,以保存当时的计算状态和数据。Hook触发的保证
Hook 将至少被触发一次,即,当指定事件
PostStart或PreStop发生时,hook 有可能被多次触发。hook handler 的实现需要保证即使多次触发,执行也不会出错。通常来说,hook 实际值被触发一次。例如:如果 HTTP hook 的服务端已经停机,或者因为网络的问题不能接收到请求,请求将不会被再次发送。在极少数的情况下, 触发两次 hook 的事情会发生。例如,如果 kueblet 在触发 hook 的过程中重启了,该 hook 将在 Kubelet 重启后被再次触发。
调试 hook handler
Hook handler 的日志并没有在 Pod 的 events 中发布。如果 handler 因为某些原因失败了,kubernetes 将广播一个事件
PostStarthook 发送FailedPreStopHook事件。 可以执行命令kubectl describe pod $(pod_name)以查看这些事件。
四、容器生命周期事件处理
Kubernetes 中支持容器的 postStart 和 preStop 事件,本文阐述了如何向容器添加生命周期事件处理程序(handler)。
postStart容器启动时,Kubernetes 立刻发送 postStart 事件,但不确保对应的 handler 是否能在容器的EntryPoint之前执行preStop容器停止前,Kubernetes 发送 preStop 事件
定义postStart和preStop处理程序
创建一个包含单一容器的 Pod,并为该容器关联 postStart 和 preStop 处理程序(handler)。Pod 的yaml文件定义如下:
1 | apiVersion: v1 |
在该例子中,请注意:
- postStart 命令向
usr/share/message文件写入了一行文字 - preStop 命令优雅地关闭了 nginx
如果容器碰到问题,被 Kubernetes 关闭,这个操作是非常有帮助的,可以使得您的程序在关闭前执行必要的清理任务
- 创建pod
1 | [root@k8s-master k8s-yamls]# kubectl apply -f lifecycle-demo.yaml |
- 进入容器的命令行终端:
1 | [root@k8s-master k8s-yamls]# kubectl exec -it lifecycle-demo -- /bin/bash |
总结
Kubernetes 在容器启动后立刻发送 postStart 事件,但是并不能确保 postStart 事件处理程序在容器的 EntryPoint 之前执行。
postStart 事件处理程序相对于容器中的进程来说是异步的(同时执行),然而,Kubernetes 在管理容器时,将一直等到 postStart 事件处理程序结束之后,才会将容器的状态标记为 Running。
Kubernetes 在决定关闭容器时,立刻发送 preStop 事件,并且,将一直等到 preStop 事件处理程序结束或者 Pod 的 --grace-period 超时,才删除容器。
注意: Kubernetes 只在 Pod Teminated 状态时才发送 preStop 事件,这意味着,如果 Pod 已经进入了 Completed 状态, preStop 事件处理程序将不会被调用