一、存活探针
**目的:存活探针决定何时重启容器。**如果存活探针失败,kubelet 会杀死容器,然后根据 Pod 的 restartPolicy 来重启它。
适用场景 :用于检测死锁 或应用僵死但进程还在的情况。通过重启容器,可以帮助应用从故障状态中恢复。
示例:检测 HTTP 服务死锁
假设你有一个 Web 应用,但在某些情况下(如高负载),它会进入死锁状态,无法响应任何请求,但进程仍在。这时可以使用 HTTP GET 存活探针。
bash
apiVersion: v1
kind: Pod
metadata:
name: liveness-http-pod
spec:
containers:
- name: liveness-http-container
image: my-app:v1.0
ports:
- containerPort: 8080
livenessProbe:
httpGet:
# 探针将访问容器的 8080 端口的 /health 路径
path: /health
port: 8080
initialDelaySeconds: 3 # 容器启动后等待 3 秒再开始探测
periodSeconds: 5 # 每 5 秒执行一次探测
failureThreshold: 1 # 连续失败 1 次后即判定为失败
工作流程:
- 容器启动。
- 等待 3 秒 (initialDelaySeconds)。
- kubelet 开始每隔 5 秒 (periodSeconds) 向 http://:8080/health 发送 GET 请求。
- 如果 /health 路径返回的状态码在 200 到 399 之间,则探测成功。
- 如果该路径返回 404 或 500 等错误状态码,或者连接超时,则探测失败。
- 一旦失败次数达到 failureThreshold (这里是 1),kubelet 就会杀死并重启容器。
**说明:**存活探针不会等待就绪探针成功。 如果你想在执行存活探针前等待,你可以定义义 initialDelaySeconds,或者使用启动探针。
二、就绪探针
目的 :判断容器是否已准备好接收流量 。如果就绪探针失败,Endpoint Controller 会从与 Pod 匹配的所有 Service 的端点(Endpoint)中移除该 Pod 的 IP 地址。
适用场景 :这种探针在等待应用执行耗时 的初始任务 时非常有用; 例如:建立网络连接、加载文件和预热缓存。在容器的生命周期后期, 就绪探针也很有用,例如,从临时故障或过载中恢复时。就绪探针在容器的整个生命期内持续运行。
关键问题:我的应用准备好服务了吗?如果没准备好,就别把流量发给我。
示例:应用启动慢或依赖服务不可用
假设你的应用启动需要 30 秒来加载缓存和连接数据库。在准备好之前,它不应接收任何流量。
bash
apiVersion: v1
kind: Pod
metadata:
name: readiness-tcp-pod
spec:
containers:
- name: readiness-tcp-container
image: my-app:v2.0
ports:
- containerPort: 8080
readinessProbe:
tcpSocket:
# 探针只检查 8080 端口是否能打开
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
# 允许连续失败 3 次,给应用更多启动时间
failureThreshold: 3
# 必须连续成功 1 次才算就绪
successThreshold: 1
工作流程:
- 容器启动。
- 等待 5 秒后开始探测。
- kubelet 尝试与容器的 8080 端口建立 TCP 连接。
- 如果连接成功,则探测成功。
- 应用可能启动较慢,前几次探测可能会失败。kubelet 会尝试 3 次 (failureThreshold: 3),即 30 秒内如果端口仍未打开,Pod 会一直处于"未就绪"状态。
- 一旦有一次探测成功 (successThreshold: 1),Pod 就会被标记为就绪,并被添加到 Service 的负载均衡池中。
三、启动探针
目的 :保护慢启动容器。在启动探针成功之前,所有其他探针(存活和就绪)都会处于禁用状态。
适用场景 :用于处理启动时间非常长的旧应用。在没有启动探针时,我们只能将livenessProbe 的 initialDelaySeconds 设得很大,但这不够灵活。启动探针允许应用在启动期间无限次地失败,直到成功为止。
关键问题 :我的应用启动完成了吗?在完成之前,先别用存活和就绪探针来打扰我。
示例:处理启动极慢的遗留应用
假设一个 Java 遗留应用,启动可能需要 2 到 5 分钟,时间不固定。
bash
apiVersion: v1
kind: Pod
metadata:
name: startup-exec-pod
spec:
containers:
- name: startup-exec-container
image: legacy-java-app:v3
ports:
- containerPort: 8088
startupProbe:
exec:
# 通过检查一个特定文件是否存在来判断应用是否启动完成
command:
- cat
- /tmp/app-initialized
failureThreshold: 60 # 尝试 60 次
periodSeconds: 5 # 每 5 秒一次 (总超时时间 = 60 * 5 = 300秒 = 5分钟)
livenessProbe:
httpGet:
path: /health
port: 8080
periodSeconds: 10 # 启动成功后,每10秒检查一次存活状态
readinessProbe:
httpGet:
path: /ready
port: 8080
periodSeconds: 5 # 启动成功后,每5秒检查一次就绪状态
工作流程:
- 容器启动。
- kubelet 立即开始执行启动探针(没有 initialDelaySeconds),每 5 秒执行一次 cat /tmp/app-initialized 命令。
- 在应用完成启动之前,这个文件不存在,命令会失败。kubelet 会一直重试,最多尝试 60 次(总共 5 分钟)。
- 一旦应用启动完成,它创建了 /tmp/app-initialized 文件,启动探针成功。
- 启动探针成功后,kubelet 才会开始运行存活探针和就绪探针。
- 这样,慢启动的应用就不会因为在启动期间存活探针失败而被意外重启。
四、最佳实践
- **所有重要 Pod 都应定义就绪探针:**这是实现服务可靠性和滚动更新平滑性的关键。
- **谨慎使用存活探针:**只有当应用在失败时确实能通过重启恢复时才使用。配置不当的存活探针可能导致频繁重启,陷入 CrashLoopBackOff。
- **优先使用启动探针处理慢启动:**代替设置很长的 initialDelaySeconds,它更灵活、更可靠。
- 探针检查逻辑要轻量:探针端点或命令不应消耗过多资源,且执行时间要短(默认为 1 秒超时)。
- 探针配置要与应用启动时间匹配:合理设置 initialDelaySeconds、periodSeconds 和 failureThreshold,避免误判。