kubernetes之pod健康检查

Posted ssgeek

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了kubernetes之pod健康检查相关的知识,希望对你有一定的参考价值。

kubernetes之pod健康检查

1、概述和分类

pod通过两类探针来检查容器的健康状态。分别是LivenessProbe(存活性探测)和ReadinessProbe(就绪型探测)

2、LivenessProbe探针(存活性探测)

用于判断容器是否健康(Running状态)并反馈给kubelet。有不少应用程序长时间持续运行后会逐渐转为不可用的状态,并且仅能通过重启操作恢复,kubernetes的容器存活性探测机制可发现诸如此类问题,并依据探测结果结合重启策略触发后的行为。存活性探测是隶属于容器级别的配置,kubelet可基于它判定何时需要重启一个容器。如果一个容器不包含LivenessProbe探针,那么kubelet认为该容器的LivenessProbe探针返回的值永远是Success。Pod spec为容器列表中的相应容器定义其专用的探针即可启用存活性检测,目前,kubernetes的容器支持存活性检测的方法包含以下三种:ExecAction、TCPSocketAction和HTTPGetAction。

3、ReadinessProbe探针(就绪型探测)

用于判断容器服务是否可用(Ready状态),达到Ready状态的Pod才可以接收请求。对于被Service管理的Pod,Service与Pod Endpoint的关联关系也将基于Pod是否Ready进行设置。Pod对象启动后,容器应用通常需要一段时间才能完成其初始化的过程,例如加载配置或数据,甚至有些程序需要运行某类的预热过程,若在此阶段完成之前即接入客户端的请求,势必会因为等待太久而影响用户体验。因此应该避免于Pod对象启动后立即让其处理客户端请求。而是等待容器初始化工作执行完成并转为Ready状态。尤其是存在其他提供相同服务的Pod对象的场景更是如此。如果在运行过程中Ready状态变为False,则系统自动将其从Service的后端Endpoint列表中隔离出去,后续再把恢复到Ready状态的Pod加回后端Endpoint列表。这样就能保证客户端在访问Service时不会被转发到服务不可用的Pod示例上。

4、探针的实现方式

LivenessProbeReadinessProbe均可配置以下三种探针实现方式:
可参考官方文档:https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/

4.1、ExecAction

通过在目标容器中执行由用户自定义的命令来判定容器的健康状态,即在容器内部执行一个命令,如果改命令的返回码为0,则表明容器健康。spec.containers.LivenessProbe字段用于定义此类检测,它只有一个可用属性command,用于指定要执行的命令,下面是在资源清单文件中使用liveness-exec方式的示例:

apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness-exec
  name: liveness-exec
spec:
  containers:
  - name: liveness-demo
    image: busybox
    args:
    - /bin/sh
    - -c
    - touch /tmp/healthy; sleep 60; rm -rf /tmp/healthy; sleep 600
    livenessProbe:
      exec:
        command:
        - test
        - -e
        - /tmp/healthy
      initialDelaySeconds: 15
      timeoutSeconds: 1

上面的资源清单中定义了一个Pod对象,基于busybox镜像启动一个运行“touch /tmp/healthy; sleep 60; rm -rf /tmp/healthy; sleep 600”命令的容器,此命令在容器启动时创建/tmp/healthy文件,并于60秒之后将其删除,存活性探针运行“test -e /tmp/healthy”命令检查文件的存在性,若文件存在则返回状态码为0,表示成功通过测试。
创建该资源,并通过kubectl describe pods liveness-exec查看详细信息

Containers:
  liveness-demo:
    Container ID:  docker://a2974585905bdeef4ab39ba9a87bf710a61beae5180f31907ba33c8725c0bf79
    Image:         busybox
    Image ID:      docker-pullable://busybox@sha256:895ab622e92e18d6b461d671081757af7dbaa3b00e3e28e12505af7817f73649
    Port:          <none>
    Host Port:     <none>
    Args:
      /bin/sh
      -c
      touch /tmp/healthy; sleep 60; rm -rf /tmp/healthy; sleep 600
    State:          Running
      Started:      Wed, 14 Aug 2019 11:26:09 +0800
    Last State:     Terminated
      Reason:       Error
      Exit Code:    137
      Started:      Wed, 14 Aug 2019 11:24:10 +0800
      Finished:     Wed, 14 Aug 2019 11:26:08 +0800
    Ready:          True
    Restart Count:  2
    Liveness:       exec [test -e /tmp/healthy] delay=0s timeout=1s period=10s #success=1 #failure=3
    Environment:    <none>
......
Events:
  Type     Reason     Age                  From                  Message
  ----     ------     ----                 ----                  -------
  Normal   Scheduled  2m26s                default-scheduler     Successfully assigned default/liveness-exec to 172.16.1.66
  Warning  Unhealthy  57s (x3 over 77s)    kubelet, 172.16.1.66  Liveness probe failed:
  Normal   Pulling    27s (x2 over 2m23s)  kubelet, 172.16.1.66  pulling image "busybox"
  Normal   Pulled     27s (x2 over 2m23s)  kubelet, 172.16.1.66  Successfully pulled image "busybox"
  Normal   Killing    27s                  kubelet, 172.16.1.66  Killing container with id docker://liveness-demo:Container failed liveness probe.. Container will be killed and recreated.
  Normal   Created    26s (x2 over 2m22s)  kubelet, 172.16.1.66  Created container
  Normal   Started    25s (x2 over 2m21s)  kubelet, 172.16.1.66  Started container

输出信息中清晰的显示了容器健康状态检测变化的相关信息:容器当前处于Running状态,但前一次是Terminated,原因是退出码为137的错误信息,它表示进程是被外部信号所终止的。137事实上是由两部分数字之和生成的:128+signum,其中signum是导致进程终止信号的数字标识。9标识SIGKILL,这意味着进程是被强行终止的
待容器重启完成后再次查看,容器已经处于正常运行状态,直到文件再次被删除,存活性探测失败而重启,从下面结果可以看出,名为liveness-exec的pod在10分钟内重启了5次

[root@master01 demo]# kubectl get pods liveness-exec 
NAME            READY   STATUS    RESTARTS   AGE
liveness-exec   1/1     Running   5          10m

需要注意的是,exec指定的命令运行于容器中,会消耗容器的可用资源配额,另外,考虑到探针操作的效率本身等因素,探针操作的命令应该尽可能简单和轻量。

4.2、HTTPGetAction

通过容器的ip地址,端口号及路径调用HTTPGet方法,如果响应的状态码大于等于200且小于400,则认为容器健康,spec.containers.livenessProbe.httpGet字段用于定义此类检测,它的可用配置字段包括如下几个:

  • host :请求的主机地址,默认为Pod IP;也可以在httpHeaders中使用Host:来定义
  • port :请求的端口,必选字段
  • httpHeader <[]Object>:自定义的请求报文首部
  • path :请求的HTTP资源路径,即URL path
  • scheme:建立连接使用的协议,仅可为HTTPHTTPS,默认为HTTP
    下面是在资源清单文件中使用liveness-http方式的示例,通过lifecycle最后那个的postStart hook创建了一个专用于httpGet测试的页面文件healthz
apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness
  name: liveness-http
spec:
  containers:
  - name: liveness-demo
    image: nginx:1.12-alpine
    ports:
    - name: http
      containerPort: 80
    lifecycle:
      postStart:
        exec:
          command:
          - /bin/sh
          - -c
          - 'echo Healty > /usr/share/nginx/html/healthz'
    livenessProbe:
      httpGet:
        path: /healthz
        port: http
        scheme: HTTP
      initialDelaySeconds: 30
      timeoutSeconds: 1

上面的清单文件中定义的httpGet测试中,请求的资源路径为/healthz,地址默认为Pod IP,端口使用了容器中定义的端口名称HTTP,这也是明确为容器指明要暴露的端口的用途之一,通过kubectl describe pods liveness-http查看容器正常运行,健康状态检测为正常

Containers:
  liveness-demo:
    Container ID:   docker://bf05e0a9e6e1ac95f67b91f0b167b9fc2e3ad0bd0ffa4336debcc4b3c24978a7
    Image:          nginx:1.12-alpine
    Image ID:       docker-pullable://nginx@sha256:3a7edf11b0448f171df8f4acac8850a55eff30d1d78c46cd65e7bc8260b0be5d
    Port:           80/TCP
    Host Port:      0/TCP
    State:          Running
      Started:      Wed, 14 Aug 2019 12:01:31 +0800
    Ready:          True
    Restart Count:  0
    Liveness:       http-get http://:http/healthz delay=30s timeout=1s period=10s #success=1 #failure=3
    Environment:    <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-g7ls6 (ro)
Conditions:
  Type              Status
  Initialized       True 
  Ready             True 
  ContainersReady   True 
  PodScheduled      True 
Volumes:
  default-token-g7ls6:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  default-token-g7ls6
    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  6m51s  default-scheduler     Successfully assigned default/liveness-http to 172.16.1.66
  Normal  Pulling    6m48s  kubelet, 172.16.1.66  pulling image "nginx:1.12-alpine"
  Normal  Pulled     6m43s  kubelet, 172.16.1.66  Successfully pulled image "nginx:1.12-alpine"
  Normal  Created    6m43s  kubelet, 172.16.1.66  Created container
  Normal  Started    6m42s  kubelet, 172.16.1.66  Started container

通过kubectl exec命令删除经由postStart hook创建的测试页面healthz

kubectl exec liveness-http rm /usr/share/nginx/html/healthz

再次查看资源详细信息,事件输出中的信息表明探测失败,容器被杀掉后被重新创建

......
Events:
  Type     Reason     Age               From                  Message
  ----     ------     ----              ----                  -------
  Normal   Scheduled  12m               default-scheduler     Successfully assigned default/liveness-http to 172.16.1.66
  Normal   Pulling    12m               kubelet, 172.16.1.66  pulling image "nginx:1.12-alpine"
  Normal   Pulled     12m               kubelet, 172.16.1.66  Successfully pulled image "nginx:1.12-alpine"
  Warning  Unhealthy  3s (x3 over 23s)  kubelet, 172.16.1.66  Liveness probe failed: HTTP probe failed with statuscode: 404
  Normal   Created    2s (x2 over 12m)  kubelet, 172.16.1.66  Created container
  Normal   Killing    2s                kubelet, 172.16.1.66  Killing container with id docker://liveness-demo:Container failed liveness probe.. Container will be killed and recreated.
  Normal   Pulled     2s                kubelet, 172.16.1.66  Container image "nginx:1.12-alpine" already present on machine
  Normal   Started    1s (x2 over 12m)  kubelet, 172.16.1.66  Started container

一般来说,HTTP类型的探测操作应该针对专用的URL路径进行,例如,示例中为其准备的/healthz,另外,此URL路径对应的web资源应该以轻量化的方式在内部对应用程序的各关键组件进行全面检测以确保它们可以正常向客户端提供完整的服务。
这种检测方式仅对分层架构中的当前一层有效,例如,它能检测应用程序工作正常与否的状态,但重启操作却无法解决其后端服务(如数据库或缓存服务)导致的故障,此时,容器可能会被一次次重启,直到后端服务恢复正常为止。

4.3、TCPSocketAction

通过容器的IP地址和端口号进行TCP检查,如果能够建立TCP连接,则表明容器健康。相比较来说,它比基于HTTP的探测要更高效,更节约资源,但精准度略低,毕竟建立连接成功未必意味着页面资源可用,spec.containers.livenessProbe.tcpSocket字段用于定义此类检测,它主要包含以下两个可用的属性:

  • host:请求连接的目标IP地址,默认为Pod IP
  • port:请求连接的目标端口,必选字段
    下面是在资源清单文件中使用liveness-tcp方式的示例,它向Pod IP的80/tcp端口发起连接请求,并根据连接建立的状态判定测试结果:
apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness
  name: liveness-tcp
spec:
  containers:
  - name: liveness-tcp-demo
    image: nginx:1.12-alpine
    ports:
    - name: http
      containerPort: 80
    livenessProbe:
      tcpSocket:
        port: 80
      initialDelaySeconds: 30
      timeoutSeconds: 1

通过kubectl describe pods liveness-http查看容器正常运行,健康状态检测为正常

Containers:
  liveness-tcp-demo:
    Container ID:   docker://816b27781aeb384e1305e0a5badebd5ea21ea98c834e62179cd1dac2a704ccd7
    Image:          nginx:1.12-alpine
    Image ID:       docker-pullable://nginx@sha256:3a7edf11b0448f171df8f4acac8850a55eff30d1d78c46cd65e7bc8260b0be5d
    Port:           80/TCP
    Host Port:      0/TCP
    State:          Running
      Started:      Wed, 14 Aug 2019 12:29:12 +0800
    Ready:          True
    Restart Count:  0
    Liveness:       tcp-socket :80 delay=30s timeout=1s period=10s #success=1 #failure=3
    Environment:    <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-g7ls6 (ro)
Conditions:
  Type              Status
  Initialized       True 
  Ready             True 
  ContainersReady   True 
  PodScheduled      True 
Volumes:
  default-token-g7ls6:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  default-token-g7ls6
    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  62s   default-scheduler     Successfully assigned default/liveness-tcp to 172.16.1.66
  Normal  Pulled     60s   kubelet, 172.16.1.66  Container image "nginx:1.12-alpine" already present on machine
  Normal  Created    60s   kubelet, 172.16.1.66  Created container
  Normal  Started    59s   kubelet, 172.16.1.66  Started container

5、探测行为属性

使用kubectl describe命令查看配置了存活性探测或者就绪型探测对象的详细信息时,其相关内容中会包含如下内容:

Liveness:       exec [test -e /tmp/healthy] delay=15s timeout=1s period=10s #success=1 #failure=3

它给出了探测方式及其额外的配置属性delaytimeoutperiodsuccessfailure及其各自的相关属性值,用户没有明确定义这些属性字段时,他们会使用各自的默认值,这些属性值可通过spec.containers.livenessProbe的如下属性字段来给出:

  • initialDelaySeconds <integer> :存活性探测延迟时长,即容器启动多久后再开始第一次探测操作,显示为delay属性,默认为0秒,即容器启动后立刻开始进行探测。
  • timeoutSeconds <integer>:存活性探测的超时时长,显示为timeout属性,默认为1s,最小值也为1s。
  • periodSeconds <integer>:存活性探测的频度,显示为period属性,默认为10s,最小值为1s;过高的频率会对pod对象带来较大的额外开销,而过低的频率又会使得对错误的反应不及时。
  • successThreshold <integer>:处于失败状态时,探测操作至少连续多少次的成功才被认为是通过检测,显示为#success属性,默认值为1,最小值也为1。
  • failureThreshold:处于成功状态时,探测操作至少连续多少次的失败才被视为是检测不通过,显示为#failure属性,默认值为3,最小值为1。

6、扩展的探测机制

kubernetesReadinessProbe机制可能无法满足某些复杂应用对容器内服务可用状态的判断,所以kubernetes1.11版本开始引入了Pod Ready++特性对Readiness探测机制进行扩展,在1.14版本时达到GA稳定版本,称其为Pod Readiness Gates
通过Pod Readiness Gates机制,用户可以将自定义的ReadinessProbe探测方式设置在Pod上,辅助kubernetes设置Pod何时达到服务可用状态Ready,为了使自定义的ReadinessProbe生效,用户需要提供一个外部的控制器Controller来设置相应的Condition状态。PodReadiness Gatespod定义中的ReadinessGates字段进行设置,如下示例设置了一个类型为www.example.com/feature-1的新Readiness Gates

Kind: Pod
......
spec:
  readinessGates:
    - conditionType: "www.example.com/feature-1"
status:
  conditions:
    - type: Ready  # kubernetes系统内置的名为Ready的Condition
      status: "True"
      lastProbeTime: null
      lastTransitionTime: 2018-01-01T00:00:00Z
    - type: "www.example.com/feature-1"   # 用户定义的Condition
      status: "False"
      lastProbeTime: null
      lastTransitionTime: 2018-01-01T00:00:00Z
  containerStatuses:
    - containerID: docker://abcd...
      ready: true
......

新增的自定义Condition的状态status将由用户自定义的外部控制器设置,默认值为Falsekubernetes将在判断全部readinessGates条件都为True时,才设置pod为服务可用状态(Ready或True)
参考地址:https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#pod-readiness-gate

以上是关于kubernetes之pod健康检查的主要内容,如果未能解决你的问题,请参考以下文章

k8s探测机制之pod健康检查

云原生之K8S中Pod健康检测、服务可用性检查详解

关于 Kubernetes中Pod健康检测和服务可用性检查的一些笔记(LivenessProbe+ReadinessProbe)

健康检查调度约束

聊聊Kubernetes里的健康检查和Ingress方式服务暴露的流量走向

Pod 生命周期重启策略健康检查服务可用性检查