一:健康检查概述
又被称为探针(probe),探针是检查pod资源
注意:规则可以同时定义
1.1 探针策略类型
- livenessProbe 如果检查失败不存活,将杀死容器,根据Pod的restartPlicy来操作
kubelet](https://kubernetes.io/zh/docs/admin/kubelet/) 使用存活探测器来知道什么时候要重启容器。例如,存活探测器可以捕捉到死锁(应用程序在运行,但是无法继续执行后面的步骤)。这样的情况下重启容器有助于让应用程序在有问题的情况下更可用。
- ReadinessProbe 如果检查失败没有准备好,kubernetes会把pod从service endpoints中剔除,endpoint相当于后端的群集,从群集中剔除掉
kubelet 使用就绪探测器可以知道容器什么时候准备好了并可以开始接受请求流量, 当一个 Pod 内的所有容器都准备好了,才能把这个 Pod 看作就绪了。这种信号的一个用途就是控制哪个 Pod 作为 Service 的后端。在 Pod 还没有准备好的时候,会从 Service 的负载均衡器中被剔除的。
备注:还有一个启动探测器startup probes
kubelet 使用启动探测器可以知道应用程序容器什么时候启动了。如果配置了这类探测器,就可以控制容器在启动成功后再进行存活性和就绪检查,确保这些存活、就绪探测器不会影响应用程序的启动。这可以用于对慢启动容器进行存活性检测,避免它们在启动运行之前就被杀掉
官方文档参考:https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/
1.2 probe 探针支持三种检查方法
- httpGet:发送http请求,返回200-400范围状态码为成功
- exec:执行shell命令返回状态码为0代表成功
exec检查后面所有pod资源,触发策略就执行
- tcpSocket:发起tcp socket建立成功 tcpsoucket 是三次握手规则
二:使用exec检查方法
定义存活命令
许多长时间运行的应用程序最终会过渡到断开的状态,除非重新启动,否则无法恢复。
Kubernetes 提供了存活探测器livenessprobe 来发现并补救这种情况。
2.1 编辑测试pod的yaml
[root@master1 ~]# vim pod4.yaml
apiVersion: v1
kind: Pod
metadata:
labels:
test: liveness
name: liveness-exec
spec:
containers:
- name: liveness
image: busybox
args:
- /bin/sh
- -c
- touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 300
livenessProbe:
#执行存活的exec探针策略
exec:
command:
- cat
- /tmp/healthy
initialDelaySeconds: 5
#容器启动5秒后开始探测
periodSeconds: 5
#每5秒创建一次
在这个配置文件中,可以看到 Pod 中只有一个容器。
periodSeconds
字段指定了 kubelet 应该每 5 秒执行一次存活探测。
initialDelaySeconds
字段告诉 kubelet 在执行第一次探测前应该等待 5 秒。
kubelet 在容器内执行命令 cat /tmp/healthy
来进行探测。
如果命令执行成功并且返回值为 0,kubelet 就会认为这个容器是健康存活的。
如果这个命令返回非 0 值,kubelet 会杀死这个容器并重新启动它。
2.2 启动yaml创建pod
[root@master1 ~]# kubectl apply -f pod4.yaml
pod/liveness-exec created
[root@master1 ~]# kubectl get pods -w
liveness-exec 0/1 ContainerCreating 0 2s
liveness-exec 1/1 Running 0 3s
当容器启动时,执行如下的命令:
/bin/sh -c "touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 300"
这个容器生命的前 30 秒, /tmp/healthy
文件是存在的。
所以在这最开始的 30 秒内,执行命令 cat /tmp/healthy
会返回成功码。
30 秒之后,执行命令 cat /tmp/healthy
就会返回失败码。
2.3 在 30 秒内,查看 Pod 的事件
^C[root@master1 ~]# kubectl describe pod liveness-exec
输出结果显示还没有存活探测器失败:
FirstSeen LastSeen Count From SubobjectPath Type Reason Message
--------- -------- ----- ---- ------------- -------- ------ -------
24s 24s 1 {default-scheduler } Normal Scheduled Successfully assigned liveness-exec to worker0
23s 23s 1 {kubelet worker0} spec.containers{liveness} Normal Pulling pulling image "k8s.gcr.io/busybox"
23s 23s 1 {kubelet worker0} spec.containers{liveness} Normal Pulled Successfully pulled image "k8s.gcr.io/busybox"
23s 23s 1 {kubelet worker0} spec.containers{liveness} Normal Created Created container with docker id 86849c15382e; Security:[seccomp=unconfined]
23s 23s 1 {kubelet worker0} spec.containers{liveness} Normal Started Started container with docker id 86849c15382e
2.4 35 秒之后,再来看 Pod 的事件:
[root@master1 ~]# kubectl describe pod liveness-exec
在输出结果的最下面,有信息显示存活探测器失败了,这个容器被杀死并且被重建了。
FirstSeen LastSeen Count From SubobjectPath Type Reason Message
--------- -------- ----- ---- ------------- -------- ------ -------
37s 37s 1 {default-scheduler } Normal Scheduled Successfully assigned liveness-exec to worker0
36s 36s 1 {kubelet worker0} spec.containers{liveness} Normal Pulling pulling image "k8s.gcr.io/busybox"
36s 36s 1 {kubelet worker0} spec.containers{liveness} Normal Pulled Successfully pulled image "k8s.gcr.io/busybox"
36s 36s 1 {kubelet worker0} spec.containers{liveness} Normal Created Created container with docker id 86849c15382e; Security:[seccomp=unconfined]
36s 36s 1 {kubelet worker0} spec.containers{liveness} Normal Started Started container with docker id 86849c15382e
2s 2s 1 {kubelet worker0} spec.containers{liveness} Warning Unhealthy Liveness probe failed: cat: can't open '/tmp/healthy': No such file or directory
2.5 再等另外 30 秒,检查看这个容器被重启了:
[root@master1 ~]# kubectl get pods -w
liveness-exec 1/1 Running 0 60s
liveness-exec 1/1 Running 1 93s
liveness-exec 1/1 Running 2 2m48s
liveness-exec 1/1 Running 3 4m6s
输出结果显示 RESTARTS
的值增加了
三:httpGet方式
定义一个存活态 HTTP 请求接口
第二种类型的存活探测方式是使用 HTTP GET 请求。下面是一个 Pod 的配置文件,其中运行一个基于 liveness
镜像的容器。
3.1 编写yaml
[root@master1 ~]# vim pod5.yaml
apiVersion: v1
kind: Pod
metadata:
labels:
test: liveness
name: liveness-http
spec:
containers:
- name: nginx
image: nginx
# args:
# - /server
livenessProbe:
httpGet:
path: /healthz
port: 80
httpHeaders:
- name: Custom-Header
value: Awesome
initialDelaySeconds: 3
periodSeconds: 3
在这个配置文件中,可以看到 Pod 也只有一个容器。
periodSeconds
字段指定了 kubelet 每隔 3 秒执行一次存活探测。
initialDelaySeconds
字段告诉 kubelet 在执行第一次探测前应该等待 3 秒。
kubelet 会向容器内运行的服务(服务会监听 80 端口)发送一个 HTTP GET 请求来执行探测。如果服务上 /healthz
路径下的处理程序返回成功码,则 kubelet 认为容器是健康存活的。如果处理程序返回失败码,则 kubelet 会杀死这个容器并且重新启动它。
任何大于或等于 200 并且小于 400 的返回码标示成功,其它返回码都标示失败。
容器存活的最开始 10 秒中,/healthz
处理程序返回一个 200 的状态码。之后处理程序返回 500 的状态码。
http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
duration := time.Now().Sub(started)
if duration.Seconds() > 10 {
w.WriteHeader(500)
w.Write([]byte(fmt.Sprintf("error: %v", duration.Seconds())))
} else {
w.WriteHeader(200)
w.Write([]byte("ok"))
}
})
kubelet 在容器启动之后 3 秒开始执行健康检测。所以前几次健康检查都是成功的。但是 10 秒之后,健康检查会失败,并且 kubelet 会杀死容器再重新启动容器。
3.2 创建一个 Pod 来测试 HTTP 的存活检测:
[root@master1 ~]#kubectl apply -f pod5.yaml
pod/liveness-http created
[root@master1 ~]# kubectl get pods -w
NAME READY STATUS RESTARTS AGE
liveness-http 0/1 ContainerCreating 0 4s
my-tomcat-6cbc7c4d65-gpkxr 1/1 Running 0 26h
my-tomcat-6cbc7c4d65-hdmhc 1/1 Running 0 26h
nginx-6c94d899fd-xsxct 1/1 Running 1 4d22h
nginx-deployment-78cdb5b557-6z2sf 1/1 Running 1 2d22h
nginx-deployment-78cdb5b557-9pdf8 1/1 Running 1 2d22h
nginx-deployment-78cdb5b557-f2hx2 1/1 Running 1 2d22h
pod1 1/1 Running 1 2d12h
liveness-http 1/1 Running 0 18s
liveness-http 1/1 Running 1 37s
liveness-http 1/1 Running 2 49s
3.3 通过看 Pod 事件来检测存活探测器已经失败了并且容器被重新启动了。
但是原因并不是因为之前预想的
原因是因为在容器还未启动的情况下,就执行探测,结果可想而知,直接就kill掉然后重建
^C[root@master1 ~]kubectl describe pod liveness-http
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 55s default-scheduler Successfully assigned default/liveness-http to 192.168.247.143
Normal Pulling 8s (x3 over 54s) kubelet, 192.168.247.143 pulling image "nginx"
Normal Killing 8s (x2 over 29s) kubelet, 192.168.247.143 Killing container with id docker://nginx:Container failed liveness probe.. Container will be killed and recreated.
Normal Pulled 6s (x3 over 39s) kubelet, 192.168.247.143 Successfully pulled image "nginx"
Normal Created 6s (x3 over 39s) kubelet, 192.168.247.143 Created container
Normal Started 6s (x3 over 38s) kubelet, 192.168.247.143 Started container
Warning Unhealthy 2s (x7 over 35s) kubelet, 192.168.247.143 Liveness probe failed: HTTP probe failed with statuscode: 404
Normal Killing 9s (x3 over 42s) kubelet, 192.168.247.143 Killing container with id docker://nginx:Container failed liveness probe.. Container will be killed and recreated.
备注:在 1.13(包括 1.13版本)之前的版本中,如果在 Pod 运行的节点上设置了环境变量 http_proxy
(或者 HTTP_PROXY
),HTTP 的存活探测会使用这个代理。在 1.13 之后的版本中,设置本地的 HTTP 代理环境变量不会影响 HTTP 的存活探测
3.4 查看日志
探针进行探测,三秒探测一次
^C[root@master1 ~]# kubectl logs liveness-http
2020/05/13 05:00:04 [error] 6#6: *1 open() "/usr/share/nginx/html/healthz" failed (2: No such file or directory), client: 172.17.45.1, server: localhost, request: "GET /healthz HTTP/1.1", host: "172.17.45.5:80"
172.17.45.1 - - [13/May/2020:05:00:04 +0000] "GET /healthz HTTP/1.1" 404 154 "-" "kube-probe/1.12" "-"
2020/05/13 05:00:07 [error] 6#6: *2 open() "/usr/share/nginx/html/healthz" failed (2: No such file or directory), client: 172.17.45.1, server: localhost, request: "GET /healthz HTTP/1.1", host: "172.17.45.5:80"
172.17.45.1 - - [13/May/2020:05:00:07 +0000] "GET /healthz HTTP/1.1" 404 154 "-" "kube-probe/1.12" "-"
2020/05/13 05:00:10 [error] 6#6: *3 open() "/usr/share/nginx/html/healthz" failed (2: No such file or directory), client: 172.17.45.1, server: localhost, request: "GET /healthz HTTP/1.1", host: "172.17.45.5:80"
172.17.45.1 - - [13/May/2020:05:00:10 +0000] "GET /healthz HTTP/1.1" 404 154 "-" "kube-probe/1.12" "-"
四:定义 TCP 的存活探测
第三种类型的存活探测是使用 TCP 套接字。
通过配置,kubelet 会尝试在指定端口和容器建立套接字链接。
如果能建立链接,这个容器就被看作是健康的,如果不能则这个容器就被看作是有问题的。
4.1 编辑yaml文件
[root@master1 ~]# vim pod6.yaml
apiVersion: v1
kind: Pod
metadata:
name: liveness-tcp
labels:
app: liveness-tcp
spec:
containers:
- name: liveness-tcp
image: nginx
ports:
- containerPort: 80
readinessProbe:
tcpSocket:
port: 80
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
tcpSocket:
port: 80
initialDelaySeconds: 15
periodSeconds: 20
TCP 检测的配置和 HTTP 检测非常相似。
这个yaml同时使用就绪和存活探测器。kubelet 会在容器启动 5 秒后发送第一个就绪探测。这会尝试连接 goproxy
容器的 8080 端口。如果探测成功,这个 Pod 会被标记为就绪状态,kubelet 将继续每隔 10 秒运行一次检测。
除了就绪探测,这个配置包括了一个存活探测。kubelet 会在容器启动 15 秒后进行第一次存活探测。就像就绪探测一样,会尝试连接 liveness-tcp容器的 80 端口。如果存活探测失败,这个容器会被重新启动。
4.2 启动pod
[root@master1 ~]# kubectl apply -f pod6.yaml
pod/liveness-tcp created
[root@master1 ~]# kubectl get pods -w
NAME READY STATUS RESTARTS AGE
liveness-tcp 0/1 ContainerCreating 0 10s
liveness-tcp 0/1 Running 0 17s
liveness-tcp 1/1 Running 0 29s
4.3 15 秒之后,通过看 Pod 事件来检测存活探测器:
一切正常
[root@master1 ~]# kubectl describe pod liveness-tcp
Liveness: tcp-socket :80 delay=15s timeout=1s period=20s #success=1 #failure=3
Readiness: tcp-socket :80 delay=5s timeout=1s period=10s #success=1 #failure=3
4.4 删掉pod,修改yaml文件
[root@master1 ~]# kubectl delete -f pod6.yaml
pod "liveness-tcp" deleted
[root@master1 ~]# vim pod6.yaml
apiVersion: v1
kind: Pod
metadata:
name: liveness-tcp
labels:
app: liveness-tcp
spec:
containers:
- name: liveness-tcp
image: nginx
ports:
- containerPort: 80
args:
- /bin/sh
- -c
- sleep 20;killall -3 nginx
readinessProbe:
tcpSocket:
port: 80
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
tcpSocket:
port: 80
initialDelaySeconds: 15
periodSeconds: 20
4.5 重新生成pod
[root@master1 ~]# kubectl apply -f pod6.yaml
pod/liveness-tcp created
[root@master1 ~]# kubectl get pods -w
NAME READY STATUS RESTARTS AGE
liveness-tcp 0/1 ContainerCreating 0 4s
liveness-tcp 0/1 Running 0 6s
liveness-tcp 0/1 Error 0 26s
liveness-tcp 0/1 Running 1 31s
liveness-tcp 0/1 Error 1 51s
liveness-tcp 0/1 CrashLoopBackOff 1 63s
liveness-tcp 0/1 Running 2 68s
liveness-tcp 0/1 Error 2 88s
liveness-tcp 0/1 CrashLoopBackOff 2 103s
liveness-tcp 0/1 Running 3 2m
liveness-tcp 0/1 Error 3 2m20s
4.6 查看pod的describe
[root@master1 ~]# kubectl describe pod liveness-tcp
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 2m56s default-scheduler Successfully assigned default/liveness-tcp to 192.168.247.143
Normal Started 108s (x3 over 2m51s) kubelet, 192.168.247.143 Started container
Warning Unhealthy 97s (x4 over 2m37s) kubelet, 192.168.247.143 Readiness probe failed: dial tcp 172.17.45.2:80: connect: connection refused
Warning Unhealthy 93s (x2 over 2m33s) kubelet, 192.168.247.143 Liveness probe failed: dial tcp 172.17.45.2:80: connect: connection refused
Warning BackOff 73s (x4 over 2m5s) kubelet, 192.168.247.143 Back-off restarting failed container
Normal Pulling 61s (x4 over 2m55s) kubelet, 192.168.247.143 pulling image "nginx"
Normal Pulled 56s (x4 over 2m51s) kubelet, 192.168.247.143 Successfully pulled image "nginx"
Normal Created 56s (x4 over 2m51s) kubelet, 192.168.247.143 Created container
五:总结:
后端节点是网站服务器的话,使用tcp/http进行探针访问会导致日志访问流量变大
所以用exec用的多
5.1 对于 HTTP 或者 TCP 存活检测可以使用命名的[容器端口]
ports:
- name: liveness-port
containerPort: 8080
hostPort: 8080
livenessProbe:
httpGet:
path: /healthz
port: liveness-port
六:启动探针
备注:使用启动探测器保护慢启动容器
有时候,会有一些现有的应用程序在启动时需要较多的初始化时间。
要不影响对引起探测死锁的快速响应,这种情况下,设置存活探测参数是要技巧的。
技巧就是使用一个命令来设置启动探测,针对HTTP 或者 TCP 检测,可以通过设置 failureThreshold * periodSeconds
参数来保证有足够长的时间应对糟糕情况下的启动时间。
ports:
- name: liveness-port
containerPort: 8080
hostPort: 8080
livenessProbe:
httpGet:
path: /healthz
port: liveness-port
failureThreshold: 1
periodSeconds: 10
startupProbe:
httpGet:
path: /healthz
port: liveness-port
failureThreshold: 30
periodSeconds: 10
有启动探测,应用程序将会有最多 5 分钟(30 * 10 = 300s) 的时间来完成它的启动。 一旦启动探测成功一次,存活探测任务就会接管对容器的探测,对容器死锁可以快速响应。 如果启动探测一直没有成功,容器会在 300 秒后被杀死,并且根据 restartPolicy
来设置 Pod 状态
七:探针参数
探测器有很多配置字段,可以使用这些字段精确的控制存活和就绪检测的行为:
-
initialDelaySeconds
:容器启动后要等待多少秒后存活和就绪探测器才被初始化,默认是 0 秒,最小值是 0。 -
periodSeconds
:执行探测的时间间隔(单位是秒)。默认是 10 秒。最小值是 1。 -
timeoutSeconds
:探测的超时后等待多少秒。默认值是 1 秒。最小值是 1。 -
successThreshold
:探测器在失败后,被视为成功的最小连续成功数。默认值是 1。存活探测的这个值必须是 1。最小值是 1。 -
failureThreshold
:当 Pod 启动了并且探测到失败,Kubernetes 的重试次数。存活探测情况下的放弃就意味着重新启动容器。就绪探测情况下的放弃 Pod 会被打上未就绪的标签。默认值是 3。最小值是 1。
HTTP 探测器可以在 httpGet
上配置额外的字段:
-
host
:连接使用的主机名,默认是 Pod 的 IP。也可以在 HTTP 头中设置 “Host” 来代替。 -
scheme
:用于设置连接主机的方式(HTTP 还是 HTTPS)。默认是 HTTP。 -
path
:访问 HTTP 服务的路径。 -
httpHeaders
:请求中自定义的 HTTP 头。HTTP 头字段允许重复。 -
port
:访问容器的端口号或者端口名。如果数字必须在 1 ~ 65535 之间。
对于 HTTP 探测,kubelet 发送一个 HTTP 请求到指定的路径和端口来执行检测。除非 httpGet
中的 host
字段设置了,否则 kubelet 默认是给 Pod 的 IP 地址发送探测。如果 scheme
字段设置为了 HTTPS
,kubelet 会跳过证书验证发送 HTTPS 请求。大多数情况下,不需要设置host
字段。这里有个需要设置 host
字段的场景,假设容器监听 127.0.0.1,并且 Pod 的 hostNetwork
字段设置为了 true
。那么 httpGet
中的 host
字段应该设置为 127.0.0.1。可能更常见的情况是如果 Pod 依赖虚拟主机,你不应该设置 host
字段,而是应该在 httpHeaders
中设置 Host
。
对于一次 TCP 探测,kubelet 在节点上(不是在 Pod 里面)建立探测连接,这意味着你不能在 host
参数上配置 service name,因为 kubelet 不能解析 service name。