应用在运行过程中难免会出现错误,如程序异常、软件异常、硬件故障、网络故障等。因此,系统通过一些手段来判断应用是否运行正常,这些手段称之为健康检查(诊断)。

前置知识

回顾一下Pod的生命周期:

image-20220217105117106

检查机制(Check mechanisms)

有以下几种方法来检查容器:

  • exec:在容器内执行指定命令。如果命令退出时返回码为 0 则认为诊断成功。
  • gRPC:使用gRPC执行远程过程调用。目标应该实施gRPC运行状况检查。如果响应的状态为SERVING,则认为诊断成功。gRPC检查是一项Alpha功能,仅当您启用GRPCContainerProbe时才可用。
  • httpGet:对容器的 IP 地址上指定端口和路径执行 HTTP Get 请求。如果响应的状态码大于等于 200 且小于 400,则诊断被认为是成功的。
  • tcpSocket:对容器的 IP 地址上的指定端口执行 TCP 检查。如果端口打开,则诊断被认为是成功的。

每次探测都将获得以下三种结果之一:

  • Success(成功):容器通过了诊断。
  • Failure(失败):容器未通过诊断。
  • Unknown(未知):诊断失败,因此不会采取任何行动。

针对运行中的容器,kubelet 可以选择是否执行以下三种探针,以及如何针对探测结果作出反应:

  • livenessProbe:指示容器是否正在运行。如果存活态探测失败,则 kubelet 会杀死容器, 并且容器将根据其重启策略决定未来。如果容器不提供存活探针, 则默认状态为 Success
  • readinessProbe:指示容器是否准备好为请求提供服务。如果就绪态探测失败, 端点控制器将从与 Pod 匹配的所有服务的端点列表中删除该 Pod 的 IP 地址。 初始延迟之前的就绪态的状态值默认为 Failure。 如果容器不提供就绪态探针,则默认状态为 Success
  • startupProbe: 指示容器中的应用是否已经启动。如果提供了启动探针,则所有其他探针都会被 禁用,直到此探针成功为止。如果启动探测失败,kubelet 将杀死容器,而容器依其重启策略进行重启。 如果容器没有提供启动探测,则默认状态为 Success

exec方式检测存活示例

创建一个 Pod,其中运行一个基于 busybox 镜像的容器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
apiVersion: v1
kind: Pod
metadata:
labels:
test: liveness
name: liveness-exec
spec:
containers:
- name: liveness
image: registry.aliyuncs.com/google_containers/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

当容器启动时,执行如下的命令:

1
/bin/sh -c "touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600"

这个容器生命的前 30 秒, /tmp/healthy 文件是存在的。 所以在这最开始的 30 秒内,执行命令 cat /tmp/healthy 会返回成功代码。 30 秒之后,执行命令 cat /tmp/healthy 就会返回失败代码。

事件如下:

1
2
3
4
5
6
7
8
9
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 41s default-scheduler Successfully assigned default/liveness-exec to node-1
Normal Pulling 40s kubelet Pulling image "registry.aliyuncs.com/google_containers/busybox"
Normal Pulled 39s kubelet Successfully pulled image "registry.aliyuncs.com/google_containers/busybox" in 727.748993ms
Normal Created 39s kubelet Created container liveness
Normal Started 39s kubelet Started container liveness
Warning Unhealthy 1s (x2 over 6s) kubelet Liveness probe failed: cat: can't open '/tmp/healthy': No such file or directory

检查看这个容器被重启了:

1
2
3
[root@master test]# kubectl get pod
NAME READY STATUS RESTARTS AGE
liveness-exec 1/1 Running 1 82s

httpGet方式检测存活示例

创建一个 Pod ,其中运行一个基于 liveness 镜像的容器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
apiVersion: v1
kind: Pod
metadata:
labels:
test: liveness
name: liveness-http
spec:
containers:
- name: liveness
image: mirrorgooglecontainers/liveness
args:
- /server
livenessProbe:
httpGet:
path: /healthz
port: 8080
httpHeaders:
- name: Custom-Header
value: Awesome
initialDelaySeconds: 3
periodSeconds: 3

通过看 Pod 事件来检测存活探测器已经失败了并且容器被重新启动了。

1
kubectl describe pod liveness-http

事件如下:

1
2
3
4
5
6
7
8
9
10
11
12
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 100s default-scheduler Successfully assigned default/liveness-http to node-1
Normal Pulled 84s kubelet Successfully pulled image "mirrorgooglecontainers/liveness" in 15.537483195s
Normal Pulled 52s kubelet Successfully pulled image "mirrorgooglecontainers/liveness" in 15.375906607s
Normal Pulled 19s kubelet Successfully pulled image "mirrorgooglecontainers/liveness" in 15.388654652s
Normal Created 18s (x3 over 83s) kubelet Created container liveness
Normal Started 18s (x3 over 83s) kubelet Started container liveness
Warning Unhealthy 1s (x9 over 73s) kubelet Liveness probe failed: HTTP probe failed with statuscode: 500
Normal Killing 1s (x3 over 67s) kubelet Container liveness failed liveness probe, will be restarted
Normal Pulling 1s (x4 over 99s) kubelet Pulling image "mirrorgooglecontainers/liveness"

tcpSocket方式检测存活示例

创建一个 Pod ,其中运行一个基于 goproxy 镜像的容器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
apiVersion: v1
kind: Pod
metadata:
name: goproxy
labels:
app: goproxy
spec:
containers:
- name: goproxy
image: mirrorgooglecontainers/goproxy:0.1
ports:
- containerPort: 8080
readinessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 15
periodSeconds: 20

kubelet 会在容器启动 5 秒后发送第一个就绪探测。 这会尝试连接 goproxy 容器的 8080 端口。 如果探测成功,这个 Pod 会被标记为就绪状态,kubelet 将继续每隔 10 秒运行一次检测。

除了就绪探测,这个配置包括了一个存活探测。 kubelet 会在容器启动 15 秒后进行第一次存活探测。 与就绪探测类似,会尝试连接 goproxy 容器的 8080 端口。 如果存活探测失败,这个容器会被重新启动。