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

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

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

存活探针的常见模式是使用与就绪探针相同的低成本 HTTP 端点,但设置更高的 `failureThreshold`。这可以确保 Pod 在被强制终止之前,有一段时间会被观察到处于 NotReady 状态。

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

开始之前

定义一个存活命令

许多长时间运行的应用程序最终会进入损坏状态,除非重启否则无法恢复。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 包含一个 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: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 请求。以下是运行基于 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 socket。使用此配置,kubelet 将尝试在指定的端口上打开一个 socket 连接到你的容器。如果能够建立连接,则认为容器是健康的;如果不能,则认为是失败。

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 将被标记为 Ready 并将从 Services 接收流量。如果就绪探针失败,则 Pod 将被标记为 NotReady 并且不会从任何 Services 接收流量。

要尝试 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(使用 - 作为分隔符)。

配置问题(例如:不正确的端口或 Service、未实现的健康检查协议)被视为探针失败,类似于 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 特性门控设置为 falsegrpc-health-probe 不会遵守 timeoutSeconds 设置(默认值为 1s),而内置探针会在超时时失败。

使用命名端口

例如

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 Services 接收流量。

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

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

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

就绪探针和存活探针可以并行用于同一个容器。同时使用这两种探针可以确保流量不会发送到尚未就绪的容器,并在容器失败时将其重新启动。

配置探针

  • timeoutSeconds: 探针超时前等待的秒数。默认为 1 秒。最小值为 1。
  • successThreshold: 在失败后,探针被视为成功的最小连续成功次数。默认为 1。对于存活探针和启动探针,此值必须为 1。最小值为 1。

HTTP 探针

  • 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.33,其中 1.33 是 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 参数中使用 Service 名称,因为 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 参考文档

最后修改于 2025 年 1 月 17 日下午 1:48 PST:更新清单文件中的镜像标签 (7b5ca4d7a5)