本文主要系统的介绍pod和pod健康状态探测。探测类型包括liveness,readiness和startup。
Pod是Kubernetes创建或部署的最小且最简单的基本单位,Pod表示在集群上运行的进程。
Pod封装了一个或多个运行应用程序的容器,存储资源,唯一的网络IP和控制容器运行方式的一些选项。一般会将docker作为k8s的容器运行时(container runtime),不过也可以使用其他容器运行时,如CRI-O(k8s项目),Containerd(docker项目)等。
容器运行时负责真正管理镜像和容器的生命周期。
查看pod的yaml参数:kubectl explain pods.spec.containers
运行单个容器的容器: 一个pod存在一个容器;
运行多个需要协同工作的容器的pod:一个pod存在多个容器协同工作,该应用程序由紧密耦合且需要共享资源的多个位于同一地点的容器组成。多个容器是紧耦合的,共享网络和存储,通过lo接口互相通信,对外呈现为一个单独的pod实体。如果想水平扩展pod,可通过Controller Manager复制多个pod提供负载均衡服务。
创建pod时,可以用lifecycle来配置容器在运行前和关闭前的一些动作。
kubectl explain pod.spec.containers.lifecycle
lifecycle有两个钩子函数:
创建容器后立即调用PostStart。如果处理程序失败,容器将根据其终止并重启策略重新启动。直到preStart完成才会进入下一步,如livenessProbe。
# kubectl explain pod.spec.containers.lifecycle.postStart
# cat preStart-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: perstart-exec-pod
namespace: default
spec:
containers:
- name: perstart-exec-container
image: busybox:latest
imagePullPolicy: IfNotPresent
lifecycle:
postStart:
exec:
command: ['/bin/sh','-c','echo test >>/tmp/index.html']
command: ['/bin/httpd']
args: ['-f','-h /tmp']
preStart类似,没有好的栗子,不做介绍。
kubectl explain pods.status.phase
通过phase字段来表示pod的状态,该字段是只读的。Pod生命周期中的状态包括:
当创建pod后,如何确定pod容器是否正常启动?pod中的应用是否正常启动?pod应用是否正常返回?是否可以对pod进行健康检查?kubelet可以选择对正在运行的容器进行三种健康状态探测并进行相应处理:livenessProbe,readinessProbe和startupProbe。yaml字段查看:kubectl explain pod.spec.containers.livenessProbe
livenessProbe
: 存活探针,探测容器是否正在运行。如果存活探针失败,则kubelet将kill掉容器,并且容器将接受其重启策略。如果容器未配置存活探针,则默认状态为Success
。readinessProbe
: 就绪探针,探测容器是否准备好服务请求。如果就绪探针失败,则会从对应的service中移除Pod的IP地址。初始延迟之前的默认就绪状态为Failure
。如果容器未提供就绪探针,则默认状态为Success
。startupProbe
: 启动探针,探测容器中的应用程序是否已启动,通常用来探测需要额外启动时间的容器应用程序。如果提供了启动探针,则将禁用所有其他探针,直到启动探针成功。如果启动探针失败,则kubelet将杀死Container,并且Container将接受其重启策略。如果容器未提供启动探针,则默认状态为Success
。容器的重启策略包括:Always(正常和异常退出都重启)、OnFailure(异常退出重启)和Never(从不重启),默认值为Always。kubectl explain pod.spec.restartPolicy
每种探测都支持三种探测方法(探针):
每个探针具有以下三个结果之一:
容器长时间运行出现故障需要重启恢复时,可用存活探针。
示例:
# exec探针探测
apiVersion: v1
kind: Pod
metadata:
name: liveness-exec-pod
namespace: default
spec:
containers:
- name: liveness-exec-container
image: busybox:latest
# 容器启动执行命令
command: ['/bin/sh','-c','touch /tmp/healthy; sleep 10;rm -f /tmp/healthy; sleep 50;']
livenessProbe:
exec:
# 通过检测目录是否存在探测系统是否正常启动
command: ['test','-e','/tmp/healthy']
# 初始化延迟探测,第一次探测之前等待1s,因为主程序未必启动完成
initialDelaySeconds: 1
# 每3秒探测一次
periodSeconds: 3
# 最大失败次数
failureThreshold: 2
# 探测超时时长
timeoutSeconds: 1
生成pod看下,可以看到pod启动成功了,但过会会检测失败,被kill,重启,再检测失败,再重启。。。
# httpGet探针探测
apiVersion: v1
kind: Pod
metadata:
name: liveness-httpget-pod
namespace: default
spec:
containers:
- name: liveness-httpget-container
image: nginx:latest
ports:
- name: nginx
containerPort: 80
livenessProbe:
# 访问首页,探测nginx是否正常返回
httpGet:
port: nginx
path: /index.html
initialDelaySeconds: 1
periodSeconds: 3
生成pod看下,由于探针探测到http会返回404,可以看到第一次pod启动就失败了(killing),然后可以看到根据重启策略(默认always)重启了机器方便恢复(started),但由于配置没变探测还是返回404Unhealthy,所以会循环kill重启。所以这不是个恰当的栗子,因为重启了也不能恢复,所以容器会一直重启,先凑合着看吧。
# kubectl apply -f liveness-httpget.yaml
# kubectl get pods -w
NAME READY STATUS RESTARTS AGE
liveness-httpget-pod 1/1 Running 3 26s
liveness-httpget-pod 0/1 CrashLoopBackOff 5 3m13s
# kubectl describe pods liveness-httpget-pod
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 22s default-scheduler Successfully assigned default/liveness-httpget-pod to node1
Normal Pulled 4s (x3 over 21s) kubelet, node1 Container image "nginx:latest" already present on machine
Normal Created 4s (x3 over 21s) kubelet, node1 Created container liveness-httpget-container
Normal Killing 4s (x2 over 13s) kubelet, node1 Container liveness-httpget-container failed liveness probe, will be restarted
Normal Started 3s (x3 over 21s) kubelet, node1 Started container liveness-httpget-container
Warning Unhealthy 1s (x7 over 19s) kubelet, node1 Liveness probe failed: HTTP probe failed with statuscode: 404
应用程序可能需要在启动过程中加载大数据或配置文件,或者在启动后依赖于外部服务。在这种情况下,您不想杀死应用程序,但也不想发送请求。Kubernetes提供了准备就绪探针以检测和缓解这些情况。带有容器的容器报告其容器尚未准备就绪,无法通过Kubernetes Services接收流量。
示例:
# httpGet探针探测,readinessProbe 和livenessProbe写法差不多,只不过字段名不同
apiVersion: v1
kind: Pod
metadata:
name: readiness-httpget-pod
namespace: default
spec:
containers:
- name: readiness-httpget-container
image: nginx:latest
ports:
- name: nginx
containerPort: 80
readinessProbe:
httpGet:
port: nginx
path: /ihealthy.html
initialDelaySeconds: 1
periodSeconds: 3
生成个pod看下,可以看到容器创建了,也成功启动了,但启动后执行就绪检测失败,容器unhealthy。然后get pod 看下,readiness-httpget-pod 一直处于unready状态。但为什么没有重启恢复?なぜだ??因为就绪探针的故障处理方式是如果该pod加入了service,仅会从service中剔除pod ip。
这里可以很明确的看到liveness和readiness的区别:前者是在启动时检测容器是否故障,处理方法是重启,可以看到都RESTART了7次了,后者是在启动后检测程序是否就绪,处理方法是剔除pod ip。
# kubectl apply -f readiness-httpget.yaml
# kubectl describe pods readiness-httpget-pod
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 8s default-scheduler Successfully assigned default/readiness-httpget-pod to node2
Normal Pulled 7s kubelet, node2 Container image "nginx:latest" already present on machine
Normal Created 7s kubelet, node2 Created container readiness-httpget-container
Normal Started 6s kubelet, node2 Started container readiness-httpget-container
Warning Unhealthy 1s (x2 over 4s) kubelet, node2 Readiness probe failed: HTTP probe failed with statuscode: 404
## kubectl get pods -w
NAME READY STATUS RESTARTS AGE
liveness-httpget-pod 0/1 CrashLoopBackOff 7 8m7s
readiness-httpget-pod 0/1 Running 0 5m50s
所以总结: 存活探针和就绪探针可以并行用于同一容器。同时使用这两者可以确保流量不会到达尚未准备就绪的容器,并且可以确保容器在发生故障时重新启动。
直接复制官方的,已放弃治疗。。。
在处理可能在首次初始化时需要额外启动时间的旧版应用程序时,会用到startupProbe。在这种情况下,在不影响对激发此类探测器的死锁的快速响应的情况下,如果设置活动性探测器参数可能很棘手。诀窍是使用相同的命令HTTP或TCP检查来设置启动探针,并使用failThreshold * periodSecond足够长的时间来覆盖最坏情况下的启动时间。
ports:
- name: liveness-port
containerPort: 8080
hostPort: 8080
livenessProbe:
httpGet:
path: /healthz
port: liveness-port
failureThreshold: 1
periodSeconds: 10
startupProbe:
httpGet:
path: /healthz
port: liveness-port
failureThreshold: 30
periodSeconds: 10
健康检测仅做检测,恢复由重启进行,不能检测那块写故障恢复的命令,可能会干扰重启恢复。
超详细的pod官方介绍
probe官方实践