# Pod 的一生

原文：[Pod Lifecycle](https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/)

本文阐述了 Pod 的生命周期，这并不是一篇全面的文档，仅是这一话题的简介。

# Pod 的阶段（Phase）

按照《[API 公约](http://kubernetes.io/v1.1/docs/user-guide/pod-states.html#restartpolicy)》 的描述，Pod 阶段是对生命周期的一个阶段的概括。他不是一个对 Pod 或者容器层次的状态的详尽结论，也不是一个全面的状态机。

PodPhase 很封闭。除了本文提到的内容，不应该对一个 Pod 的 PodPhase 做出任何假设。

- Pending：Pod 被系统接收，但是其中的一个或者多个容器镜像尚未创建。这一过程包括下载镜像的时间，以及被计划运行之前的时间。
- Running：该 Pod 被分派给一个 Node，并且已经创建了所有容器。至少一个容器还在运行，或者正在被启动以及重启。
- Succeeded：Pod 中的所有容器都已经成功结束，并且不会重新启动。
- Failed：Pod 里所有的容器都已经结束运行，且至少一个容器的退出结果是失败的（被系统结束，或非 0 的退出状态）。
- Unknown：因为某些原因无法获得 Pod 状态，一般来说是 Pod 所处主机的通信出了故障。

# Pod 状态（Condition）

Pod 中容器的就绪检测会报告就绪状况，其取值可能是 True、False 或者 Unknown。

# 容器检测

Kubelet 会周期性的对容器进行[检测](https://godoc.org/k8s.io/kubernetes/pkg/api/v1#Probe) 。这一过程是如下[三种方法](https://godoc.org/k8s.io/kubernetes/pkg/api/v1#Handler)之一：

- ExecAction：执行一个容器内的指定命令，如果命令返回码为 0 ，则代表成功。
- TCPSocketAction：对目标容器的 IP 地址执行 TCP 操作，如果指定端口开放，则代表成功。
- HTTPGetAction：对目标容器的 IP 地址、端口号以及路径进行 HTTP Get 操作，如果返回码在 200 和 400 之间，则检测成功。

检测过程可能会有三种结果之一：

- Success：容器成功通过检测。
- Failure：容器检测失败。
- Unknown：检测未能完成。

目前 Kubelet 能够有两种独立的检测可以被触发：

- LivenessProbe：用于检查当前容器是否存活，也就是正在运行。LivenessProbe 会告知 Kubelete 容器的健康状况。如果 LinenessProbe 失败，Kubelete 会杀掉这个容器，接下来，容器会根据 [RestartPolicy（重启动策略）](http://kubernetes.io/v1.1/docs/user-guide/pod-states.html#restartpolicy)进行后续动作。容器在没进行这一检测之前的状态被标记为 Success。
- ReadinessProbe：当前容器是否已经就绪，可以对外提供服务。如果这一检测失败的话，即使这一Pod 还在运行，Endpoint 控制器也会把这个 Pod 的 IP 地址从相关服务中移除，这样他就不会从 Proxy 获取流量（例如容器在提供服务之前会有一个较长的启动时间，或者容器正在关机维护）。在初始化完成之前，缺省的就绪状态是 Failure。当没有进行检测的时候，容器的就绪状态缺省假设为 Success。

# 容器状态（Statuses）

对容器状态的详细信息可以参考[ContainerStatuses](https://godoc.org/k8s.io/kubernetes/pkg/api/v1#PodStatus)。

# 重启策略

RestartPolicy 有几个可能的取值：Always，OnFailure 以及 Never。他的缺省值是 Always。RestartPolicy 作用于一个 Pod 中的所有容器。RestartPolicy 适用于同一 Node 的 Kubelet 的重启动操作。失败的容器会被 Kubelet 重启，重启之前会有一个渐进的延迟，延迟时长是同步频率的 0、1、2、4、8...倍，上限是五分钟，成功执行 10 分钟后会复位（延迟时间）。在 [Pod 文档中] 提到，Pod 一旦绑定到了一个 Node 上，就不会再绑定到其他 Node 了。这意味着即便是只有一个 Pod，也需要有控制器来进行操作，这样在 Node 失败的时候，才能保证 Pod 的存活。

目前有三种可用的控制器：

- Job：用来执行会结束的 Pod (例如批处理运算)。
- ReplicationController：不需要结束的 Pod (例如 Web Server)。
- DaemonSet：每台（物理）机只能运行一个的 Pod，这种 Pod 提供机器相关的系统服务。如果在 ReplicationController 或者 Daemon 之间举棋不定，可以参考 [Daemon Set vs Replication Controller](http://kubernetes.io/v1.1/docs/admin/daemons.html#daemon-set-versus-replication-controller)。

ReplicationController 是唯一符合 RestartPolicy = Always 需要的。Job 就适合另外两种。

所有三种控制器都有对应的 PodTemplate，跟 Pod 的字段一致。建议创建控制器，让控制器创建 Pod，而不是自行直接创建 Pod。这是因为 Pod 不具备适应服务器失败的能力，而控制器可以。

# Pod 的生命期

一般来说， Pod 创建之后就不会消失，除非被手工销毁。销毁手段可能是人工、ReplicationController 或者其他控制器。唯一的例外是处于 Succeeded 或者 Failed 阶段一定时间的 Pod 会因过期（由 Master 决定）而被自动销毁。

如果一个 Node 崩溃或者从集群断开，系统内的实体（目前称为 NodeController ）会负责执行策略（例如超时）并把丢失的 Node 中的所有 Pod 标记为 Failed。

# 例子

- Pod 在 Running 状态，1 个容器，该容器成功退出
  - 记录结束事件
  - RestartPolicy 如果是
    - Always：重启容器，Pod 保持 Running 状态
    - OnFailure：Pod 转为 Succeeded 状态
    - Never：Pod 转为 Succeeded 状态
- Pod 在 Running 状态，一个容器，容器失败退出
  - 记录失败事件
  - 如果 RestartPolicy 是：
    - Always：重启容器，Pod 保持 Running 状态
    - OnFailure：重启容器，Pod 保持 Running 状态
    - Never：Pod 进入 Failed 状态
- Pod 在 Running 状态，两个容器，容器 1 失败退出
  - 记录失败事件
  - 如果 RestartPolicy 是：
    - Always：重启容器， Pod 保持 Running 状态
    - OnFailure：重启容器，Pod 保持 Running 状态
    - Never：Pod 保持 Running 状态
  - 容器 2 退出...
    - 记录失败事件
    - 如果 Restart Policy 是：
      - Always：重启容器，Pod 保持 Running 状态
      - OnFailure：重启容器，Pod 保持 running 状态
      - Never：Pod 进入 Failed 状态
- Pod 在 Running 状态，容器内存不足
  - 容器失败退出
  - 记录 OOM 事件
  - 如果 RestartPolicy 是：
    - Always：重启容器，Pod 保持 Running
    - OnFailure：重启容器，Pod 保持 Running    - Never：记录失败事件，Pod 进入 Failed 状态
- Pod 在 Running 状态，一个磁盘坏掉。
  - 所有容器被 Kill
  - 记录事件
  - Pod 进入失败状态
  - 如果在控制器之下运行，则 Pod 会在其他位置被创建
- Pod 在 Running 状态，所在 Node 被断开
  - NodeController 等待超时长
  - NodeController 标记 Pod 为 Failed 状态
  - 如果在控制器之下运行，则 Pod 会在其他位置被创建

~~~go
type PodStatus struct {
    // Current condition of the pod.
    // More info: http://releases.k8s.io/HEAD/docs/user-guide/pod-states.md#pod-phase
    Phase PodPhase `json:"phase,omitempty"`
    // Current service state of pod.
    // More info: http://releases.k8s.io/HEAD/docs/user-guide/pod-states.md#pod-conditions
    Conditions []PodCondition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"`
    // A human readable message indicating details about why the pod is in this condition.
    Message string `json:"message,omitempty"`
    // A brief CamelCase message indicating details about why the pod is in this state.
    // e.g. 'OutOfDisk'
    Reason string `json:"reason,omitempty"`

    // IP address of the host to which the pod is assigned. Empty if not yet scheduled.
    HostIP string `json:"hostIP,omitempty"`
    // IP address allocated to the pod. Routable at least within the cluster.
    // Empty if not yet allocated.
    PodIP string `json:"podIP,omitempty"`

    // RFC 3339 date and time at which the object was acknowledged by the Kubelet.
    // This is before the Kubelet pulled the container image(s) for the pod.
    StartTime *unversioned.Time `json:"startTime,omitempty"`

    // The list has one entry per container in the manifest. Each entry is currently the output
    // of `docker inspect`.
    // More info: http://releases.k8s.io/HEAD/docs/user-guide/pod-states.md#container-statuses
    ContainerStatuses []ContainerStatus `json:"containerStatuses,omitempty"`
}
~~~

~~~go
type ContainerState struct {
    // Details about a waiting container
    Waiting *ContainerStateWaiting `json:"waiting,omitempty"`
    // Details about a running container
    Running *ContainerStateRunning `json:"running,omitempty"`
    // Details about a terminated container
    Terminated *ContainerStateTerminated `json:"terminated,omitempty"`
}
~~~
