赞
踩
本文作者 LEE,老李,一个在 IT 行业摸爬滚打 16 年的技术老兵。
因为 k8s 中采用大量的异步机制、以及多种对象关系设计上的解耦,当应用实例数 增加/删除、或者应用版本发生变化触发滚动升级时,系统并不能保证应用相关的 service、ingress 配置总是及时能完成刷新。在一些情况下,往往只是新的 Pod 完成自身初始化,系统尚未完成 Endpoint、负载均衡器等外部可达的访问信息刷新,老得 Pod 就立即被删除,最终造成服务短暂的额不可用,这对于生产来说是不可接受的,所以 k8s 就加入了一些存活性探针:StartupProbe、LivenessProbe、ReadinessProbe。
###Pod 重启策略
###Pod 常见状态转换场景
K8S 提供了 3 种探针:
在 Kubernetes 中 Pod 是最小的计算单元,而一个 Pod 又由多个容器组成,相当于每个容器就是一个应用,应用在运行期间,可能因为某也意外情况致使程序挂掉。
那么如何监控这些容器状态稳定性,保证服务在运行期间不会发生问题,发生问题后进行重启等机制,就成为了重中之重的事情,考虑到这点 kubernetes 推出了活性探针机制。
有了存活性探针能保证程序在运行中如果挂掉能够自动重启,但是还有个经常遇到的问题,比如说,在 Kubernetes 中启动 Pod,显示明明 Pod 已经启动成功,且能访问里面的端口,但是却返回错误信息。还有就是在执行滚动更新时候,总会出现一段时间,Pod 对外提供网络访问,但是访问却发生 404,这两个原因,都是因为 Pod 已经成功启动,但是 Pod 的的容器中应用程序还在启动中导致,考虑到这点 Kubernetes 推出了就绪性探针机制。
如果三个探针同时存在,先执行 StartupProbe 探针,其他两个探针将会被暂时禁用,直到 pod 满足 StartupProbe 探针配置的条件,其他 2 个探针启动,如果不满足按照规则重启容器。
另外两种探针在容器启动后,会按照配置,直到容器消亡才停止探测,而 StartupProbe 探针只是在容器启动后按照配置满足一次后,不在进行后续的探测。
LivenessProbe 和 ReadinessProbe 两种探针都支持下面三种探测方法:
探针探测结果有以下值:
LivenessProbe 和 ReadinessProbe 两种探针的相关属性 探针(Probe)有许多可选字段,可以用来更加精确的控制 Liveness 和 Readiness 两种探针的行为(Probe):
Tips:initialDelaySeconds 在 ReadinessProbe 其实可以不用配置,不配置默认 pod 刚启动,开始进行 ReadinessProbe 探测,但那有怎么样,除了 StartupProbe,ReadinessProbe、LivenessProbe 运行在 pod 的整个生命周期,刚启动的时候 ReadinessProbe 检测失败了,只不过显示 READY 状态一直是 0/1,ReadinessProbe 失败并不会导致重启 pod,只有 StartupProbe、LivenessProbe 失败才会重启 pod。而等到多少 s 后,真正服务启动后,检查 success 成功后,READY 状态自然正常
StartupProbe 探针支持下面三种探测方法:
探针探测结果有以下值:
StartupProbe 探针属性
Tips:在 StartupProbe 执行完之后,其他 2 种探针的所有配置才全部启动,相当于容器刚启动的时候,所以其他 2 种探针如果配置了 initialDelaySeconds,建议不要给太长。
1. 通过 exec 方式做健康探测
[root@localhost ~]# vim liveness-exec.yaml 复制代码 apiVersion: v1 kind: Pod metadata: name: liveness-exec labels: app: liveness spec: containers: - name: liveness image: busybox args: #创建测试探针探测的文件 - /bin/sh - -c - touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600 LivenessProbe: initialDelaySeconds: 10 #延迟检测时间 periodSeconds: 5 #检测时间间隔 exec: #使用命令检查 command: #指令,类似于运行命令sh - cat #sh 后的第一个内容,直到需要输入空格,变成下一行 - /tmp/healthy #由于不能输入空格,需要另外声明,结果为sh cat"空格"/tmp/healthy 复制代码
思路整理:
容器在初始化后,执行(/bin/sh -c “touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600”)首先创建一个 /tmp/healthy 文件,然后执行睡眠命令,睡眠 30 秒,到时间后执行删除 /tmp/healthy 文件命令。
而设置的存活探针检检测方式为执行 shell 命令,用 cat 命令输出 healthy 文件的内容,如果能成功执行这条命令一次(默认 successThreshold:1),存活探针就认为探测成功,由于没有配置(failureThreshold、timeoutSeconds),所以执行(cat /tmp/healthy)并只等待 1s,如果 1s 内执行后返回失败,探测失败。
在前 30 秒内,由于文件存在,所以存活探针探测时执行 cat /tmp/healthy 命令成功执行。30 秒后 healthy 文件被删除,所以执行命令失败,Kubernetes 会根据 Pod 设置的重启策略来判断,是否重启 Pod。
2. 通过 HTTP 方式做健康探测
[root@localhost ~]# vi liveness-http.yaml 复制代码 apiVersion: v1 kind: Pod metadata: name: liveness-http labels: test: liveness spec: containers: - name: liveness image: test.com/test-http-prober:v0.0.1 LivenessProbe: failureThreshold: 5 #检测失败5次表示未就绪 initialDelaySeconds: 20 #延迟加载时间 periodSeconds: 10 #重试时间间隔 timeoutSeconds: 5 #超时时间设置 successThreshold: 2 #检查成功为2次表示就绪 httpGet: scheme: HTTP port: 8081 path: /ping 复制代码
思路整理:
在 pod 启动后,初始化等待 20s 后,LivenessProbe 开始工作,去请求 http://Pod_IP:8081/ping 接口,类似于 curl -I http://Pod_IP:8081/ping 接口,考虑到请求会有延迟(curl -I 后一直出现假死状态),所以给这次请求操作一直持续 5s,如果 5s 内访问返回数值在>=200 且<=400 代表第一次检测 success,如果是其他的数值,或者 5s 后还是假死状态,执行类似(ctrl+c)中断,并反回 failure 失败。
等待 10s 后,再一次的去请求 http://Pod_IP:8081/ping 接口。如果有连续的 2 次都是 success,代表无问题。如果期间有连续的 5 次都是 failure,代表有问题,直接重启 pod,此操作会伴随 pod 的整个生命周期。
Tips
Http Get 探测方式有如下可选的控制字段:
3. 通过 TCP 方式做健康探测
[root@localhost ~]# vi liveness-tcp.yaml 复制代码 apiVersion: v1 kind: Pod metadata: name: liveness-tcp labels: app: liveness spec: containers: - name: liveness image: nginx LivenessProbe: initialDelaySeconds: 15 periodSeconds: 20 tcpSocket: port: 80 复制代码
思路整理:
TCP 检查方式和 HTTP 检查方式非常相似,在容器启动 initialDelaySeconds 参数设定的时间后,kubelet 将发送第一个 LivenessProbe 探针,尝试连接容器的 80 端口,类似于 telnet 80 端口。 每隔 20 秒(periodSeconds)做探测,如果连接失败则将杀死 Pod 重启容器。
ReadinessProbe 探针使用方式和 LivenessProbe 探针探测方法一样,也是支持三种,只是一个是用于探测应用的存活,一个是判断是否对外提供流量的条件。
[root@localhost ~]# vim readiness-exec.yaml 复制代码 apiVersion: v1 kind: Pod metadata: name: readiness-exec labels: app: readiness-exec spec: containers: - name: readiness-exec image: busybox args: #创建测试探针探测的文件 - /bin/sh - -c - touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600 LivenessProbe: initialDelaySeconds: 10 periodSeconds: 5 exec: command: - cat - /tmp/healthy --- apiVersion: v1 kind: Pod metadata: name: readiness-http labels: app: readiness-http spec: containers: - name: readiness-http image: test.com/test-http-prober:v0.0.1 ports: - name: server containerPort: 8080 - name: management containerPort: 8081 ReadinessProbe: initialDelaySeconds: 20 periodSeconds: 5 timeoutSeconds: 10 httpGet: scheme: HTTP port: 8081 path: /ping --- apiVersion: v1 kind: Pod metadata: name: readiness-tcp labels: app: readiness-tcp spec: containers: - name: readiness-tcp image: nginx LivenessProbe: initialDelaySeconds: 15 periodSeconds: 20 tcpSocket: port: 80 复制代码
terminationGracePeriodSeconds 这个参数非常的重要,具体讲解。请参考我的另外一篇文章《详细解读 Kubernetes 中 Pod 优雅退出,帮你解决大问题》, 里面有详细的解释,我这里说下其他的内容。
Tips: terminationGracePeriodSeconds 不能用于 ReadinessProbe,如果将它应用于 ReadinessProbe 将会被 apiserver 接口所拒绝
LivenessProbe:
httpGet:
path: /ping
port: liveness-port
failureThreshold: 1
periodSeconds: 30
terminationGracePeriodSeconds: 30 # 宽限时间30s
复制代码
[root@localhost ~]# vim startup.yaml 复制代码 apiVersion: v1 kind: Pod metadata: name: startup labels: app: startup spec: containers: - name: startup image: nginx StartupProbe: failureThreshold: 3 # 失败阈值,连续几次失败才算真失败 initialDelaySeconds: 5 # 指定的这个秒以后才执行探测 timeoutSeconds: 10 # 探测超时,到了超时时间探测还没返回结果说明失败 periodSeconds: 5 # 每隔几秒来运行这个 httpGet: path: /test prot: 80 复制代码
思路整理:
在容器启动 initialDelaySeconds (5 秒) 参数设定的时间后,kubelet 将发送第一个 StartupProbe 探针,尝试连接容器的 80 端口。 如果连续探测失败没有超过 3 次 (failureThreshold) ,且每次探测间隔为 5 秒 (periodSeconds) 和探测执行时间不超过超时时间 10 秒/每次 (timeoutSeconds),则认为探测成功,反之探测失败,kubelet 直接杀死 Pod。
通过对三种探针的探索,我们能够得到一句话的总结:理解底层结构,能够最大程度在可用性、安全性,持续性等方面让 Pod 达到最佳工作状态。 凡事没有“银弹”,尤其对重要的业务需要一个案例一个解决方案,希望这次的分析能提供给大家开启一个思路之门。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。