配置存活探针、就绪探针和启动探针

此页面展示了如何为容器配置存活探针、就绪探针和启动探针。

有关探针的更多信息,请参阅 存活探针、就绪探针和启动探针

kubelet 使用存活探针来了解何时重启容器。例如,存活探针可以捕获死锁,在这种情况下,应用程序正在运行,但无法取得进展。在这种状态下重启容器可以帮助应用程序即使出现错误也能保持可用性。

存活探针的常见模式是使用与就绪探针相同的低成本 HTTP 端点,但具有更高的失败阈值。这可以确保在 pod 被强制杀死之前,它会被观察到一段时间处于不可用状态。

kubelet 使用就绪探针来了解容器何时准备好开始接收流量。当所有容器都就绪时,Pod 被认为是就绪的。此信号的一种用途是控制哪些 Pod 用作服务的端点。当 Pod 未准备好时,它将从服务负载均衡器中移除。

kubelet 使用启动探针来了解容器应用程序何时启动。如果配置了这种探针,则存活探针和就绪探针只有在它成功后才会开始,确保这些探针不会干扰应用程序启动。这可用于在启动缓慢的容器上采用存活检查,避免它们在启动并运行之前被 kubelet 杀死。

开始之前

你需要拥有一个 Kubernetes 集群,并且 kubectl 命令行工具必须配置为与你的集群通信。建议在至少有两个节点的集群上运行本教程,这些节点不充当控制平面主机。如果你还没有集群,可以使用 minikube 创建一个,或者你可以使用以下 Kubernetes playground 之一

定义存活命令

许多长时间运行的应用程序最终会过渡到故障状态,并且无法恢复,除非被重启。Kubernetes 提供存活探针来检测和解决此类情况。

在本练习中,你将创建一个 Pod,该 Pod 运行基于 registry.k8s.io/busybox 镜像的容器。以下是 Pod 的配置文件

apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness
  name: liveness-exec
spec:
  containers:
  - name: liveness
    image: registry.k8s.io/busybox
    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 只有一个 ContainerperiodSeconds 字段指定 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"
Normal  Pulled     7s    kubelet, node01    Successfully pulled image "registry.k8s.io/busybox"
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"
Normal   Pulled     53s                kubelet, node01    Successfully pulled image "registry.k8s.io/busybox"
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 请求。以下是运行基于 registry.k8s.io/e2e-test-images/agnhost 镜像的容器的 Pod 的配置文件。

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 秒运行第一个存活探针。这将尝试连接到端口 8080 上的 goproxy 容器。如果存活探针失败,容器将被重启。kubelet 将继续每 10 秒运行此检查。

除了存活探针之外,此配置还包括一个就绪探针。kubelet 将在容器启动后 15 秒运行第一个就绪探针。与存活探针类似,这将尝试连接到端口 8080 上的 goproxy 容器。如果探针成功,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 健康检查端点在设置 servicereadiness 时以不同方式响应此请求。这使你能够为不同类型的容器健康检查使用相同的端点,而不是监听两个不同的端口。如果你想指定你自己的自定义服务名称并指定探针类型,Kubernetes 项目建议你使用将这些名称连接起来的名称。例如:myservice-liveness(使用 - 作为分隔符)。

配置问题(例如:端口或服务错误、未实现的健康检查协议)被视为探测失败,类似于 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 = 300 秒)的时间完成启动。一旦启动探测成功一次,存活探测就会接管,以提供对容器死锁的快速响应。如果启动探测从未成功,则容器将在 300 秒后被杀死,并受 pod 的 restartPolicy 影响。

定义就绪探测

有时,应用程序暂时无法提供服务。例如,应用程序可能需要在启动期间加载大型数据或配置文件,或者在启动后依赖外部服务。在这种情况下,您不希望杀死应用程序,但您也不希望向其发送请求。Kubernetes 提供就绪探测来检测和缓解这些情况。具有报告未就绪的容器的 pod 不会通过 Kubernetes 服务接收流量。

就绪探测的配置类似于存活探测。唯一的区别是您使用 readinessProbe 字段而不是 livenessProbe 字段。

readinessProbe:
  exec:
    command:
    - cat
    - /tmp/healthy
  initialDelaySeconds: 5
  periodSeconds: 5

HTTP 和 TCP 就绪探测的配置也与存活探测相同。

就绪探测和存活探测可以并行用于同一个容器。使用两者可以确保流量不会到达尚未准备好接收流量的容器,并且在容器发生故障时会重新启动容器。

配置探测

探测 具有许多字段,您可以使用这些字段来更精确地控制启动、存活和就绪检查的行为。

  • initialDelaySeconds:容器启动后启动、存活或就绪探测开始之前的秒数。如果定义了启动探测,则存活探测和就绪探测延迟将在启动探测成功后开始。如果 periodSeconds 的值大于 initialDelaySeconds,则 initialDelaySeconds 将被忽略。默认为 0 秒。最小值为 0。
  • periodSeconds:执行探测的频率(以秒为单位)。默认为 10 秒。最小值为 1。
  • timeoutSeconds:探测超时之前的秒数。默认为 1 秒。最小值为 1。
  • successThreshold:探测在失败后被认为成功所需的连续成功次数。默认为 1。对于存活探测和启动探测,必须为 1。最小值为 1。
  • failureThreshold:探测连续失败 failureThreshold 次后,Kubernetes 会认为整体检查失败:容器就绪/健康/存活。默认为 3。最小值为 1。对于启动探测或存活探测,如果至少 failureThreshold 个探测失败,Kubernetes 会将容器视为不健康,并触发对该特定容器的重启。kubelet 会遵循该容器的 terminationGracePeriodSeconds 设置。对于失败的就绪探测,kubelet 会继续运行失败检查的容器,并继续运行更多探测;因为检查失败,kubelet 会将 Pod 上的 Ready 条件 设置为 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.31,其中 1.31 是 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: ""

TCP 探测

对于 TCP 探测,kubelet 会在节点而不是 Pod 中建立探测连接,这意味着您不能在 host 参数中使用服务名称,因为 kubelet 无法解析它。

探测级别的 terminationGracePeriodSeconds

功能状态: Kubernetes v1.28 [稳定]

在 1.25 及更高版本中,用户可以在探测规范中指定探测级别的 terminationGracePeriodSeconds。当同时设置了 Pod 级别的和探测级别的 terminationGracePeriodSeconds 时,kubelet 会使用探测级别的值。

设置 terminationGracePeriodSeconds 时,请注意以下几点。

  • 如果 Pod 上存在探测级别的 terminationGracePeriodSeconds 字段,kubelet 始终会遵循它。

  • 如果您有现有的 Pod,其中设置了 terminationGracePeriodSeconds 字段,并且您不再希望使用每个探测的终止宽限期,则必须删除这些现有的 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 参考。

最后修改时间:2024 年 7 月 27 日,美国太平洋时间下午 5:28:更新 configure-liveness-readiness-startup-probes.md (#47098) (78c0660152)