使用容器生命周期回调,指容器生命周期中事件触发,运行指定代码

概述

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

容器回调

有两个回调暴露给容器:

PostStart

这个回调在创建容器之后立即执行。 但是,不能保证回调会在容器入口点(ENTRYPOINT)之前执行。 没有参数传递给处理程序。

PreStop

在容器因 API 请求或者管理事件(诸如存活态探针失败、资源抢占、资源竞争等)而被终止之前, 此回调会被调用。 如果容器已经处于终止或者完成状态,则对 preStop 回调的调用将失败。 此调用是阻塞的,也是同步调用,因此必须在删除容器的调用之前完成。 没有参数传递给处理程序。

回调处理程序的实现
容器可以通过实现和注册该回调的处理程序来访问回调。 针对容器,有两种类型的回调处理程序可供实现:

  • Exec - 在容器的 cgroups 和名称空间中执行特定的命令(例如 pre-stop.sh)。 命令所消耗的资源计入容器的资源消耗。
  • HTTP - 对容器上的特定端点执行 HTTP 请求。

回调处理程序执行
当调用容器生命周期管理回调时,Kubernetes 管理系统在注册了回调的容器中执行处理程序。

回调处理程序调用在包含容器的 Pod 上下文中是同步的。 这意味着对于 PostStart 回调,容器入口点和回调异步触发。 但是,如果回调运行或挂起的时间太长,则容器无法达到 running 状态。

行为与 PreStop 回调的行为类似。 如果回调在执行过程中挂起,Pod 阶段将保持在 Terminating 状态, 并在 Pod 结束的 terminationGracePeriodSeconds 之后被杀死。 如果 PostStart 或 PreStop 回调失败,它会杀死容器。

用户应该使他们的回调处理程序尽可能的轻量级。 但也需要考虑长时间运行的命令也很有用的情况,比如在停止容器之前保存状态。

回调发送保证
回调的发送应该是 至少一次,这意味着对于任何给定的事件,例如 PostStart 或 PreStop,回调可以被调用多次。 如何正确处理,是回调实现所要考虑的问题。

通常情况下,只会进行单次寄送。 例如,如果 HTTP 回调接收器宕机,无法接收流量,则不会尝试重新发送。 然而,偶尔也会发生重复寄送的可能。 例如,如果 kubelet 在发送回调的过程中重新启动,回调可能会在 kubelet 恢复后重新发送。

调试回调处理程序
回调处理程序的日志不会在 Pod 事件中公开。 如果处理程序由于某种原因失败,它将发出一个事件。 对于 PostStart,是 FailedPostStartHook 事件,对于 PreStop,是 FailedPreStopHook 事件。 您可以通过运行 kubectl describe pod <pod_name> 命令来查看这些事件。 下面是运行这个命令的一些事件输出示例:

Events:
  FirstSeen    LastSeen    Count    From                            SubobjectPath        Type        Reason        Message
  ---------    --------    -----    ----                            -------------        --------    ------        -------
  1m        1m        1    {default-scheduler }                                Normal        Scheduled    Successfully assigned test-1730497541-cq1d2 to gke-test-cluster-default-pool-a07e5d30-siqd
  1m        1m        1    {kubelet gke-test-cluster-default-pool-a07e5d30-siqd}    spec.containers{main}    Normal        Pulling        pulling image "test:1.0"
  1m        1m        1    {kubelet gke-test-cluster-default-pool-a07e5d30-siqd}    spec.containers{main}    Normal        Created        Created container with docker id 5c6a256a2567; Security:[seccomp=unconfined]
  1m        1m        1    {kubelet gke-test-cluster-default-pool-a07e5d30-siqd}    spec.containers{main}    Normal        Pulled        Successfully pulled image "test:1.0"
  1m        1m        1    {kubelet gke-test-cluster-default-pool-a07e5d30-siqd}    spec.containers{main}    Normal        Started        Started container with docker id 5c6a256a2567
  38s        38s        1    {kubelet gke-test-cluster-default-pool-a07e5d30-siqd}    spec.containers{main}    Normal        Killing        Killing container with docker id 5c6a256a2567: PostStart handler: Error executing in Docker Container: 1
  37s        37s        1    {kubelet gke-test-cluster-default-pool-a07e5d30-siqd}    spec.containers{main}    Normal        Killing        Killing container with docker id 8df9fdfd7054: PostStart handler: Error executing in Docker Container: 1
  38s        37s        2    {kubelet gke-test-cluster-default-pool-a07e5d30-siqd}                Warning        FailedSync    Error syncing pod, skipping: failed to "StartContainer" for "main" with RunContainerError: "PostStart handler: Error executing in Docker Container: 1"
  1m         22s         2     {kubelet gke-test-cluster-default-pool-a07e5d30-siqd}    spec.containers{main}