kubernetes-健康检查
一、为什么需要健康检查(意义)
为了保证服务集群的稳定性,需要及时的对故障服务进行处理。无论是通过下线的方式,不再将请求转发到该服务,还是通过重启的方式,使服务得以自动恢复,都是有效的处理方式。
二、健康检查的策略
livenessProbe 存活性探针
kubelet 使用存活探测器来知道什么时候要重启容器。 例如,存活探测器可以捕捉到死锁(应用程序在运行,但是无法继续执行后面的步骤)。 这样的情况下重启容器有助于让应用程序在有问题的情况下更可用。
重启策略有以下几种:
- always:默认的重启策略,当容器停止,会创建新的容器
- onFailure:容器异常退出,重启容器
- Never:当容器终止退出,从不重启容器
readinessProbe 就绪性探针
kubelet 使用就绪探测器可以知道容器什么时候准备好了并可以开始接受请求流量, 当一个 Pod 内的所有容器都准备好了,才能把这个 Pod 看作就绪了。 控制器就会将此容器所在的Pod加入到Service的EndPoint列表中,那么Service则会将流量分发到该Pod上。反之,如果就绪性探针检查失败,表明该Pod还没准备好,不可以接收请求,那么控制器就会将此容器所在的Pod从对应的Service的EndPoint列表中移除。
总结
两者的区别简单来讲,
livenessProbe,容器不存活,就重启该容器
readinessProbe,容器不就绪,就不转发请求给该容器
三、健康检查的检测方式
- Exec:通过执行命令来检查服务是否正常,针对复杂检测或无HTTP接口的服务,命令返回值为0则表示容器健康。
- HTTPGet:通过发送http或https请求检查服务是否正常,返回【200,400)状态码则表明容器健康。
- TCPSocket:尝试在指定端口上建立TCP连接。如果它可以建立连接,容器被认为是健康的; 如果它不能被认为是不健康的。这常用于对gRPC或FTP服务的探测。
LivenessProbe探针配置
exec方式做健康探测
#pods/probe/exec-liveness.yaml
apiVersion: v1
kind: Pod
metadata:
labels:
test: liveness
name: liveness-exec
spec:
containers:
- name: liveness
image: k8s.gcr.io/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
- 当容器启动时,执行如下的命令:
/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 就会返回失败代码。
periodSeconds
: 每 5 秒执行一次存活探测initialDelaySeconds
: 服务启动后 应该等待 5 秒exec
:执行命令cat /tmp/healthy
来进行探测。 如果命令执行成功并且返回值为 0,kubelet 就会认为这个容器是健康存活的。 如果这个命令返回非 0 值,kubelet 会杀死这个容器并重新启动它。
在 30 秒内,查看 Pod 的事件:
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
35 秒之后,再来看 Pod 的事件:
kubectl describe pod liveness-exec
输出结果显示 RESTARTS
的值增加了 1。
NAME READY STATUS RESTARTS AGE
liveness-exec 1/1 Running 1 1m
HTTP方式做健康探测
#pods/probe/tcp-liveness-readiness.yaml
apiVersion: v1
kind: Pod
metadata:
labels:
test: liveness
name: liveness-http
spec:
containers:
- name: liveness
image: k8s.gcr.io/liveness
args:
- /server
livenessProbe:
httpGet:
path: /healthz
port: 8080
httpHeaders:
- name: Custom-Header
value: Awesome
initialDelaySeconds: 3
periodSeconds: 3
在这个配置文件中,可以看到 Pod 也只有一个容器。
periodSeconds
字段指定了 kubelet 每隔 3 秒执行一次存活探测。initialDelaySeconds
字段告诉 kubelet 在执行第一次探测前应该等待 3 秒。 kubelet 会向容器内运行的服务(服务会监听 8080 端口)发送一个 HTTP GET 请求来执行探测。 如果服务器上/healthz
路径下的处理程序返回成功代码,则 kubelet 认为容器是健康存活的。 如果处理程序返回失败代码,则 kubelet 会杀死这个容器并且重新启动它。任何大于或等于 200 并且小于 400 的返回代码标示成功,其它返回代码都标示失败。
可以在这里看服务的源码 server.go。
容器存活的最开始 10 秒中,
/healthz
处理程序返回一个 200 的状态码。之后处理程序返回 500 的状态码。
httpGet探测方式有如下可选的控制字段
- host:要连接的主机名,默认为Pod IP,可以在http request head中设置host头部。
- scheme: 用于连接host的协议,默认为HTTP。
- path:http服务器上的访问URI。
- httpHeaders:自定义HTTP请求headers,HTTP允许重复headers。
- port: 容器上要访问端口号或名称。
对于 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
。
针对 HTTP 探针,kubelet 除了必需的 Host
头部之外还发送两个请求头部字段: User-Agent
和 Accept
。这些头部的默认值分别是 kube-probe/{{ skew latestVersion >}}
(其中 1.22
是 kubelet 的版本号)和 */*
。
你可以通过为探测设置 .httpHeaders
来重载默认的头部字段值;例如:
livenessProbe:
httpGet:
httpHeaders:
- name: Accept
value: application/json
startupProbe:
httpGet:
httpHeaders:
- name: User-Agent
value: MyUserAgent
你也可以通过将这些头部字段定义为空值,从请求中去掉这些头部字段。
livenessProbe:
httpGet:
httpHeaders:
- name: Accept
value: ""
startupProbe:
httpGet:
httpHeaders:
- name: User-Agent
value: ""
TCP方式做健康探测
第三种类型的存活探测是使用 TCP 套接字。 通过配置,kubelet 会尝试在指定端口和容器建立套接字链接。 如果能建立连接,这个容器就被看作是健康的,如果不能则这个容器就被看作是有问题的。
# pods/probe/tcp-liveness-readiness.yaml
apiVersion: v1
kind: Pod
metadata:
name: goproxy
labels:
app: goproxy
spec:
containers:
- name: goproxy
image: k8s.gcr.io/goproxy:0.1
ports:
- containerPort: 8080
readinessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 15
periodSeconds: 20
如你所见,TCP 检测的配置和 HTTP 检测非常相似。 下面这个例子同时使用就绪和存活探测器。kubelet 会在容器启动 5 秒后发送第一个就绪探测。 这会尝试连接
goproxy
容器的 8080 端口。 如果探测成功,这个 Pod 会被标记为就绪状态,kubelet 将继续每隔 10 秒运行一次检测。除了就绪探测,这个配置包括了一个存活探测。 kubelet 会在容器启动 15 秒后进行第一次存活探测。 与就绪探测类似,会尝试连接
goproxy
容器的 8080 端口。 如果存活探测失败,这个容器会被重新启动。
配置探针(Probe)相关属性
探针(Probe)有许多可选字段,可以用来更加精确的控制Liveness和Readiness两种探针的行为(Probe):
- initialDelaySeconds:Pod启动后延迟多久才进行检查,单位:秒。
- periodSeconds:检查的间隔时间,默认为10,单位:秒。
- timeoutSeconds:探测的超时时间,默认为1,单位:秒。
- successThreshold:探测失败后认为成功的最小连接成功次数,默认为1,在Liveness探针中必须为1,最小值为1。
- failureThreshold:探测失败的重试次数,重试一定次数后将认为失败,在readiness探针中,Pod会被标记为未就绪,默认为3,最小值为1。