配置 Liveness、Readiness 和 Startup 探针
此页面展示了如何为容器配置存活探针、就绪探针和启动探针。
有关探针的更多信息,请参阅存活探针、就绪探针和启动探针
kubelet 使用存活探针来知道何时重启容器。例如,存活探针可以捕获死锁,即应用程序正在运行,但无法取得进展。在这种状态下重启容器有助于使应用程序在存在 Bug 的情况下更可用。
存活探针的常见模式是使用与就绪探针相同的低成本 HTTP 端点,但具有更高的 failureThreshold。这确保了在 Pod 被硬杀死之前,它被观察到在一段时间内处于非就绪状态。
kubelet 使用就绪探针来知道容器何时准备好开始接收流量。此信号的一个用途是控制哪些 Pod 用作 Service 的后端。当 Pod 的 Ready
Condition 为 true 时,它被认为是就绪的。当 Pod 未就绪时,它会从 Service 负载均衡器中移除。当 Pod 的节点的 Ready
Condition 不为 true 时,当 Pod 的一个 readinessGates
为 false 时,或者当其至少一个容器未就绪时,Pod 的 Ready
Condition 为 false。
kubelet 使用启动探针来知道容器应用程序何时启动。如果配置了此类探针,则存活探针和就绪探针不会开始,直到它成功,确保这些探针不会干扰应用程序启动。这可用于在启动缓慢的容器上采用存活检查,避免它们在启动并运行之前被 kubelet 杀死。
注意
存活探针是恢复应用程序故障的强大方式,但应谨慎使用。必须仔细配置存活探针,以确保它们真正指示不可恢复的应用程序故障,例如死锁。注意
存活探针的错误实现可能导致级联故障。这会导致容器在高负载下重启;由于应用程序可伸缩性降低,客户端请求失败;以及由于某些失败的 Pod,剩余 Pod 的工作负载增加。了解就绪探针和存活探针之间的区别以及何时将它们应用于你的应用程序。准备工作
你需要有一个 Kubernetes 集群,并且 kubectl 命令行工具已配置为与你的集群通信。建议在至少有两个不作为控制平面主机的节点上运行本教程。如果你还没有集群,可以使用 minikube 创建一个,或者你可以使用这些 Kubernetes 操场之一
定义存活命令
许多长时间运行的应用程序最终会进入损坏状态,除了重启之外无法恢复。Kubernetes 提供了存活探针来检测和补救此类情况。
在本练习中,你将创建一个 Pod,它运行一个基于 registry.k8s.io/busybox:1.27.2
镜像的容器。以下是 Pod 的配置文件
apiVersion: v1
kind: Pod
metadata:
labels:
test: liveness
name: liveness-exec
spec:
containers:
- name: liveness
image: registry.k8s.io/busybox:1.27.2
args:
- /bin/sh
- -c
- touch /tmp/healthy; sleep 30; rm -f /tmp/healthy; sleep 600
livenessProbe:
exec:
command:
- cat
- /tmp/healthy
initialDelaySeconds: 5
periodSeconds: 5
在配置文件中,你可以看到 Pod 有一个 Container
。periodSeconds
字段指定 kubelet 应该每 5 秒执行一次存活探针。initialDelaySeconds
字段告诉 kubelet 它应该等待 5 秒钟,然后执行第一次探针。为了执行探针,kubelet 在目标容器中执行命令 cat /tmp/healthy
。如果命令成功,它返回 0,kubelet 认为容器是存活且健康的。如果命令返回非零值,kubelet 会杀死容器并重新启动它。
容器启动时,它会执行此命令
/bin/sh -c "touch /tmp/healthy; sleep 30; rm -f /tmp/healthy; sleep 600"
在容器生命的前 30 秒,存在一个 /tmp/healthy
文件。因此,在前 30 秒内,命令 cat /tmp/healthy
返回成功代码。30 秒后,cat /tmp/healthy
返回失败代码。
创建 Pod
kubectl apply -f https://k8s.io/examples/pods/probe/exec-liveness.yaml
在 30 秒内,查看 Pod 事件
kubectl describe pod liveness-exec
输出表明尚未有存活探针失败
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 11s default-scheduler Successfully assigned default/liveness-exec to node01
Normal Pulling 9s kubelet, node01 Pulling image "registry.k8s.io/busybox:1.27.2"
Normal Pulled 7s kubelet, node01 Successfully pulled image "registry.k8s.io/busybox:1.27.2"
Normal Created 7s kubelet, node01 Created container liveness
Normal Started 7s kubelet, node01 Started container liveness
35 秒后,再次查看 Pod 事件
kubectl describe pod liveness-exec
在输出底部,有消息指示存活探针已失败,并且失败的容器已被杀死并重新创建。
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 57s default-scheduler Successfully assigned default/liveness-exec to node01
Normal Pulling 55s kubelet, node01 Pulling image "registry.k8s.io/busybox:1.27.2"
Normal Pulled 53s kubelet, node01 Successfully pulled image "registry.k8s.io/busybox:1.27.2"
Normal Created 53s kubelet, node01 Created container liveness
Normal Started 53s kubelet, node01 Started container liveness
Warning Unhealthy 10s (x3 over 20s) kubelet, node01 Liveness probe failed: cat: can't open '/tmp/healthy': No such file or directory
Normal Killing 10s kubelet, node01 Container liveness failed liveness probe, will be restarted
再等待 30 秒,并验证容器已重新启动
kubectl get pod liveness-exec
输出显示 RESTARTS
已增加。请注意,RESTARTS
计数器在失败的容器返回到运行状态后立即增加
NAME READY STATUS RESTARTS AGE
liveness-exec 1/1 Running 1 1m
定义存活 HTTP 请求
另一种存活探针使用 HTTP GET 请求。以下是一个 Pod 的配置文件,该 Pod 运行一个基于 registry.k8s.io/e2e-test-images/agnhost
镜像的容器。
apiVersion: v1
kind: Pod
metadata:
labels:
test: liveness
name: liveness-http
spec:
containers:
- name: liveness
image: registry.k8s.io/e2e-test-images/agnhost:2.40
args:
- liveness
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。
http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
duration := time.Now().Sub(started)
if duration.Seconds() > 10 {
w.WriteHeader(500)
w.Write([]byte(fmt.Sprintf("error: %v", duration.Seconds())))
} else {
w.WriteHeader(200)
w.Write([]byte("ok"))
}
})
kubelet 在容器启动 3 秒后开始执行健康检查。因此,最初的几次健康检查将成功。但 10 秒后,健康检查将失败,kubelet 将杀死并重新启动容器。
要尝试 HTTP 存活检查,请创建一个 Pod
kubectl apply -f https://k8s.io/examples/pods/probe/http-liveness.yaml
10 秒后,查看 Pod 事件以验证存活探针已失败且容器已重新启动
kubectl describe pod liveness-http
在 v1.13 之后的版本中,本地 HTTP 代理环境变量设置不会影响 HTTP 存活探针。
定义 TCP 存活探针
第三种类型的存活探针使用 TCP 套接字。通过此配置,kubelet 将尝试在指定端口上打开与容器的套接字。如果能够建立连接,则容器被认为是健康的;如果不能,则被认为是失败的。
apiVersion: v1
kind: Pod
metadata:
name: goproxy
labels:
app: goproxy
spec:
containers:
- name: goproxy
image: registry.k8s.io/goproxy:0.1
ports:
- containerPort: 8080
readinessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 15
periodSeconds: 10
livenessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 15
periodSeconds: 10
如你所见,TCP 检查的配置与 HTTP 检查非常相似。此示例同时使用了就绪探针和存活探针。kubelet 将在容器启动 15 秒后运行第一个存活探针。它将尝试连接到 goproxy
容器的 8080 端口。如果存活探针失败,容器将重新启动。kubelet 将每 10 秒继续运行此检查。
除了存活探针,此配置还包括一个就绪探针。kubelet 将在容器启动 15 秒后运行第一个就绪探针。与存活探针类似,它将尝试连接到 goproxy
容器的 8080 端口。如果探针成功,Pod 将被标记为就绪并接收来自服务的流量。如果就绪探针失败,Pod 将被标记为未就绪并且不会接收来自任何服务的流量。
要尝试 TCP 存活检查,请创建一个 Pod
kubectl apply -f https://k8s.io/examples/pods/probe/tcp-liveness-readiness.yaml
15 秒后,查看 Pod 事件以验证存活探针
kubectl describe pod goproxy
定义 gRPC 存活探针
Kubernetes v1.27 [稳定]
如果你的应用程序实现了 gRPC 健康检查协议,此示例展示了如何配置 Kubernetes 以将其用于应用程序存活检查。同样,你可以配置就绪探针和启动探针。
这是一个示例清单
apiVersion: v1
kind: Pod
metadata:
name: etcd-with-grpc
spec:
containers:
- name: etcd
image: registry.k8s.io/etcd:3.5.1-0
command: [ "/usr/local/bin/etcd", "--data-dir", "/var/lib/etcd", "--listen-client-urls", "http://0.0.0.0:2379", "--advertise-client-urls", "http://127.0.0.1:2379", "--log-level", "debug"]
ports:
- containerPort: 2379
livenessProbe:
grpc:
port: 2379
initialDelaySeconds: 10
要使用 gRPC 探针,必须配置 port
。如果你想区分不同类型的探针和不同功能的探针,可以使用 service
字段。你可以将 service
设置为 liveness
,并使你的 gRPC 健康检查端点对此请求的响应与将 service
设置为 readiness
时不同。这允许你为不同类型的容器健康检查使用相同的端点,而不是监听两个不同的端口。如果你想指定自己的自定义服务名称并同时指定探针类型,Kubernetes 项目建议你使用将这些名称连接起来的名称。例如:myservice-liveness
(使用 -
作为分隔符)。
注意
与 HTTP 或 TCP 探针不同,你不能按名称指定健康检查端口,也不能配置自定义主机名。配置问题(例如:端口或服务不正确,未实现的健康检查协议)被视为探针失败,类似于 HTTP 和 TCP 探针。
要尝试 gRPC 存活检查,请使用以下命令创建一个 Pod。在下面的示例中,etcd Pod 配置为使用 gRPC 存活探针。
kubectl apply -f https://k8s.io/examples/pods/probe/grpc-liveness.yaml
15 秒后,查看 Pod 事件以验证存活检查未失败
kubectl describe pod etcd-with-grpc
使用 gRPC 探针时,需要注意一些技术细节
- 探针针对 Pod IP 地址或其主机名运行。请务必将你的 gRPC 端点配置为监听 Pod 的 IP 地址。
- 探针不支持任何身份验证参数(如
-tls
)。 - 内置探针没有错误代码。所有错误都被视为探针失败。
- 如果
ExecProbeTimeout
功能门设置为false
,grpc-health-probe 将**不**遵守timeoutSeconds
设置(默认为 1 秒),而内置探针将在超时时失败。
使用命名端口
你可以为 HTTP 和 TCP 探针使用命名 port
。gRPC 探针不支持命名端口。
例如
ports:
- name: liveness-port
containerPort: 8080
livenessProbe:
httpGet:
path: /healthz
port: liveness-port
使用启动探针保护启动缓慢的容器
有时,你必须处理在首次初始化时需要额外启动时间的应用程序。在这种情况下,很难设置存活探针参数,而不会影响死锁的快速响应,这是此类探针的动机。解决方案是使用相同的命令、HTTP 或 TCP 检查设置启动探针,其中 failureThreshold * periodSeconds
足够长以覆盖最坏情况的启动时间。
因此,前面的示例将变为
ports:
- name: liveness-port
containerPort: 8080
livenessProbe:
httpGet:
path: /healthz
port: liveness-port
failureThreshold: 1
periodSeconds: 10
startupProbe:
httpGet:
path: /healthz
port: liveness-port
failureThreshold: 30
periodSeconds: 10
由于启动探针,应用程序将有最长 5 分钟 (30 * 10 = 300s) 完成其启动。一旦启动探针成功一次,存活探针就会接管,以快速响应容器死锁。如果启动探针从未成功,容器将在 300 秒后被杀死并受 Pod 的 restartPolicy
约束。
定义就绪探针
有时,应用程序暂时无法提供流量。例如,应用程序可能需要在启动期间加载大型数据或配置文件,或者在启动后依赖外部服务。在这种情况下,你不想杀死应用程序,但你也不想向它发送请求。Kubernetes 提供了就绪探针来检测和缓解这些情况。容器报告未就绪的 Pod 不会通过 Kubernetes Services 接收流量。
注意
就绪探针在容器的整个生命周期中运行。注意
就绪探针和存活探针的成功不相互依赖。如果你想在执行就绪探针之前等待,你应该使用initialDelaySeconds
或 startupProbe
。就绪探针的配置类似于存活探针。唯一的区别是你使用 readinessProbe
字段而不是 livenessProbe
字段。
readinessProbe:
exec:
command:
- cat
- /tmp/healthy
initialDelaySeconds: 5
periodSeconds: 5
HTTP 和 TCP 就绪探针的配置也与存活探针相同。
就绪探针和存活探针可以同时用于同一个容器。同时使用两者可以确保流量不会到达尚未就绪的容器,并在容器失败时重新启动。
配置探针
探针有许多字段,你可以使用它们更精确地控制启动、存活和就绪检查的行为
initialDelaySeconds
:容器启动后,在启动、存活或就绪探针启动之前的秒数。如果定义了启动探针,则存活和就绪探针的延迟不会开始,直到启动探针成功。如果periodSeconds
的值大于initialDelaySeconds
,则initialDelaySeconds
将被忽略。默认为 0 秒。最小值为 0。periodSeconds
:执行探针的频率(秒)。默认为 10 秒。最小值为 1。当容器未就绪时,ReadinessProbe
可能会在配置的periodSeconds
间隔之外的时间执行。这是为了更快地使 Pod 就绪。timeoutSeconds
:探针超时前的秒数。默认为 1 秒。最小值为 1。successThreshold
:探针在失败后被认为是成功的最小连续成功次数。默认为 1。对于存活探针和启动探针,必须为 1。最小值为 1。failureThreshold
:探针连续失败failureThreshold
次后,Kubernetes 认为整体检查已失败:容器**未**就绪/健康/存活。默认为 3。最小值为 1。对于启动或存活探针,如果至少有failureThreshold
次探针失败,Kubernetes 会将容器视为不健康并触发该特定容器的重启。kubelet 遵守该容器的terminationGracePeriodSeconds
设置。对于失败的就绪探针,kubelet 继续运行失败检查的容器,并继续运行更多探针;由于检查失败,kubelet 将 Pod 上的Ready
condition 设置为false
。terminationGracePeriodSeconds
:配置一个宽限期,让 kubelet 在触发关闭失败容器和强制容器运行时停止该容器之间等待。默认为继承 Pod 级别的terminationGracePeriodSeconds
值(如果未指定,则为 30 秒),最小值为 1。有关更多详细信息,请参阅探针级别的terminationGracePeriodSeconds
。
注意
就绪探针的错误实现可能导致容器中进程数量不断增加,如果不对其进行检查,则会导致资源耗尽。HTTP 探针
HTTP 探针有可以在 httpGet
上设置的附加字段
host
:要连接的主机名,默认为 Pod IP。你可能希望在httpHeaders
中设置 "Host"。scheme
:用于连接主机的方案(HTTP 或 HTTPS)。默认为 "HTTP"。path
:HTTP 服务器上要访问的路径。默认为 "/"。httpHeaders
:要在请求中设置的自定义头部。HTTP 允许重复头部。port
:容器上要访问的端口的名称或编号。编号必须在 1 到 65535 的范围内。
对于 HTTP 探针,kubelet 向指定的端口和路径发送 HTTP 请求以执行检查。kubelet 将探针发送到 Pod 的 IP 地址,除非地址被 httpGet
中可选的 host
字段覆盖。如果 scheme
字段设置为 HTTPS
,kubelet 会发送 HTTPS 请求,跳过证书验证。在大多数情况下,你不需要设置 host
字段。这里有一个你会设置它的场景。假设容器监听 127.0.0.1,并且 Pod 的 hostNetwork
字段为 true。那么 httpGet
下的 host
应该设置为 127.0.0.1。如果你的 Pod 依赖于虚拟主机(这可能是更常见的情况),则不应使用 host
,而应在 httpHeaders
中设置 Host
头部。
对于 HTTP 探针,kubelet 除了强制性的 Host
头部之外,还会发送两个请求头部
User-Agent
:默认值为kube-probe/1.34
,其中1.34
是 kubelet 的版本。Accept
:默认值为*/*
。
你可以通过为探针定义 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: ""
注意
当 kubelet 使用 HTTP 探测 Pod 时,它只在重定向到同一主机时才遵循重定向。如果 kubelet 在探测期间收到 11 次或更多重定向,则认为探测成功并创建相关事件
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 29m default-scheduler Successfully assigned default/httpbin-7b8bc9cb85-bjzwn to daocloud
Normal Pulling 29m kubelet Pulling image "docker.io/kennethreitz/httpbin"
Normal Pulled 24m kubelet Successfully pulled image "docker.io/kennethreitz/httpbin" in 5m12.402735213s
Normal Created 24m kubelet Created container httpbin
Normal Started 24m kubelet Started container httpbin
Warning ProbeWarning 4m11s (x1197 over 24m) kubelet Readiness probe warning: Probe terminated redirects
如果 kubelet 收到重定向,其中主机名与请求不同,则探针的结果被视为成功,kubelet 创建一个事件来报告重定向失败。
TCP 探针
对于 TCP 探针,kubelet 在节点上建立探针连接,而不是在 Pod 中,这意味着你不能在 host
参数中使用服务名称,因为 kubelet 无法解析它。
探针级别 terminationGracePeriodSeconds
Kubernetes v1.28 [stable]
在 1.25 及更高版本中,用户可以在探针规范中指定探针级别的 terminationGracePeriodSeconds
。当同时设置了 Pod 级别和探针级别的 terminationGracePeriodSeconds
时,kubelet 将使用探针级别的值。
设置 terminationGracePeriodSeconds
时,请注意以下事项
如果 Pod 上存在探针级别的
terminationGracePeriodSeconds
字段,kubelet 总是会遵守它。如果你有已设置
terminationGracePeriodSeconds
字段的现有 Pod,并且你不再希望使用每个探针的终止宽限期,则必须删除这些现有 Pod。
例如
spec:
terminationGracePeriodSeconds: 3600 # pod-level
containers:
- name: test
image: ...
ports:
- name: liveness-port
containerPort: 8080
livenessProbe:
httpGet:
path: /healthz
port: liveness-port
failureThreshold: 1
periodSeconds: 60
# Override pod-level terminationGracePeriodSeconds #
terminationGracePeriodSeconds: 60
就绪探针不能设置探针级别的 terminationGracePeriodSeconds
。它将被 API 服务器拒绝。
下一步
- 了解有关容器探针的更多信息。
你还可以阅读以下 API 参考