现在在Pod的整个生命周期中,能影响到Pod的就只剩下健康检查这一部分了。在k8s集群当中,我们可以通过配置liveness probe(存活探针)
和readiness probe(可读性探针)
来影响容器的生命周期。
kubelet通过
liveness probe
来确定你的应用程序是否正在运行,通俗点讲就是是否还活着
。一般来说,如果你的程序一旦崩溃了,kubernetes就会立刻知道这个程序已经终止了,然后就会重启这个程序。而我们的liveness probe
的目的就是来捕获到当前应用程序还没有终止,还没有崩溃,如果出现了这些情况,那么就重启处于该状态下的容器,使应用程序在存在bug的情况下依然能够继续运行下去。kubelet使用
readiness probe
来确定容器是否已经就绪就可以接收流量过来了。这个探针通俗点讲就是说是否准备好了
,现在可以开始工作了。只有当Pod中的容器都处于就绪状态的时候kubelet才会认定该Pod处于就绪状态,因为一个Pod下面可能会有多个容器。当然Pod如果处于非就绪状态,那么我们就会将它从Service的Endpoints列表中移除出来,这样我们的流量就不会被路由到这个Pod里面来了。
和前面的钩子函数一样的,我们这两个探针支持下面的几种配置方式:
- exec: 执行一段命令
- http:检测某个http请求
- tcpSocket:使用此配置,kubectl将尝试在指定端口上打开容器的套接字。如果可以建立连接,容器被认为是健康的,如果不能就认为是失败的。实际上就是检查端口。
我们先来给大家演示下存活探针的使用方法,首先我们用exec执行命令的方式来检测容器的存活,如下:(liveness-exec.yaml)
apiVersion: v1
kind: Pod
metadata:
name: liveness-exec
spec:
containers:
- name: liveness
image: busybox
args:
- /bin/sh
- -c
- touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600
livenessProbe:
exec:
command:
- cat
- /tmp/healthy
initialDelaySeconds: 5
periodSeconds: 5
我们这里需要用到一个新的属性:livenessProbe
,下面通过exec执行一段命令:
-
periodSeconds
: 表示让kubelet每隔5s执行一次存活探针,也就是每5s执行一次上面的cat /tmp/healthy
命令,如果命令执行成功了,将返回0,那么kubelet就会认为当前这个容器是存活的,如果返回的是非0值,那么kubelet就会把该容器杀掉然后重启它。periodSeconds
的值默认是10s,最小1s。 -
initialDelaySeconds
: 表示在第一次执行探针的时候要等待5s,这样能够确保我们的容器能够有足够的时间启动起来。大家可以想象一下,如果你的第一次执行探针等候的时间太短,是不是很有可能容器还没正常启动起来,所以存活探针很可能始终都是失败的,这样就会无休止的重启下去。
我们在容器启动的时候,执行了如下命令:
/bin/sh -c "touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600"
意思是说在容器最开始的30s内创建一个/tmp/healthy
文件,在这30s内执行cat /tmp/healthy
命令都会返回一个成功的返回码。30s后,我们删除这个文件,现在执行cat /tmp/healthy
是不是就会失败(默认检测失败3次才认为失败
),所以这个时候就会重启容器了。
以下是过程
.......
.......
.......
Conditions:
Type Status
Initialized True
Ready False
ContainersReady False
PodScheduled True
Volumes:
default-token-557h9:
Type: Secret (a volume populated by a Secret)
SecretName: default-token-557h9
Optional: false
QoS Class: BestEffort
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute for 300s
node.kubernetes.io/unreachable:NoExecute for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled <unknown> default-scheduler Successfully assigned default/liveness-exec to node02
Normal Pulling 2s kubelet, node02 Pulling image "busybox"
##################################################################
.......
.......
.......
Conditions:
Type Status
Initialized True
Ready True
ContainersReady True
PodScheduled True
Volumes:
default-token-557h9:
Type: Secret (a volume populated by a Secret)
SecretName: default-token-557h9
Optional: false
QoS Class: BestEffort
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute for 300s
node.kubernetes.io/unreachable:NoExecute for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled <unknown> default-scheduler Successfully assigned default/liveness-exec to node02
Normal Pulling 10s kubelet, node02 Pulling image "busybox"
Normal Pulled 4s kubelet, node02 Successfully pulled image "busybox"
Normal Created 4s kubelet, node02 Created container liveness
Normal Started 4s kubelet, node02 Started container liveness
##################################################################
.......
.......
.......
Conditions:
Type Status
Initialized True
Ready True
ContainersReady True
PodScheduled True
Volumes:
default-token-557h9:
Type: Secret (a volume populated by a Secret)
SecretName: default-token-557h9
Optional: false
QoS Class: BestEffort
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute for 300s
node.kubernetes.io/unreachable:NoExecute for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled <unknown> default-scheduler Successfully assigned default/liveness-exec to node02
Normal Pulling 16s kubelet, node02 Pulling image "busybox"
Normal Pulled 10s kubelet, node02 Successfully pulled image "busybox"
Normal Created 10s kubelet, node02 Created container liveness
Normal Started 10s kubelet, node02 Started container liveness
##################################################################
.......
.......
.......
Volumes:
default-token-557h9:
Type: Secret (a volume populated by a Secret)
SecretName: default-token-557h9
Optional: false
QoS Class: BestEffort
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute for 300s
node.kubernetes.io/unreachable:NoExecute for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled <unknown> default-scheduler Successfully assigned default/liveness-exec to node02
Normal Pulling 64s kubelet, node02 Pulling image "busybox"
Normal Pulled 58s kubelet, node02 Successfully pulled image "busybox"
Normal Created 58s kubelet, node02 Created container liveness
Normal Started 58s kubelet, node02 Started container liveness
Warning Unhealthy 14s (x3 over 24s) kubelet, node02 Liveness probe failed: cat: can't open '/tmp/healthy': No such file or directory
Normal Killing 14s kubelet, node02 Container liveness failed liveness probe, will be restarted
我们可以观察到容器是正常启动的,在隔一会,比如60s后,再查看下Pod的Event,在最下面有一条信息显示liveness probe失败
了,容器将要重启。然后可以查看到Pod的RESTARTS
值加1了:(我这里已经加到5了)
[root@node01 ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
hook-demo1 1/1 Running 0 25d
init-demo 1/1 Running 0 25d
liveness-exec 1/1 Running 5 7m20s
nginx-deploy-745bd74b44-4k2np 1/1 Running 0 26d
nginx-deploy-745bd74b44-jr7wv 1/1 Running 0 26d
nginx-deploy-745bd74b44-ngkrg 1/1 Running 0 26d
nginx-deploy-745bd74b44-plgkw 1/1 Running 0 26d
同样的,根据 periodSeconds 属性我们可以知道 kubelet 需要每隔3秒执行一次 liveness Probe,该探针将向容器中的 server 的 8080 端口发送一个 HTTP GET 请求。如果 server 的 /healthz 路径的 handler 返回一个成功的返回码,kubelet 就会认定该容器是活着的并且很健康,如果返回失败的返回码,kubelet 将杀掉该容器并重启它。initialDelaySeconds 指定kubelet 在该执行第一次探测之前需要等待3秒钟。
apiVersion: v1
kind: Pod
metada:
name: liveness-http
spec:
containers:
- name: liveness
image: cnych/liveness
args:
- /server
livenessProbe:
httpGet:
path: /healthz
port: 8080
httpHeaders:
- name: X-Custom-Header
value: Awesome
initialDelaySeconds: 3
periodSeconds: 3
除了上面的 exec 和 httpGet 两种检测方式之外,还可以通过 tcpSocket 方式来检测端口是否正常,大家可以按照上面的方式结合kubectl explain命令自己来验证下这种方式。
另外前面我们提到了探针里面有一个initialDelaySeconds
的属性,可以来配置第一次执行探针的等待时间,对于启动非常慢的应用这个参数非常有用,比如 Jenkins、Gitlab 这类应用,但是如何设置一个合适的初始延迟时间呢?这个就和应用具体的环境有关系了,所以这个值往往不是通用的
,这样的话可能就会导致一个问题,我们的资源清单在别的环境下可能就会健康检查失败了,为解决这个问题,在 Kubernetes v1.16 版本官方特地新增了一个startupProbe(启动探针)
,该探针将推迟所有其他探针
,直到 Pod 完成启动为止
,使用方法和存活探针一样:
startupProbe:
httpGet:
path: /healthz
port: 8080
failureThreshold: 30 # 尽量设置大点
periodSeconds: 10
比如上面这里的配置表示我们的慢速容器最多可以有5分钟(30个检查 * 10秒= 300s)来完成启动。
有的时候,应用程序可能暂时无法对外提供服务,例如,应用程序可能需要在启动期间加载大量数据或配置文件。在这种情况下,您不想杀死应用程序,也不想对外提供服务。那么这个时候我们就可以使用readiness probe来检测和减轻这些情况。 Pod 中的容器可以报告自己还没有准备,不能处理 Kubernetes 服务发送过来的流量。readiness probe的配置跟liveness probe基本上一致的。唯一的不同是使用readinessProbe而不是livenessProbe。两者如果同时使用的话就可以确保流量不会到达还未准备好的容器,准备好过后,如果应用程序出现了错误,则会重新启动容器。对于就绪探针我们会在后面 Service 的章节和大家继续介绍。
另外除了上面的initialDelaySeconds和periodSeconds属性外,探针还可以配置如下几个参数:
- timeoutSeconds:探测超时时间,默认1秒,最小1秒。
- successThreshold:探测失败后,最少连续探测成功多少次才被认定为成功。默认是 1,但是如果是
liveness
则必须是 1。最小值是 1。 - failureThreshold:探测成功后,最少连续探测失败多少次才被认定为失败。默认是 3,最小值是 1。