Kubernetes - 工作负载:Pod 生命周期

Table of Contents

https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/

Pods 遵循定义的生命周期,从 Pending 阶段(Phase)开始,至少有一个主容器开始运行,变更为 Running 状态,最终的状态是 Succssed 或者 Failed 状态,取决于 Pod 中的任何容器是否因失败而终止。

在 Pod 运行的同时,kubelet 能够重启容器来应对某些故障(faults)。在 Pod 中,Kubernetes 跟踪不同容器的状态(states),然后决定采用相应的措施 来试 Pod 再次变为健康状态。

在 Kubernetes API 中,Pod 有规范(spec)和实际状态。Pod 对象的状态由一组 Pod conditions 组成。You can also inject custom readiness information into the condition data for a Pod, if that is useful to your application.

Pods 一生中只会被 调度 一次。一旦 Pod 分配给某个节点,Pod 会一直在该节点上运行直到停止或者 被终止

1. Pod 一生

跟应用程序容器一样,Pods 被认为是一个相对短暂的(而不是永久的)实体。Pods 创建时,会给分配一个唯一 ID(UID),然后被调度到节点上会一直运行到终止 (重启或删除)。如果节点挂了,调度到该节点的 Pods 在超过一定时间后删除。

Pods 没有自愈功能。如果 Pod 被调度到一个节点,然后失败了,Pod 会被删除;同时,节点资源紧缺或者维护时,Pods 也无法幸免。Kubernetes 使用更高级别的抽象, 即控制器(controller),来负责管理相对一次性的 Pod 实例的工作。

给定的 Pod(由 UID 标识)永远不会『重新调度』到不同的节点上;而是被一个的新的替换掉,相似的 Pod,甚至可以是相同的名字,但拥有不同的 UID。

当某件事物与 Pod 由相同的寿命时,比如说卷,意味着只要 Pod(确切的说是 UID)存在,它们就存在。如果 Pods 因为任何原因被删除,或者是被替换,关联的事物 也会被销毁,然后创建一个新的。

Sorry, your browser does not support SVG.

Figure 1: Pod 图解:一个容器多个 Pod,一个文件提取器和一个 web 服务,使用持久卷共享存储

2. Pod 阶段(phase)

Pod 的 status 字段是一个 PodStatus 对象,它包含一个 phase 字段。

Pod 的 phase 是对 Pod 生命周期高度精简的概括。phase 设计不是为了全面汇总容器或者 Pod 状态,也不打算成为一个全面的状态机。

Pod phase 值的数量和含义受到严格保护(tightly guarded)。除了此处给定的 phase 值之外,不会有其他的值。下面是可能的值:

描述
Pending Pod 已经被集群接受,但是其中的一个或者多个容器还没准备好运行。这包括 Pod 等待被调度所花费的时间,以及通过网络下载镜像所花费的时间
Running Pod 已绑定到节点,并且所有的容器都已创建。至少有一个容器在运行,或者正在启动或者重新启动
Succeeded Pod 中的所有容器已成功终止,并且不会重启
Failed Pod 所有的容器都已被终止,至少有一个容器终止失败了。也就是说,容器要么以非零状态退出,要么被系统终止
Unknown 由于某种原因,无法获得 Pod 的状态。此阶段通常是由于与 Pod 所在节点通信时发生错误而导致的

注意: 删除 Pod 时,通过一些 kubectl 命令指定为 TerminatingTerminating 状态不是 Pod 的 phases。 Pod 被给予优雅终止的时间限制,默认是 30s。你可以使用 --force强制终止 Pod

如果节点挂了或者与集群失联了,Kubernetes 会将丢失节点上的所有 Pod 的 phase 设置为 Failed。

3. 容器状态

除了 Pod 的总 phase,Kubernetes 还会跟踪 Pod 每个容器的状态。你可以使用 容器生命周期 hooks 来触发时间,以在容器生命周期中的某些时间点运行。

一旦调度程序将 Pod 分配给节点之后,kubelet 开始使用容器运行时为该 Pod 创建容器。容器有三中可能的状态: Waiting, RunningTerminated

要检查 Pod 容器的状态,你可以使用 kubectl describe pod <name of pod> 。输出展示了 Pod 中每个容器的状态。

3.1. Waiting

如果容器即不是 Running 也不是 Terminated 状态,那它就是 Waiting 。处于 Waiting 状态的容器仍在执行它所需的操作,以完成启动: 比如,从容器镜像中心拉取镜像,或者应用 Secret 数据。当你使用 kubectl 查询到带有 Waiting 状态的 Pod 时,你同时会看到一个原因字段,来总结 为什么容器处于该状态。

3.2. Running

Running 状态标识容器正在健康的运行。如果有 postStart hook 配置了,它也已经执行并且完成。当你使用 kubectl 查询 Running 状态的容器, 你还可以看到有关容器什么时候进入运行状态的信息。

3.3. Terminated

处于 Terminated 状态的容器开始执行,然后运行完成或者由于某种原因而失败。当你使用 kubectl 查询 Terminated 状态的容器,你会看到原因, 和退出码,以及该容器执行期间的开始和结束时间。

4. 容器重启策略

Pod 的 spec 包含 restartPolicy 字段,其值可能是 Always, OnFailureNever 。默认是 Always

restartPolicy 会应用到 Pod 中的每个容器。 restartPolicy 仅指同一节点上的 kubelet 重新启动容器。在容器在 Pod 中退出之后, kubelet 会已指数即的退避延迟(back-off delay)(10s, 20s, 40s, …)重新启动它们,上限为 5 分钟。一旦容器执行了 10 分钟之后没有任何问题, kubelet 会重置该容器退避(back-off)计数器。

5. Pod conditions

A Pod has a PodStatus, which has an array of PodConditions through which the Pod has or has not passed:

  • PodScheduled Pod 已经被调度到节点上;
  • ContainersReady Pod 中的所有容器都已经 ready;
  • Initialized 所有的 init containers 已经成功启动;
  • Ready Pod 能够处理请求,应该把它加到匹配 Services 的负载均衡池中;
字段名 描述
type 该 Pod condition 名字
lastProbeTime 上一次探测 Pod condition 的时间戳
lastTransitionTime Pod 上一次状态变更的时间戳
reason 机器可读的大小写(UpperCamelCase)的文本,表示最后一次变更的原因
message 人类可读的消息,表示最后一次状态转换的详细信息

5.1. Pod readiness(就绪)

FEATURE STATE: Kubernetes v1.14 [stable]

你的应用程序可以向 PodStatus 注入额外的反馈或者引号: Pod readiness 。要使用此功能,请在 Pod 的 spec 中设置 readinessGates , 指定 kubelet 为 Pod 就绪状态评估的条件列表。

Readiness gates 由 Pod 当前的 status.condition 决定。如果 Kubernetes 在 status.condition 中找不到这个 condition 字段, condition 的状态会默认设置为 "False"

kind: Pod
...
spec:
  readinessGates:
    - conditionType: "www.example.com/feature-1"
      status:
        conditions:
          - type: Ready                              # a built in PodCondition
            status: "False"
            lastProbeTime: null
            lastTransitionTime: 2018-01-01T00:00:00Z
            - type: "www.example.com/feature-1"        # an extra PodCondition
              status: "False"
              lastProbeTime: null
              lastTransitionTime: 2018-01-01T00:00:00Z
              containerStatuses:
                - containerID: docker://abcd...
                  ready: true
...

你添加的 Pod conditions 必须符合 Kubernetes label key 格式

5.2. Status for Pod readiness

kubectl patch 命令不支持修补对象状态。要设置这些 status.condition ,应用程序和操作人员应该是用 PATCH 操作。你可以使用 Kubernetes 客户端库 编写代码来自定义设置 Pod conditions。

对于一个 Pod 使用自定义 conditions,当以下两个语句均适用时,Pod 才会被评估为 ready:

  • Pod 中所有的容器都已经 ready;
  • 所有指定 readinessGateTrue

如果第一个条件满足,第二个没有值或者是 False ,kubelet 会把 Pod 的 condition 设置为 ContainersReady

6. 容器探针

探针(Probe) 是 kubelet 对容器定时执行的诊断。为了执行诊断,kubelet 调用容器上实现的处理器(handler)程序。一共有三类的处理器:

  • ExecAction 容器内执行命令。命令的退出状态码是 0,会被认为是诊断成功;
  • TCPSocketAction 根据指定的端口对容器执行 TCP 检查。如果端口打开,则认为诊断成功;
  • HTTPGetAction 针对特定的端口和路径上的执行 HTTP get 请求。如果响应的状态 >=200 并且 <400 ,则认为诊断成功;

每个探针具有三中结果之一:

  • Success 容器探测成功;
  • Failure 容器探测失败;
  • Unknown 探测失败,因此不应采取任何措施。

kubelet 可以对正在运行的容器执行三种探测,并做出反应:

  • livenessProbe 指示容器是否正在运行。如果 liveness 探针失败,kubelet 会杀掉容器,并且容器要遵循重启策略。如果一个容器没有提供 liveness 探针, 默认的状态是 Success
  • readinessProbe 指示容器是否准备好处理请求。如果 readiness 探针失败,端点控制器会把 Pod 的 IP 从所有匹配的 Services 的端点中移出。 初始化延迟(initial delay)之前的默认 readiness 探针会被设置为 Failure 。如果容器未提供 readiness 探针,默认值为 Success
  • startupProbe 指示是否启动容器中的应用程序。如果提供了 startup 探针,所有其他的探针会失效,直到它执行成功。 如果 startup 探针失败了,kubelet 会杀掉容器,容器必须遵守其重启策略。如果容器没有提供 startup 探针,默认状态是 Success

更多如何设置 liveness,readiness 或者 startup 探针,查看 配置文档

6.1. 什么时候你应该使用 liveness 探针?

FEATURE STATE: Kubernetes v1.0 [stable]

如果你容器中运行的进程遇到问题或运行不正常会自我崩溃,你不一定需要 liveness 探测;kubelet 会自动根据 Pod 的 restartPolicy 执行正确的操作。

如果你希望你的容器在探测失败时被杀死并重新启动,请指定一个 liveness 探测,并指定 restartPolicy 的值是 Always 或者 OnFailure

6.2. 什么时候你应该使用 readiness 探针?

FEATURE STATE: Kubernetes v1.0 [stable]

如果你只想在探测成功之后才向 Pod 发送流量,可以指定 readiness 探针。这种场景下,readiness 探针可能和 liveness 探针相同,但是 readiness 表示的是 Pod 在不接受任何流量的情况下启动,并且探测成功之后才开始接收流量。如果你的容器需要在启动过程中加载大量数据,配置文件,或者迁移。 这种情况下,建议使用 readiness 探针。

If you want your container to be able to take itself down for maintenance, you can specify a readiness probe that checks an endpoint specific to readiness that is different from the liveness probe.

注意: 如果你希望在删除 Pod 时能够处理完请求,则不一定需要准备 readiness 探针。删除后,无论是否有 readiness 探针,Pod 都会自动将自己设置为 未就绪状态。等待 Pod 中的容器停止时,Pod 仍然是未就绪状态。

6.3. 什么时候你应该使用 startup 探针?

FEATURE STATE: Kubernetes v1.20 [stable]

对于需要很长时间太能投入使用的 Pod,startup 探针非常有用。与其设置较长时间的 liveness 间隔,你可以配置一个单独的配置,以便在容器启动时 对其进行探测,所允许的时间比活动间隔所允许的时间长。

如果你的容器通常启动时间比 initialDelaySeconds + failureThreshold × periodSeconds 要长,你应该指定 startup 探针来检查,它检查 与 liveness 探针相同的端点。默认的 periodSeconds 是 10s。然后,应该将其 failureThreshold 设置的足够高,以允许容器启动, 而不更改 liveness 探针的默认值。这有助于放置死锁。

7. Pod 的终止

因为 Pod 表达的是集群中节点中进程的运行,在不再需要这些进程时能够优雅终止非常重要(而不是直接被 KILL 信号突然终止,而没有机会清理)。

设计的目标使你能够请求删除并知道进程何时终止,而且还可以确保删除操作最终完成。当你请求删除一个 Pod,再允许 Pod 被强行杀死之前,集群记录并跟踪预设的 宽限期。有了适当的强制关闭跟踪,kubelet 就会尝试正优雅关闭。

通常,容器运行时将 TERM 信号发送到每个容器的主进程。许多容器运行时都遵循容器镜像中定义的 STOPSIGNAL 值而不是 TERM。一旦宽限期过了,会被剩余的 进行发送 KILL 信号,Pod 然后被 API server 删除。当进程等待终止时,如果 kubelet 或者容器的运行时的管理服务重启了,集群会从头开始重试,包括整个 原始的宽限期。

一个示例流程:

  1. 你可以使用 kubectl 工具手动删除一个 Pod,将使用默认的宽限期(30s);
  2. API server 中的 Pod 更新时间超过了宽限期会被与宽限期一起认为『死亡』。如果你使用 kubectl describe 来检查你删除的 Pod, Pod 显示为『Terminating』。kubelet 看到 Pod 被标记为 Terminating 后,kubelet 开始从本地关闭 Pod 进程的流程;
    1. 如果 Pod 中的一个容器定义了 preStop Hook,kubelet 会在容器内部运行该 hook。如果 preStop hook 在宽限期结束之后,仍然在运行, kubelet 会请求将一次性的宽限期延长 2 秒;

      注意: 如果 preStop hook 需要比默认宽限期允许更长的时间才能完成,你必须要指定 terminationGracePeriodSeconds 来适配。

    2. kubelet 触发容器运行时向每个容器内的 1 号进程发送 TERM 信号;

      注意: Pod 中的容器再不同时间以任意的顺序接收 TERM 信号。如果关闭顺序很重要,考虑使用 preStop hook 进行同步。

  3. 在 kubelet 开始优雅关闭的同时,控制平面会从端点部分移除正在关闭的 Pod。这表示选择器配置的 Services,ReplicaSet 和其他的工作负载资源 将不再认为正在关闭的 Pod 是一个有效的副本。终止宽限期一开始,逐渐关闭的 Pod 就无法继续为流量提供服务,因为负载平衡器(如服务代理)会从端点列表中删除 Pod;
  4. 当宽限期时间到了,kubeclt 触发强制关闭。容器运行时发送 SIGKILL 信号到正在运行的任何容器。如果该运行时是用了一个隐藏的 pause 容器, 那么 kubelet 还会清除该容器;
  5. 通过将宽限期的值设置为 0(表示立即删除),kubelet 触发从 API server 中强制删除 Pod 对象的操作;
  6. API server 删除 Pod 的 API 对象,然后该对象就不会被任何客户端看见。

7.1. 强制终止Pod

注意:强制删除可能会对某些工作负载及其 Pod 造成破坏。

默认情况下,所有的删除操作在 30 秒都是优雅的。 kubectl delete 命令支持 --grace-period=<seconds> 选项允许你覆盖默认值。

将宽限期设置为 0 会强制且立刻从 API server 中删除 Pod。如果 Pod 仍旧在节点上运行,强制删除会触发 kubelet 立即开始清理。

注意: 你必须指定附加参数 --force--grace-period=0 两个一起才能执行删除。

强制删除后,API server 不会等待 kubelet 的删除确认。它会立刻删除 API 中的 Pod,因为可以使用相同名称创建一个 Pod。在节点上,被设置为立刻终止的 Pod 在被强制杀死之前,仍旧会给一个小的宽限期。

如果你需要强制删除一个是 StatefulSet 一部分的 Pod,参阅文档:从 StatefulSet 中删除 Pods

7.2. 垃圾回收失败的 Pods

对于失败的 Pods,API 对象将保留在集群的 API 中,直到人工或者控制器进程明确将其删除为止。

控制平面清理终止的 Pods(phase 为 succeeded 或者 Failed ),当 Pod 的数量超过阈值时(由 kube-controller-manager 的 terminated-pod-gc-threshold 参数决定)。这样可以避免资源泄露,因为 Pods 会随着时间的推移而创建和终止。

8. 下一步

First created: 2021-04-24 08:50:04
Last updated: 2021-11-15 Mon 10:18
Power by Emacs 27.2 (Org mode 9.4.6)