容器生命周期钩子

本页面描述了 kubelet 管理的容器如何利用容器生命周期钩子框架来在它们的管理生命周期中运行由事件触发的代码。

概览

类似于许多具有组件生命周期钩子的编程语言框架,例如 Angular,Kubernetes 为容器提供了生命周期钩子。这些钩子使容器能够感知其管理生命周期中的事件,并在相应的生命周期钩子被执行时运行处理程序中实现的代码。

容器钩子

有两个钩子暴露给容器

PostStart

此钩子在容器创建后立即执行。然而,不能保证此钩子会在容器的 ENTRYPOINT 之前执行。不会向处理程序传递任何参数。

PreStop

此钩子在容器因 API 请求或管理事件(例如存活性/启动探针失败、抢占、资源争用等)而终止前立即调用。如果容器已处于已终止或已完成状态,则对 PreStop 钩子的调用会失败,并且此钩子必须在向容器发送 TERM 信号以停止容器之前完成。Pod 的终止优雅停止期倒计时在 PreStop 钩子执行之前开始,因此无论处理程序的结果如何,容器最终都会在 Pod 的终止优雅停止期内终止。不会向处理程序传递任何参数。

关于终止行为的详细描述可以在Pod 的终止中找到。

StopSignal

StopSignal 生命周期可以用来定义一个停止信号,该信号将在容器停止时发送给容器。如果你设置了这个字段,它会覆盖容器镜像中定义的任何 STOPSIGNAL 指令。

关于使用自定义停止信号的终止行为的详细描述可以在停止信号中找到。

钩子处理程序实现

容器可以通过实现并注册该钩子的处理程序来访问钩子。可以为容器实现三种类型的钩子处理程序

  • Exec - 在容器的 cgroup 和命名空间内执行特定命令,例如 pre-stop.sh。命令消耗的资源会计入容器。
  • HTTP - 对容器上的特定端点执行 HTTP 请求。
  • Sleep - 暂停容器指定的时长。这是一个 Beta 级特性,通过 PodLifecycleSleepAction 特性门控默认启用。

钩子处理程序执行

当调用容器生命周期管理钩子时,Kubernetes 管理系统根据钩子操作执行处理程序,httpGettcpSocket (已废弃) 和 sleep 由 kubelet 进程执行,exec 在容器中执行。

PostStart 钩子处理程序的调用在容器创建时启动,这意味着容器的 ENTRYPOINT 和 PostStart 钩子会同时触发。然而,如果 PostStart 钩子执行时间过长或挂起,它会阻止容器转换为 running 状态。

PreStop 钩子与停止容器的信号并非异步执行;该钩子必须在发送 TERM 信号之前完成其执行。如果在执行过程中 PreStop 钩子挂起,则 Pod 的阶段将是 Terminating 并保持该状态,直到其 terminationGracePeriodSeconds 超时后 Pod 被杀死。此优雅停止期适用于 PreStop 钩子执行和容器正常停止所需的总时间。例如,如果 terminationGracePeriodSeconds 是 60 秒,钩子需要 55 秒完成,而容器在收到信号后正常停止需要 10 秒,那么由于 terminationGracePeriodSeconds 小于这两件事所需总时间 (55+10),容器将在正常停止前被杀死。

如果 PostStartPreStop 钩子失败,它将杀死容器。

用户应使其钩子处理程序尽可能轻量。然而,在某些情况下,长时间运行的命令是有意义的,例如在停止容器之前保存状态。

钩子传递保证

钩子传递旨在实现 至少一次,这意味着对于任何给定的事件,例如 PostStartPreStop,钩子可能会被调用多次。钩子实现需要正确处理这种情况。

通常,只会进行单次传递。例如,如果 HTTP 钩子接收器关闭且无法接收流量,则不会尝试重新发送。然而,在某些罕见情况下,可能会发生重复传递。例如,如果在发送钩子时 kubelet 重启,kubelet 重新启动后可能会重新发送该钩子。

调试钩子处理程序

钩子处理程序的日志不会暴露在 Pod 事件中。如果处理程序由于某种原因失败,它会广播一个事件。对于 PostStart,这是 FailedPostStartHook 事件;对于 PreStop,这是 FailedPreStopHook 事件。要自己生成失败的 FailedPostStartHook 事件,修改 lifecycle-events.yaml 文件,将 postStart 命令更改为 "badcommand" 并应用。以下是运行 kubectl describe pod lifecycle-demo 后你将看到的事件示例输出

Events:
  Type     Reason               Age              From               Message
  ----     ------               ----             ----               -------
  Normal   Scheduled            7s               default-scheduler  Successfully assigned default/lifecycle-demo to ip-XXX-XXX-XX-XX.us-east-2...
  Normal   Pulled               6s               kubelet            Successfully pulled image "nginx" in 229.604315ms
  Normal   Pulling              4s (x2 over 6s)  kubelet            Pulling image "nginx"
  Normal   Created              4s (x2 over 5s)  kubelet            Created container lifecycle-demo-container
  Normal   Started              4s (x2 over 5s)  kubelet            Started container lifecycle-demo-container
  Warning  FailedPostStartHook  4s (x2 over 5s)  kubelet            Exec lifecycle hook ([badcommand]) for Container "lifecycle-demo-container" in Pod "lifecycle-demo_default(30229739-9651-4e5a-9a32-a8f1688862db)" failed - error: command 'badcommand' exited with 126: , message: "OCI runtime exec failed: exec failed: container_linux.go:380: starting container process caused: exec: \"badcommand\": executable file not found in $PATH: unknown\r\n"
  Normal   Killing              4s (x2 over 5s)  kubelet            FailedPostStartHook
  Normal   Pulled               4s               kubelet            Successfully pulled image "nginx" in 215.66395ms
  Warning  BackOff              2s (x2 over 3s)  kubelet            Back-off restarting failed container

接下来

最后修改于 2025 年 4 月 8 日 下午 12:56 PST: KEP 4960: Container Stop Signals (#49857) (fea3a58caf)