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

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

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

kubelet 使用存活探针来了解何时重启容器。例如,存活探针可以捕获死锁,在这种情况下,应用程序正在运行,但无法取得进展。在这种状态下重启容器有助于提高应用程序的可用性,尽管存在错误。

存活探针的常见模式是使用与就绪探针相同的低成本 HTTP 端点,但具有更高的 failureThreshold。这确保了 Pod 在被硬杀之前,会被观察到一段时间处于未就绪状态。

kubelet 使用就绪探针来了解容器何时准备好开始接受流量。此信号的一种用途是控制哪些 Pod 用作服务的后端。当 Pod 的 Ready 状况为 true 时,该 Pod 被认为是就绪的。当 Pod 未就绪时,会将其从服务负载均衡器中删除。当 Pod 的节点的 Ready 状况不为 true 时,当 Pod 的 readinessGates 之一为 false 时,或当其至少有一个容器未就绪时,Pod 的 Ready 状况为 false。

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

开始之前

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

定义存活命令

许多长时间运行的应用程序最终会转换为中断状态,除非重新启动,否则无法恢复。Kubernetes 提供了存活探针来检测和纠正这种情况。

在本练习中,您将创建一个基于 registry.k8s.io/busybox 镜像运行容器的 Pod。以下是 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 健康检查端点对该请求的响应与将 service 设置为 readiness 时的响应不同。这使您可以使用相同的端点进行不同类型的容器健康检查,而不是监听两个不同的端口。如果您想指定自己的自定义服务名称,并且还指定探针类型,则 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。当容器未就绪时,ReadinessProbe 可能会在配置的 periodSeconds 间隔之外的时间执行。这是为了使 Pod 更快地就绪。
  • 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 请求以执行检查。除非地址被 httpGet 中可选的 host 字段覆盖,否则 kubelet 会将探针发送到 Pod 的 IP 地址。如果 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.32,其中 1.32 是 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 年 11 月 05 日凌晨 1:10: 删除不必要的反斜杠 (9a31b7420a)