4.Pod的生命周期和重启策略
-
生命周期
- Pod 遵循一个预定义的生命周期,起始于
Pending
阶段,如果至少 其中有一个主要容器正常启动,则进入Running
,之后取决于 Pod 中是否有容器以 失败状态结束而进入Succeeded
或者Failed
阶段。
- Pod 遵循一个预定义的生命周期,起始于
-
Pod状态
-
Pending(悬决)
Pod 已被 Kubernetes 系统接受,但有一个或者多个容器尚未创建亦未运行。
此阶段包括等待 Pod 被调度的时间和通过网络下载镜像的时间。
-
Running
(运行中)Pod 已经绑定到了某个节点,Pod 中所有的容器都已被创建。
至少有一个容器仍在运行,或者正处于启动或重启状态。
-
Succeeded
(成功)- Pod 中的所有容器都已成功终止,并且不会再重启。
-
Failed
(失败)Pod 中的所有容器都已终止,并且至少有一个容器是因为失败终止。
也就是说,容器以非 0 状态退出或者被系统终止。
如果某节点死掉或者与集群中其他节点失联,Kubernetes 会实施一种策略,将失去的节点上运行的所有 Pod 的
phase
设置为Failed
。
-
Unknown
(未知)因为某些原因无法取得 Pod 的状态。
这种情况通常是因为与 Pod 所在主机通信失败。
-
-
Pod重启策略
Pod的重启策略(RestartPolicy)应用于Pod内的所有容器,并且仅在Pod所处的Node上由kubelet进行判断和重启操作。
当某个容器异常退出或者健康检查失败时,kubelet将根据RestartPolicy的设置来进行相应的操作。
-
Pod的重启策略包括Always、OnFailure和Never,默认值为Always。
Always:当容器失效时,由kubelet自动重启该容器。
OnFailure:当容器终止运行且退出码不为0时,由kubelet自动重启该容器。
Never:不论容器运行状态如何,kubelet都不会重启该容器。
kubelet重启失效容器的时间间隔以sync-frequency乘以2n来计算,例如1、2、4、8倍等,最长延时5min,并且在成功重启后的10min后重置该时间。
-
Pod的重启策略与控制方式息息相关,当前可用于管理Pod的控制器包括ReplicationController、Job、DaemonSet及直接通过kubelet管理(静态Pod)。每种控制器对Pod的重启策略要求如下:
RC和DaemonSet:必须设置为Always,需要保证该容器持续运行。
Job:OnFailure或Never,确保容器执行完成后不再重启。
kubelet:在Pod失效时自动重启它,不论将RestartPolicy设置为什么值,也不会对Pod进行健康检查。
结合Pod的状态和重启策略,列出一些常见的状态转换场景:
5.Pod健康检查和服务可用性检查
5.1 Kubernetes 对 Pod 的健康状态可以通过探针来检查
Kubernetes 对 Pod 的健康状态可以通过探针来检查,kubelet定期执行探针来诊断容器的健康状况。
-
LivenessProbe探针:
-
用于判断容器是否存活(Running状态)
如果LivenessProbe探针探测到容器不健康,则kubelet将杀掉该容器,并根据容器的重启策略做相应的处理。
如果一个容器不包含LivenessProbe探针,那么kubelet认为该容器的LivenessProbe探针返回的值永远是Success。
-
-
ReadinessProbe探针:
-
用于判断容器服务是否可用(Ready状态),达到Ready状态的Pod才可以接收请求。
对于被Service管理的Pod,Service与Pod Endpoint的关联关系也将基于Pod是否Ready进行设置。
如果在运行过程中Ready状态变为False,则系统自动将其从Service的后端Endpoint列表中隔离出去,后续再把恢复到Ready状态的Pod加回后端Endpoint列表。
这样就能保证客户端在访问Service时不会被转发到服务不可用的Pod实例上。
-
-
startupProbe探针:
-
指示容器中的应用是否已经启动。
如果提供了启动探针,则所有其他探针都会被 禁用,直到此探针成功为止。
如果启动探测失败,
kubelet
将杀死容器,而容器依其 重启策略进行重启。如果容器没有提供启动探测,则默认状态为
Success
。
-
5.2 LivenessProbe和ReadinessProbe均可配置以下三种实现方式
(1)定义存活命令
ExecAction:在容器内部执行一个命令,如果该命令的返回码为0,则表明容器健康。
apiVersion: v1
kind: Pod
metadata:
labels:
test: liveness
name: liveness-exec
spec:
containers:
- name: liveness
image: k8s.gcr.io/busybox
args:
- /bin/sh
- -c
- touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600
livenessProbe:
exec:
command:
- cat
- /tmp/healthy # kubelet 在容器内执行命令 `cat /tmp/healthy` 来进行探测
initialDelaySeconds: 5 # `initialDelaySeconds` 字段告诉 kubelet 在执行第一次探测前应该等待 5 秒
periodSeconds: 5 # `periodSeconds` 字段指定了 kubelet 应该每 5 秒执行一次存活探测
在这个配置文件中,可以看到 Pod 中只有一个容器。 periodSeconds
字段指定了 kubelet 应该每 5 秒执行一次存活探测。 initialDelaySeconds
字段告诉 kubelet 在执行第一次探测前应该等待 5 秒。 kubelet 在容器内执行命令 cat /tmp/healthy
来进行探测。 如果命令执行成功并且返回值为 0,kubelet 就会认为这个容器是健康存活的。 如果这个命令返回非 0 值,kubelet 会杀死这个容器并重新启动它。
例子:
下面例子中,通过删除/tmp/healthy,展示pod通过探针检查健康状态和重启的过程。
当容器启动时,执行如下的命令:
/bin/sh -c "touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600"
这个容器生命的前 30 秒, /tmp/healthy
文件是存在的。 所以在这最开始的 30 秒内,执行命令 cat /tmp/healthy
会返回成功代码。 30 秒之后,执行命令 cat /tmp/healthy
就会返回失败错误码。
创建 Pod:
kubectl apply -f exec-liveness.yaml
在 30 秒内,查看 Pod 的事件:
kubectl describe pod liveness-exec
输出结果表明还没有存活探测器失败:
FirstSeen LastSeen Count From SubobjectPath Type Reason Message
--------- -------- ----- ---- ------------- -------- ------ -------
24s 24s 1 {default-scheduler } Normal Scheduled Successfully assigned liveness-exec to worker0
23s 23s 1 {kubelet worker0} spec.containers{liveness} Normal Pulling pulling image "k8s.gcr.io/busybox"
23s 23s 1 {kubelet worker0} spec.containers{liveness} Normal Pulled Successfully pulled image "k8s.gcr.io/busybox"
23s 23s 1 {kubelet worker0} spec.containers{liveness} Normal Created Created container with docker id 86849c15382e; Security:[seccomp=unconfined]
23s 23s 1 {kubelet worker0} spec.containers{liveness} Normal Started Started container with docker id 86849c15382e
35 秒之后,再来看 Pod 的事件:
#kubectl describe pod liveness-exec
//在输出结果的最下面,有信息显示存活探测器失败了,这个容器被杀死并且被重建了。
FirstSeen LastSeen Count From SubobjectPath Type Reason Message
--------- -------- ----- ---- ------------- -------- ------ -------
37s 37s 1 {default-scheduler } Normal Scheduled Successfully assigned liveness-exec to worker0
36s 36s 1 {kubelet worker0} spec.containers{liveness} Normal Pulling pulling image "k8s.gcr.io/busybox"
36s 36s 1 {kubelet worker0} spec.containers{liveness} Normal Pulled Successfully pulled image "k8s.gcr.io/busybox"
36s 36s 1 {kubelet worker0} spec.containers{liveness} Normal Created Created container with docker id 86849c15382e; Security:[seccomp=unconfined]
36s 36s 1 {kubelet worker0} spec.containers{liveness} Normal Started Started container with docker id 86849c15382e
2s 2s 1 {kubelet worker0} spec.containers{liveness} Warning Unhealthy Liveness probe failed: cat: can't open '/tmp/healthy': No such file or directory
再等另外 30 秒,检查看这个容器被重启了:
#kubectl get pod liveness-exec
//输出结果显示 RESTARTS 的值增加了 1。
NAME READY STATUS RESTARTS AGE
liveness-exec 1/1 Running 1 1m
(2)定义TCP的存活探测
TCPSocketAction:通过容器的IP地址和端口号执行TCP检查,如果能够建立TCP连接,则表明容器健康。
apiVersion: v1
kind: Pod
metadata:
name: goproxy
labels:
app: goproxy
spec:
containers:
- name: goproxy
image: k8s.gcr.io/goproxy:0.1
ports:
- containerPort: 8080
readinessProbe: # 就绪探针
tcpSocket: #tcp
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe: #存活探针
tcpSocket:
port: 8080
initialDelaySeconds: 15
periodSeconds: 20
就绪探针:kubelet 会在容器启动 5 秒后发送第一个就绪探测。 这会尝试连接 goproxy
容器的 8080 端口。 如果探测成功,这个 Pod 会被标记为就绪状态,kubelet 将继续每隔 10 秒运行一次检测。
存活探针:kubelet 会在容器启动 15 秒后进行第一次存活探测。 与就绪探测类似,会尝试连接 goproxy
容器的 8080 端口。 如果存活探测失败,这个容器会被重新启动。
(3)定义一个存活态HTTP请求接口
HTTPGetAction:通过容器的IP地址、端口号及路径调用HTTP Get方法,如果响应的状态码大于等于200且小于400,则认为容器健康。
apiVersion: v1
kind: Pod
metadata:
labels:
test: liveness
name: liveness-http
spec:
containers:
- name: liveness
image: k8s.gcr.io/liveness
args:
- /server
livenessProbe:
httpGet:
path: /healthz
port: 8080
httpHeaders:
- name: Custom-Header
value: Awesome
initialDelaySeconds: 3 #启动容器后进行首次健康检查的等待时间,单位为s。
periodSeconds: 3 #每隔3s检查一次
timeoutSeconds:1 #健康检查发送请求后等待响应的超时时间,单位为s。当超时发生时,kubelet会认为容器已经无法提供服务,将会重启该容器。
在这个配置文件中,可以看到 Pod 也只有一个容器。 periodSeconds
字段指定了 kubelet 每隔 3 秒执行一次存活探测。 initialDelaySeconds
字段告诉 kubelet 在执行第一次探测前应该等待 3 秒。 kubelet 会向容器内运行的服务(服务会监听 8080 端口)发送一个 HTTP GET 请求来执行探测。 如果服务器上 /healthz
路径下的处理程序返回成功代码,则 kubelet 认为容器是健康存活的。 如果处理程序返回失败代码,则 kubelet 会杀死这个容器并且重新启动它。
任何大于或等于 200 并且小于 400 的返回代码标示成功,其它返回代码都标示失败。