有些容器启动可能比较久,为了防止容器在启动过程被误杀,采取了主动探测容器启动状态的方法,而不是被动等待,好处是如果探针检测到pod已启动则马上结束探测进程,避免愚蠢而被动的等待比如延时计时器。
检测期间pod的状态是0/1,也就是运行但未就绪。
顾名思义就是检测pod是否还活着,每隔一段时间探针就要去问一下pod的存活状态,因此可想而知这个询问动作将贯穿pod的整个生命周期。确定启动成功的那一刻,这个询问就开始了,当然你也可以延迟第一次的询问时间点(没有startup probe时期的历史产物),不过既然startup probe帮你确认了就可以不用再等待了,可以在startup结束的那一刻开始liveness的检测。
需要验证liveness独立工作时是否会出现0/1这个状态,毕竟如果偶然检测失败一次就判定为未就绪的话就太影响容器的工作了,毕竟这个探针只管你的死活,不管你的情绪好坏,只要发现死亡信号打到限定次数则采取拯救措施(心肺复苏)。
这个探针周期性地检测你有没有做好战斗准备,有些容器虽然也运行起来了,但是内部某些必要进程还没有启动完毕或发生异常,则探针就可以按照规则判断容器不健康,无法参与战斗,被标记为未就绪,到并不会被kill掉。
检测未失败之前pod会是什么状态呢,是1/1吗?只有在失败上限达到之后才会变成0/1吗?
下面是Kubernetes官网对几个探针参数的说明
Probe 有很多配置字段,可以使用这些字段精确地控制活跃和就绪检测的行为:
- initialDelaySeconds:容器启动后要等待多少秒后才启动存活和就绪探针, 默认是 0 秒,最小值是 0。
- periodSeconds:执行探测的时间间隔(单位是秒)。默认是 10 秒。最小值是 1。
- timeoutSeconds:探测的超时后等待多少秒。默认值是 1 秒。最小值是 1。
- successThreshold:探针在失败后,被视为成功的最小连续成功数。默认值是 1。 存活和启动探测的这个值必须是 1。最小值是 1。
- failureThreshold:当探测失败时,Kubernetes 的重试次数。 对存活探测而言,放弃就意味着重新启动容器。 对就绪探测而言,放弃意味着 Pod 会被打上未就绪的标签。默认值是 3。最小值是 1。
如果一个容器将被判失败,我们一般等待的标准时间为periodSeconds * failureThreashold
至于timeoutSeconds会对等待时间有什么影响还没法验证
配置文件中在原有的Pod配置下增加了3个探针配置,分别为startupProbe, livenessProbe和readinessProbe
apiVersion: v1
kind: Pod
metadata:
name: probe-httpget
namespace: default
labels:
name: probe-httpget
annotations:
name: probe-httpget
spec:
restartPolicy: Always
dnsPolicy: Default
containers:
- name: nginx
image: nginx:1.23
imagePullPolicy: IfNotPresent
startupProbe:
periodSeconds: 10
timeoutSeconds: 1
successThreshold: 1
failureThreshold: 3
httpGet:
httpHeaders:
- name: Host
value: "www.example.com"
path: /
port: 80
scheme: HTTP
livenessProbe:
periodSeconds: 10
timeoutSeconds: 1
successThreshold: 1
failureThreshold: 3
httpGet:
httpHeaders:
- name: Host
value: "www.example.com"
path: /
port: 80
scheme: HTTP
readinessProbe:
periodSeconds: 1
timeoutSeconds: 1
successThreshold: 1
failureThreshold: 3
httpGet:
httpHeaders:
- name: Host
value: "www.example.com"
path: /
port: 80
scheme: HTTP
我们关注的四个参数:
periodSeconds:每隔一段时间探针就检测一次
timeoutSeconds:Pod如果对探针请求不知可否,则探针将在超时时间过后判定探测失败
successThreshold:如果检测Pod为活动状态则记一次成功,如果成功次数达到阈值则判定Pod启动成功
failureThreshold:如果检测Pod超时则记一次失败,如果失败次数达到阈值则判定Pod启动失败,需要杀死Pod执行重启
在以下这个配置实例中,我们配置启动探针每10秒检测一次,检测成功1则判定pod启动成功,失败次数达到3次则判定pod启动失败则执行pod重启
最坏的预测:容器因某种原因,每次都会在超时时间过后也不做回应,那么将在Pod创建10秒后的检测中因收到>=400的返回码而记一次失败,在第20秒开始的第二次检测中再次判为启动失败,记录第2次失败,此时已经过去20秒,再过10秒后执行第三次检测,判定第3次失败,pod将被强制重启,此时已经经过了30秒钟。
startupProbe:
periodSeconds: 10
timeoutSeconds: 1
successThreshold: 1
failureThreshold: 3
httpGet:
httpHeaders:
- name: Host
value: "www.example.com"
path: /
port: 80
scheme: HTTP
我们做一次破坏性试验,实验的配置文件如下,基于该配置,我预测Pod将在30秒后被强制重启
startupProbe:
periodSeconds: 10
timeoutSeconds: 1
successThreshold: 1
failureThreshold: 3
httpGet:
httpHeaders:
- name: Host
value: "www.example.com"
path: /abc # 该配置相当于访问一个不存在的页面,返回值将为404
port: 80
scheme: HTTP
观测点1:查看pod是否在预测时刻发生重启
pod在30秒时发生一次重启
观测点2:查看重启前后Pod的就绪状态
Pod在重启前后都处于未就绪状态
# 第30秒pod的重启次数为0
# 就绪状态为0/1,也就是这个pod未就绪
root@node-1:~/pod# kubectl get pods
NAME READY STATUS RESTARTS AGE
probe-httpget 0/1 Running 0 30s
# 第32秒pod的重启次数为1,此时Pod的Age为32秒,重启发生在2秒前,我们就可以判断重启发生在第30秒后
# 就绪状态为0/1,也就是这个pod未就绪
root@node-1:~/pod# kubectl get pods
NAME READY STATUS RESTARTS AGE
probe-httpget 0/1 Running 1 (2s ago) 32s
观测点3:查看重启后Pod的事件
通过描述信息中的Evnets,我们可以获得信息,启动探针失败原因时收到了状态码404,随即该Pod便被kill掉了,kill和第三次探测失败同时发生,这一点可以在图中标红的两个Events的Age值中发现
Events的Age下面会显示4s (x3 over 24s),这想要表达的内容是,这条Events表示3次检测发生在最近的24秒,但是由于该Events本身已经是4秒前产生的了,因此我们得知这3次检测是在20秒内完成的。
root@node-1:~/pod# kubectl describe pod probe-httpget
在以下这个配置实例中,我们配置存活探针每10秒检测一次,检测成功1次则判定pod还活着,失败次数达到3次则判定pod已死,则执行pod重启
最坏的预测:容器因某种原因进程会死掉,那么将在Pod创建10秒后的检测中因收到>=400的返回码而记一次失败,在第20秒开始的第二次检测中再次判为探测失败,记录第2次失败,此时已经过去20秒,再过10秒后执行第三次检测,判定第3次失败,pod将被强制重启,此时已经经过了30秒钟。
livenessProbe:
periodSeconds: 10
timeoutSeconds: 1
successThreshold: 1
failureThreshold: 3
httpGet:
httpHeaders:
- name: Host
value: "www.example.com"
path: /
port: 80
scheme: HTTP
我们做一次破坏性试验,实验的配置文件如下,基于该配置,我预测Pod将在40秒后被强制重启(别忘了最佳情况下还要算上启动探针的10秒)
livenessProbe:
periodSeconds: 10
timeoutSeconds: 1
successThreshold: 1
failureThreshold: 3
httpGet:
httpHeaders:
- name: Host
value: "www.example.com"
path: /abc # 该配置相当于访问一个不存在的页面,返回值将为404
port: 80
scheme: HTTP
观测点1:查看pod是否在预测时刻发生重启
pod在40秒时发生一次重启,注意此时启动探针配置正确,需要10秒的检测,检测通过后开启livenessProbe,30秒后判定容器失活,容器重启时一共经历了40秒
观测点2:查看重启前后Pod的就绪状态
Pod在重启前后都处于未就绪状态
root@node-1:~/pod# kubectl get pods -A
NAMESPACE NAME READY STATUS RESTARTS AGE
default probe-httpget 0/1 Running 1 (3s ago) 43s
观测点3:查看重启后Pod的事件
通过描述信息中的Evnets,我们可以获得信息,存活探针失败原因是收到了状态码404,随即该Pod便被kill掉了,kill和第三次探测失败同时发生,这一点可以在图中标红的两个Events的Age值中发现
Events的Age下面会显示4s (x3 over 25s),这想要表达的内容是,这条Events表示3次检测发生在最近的25秒,但是由于该Events本身已经是5秒前产生的了,因此我们得知这3次检测是在20秒内完成的。
root@node-1:~/pod# kubectl describe pod probe-httpget
从实验结果来看,存活探针的行为跟启动探针基本一致
该探针跟存活探针都是伴随这pod的整个生命周期的。就绪探针的检测周期为1秒,这个频率很高,当检测到3次失败时pod将被标记为未就绪,但是不会被重启,这一点和前两个探针不同。该探针的意义在于防止运行中的容器虽然没死但是无法响应请求(未就绪)而影响业务的情况发生,因此一旦确定容器未就绪,就立即停止对该容器的访问,保证业务的正常。试想一下因为一个容器未就绪,某个请求刚好到达该问题容器,这个请求就会一直无法被正常响应,这对用户体验来讲是很糟糕的。
readinessProbe:
periodSeconds: 1
timeoutSeconds: 1
successThreshold: 1
failureThreshold: 3
httpGet:
httpHeaders:
- name: Host
value: "www.example.com"
path: /
port: 80
scheme: HTTP
我们做一次破坏性试验,实验的配置文件如下,基于该配置,我预测容器将在10秒启动完成后,提示就绪探针失败
readinessProbe:
periodSeconds: 1
timeoutSeconds: 1
successThreshold: 1
failureThreshold: 3
httpGet:
httpHeaders:
- name: Host
value: "www.example.com"
path: /abc # 该配置相当于访问一个不存在的页面,返回值将为404
port: 80
scheme: HTTP
观测点1:查看pod是否在启动后一直处于未就绪状态
Pod在经过了54秒后任然没有就绪,而且没有重启动作发生
root@node-1:~/pod# kubectl get pod
NAME READY STATUS RESTARTS AGE
probe-httpget 0/1 Running 0 54s
观测点2:查看重启后Pod的事件
在Events中查看到了就绪探针失败的告警,但是没有任何重启的动作,符合我们的预期。直到能够探测到容器就绪,否则Pod将一直保持未就绪状态
root@node-1:~/pod# kubectl describe pod probe-httpget
这里的区别在于原先的httpGet模式被替换成了tcpSocket,其工作原理基本一致,不再赘述
apiVersion: v1
kind: Pod
metadata:
name: probe-tcp
namespace: default
labels:
name: probe-tcp
annotations:
name: probe-tcp
spec:
restartPolicy: Always
dnsPolicy: Default
containers:
- name: nginx
image: nginx:1.23
imagePullPolicy: IfNotPresent
startupProbe:
periodSeconds: 10
timeoutSeconds: 1
successThreshold: 1
failureThreshold: 3
tcpSocket:
port: 80
livenessProbe:
periodSeconds: 10
timeoutSeconds: 1
successThreshold: 1
failureThreshold: 3
tcpSocket:
port: 80
readinessProbe:
periodSeconds: 1
timeoutSeconds: 1
successThreshold: 1
failureThreshold: 3
tcpSocket:
port: 80