Kubernetes Health Check
学习参考:配置存活、就绪和启动探针
环境准备
root@master30:~# kubectl create ns healthroot@master30:~# kubectl config set-context --current --namespace healthHealth Check
应用可能会因为各种问题,变的unhealthy,例如临时连接断开,配置错误,应用本身错误。
kubelet 使用probes(探针),周期性地监控容器中应用是否为healthy状态,进一步决定什么时候要重启容器。 例如,当存活探针可以探测到应用死锁(应用在运行,但是无法继续执行后面的步骤)情况,进而重启pod,有助于提高应用的可用性,即使其中存在缺陷。
Probe Type
kubelet 使用启动探针来了解应用容器何时启动。 如果配置了这类探针,存活探针和就绪探针成功之前不会重启,确保这些探针不会影响应用的启动。 启动探针可以用于对慢启动容器进行存活性检测,避免它们在启动运行之前就被杀掉。
LivenessProbe:用于确定pod中应用是否处于healthy状态。如果liveness probe检测的状态为unhealthy,则控制器将重启创建一个同名的pod。
ReadinessProbe:用于确定pod中应用是否可以提供服务。如果返回失败状态,则**服务将从endpoints 中删除容器ip地址。**即使容器处于运行状态,也不接受代理发过来的请求。
StartupProbe:用于确定pod是否成功初始化。 如果指定,则在成功完成之前不会执行其他探测。如果此探测失败,Pod 将重新启动,就像 livenessProbe 失败一样。 这可用于在 Pod 生命周期开始时提供不同的探测参数,此时加载数据或预热缓存可能需要比稳态操作期间更长的时间。 这无法更新。
| 探针 | 核心目的 | 失败动作 |
|---|---|---|
| StartupProbe | 确认容器初始化完成 | 重启容器;成功前禁活 / 就绪探针 |
| LivenessProbe | 确认应用正常存活 | 重启容器 |
| ReadinessProbe | 确认应用可接收流量 | 从 Service 端点摘除 IP,不重启 |
Checking Methods
探针检查容器有四种不同的方法:
- httpGet,对容器的 IP 地址上指定端口和路径执行 HTTP
GET请求。如果响应的状态码大于等于 200 且小于 400,则诊断被认为是成功的 - exec,在容器内执行指定命令。如果命令退出时返回码为 0,则认为诊断成功
- tcpSocket,对容器的 IP 地址上的指定端口执行 TCP 检查。如果端口打开,则诊断被认为是成功的。 如果远程系统(容器)在打开连接后立即将其关闭,这算作是健康的
- grpc,使用 gRPC 执行一个远程过程调用。 目标应该实现 gRPC 健康检查。 如果响应的状态是 “SERVING”,则认为诊断成功
| 检测方式 | 判断成功标准 |
|---|---|
| httpGet | HTTP 状态码 200~399 |
| exec | 命令 exit code = 0 |
| tcpSocket | TCP 端口能连通 |
| grpc | gRPC 健康状态为 SERVING |
HTTP Checks-httpGet
当使用HTTP Checks,控制器使用webhoook判定容器健康情况。如果HTTP的响应码在200-399之间,判定check成功。适应范围:可以返回HTTP状态码应用。
livenessProbe
root@master30:~# vim deploy-httpGet-liveness.yamlapiVersion:apps/v1kind:Deploymentmetadata:labels:app:webname:webspec:replicas:1selector:matchLabels:app:webtemplate:metadata:labels:app:webspec:containers:-image:hub.laoma.cloud/library/httpdimagePullPolicy:IfNotPresentname:httpd# 添加livenessProbe部分livenessProbe:failureThreshold:3initialDelaySeconds:5periodSeconds:5successThreshold:1timeoutSeconds:10httpGet:path:/index.html# port填写时间web端口port:80# scheme指定协议,HTTP或者HTTPSscheme:HTTPprobe选项说明:
- initialDelaySeconds:必选。容器启动后多长时间,probe开始生效。
- timeoutSeconds:必选。probe需要多长时间完成。如果超过该值,控制器判定probe失败。默认值1s,最小值是1秒。
- periodSeconds:可选。检查频率。默认值10s,最小值是1秒。
- successThreshold:可选,连续成功最少次数后判定probe成功。默认值1,最小值是1。
- failureThreshold:可选。连续失败最少次数后判定probe失败。默认值3,最小值是1。
root@master30 ~13:55:40# kubectl apply -f deploy-httpGet-liveness.yamlroot@master30 ~13:55:52# kubectl get podNAME READY STATUS RESTARTS AGE web-7dfcbbb5df-g55c41/1 Running04s root@master30 ~13:55:56# kubectl describe pod web-7dfcbbb5df-g55c4 | grep '^IP:'IP:10.224.26.168# 删除主页文件root@master30 ~13:56:18# kubectl exec web-7dfcbbb5df-g55c4 -- rm htdocs/index.html# 观察pod状态,RESTARTS次数变位1,再次访问root@master30 ~13:56:31# kubectl get podNAME READY STATUS RESTARTS AGE web-7dfcbbb5df-g55c41/1 Running1(2s ago)79s# 容器删除需要一些时间,由参数terminationGracePeriodSeconds设定,默认值为30s。# 只有等容器删除,并创建完成后才会继续检测root@master30 ~13:57:41# curl 10.224.26.168<html><body><h1>It works!</h1></body></html># 清理环境root@master30 ~13:57:48# kubectl delete deployments.apps webreadinessProbe
root@master30 ~13:58:06# vim deploy-httpGet-readiness.yamlapiVersion:apps/v1kind:Deploymentmetadata:labels:app:webname:webspec:replicas:3selector:matchLabels:app:webtemplate:metadata:labels:app:webspec:containers:-image:hub.laoma.cloud/library/httpdimagePullPolicy:IfNotPresentname:httpd# 添加readinessProbe部分readinessProbe:failureThreshold:3initialDelaySeconds:5periodSeconds:5successThreshold:1timeoutSeconds:10httpGet:path:/index.htmlport:80scheme:HTTP# 创建应用root@master30 ~13:58:30# kubectl apply -f deploy-httpGet-readiness.yamlroot@master30 ~13:58:48# kubectl expose deployment web --port=80 --target-port=80root@master30 ~13:59:08# kubectl get svcNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S)AGE web ClusterIP10.106.133.11<none>80/TCP 6s root@master30 ~13:59:14# kubectl get podsNAME READY STATUS RESTARTS AGE web-5d6964b9f5-5hghq1/1 Running044s web-5d6964b9f5-c2tmm1/1 Running044s web-5d6964b9f5-kvrkc1/1 Running044s# 准备3个pod主页文件root@master30 ~13:59:32# for pod in $(kubectl get pods -o name | awk -F / '{print $2}'); do kubectl exec $pod -- bash -c "echo $pod > htdocs/index.html"; doneroot@master30 ~14:00:21# for i in {1..90};do curl -s 10.106.133.11;done| sort|uniq -c30web-5d6964b9f5-5hghq30web-5d6964b9f5-c2tmm30web-5d6964b9f5-kvrkc root@master30 ~14:01:18# kubectl get endpoints webNAME ENDPOINTS AGE web10.224.26.169:80,10.224.26.170:80,10.224.71.221:80 2m25s# 删除 web-5d6964b9f5-5hghq主页文件root@master30 ~14:01:33# kubectl exec web-5d6964b9f5-5hghq -- rm htdocs/index.html# web 服务的后端没有pod的iproot@master30 ~14:02:43# kubectl get endpoints webNAME ENDPOINTS AGE web10.224.26.169:80,10.224.26.170:80 4m11s# 访问svc,后端无法看到 web-9479dc55c-6bpg7root@master30 ~14:03:19# for i in {1..90};do curl -s 10.106.133.11;done|sort |uniq -c45web-5d6964b9f5-c2tmm45web-5d6964b9f5-kvrkc# 观察web-5d6964b9f5-5hghq状态,READY为0,RESTARTS数量为0root@master30 ~14:02:38# kubectl get podNAME READY STATUS RESTARTS AGE web-5d6964b9f5-5hghq0/1 Running03m55s web-5d6964b9f5-c2tmm1/1 Running03m55s web-5d6964b9f5-kvrkc1/1 Running03m55s# 清理环境root@master30:~# kubectl delete deployments.apps webExecution Checks-exec
当使用容器执行检测,kubelet代理将在容器内执行命令。返回值是0,代表check成功。
示例1:检测容器自带文件
root@master30:~# vim deploy-exec-liveness.yamlapiVersion:apps/v1kind:Deploymentmetadata:labels:app:webname:webspec:replicas:1selector:matchLabels:app:webtemplate:metadata:labels:app:webspec:containers:-image:hub.laoma.cloud/library/httpdimagePullPolicy:IfNotPresentname:httpd# 添加livenessProbe部分livenessProbe:failureThreshold:3initialDelaySeconds:5periodSeconds:5successThreshold:1timeoutSeconds:10exec:command:-cat-/usr/local/apache2/htdocs/index.html# 创建应用root@master30 ~14:38:27# kubectl apply -f deploy-exec-liveness.yamlroot@master30 ~14:38:41# kubectl get podsNAME READY STATUS RESTARTS AGE web-546966967b-jwjpw1/1 Running012s# 删除主页文件root@master30 ~14:38:53# kubectl exec web-546966967b-jwjpw -- rm htdocs/index.html# 观察pod状态,RESTARTS次数变位1root@master30 ~14:39:11# kubectl get podNAME READY STATUS RESTARTS AGE web-546966967b-jwjpw1/1 Running1(10s ago)53s示例2:检测自定义文件
root@master30 ~14:37:45# kubectl run busybox --image=busybox --image-pull-policy=IfNotPresent -o yaml --dry-run=client > busybox.ymlroot@master30 ~14:40:22# vim deploy-exec-busybox.ymlapiVersion:v1kind:Podmetadata:creationTimestamp:nulllabels:run:busyboxname:busyboxspec:containers:-image:busyboximagePullPolicy:IfNotPresentname:busybox# 添加args参数args:-/bin/sh--c-touch /tmp/healthy; sleep 10; rm-rf /tmp/healthy; sleep 100#添加livenessProbe参数livenessProbe:failureThreshold:3initialDelaySeconds:5periodSeconds:5successThreshold:1timeoutSeconds:10exec:command:-ls-/tmp/healthydnsPolicy:ClusterFirstrestartPolicy:Alwaysroot@master30 ~14:40:37# kubectl apply -f deploy-exec-busybox.ymlroot@master30 ~14:40:46# kubectl get podsNAME READY STATUS RESTARTS AGE busybox1/1 Running041s#等待一段时间后,发现重启了一次root@master30 ~14:41:27# kubectl get podsNAME READY STATUS RESTARTS AGE busybox1/1 Running1(10s ago)65sTCP Socket Checks-tcpSocket
当使用TCP socket checks,kubelet代理尝试打开容器socket。如果check可以建立连接,判定check成功。
示例:liveness probe使用TCP Socket check
root@master30:~# vim deploy-tcpSocket-liveness.yamlapiVersion:apps/v1kind:Deploymentmetadata:labels:app:webname:webspec:replicas:1selector:matchLabels:app:webtemplate:metadata:labels:app:webspec:containers:-image:hub.laoma.cloud/library/httpdimagePullPolicy:IfNotPresentname:httpd# 添加livenessProbe部分livenessProbe:failureThreshold:3initialDelaySeconds:5periodSeconds:5successThreshold:1timeoutSeconds:10tcpSocket:port:80Health Check Case
Health Check 在 Scale Up 中的应用
对于多副本应用, 当执行Scale Up操作时, 新副本会作为backend被添加到Service的负载均衡中, 与已有副本一起处理客户的请求。考虑到应用启动通常都需要一个准备阶段, 比如加载缓存数据、 连接数据库等, 从容器启动到真正能够提供服务是需要一段时间的。 我们可以通过Readiness探测判断容器是否就绪, 避免将请求发送到还没有准备好的backend。
Health Check 在滚动更新中的应用
Health Check另一个重要的应用场景是Rolling Update。 试想一下, 现有一个正常运行的多副本应用, 接下来对应用进行更新(比如使用更高版本的image) , Kubernetes会启动新副本, 然后发生了如下事件:
- 正常情况下新副本需要10秒钟完成准备工作, 在此之前无法响应业务请求。
- 由于人为配置错误, 副本始终无法完成准备工作(比如无法连接后端数据库)。
如果没有配置Health Check, 会出现怎样的情况?
因为新副本本身没有异常退出, 默认的Health Check机制会认为容器已经就绪, 进而会逐步用新副本替换现有副本, 其结果就是: 当所有旧副本都被替换后, 整个应用将无法处理请求, 无法对外提供服务。 如果这是发生在重要的生产系统上, 后果会非常严重。
如果正确配置了Health Check, 新副本只有通过了探测才会被添加到Service; 如果没有通过探测, 现有副本不会被全部替换, 业务仍然正常进行。
环境清理
root@master30:~# kubectl delete ns health