10-Health Check

强大的自愈能力是Kubernetes这类容器编排引擎的一个重要特性。

自愈的默认实现方式是自动重启发生故障的容器。

除此之外,还可以利用Liveness和Readiness探测机制设置更精细的健康检查。

默认的健康检查

每个容器启动时都会执行一个进程,此进程由Dockerfile的CMD或ENTRYPOINT指定。如果进程退出时返回码非零,则认为容器发生故障,Kubernetes就会根据restartPolicy重启容器。

下面模拟一个容器发生故障的场景

apiVersion: v1
kind: Pod
metadata:
  name: healthcheck
  labels:
    test: healthcheck
spec:
  restartPolicy: OnFailure
  containers:
     - name: healthcheck
        image: busybox
        args:
          - /bin/sh
          - -c
          - sleep 10; exit 1

Pod的restartPolicy设置为OnFailure,默认为Always。
sleep 10; exit 1模拟容器启动10秒后发生故障(返回值非0)。

可以看到容器已经重启2次

Liveness探测

在上面的例子中,容器进程返回值非零,Kubernetes则认为容器发生故障,需要重启。
有不少情况是发生了故障,但进程并不会退出。此时默认的健康检查策略已经不能满足需求。这就需要自定义判断容器是否健康的条件。

下面举例说明,创建Pod

apiVersion: v1
kind: Pod
metadata:
  name: liveness
  labels:
    test: liveness
spec:
  restartPolicy: OnFailure
  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: 10
      periodSeconds: 5

启动进程首先创建文件/tmp/healthy,30秒后删除。

现在的设定是:如果/tmp/healthy文件存在,则认为容器处于正常状态,反之则发生故障。

livenessProbe定义Liveness探测方法:
(1)通过cat命令检查/tmp/healthy文件是否存在。如果命令执行成功,返回值为零,本次Liveness探测成功;否则探测失败。
(2)initialDelaySeconds:指定容器启动10之后开始执行Liveness探测,
(3)periodSeconds:指定每5秒执行一次Liveness探测。Kubernetes如果连续执行3次Liveness探测均失败,则会杀掉并重启容器。

一段时间后通过kubectl describe pod liveness查看pod信息
关注日志部分内容

最后两条表明执行了健康检查和杀死容器(重启)

这时查看pod,发现已经重启了6次


Readiness探测

Liveness探测可以告诉Kubernetes什么时候通过重启容器实现自愈;
Readiness探测则是告诉Kubernetes什么时候可以将容器加入到Service负载均衡池中,对外提供服务(即容器准备就绪)。

Readiness探测的配置语法与Liveness探测完全一样,这里只是将前面例子中的liveness替换为了readiness

apiVersion: v1
kind: Pod
metadata:
  name: readiness
  labels:
    test: readiness
spec:
  restartPolicy: OnFailure
  containers:
  - name: readiness
    image: busybox
    args:
          - /bin/sh
          - -c
          - touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600
    readinessProbe:
      exec:
        command:
        - cat
        - /tmp/healthy
      initialDelaySeconds: 10
      periodSeconds: 5

apply后,观察pod的ready状态变化

(1)刚被创建时,READY状态为不可用。(因为10s后才开始探测)
(2)10秒后开始探测并成功返回,设置READY为可用。
(3)30秒后,/tmp/healthy被删除,连续3次Readiness探测均失败后,READY被设置为不可用。

通过kubectl describe pod readiness也可以看到Readiness探测失败的日志

注意:Readiness探测失败不会重启容器,而是将服务设置为不可用

总结

(1)Liveness探测和Readiness探测是两种Health Check机制,如果不配置,Kubernetes将对两种探测采取相同的默认行为,即通过判断容器启动进程的返回值是否为零来判断探测是否成功。

(2)两种探测的配置方法完全一样,支持的配置参数也一样。不同之处在于探测失败后的行为:Liveness探测是重启容器;Readiness探测则是将容器设置为不可用,不接收Service转发的请求。

(3)Liveness探测和Readiness探测是独立执行的,二者之间没有依赖,所以可以单独使用,也可以同时使用。

Health Check在滚动更新中的应用

Health Check一个重要的应用场景是滚动更新
现有一个正常运行的多副本应用,接下来对应用进行更新(比如使用更高版本的image),Kubernetes会启动新副本,然后发生了如下事件:
(1)正常情况下新副本需要10秒钟完成准备工作,在此之前无法响应业务请求。
(2)由于人为配置错误,副本始终无法完成准备工作(比如无法连接后端数据库)

如果没有配置Health Check,因为新副本本身没有异常退出,默认的Health Check机制会认为容器已经就绪,进而会逐步用新副本替换现有副本,其结果就是:当所有旧副本都被替换后,整个应用将无法对外提供服务。

如果正确配置了Health Check,新副本只有通过了Readiness探测才会被添加到Service;如果没有通过探测,现有副本不会被全部替换,业务仍然正常进行。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: app
spec:
  replicas: 10
  selector:
    matchLabels:
      run: app
  template:
    metadata:
      labels:
        run: app
    spec:
      containers:
      - name: app
        image: busybox
        args:
        - /bin/sh
        - -c 
        - sleep 10; touch /tmp/healthy; sleep 30000
        readinessProbe:
          exec:
            command:
            - cat
            - /tmp/healthy
          initialDelaySeconds: 10
          periodSeconds: 5

一段时间后。deployment和pod准备就绪

接下来滚动更新,修改配置文件

apiVersion: apps/v1
kind: Deployment
metadata:
  name: app
spec:
  replicas: 10
  selector:
    matchLabels:
      run: app
  template:
    metadata:
      labels:
        run: app
    spec:
      containers:
      - name: app
        image: busybox
        args:
        - /bin/sh
        - -c 
        - sleep 3000
        readinessProbe:
          exec:
            command:
            - cat
            - /tmp/healthy
          initialDelaySeconds: 10
          periodSeconds: 5

由于新副本中不存在/tmp/healthy,因此是无法通过Readiness探测的,也就是说,这时的更新时是有问题的

关注kubectl get pod输出:
(1)前5个Pod是新副本,目前处于NOT READY状态。
(2)旧副本从最初10个减少到8个。

再来看kubectl get deployment app的输出:
(1)期望10个READY的副本。
(2)UP-TO-DATE 5表示最新副本的数量。
(3)AVAILABLE 8表示当前处于READY状态的副本数。
(4)当前副本的总数为13,即8个旧副本+5个新副本。

新副本始终都无法通过Readiness探测,所以这个状态会一直保持下去。

上面模拟了一个滚动更新失败的场景。不过幸运的是:Health Check帮我们屏蔽了有缺陷的副本,同时保留了大部分旧副本,业务没有因更新失败受到影响。

为什么新创建的副本数是5个,同时只销毁了2个旧副本?原因是:滚动更新通过参数maxSurge和maxUnavailable来控制副本替换的数量。

1.maxSurge
此参数控制滚动更新过程中副本总数超过DESIRED的上限。
maxSurge默认值为25%。在上面的例子中,DESIRED为10,那么副本总数的最大值为roundUp(10+10*25%)=13,所以CURRENT就是13。

2.maxUnavailable
此参数控制滚动更新过程中,不可用的副本相占DESIRED的最大比例。
maxUnavailable默认值为25%。在上面的例子中,DESIRED为10,那么可用的副本数至少要为10 - roundDown(10* 25%)= 8,所以AVAILABLE是8。

maxSurge值越大,初始创建的新副本数量就越多;maxUnavailable值越大,初始销毁的旧副本数量就越多。

这里也可以理解滚动更新的过程:

(1)创建3个新副本使副本总数达到13个。
(2)销毁2个旧副本使可用的副本数降到8个。
(3)当2个旧副本成功销毁后,再创建2个新副本,使副本总数保持为13个。
(4)当新副本通过Readiness探测后,会使可用副本数增加,超过8。
(5)进而可以继续销毁更多的旧副本,使可用副本数回到8。
(6)旧副本的销毁使副本总数低于13,这样就允许创建更多的新副本。
(7)这个过程会持续进行,最终所有的旧副本都会被新副本替换,滚动更新完成。

如果滚动更新失败,可以通过kubectl rollout undo回滚到上一个版本

回滚完成

也可以自定义maxSurge和maxUnavailable


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容