健康检测目的及原理
我们经常会遇到Pod在启动后一会儿就挂掉然后又重启一直循环. kubernetes是如何探测Pod是否存活的呢,
什么时候Pod可以对外提供服务, 好多人认为pod running之后就可以接受流量了,真实情况是这样吗?下面我们将逐步分享下原理,以及在TKE中如何配置健康检测.
Kubernetes 提供了一种运行状态检查机制来验证Pod中的容器是否正常工作,Kubernetes 提供了三种(在1.16.0-beta.之前是2个)由kubelet执行的运行状况检查:
Readiness Probe
(就绪探针): Kubelet使用就绪探测器可以知道容器什么时候准备好了并可以开始接受请求流量,当一个Pod内所有的容器都准备好了, 才能把这个Pod看作就绪了. 就绪探测器检查通过后才会将这个Pod 加入到Service(被label选择器选中的Pod)作为 这个Service的后端. 在Pod 还没准备好的时候, 不会加入到Service的负载均衡器中.
Liveness Probe
(存活探针):
Kubelet
使用存活探针来知道是否需要重启容器, 比如, 探测到死锁或者api接口返回状态码500等等. 这种情况下重启容器有助于快速恢复业务.
Startup Probe
(启动探针):Kubelet
使用启动探针可以知道应用程序什么时候启动了。 如果配置了这类探针, 就可以控制容器在启动成功后在进行存活和就绪检查, 确保这些存活,就绪检查不会影响应用程序的启动。 可以用于对启动慢的容器进行存活行检测,避免它们在启动运行之前就被杀掉。
容器探针详解
探针是由Kubelet 对容器执行的定期检查, 目前支持三种类型:
ExecAction
(参数exec): 在容器内执行指定命令。如果命令退出时返回码为 0 则认为诊断成功。TCPSocketAction
(参数tcpSocket): 对容器的 IP 地址上的指定端口执行 TCP 检查。如果可以测试通端口,则诊断被认为是成功的。HTTPGetAction
(参数httpGet): 对容器的 IP 地址上指定端口和路径执行 HTTP Get 请求。如果响应的状态码大于等于 200 且小于 400,则诊断被认为是成功的
每次探针都拿到以下三种结果中的一个:
Success
(成功): 容器通过了诊断Failure
(失败): 容器未通过诊断Unknown
(未知): 诊断失败,因此不会采取任何行动.
针对运行中(Running)中的容器, kubelet 根据探针类型以及探测结果作出相应的动作:
livenessProbe
: 如果存活态探测失败,则 kubelet 会杀死容器, 并且容器将根据其重启策略决定下一步操作。如果容器不提供存活探针, 则默认状态为 Success.readinessProbe
: 。如果就绪态探测失败, Endpoint Controller将从与Pod匹配的所有服务的端点列表中删除该 Pod 的 IP 地址。 初始延迟之前的就绪态的状态值默认为 Failure。 如果容器不提供就绪态探针,则默认状态为 Success。startupProbe
: 如果提供了启动探针,则所有其他探针都会被 禁用,直到此探针成功为止。如果启动探测失败,kubelet 将杀死容器,而容器依其 重启策略进行重启。 如果容器没有提供启动探测,则默认状态为 Success.
健康检测探针使用场景
livenessProbe
:undefined如果容器中的进程能够在遇到问题或不健康的情况下自行崩溃,则不一定需要存活探针; kubelet 将根据 Pod 的restartPolicy自动执行修复操作。 如果你希望容器在探测失败时被杀死并重新启动,那么请指定一个存活态探针, 并指定restartPolicy 为"Always"
或"OnFailure"
readinessProbe
:undefined如果要仅在探测成功之后才开始向 Pod 发送请求流量,应该配置就绪态探针。 在这种情况下,就绪态探针可能与存活态探针相同,但是就绪态探针的存在意味着 Pod 将在启动阶段不接收任何数据,并且只有在探针探测成功后才开始接收数据(大多数情况下推荐配置就绪探针)。
如果你的容器需要加载大规模的数据、配置文件或者在启动期间执行迁移操作,可以添加一个 就绪探针。
startupProbe
:undefined对于所包含的容器需要较长时间才能启动就绪的 Pod 而言,启动探针是非常有用的。 你不再需要配置一个较长的存活态探测时间间隔,只需要设置另一个独立的配置参数, 对启动期间的容器执行探测,从而允许使用远远超出存活检测时间间隔所允许的时长。
如果你的容器启动时间通常超出initialDelaySeconds failureThreshold × periodSeconds
总值,你应该设置一个启动探针,对存活探针所使用的同一端点进行检查。 periodSeconds
的默认值是 30 秒。你应该将其 failureThreshold
设置得足够高, 以便容器有充足的时间完成启动,并且避免更改存活探针所使用的默认值。 这一设置有助于减少死锁的发生。
TKE中进行实战操作
定义 liveness
命令 探针
场景模拟: 假如运行的App服务需要读取某个文件, 当这个文件不存在了, 程序不会崩溃除非重启解决.我们可以配置一个ExecAction
类型的存活探针来解决我们的问题, 本次实战我们使用busybox 镜像来进行测试.
控制台配置方式 :
步骤: 打开TKE集群--> 工作负载--> Deployment --> 新建
在上图配置中可以看到我们只配置了一个容器. 开启了容器健康检查--> 存活检查 --> 检查方法--> 执行命令检查
命令行 直接部署
healthcheck-exec.yaml
代码语言:txt复制apiVersion: apps/v1beta2
kind: Deployment
metadata:
labels:
k8s-app: healthcheck-exec
qcloud-app: healthcheck-exec
name: healthcheck-exec
namespace: richie
spec:
replicas: 1
selector:
matchLabels:
k8s-app: healthcheck-exec
qcloud-app: healthcheck-exec
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
type: RollingUpdate
template:
metadata:
labels:
k8s-app: healthcheck-exec
qcloud-app: healthcheck-exec
spec:
containers:
- args:
- -c
- touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600
command:
- /bin/sh
image: busybox
imagePullPolicy: Always
livenessProbe:
exec:
command:
- cat /tmp/healthy
failureThreshold: 1
initialDelaySeconds: 5
periodSeconds: 5
successThreshold: 1
timeoutSeconds: 1
name: healthcheck-exec
resources:
limits:
cpu: 500m
memory: 1Gi
requests:
cpu: 250m
memory: 256Mi
kubectl apply -f healthcheck-exec.yaml
执行命令
: 当容器启动时,执行命令: /bin/sh -c "touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600"
启动延时
: 这个字段告诉kubelet 在执行第一次探测前应该等待5秒. kubelet 在容器内执行命令 cat /tmp/healthy 来进行探测。 如果命令执行成功并且返回值为 0,kubelet 就会认为这个容器是健康存活的。 如果这个命令返回非 0 值,kubelet 会杀死这个容器并重新启动它。
这个容器生命的前 30 秒, /tmp/healthy 文件是存在的。 所以在这最开始的 30 秒内,执行命令 cat /tmp/healthy 会返回成功代码。 30 秒之后,执行命令 cat /tmp/healthy 就会返回失败代码。
定义liveness
HTTP请求探针
另外一种类型的存活探测方式是使用HTTP GET 请求. 我们临时做了一个trouble-shooting的镜像,
这个镜像是一个http服务,里面有2 接口用于我们本次的测试, 第一个接口/healthz
用来模拟健康检测接口,
正常情况GET
这个接口返回2xx
, 第二个接口 POST /healthz/:code
用于修改healthz 返回状态码,模拟应用程序故障的场景.
控制台部署
步骤: 打开TKE集群--> 工作负载--> Deployment --> 新建
在上图配置中可以看到我们只配置了一个容器. 开启了容器健康检查--> 存活检查 --> 检查方法--> hTTP请求检查
命令行部署
healthcheck-http.yaml
代码语言:txt复制apiVersion: apps/v1beta2
kind: Deployment
metadata:
labels:
k8s-app: healthcheck-http
qcloud-app: healthcheck-http
name: healthcheck-http
spec:
replicas: 1
selector:
matchLabels:
k8s-app: healthcheck-http
qcloud-app: healthcheck-http
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
type: RollingUpdate
template:
metadata:
creationTimestamp: null
labels:
k8s-app: healthcheck-http
qcloud-app: healthcheck-http
spec:
containers:
- args:
- server
command:
- ./trouble-shooting
image: ccr.ccs.tencentyun.com/ruiqingzhu-tke/trouble-shooting
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
httpGet:
path: /healthz
port: 8888
scheme: HTTP
initialDelaySeconds: 3
periodSeconds: 3
successThreshold: 1
timeoutSeconds: 1
name: healthcheck-http
resources:
limits:
cpu: 500m
memory: 1Gi
requests:
cpu: 250m
memory: 256Mi
kubectl apply -f healthcheck-http.yaml
在这个配置中, 可以看到Pod也只有一个容器. 间隔时间每3秒执行一次存活探测. 启动延迟时间告诉kubelet 在执行第一次探测前应该等待3秒. kubelet 会向容器内运行的服务(服务监听8888端口)发送一个HTTP GET
请求来执行检查. 如果返回状态码大于200并且小于400认为成功.其他返回状态码都为失败。如果kubelet 收到为失败,则 kubelet 会杀死这个容器并且重新启动它。
在容器中执行curl 127.0.0.1:8888/healthz -i
正常, 通过执行curl -X POST 127.0.0.1:8888/healthz/500
模拟应用故障.
kubelet 检测到 pod返回500后,自动重启pod
定义 TCP liveness
探针
第三种liveness probe 使用TCP Socket。配置此类探针, kubelet 将尝试在指定端口上打开容器的套接字. 如果可以建立链接, 容器被认为是健康的,如果不能认为是失败。
控制台配置方式 :
步骤: 打开TKE集群--> 工作负载--> Deployment --> 新建
在上图配置中可以看到我们只配置了一个容器. 开启了容器健康检查--> 存活检查 --> 检查方法--> tcp请求检查
命令行部署
healthcheck-tcp.yaml
代码语言:txt复制apiVersion: apps/v1beta2
kind: Deployment
metadata:
labels:
k8s-app: healthcheck-tcp
qcloud-app: healthcheck-tcp
name: healthcheck-tcp
spec:
replicas: 1
selector:
matchLabels:
k8s-app: healthcheck-tcp
qcloud-app: healthcheck-tcp
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
type: RollingUpdate
template:
metadata:
labels:
k8s-app: healthcheck-tcp
qcloud-app: healthcheck-tcp
spec:
containers:
- image: nginx
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
initialDelaySeconds: 15
periodSeconds: 20
successThreshold: 1
tcpSocket:
port: 80
timeoutSeconds: 1
name: healthcheck-tcp
readinessProbe:
failureThreshold: 3
initialDelaySeconds: 5
periodSeconds: 10
successThreshold: 1
tcpSocket:
port: 80
timeoutSeconds: 1
resources:
limits:
cpu: 500m
memory: 1Gi
requests:
cpu: 250m
memory: 256Mi
kubectl apply -f healthcheck-tcp.yaml
TCP检查的配置与HTTP检查非常相似。 此示例同时使用了readiness和liveness probe。 容器启动后5秒钟,kubelet将发送第一个readiness probe。 这将尝试连接到端口80上的nginx容器。如果探测成功,则该pod将被标记为就绪。Kubelet将每隔10秒钟执行一次该检查。
除了readiness probe之外,该配置还包括liveness probe。 容器启动15秒后,kubelet将运行第一个liveness probe。 就像readiness probe一样,这将尝试连接到nginx容器上的80端口。如果liveness probe失败,容器将重新启动。
定义readness
探针
在有些场景下,应用程序暂时无法对外部流量提供服务。 例如,应用程序可能需要在启动期间加载大量数据或配置文件。 在这种情况下,你不想杀死应用程序,但你也不想发送请求。 Kubernetes提供了readiness probe来检测和减轻这些情况。 Pod中的容器可以报告自己还没有准备,不能处理Kubernetes服务发送过来的流量。
代码语言:txt复制readinessProbe:
exec:
command:
- cat
- /tmp/healthy
initialDelaySeconds: 5
periodSeconds: 5
Readiness probe的HTTP和TCP的探测器配置跟liveness probe一样。
唯一的不同是使用 readinessProbe而不是livenessProbe.
Readiness和livenss probe可以并行用于同一容器。 使用两者可以确保流量无法到达未准备好的容器,并且容器在失败时重新启动。
定义startup
探针
有时候,会有一些现有的应用程序在启动时需要较多的初始化时间。 要不影响对引起探测死锁的快速响应,这种情况下,设置存活探测参数是要技巧的。 技巧就是使用一个命令来设置启动探测,针对HTTP 或者 TCP 检测,可以通过设置 failureThreshold * periodSeconds 参数来保证有足够长的时间应对糟糕情况下的启动时间
代码语言:txt复制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
注意: startupProbe 探针1.16.0-beta 以后 版本才支持,目前tke控制上没有开放,可以通过命令进行部署.
总结
kuberntes提供了可以自检和自动恢复的能力, 大大降低人工成本. 在本次分享中简单给大家介绍了下健康检测原理以及使用,可以根据自己的业务场景进行修改测试.
欢迎大家关注本栏目,我们专注于Kubernetes生态,持续给大家分享。
声明: 本文中有些图片来自google,内容参考kubernetes官网.