05|05 Pod健康检查

现在在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: TypeStatus InitializedTrue ReadyFalse ContainersReadyFalse PodScheduledTrue Volumes: default-token-557h9: Type:Secret (a volume populated by a Secret) SecretName:default-token-557h9 Optional:false QoS Class:BestEffort Node-Selectors: Tolerations:node.kubernetes.io/not-ready:NoExecute for 300s node.kubernetes.io/unreachable:NoExecute for 300s Events: TypeReasonAgeFromMessage ------------------------- NormalScheduleddefault-schedulerSuccessfully assigned default/liveness-exec to node02 NormalPulling2skubelet, node02Pulling image "busybox"################################################################## ....... ....... ....... Conditions: TypeStatus InitializedTrue ReadyTrue ContainersReadyTrue PodScheduledTrue Volumes: default-token-557h9: Type:Secret (a volume populated by a Secret) SecretName:default-token-557h9 Optional:false QoS Class:BestEffort Node-Selectors: Tolerations:node.kubernetes.io/not-ready:NoExecute for 300s node.kubernetes.io/unreachable:NoExecute for 300s Events: TypeReasonAgeFromMessage ------------------------- NormalScheduleddefault-schedulerSuccessfully assigned default/liveness-exec to node02 NormalPulling10skubelet, node02Pulling image "busybox" NormalPulled4skubelet, node02Successfully pulled image "busybox" NormalCreated4skubelet, node02Created container liveness NormalStarted4skubelet, node02Started container liveness ################################################################## ....... ....... ....... Conditions: TypeStatus InitializedTrue ReadyTrue ContainersReadyTrue PodScheduledTrue Volumes: default-token-557h9: Type:Secret (a volume populated by a Secret) SecretName:default-token-557h9 Optional:false QoS Class:BestEffort Node-Selectors: Tolerations:node.kubernetes.io/not-ready:NoExecute for 300s node.kubernetes.io/unreachable:NoExecute for 300s Events: TypeReasonAgeFromMessage ------------------------- NormalScheduleddefault-schedulerSuccessfully assigned default/liveness-exec to node02 NormalPulling16skubelet, node02Pulling image "busybox" NormalPulled10skubelet, node02Successfully pulled image "busybox" NormalCreated10skubelet, node02Created container liveness NormalStarted10skubelet, node02Started 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: Tolerations:node.kubernetes.io/not-ready:NoExecute for 300s node.kubernetes.io/unreachable:NoExecute for 300s Events: TypeReasonAgeFromMessage ------------------------- NormalScheduleddefault-schedulerSuccessfully assigned default/liveness-exec to node02 NormalPulling64skubelet, node02Pulling image "busybox" NormalPulled58skubelet, node02Successfully pulled image "busybox" NormalCreated58skubelet, node02Created container liveness NormalStarted58skubelet, node02Started container liveness WarningUnhealthy14s (x3 over 24s)kubelet, node02Liveness probe failed: cat: can't open '/tmp/healthy': No such file or directory NormalKilling14skubelet, node02Container liveness failed liveness probe, will be restarted

我们可以观察到容器是正常启动的,在隔一会,比如60s后,再查看下Pod的Event,在最下面有一条信息显示liveness probe失败了,容器将要重启。然后可以查看到Pod的RESTARTS值加1了:(我这里已经加到5了)
[root@node01 ~]# kubectl get pods NAMEREADYSTATUSRESTARTSAGE hook-demo11/1Running025d init-demo1/1Running025d liveness-exec1/1Running57m20s nginx-deploy-745bd74b44-4k2np1/1Running026d nginx-deploy-745bd74b44-jr7wv1/1Running026d nginx-deploy-745bd74b44-ngkrg1/1Running026d nginx-deploy-745bd74b44-plgkw1/1Running026d

同样的,根据 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

【05|05 Pod健康检查】比如上面这里的配置表示我们的慢速容器最多可以有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。

    推荐阅读