Kubernetes - Pod

Table of Contents

1. 基本概念

Pod 是 Kubernetes 的基本执行单元,是对象模型中创建或者部署的最小和最简单的单元。Pod 表示在集群上运行的进程。

通常我们也把 pod 称之为应用的实例。

一个 pod 中包含的容器个数:

  • 单个容器 一个 pod 一个容器是最常见的 Kubernetes 用例;Kubernetes 直接管理 Pod,而不是容器。
  • 多个紧密耦合且需要共享资源的容器 一般也是一个应用容器,其它的容器以 sidecar 的方式存在(系统级而非应用级)。

总结 pod 的一些特性:

  • Pod 中的容器共享存储(卷)、网络、以及怎样运行这些容器的声明。共享同一个 IP 地址和端口空间,可以通过 localhost 互相发现。
  • Pod 中的内容总是一同调度,在共享的上下文(Linux namespace、cgroups、以及其它的潜在的资源隔离因素)中运行。 同一个 pod 中的容器会安排到同一个节点中。
  • Pod 的抽象定位是特定于应用的 逻辑主机 ,包含一个或者多个容器,多个容器紧密的耦合在一起。
  • Pod 内部容器之间可以通过标准的进程间(System V 信号量或者 POSIX 共享内存)通信方式通信,不同的 Pod 没有特殊的配置不能通过 IPC 通信, 通常使用 IP 地址进行通信。
  • Pod 被认为是一个短暂的实体,可能随时因为其它的原因销毁或者被其它节点的 Pod 替换掉。

2. 设计 pod 的目的

Pod 是形成内聚服务单元的多个协作过程模式的模型。它们提供了一个比它们的应用组成集合更高级的抽象,从而简化了应用的部署和管理。 Pod 可以用作部署、水平扩展和制作副本的最小单元。

为什么不在单个容器(Docker)容器中运行多个程序?

  • 透明度。Pod 内的容器对基础设施可见,使得基础设施能够向这些容器提供服务,例如流程管理和资源监控。这为用户提供了许多便利。
  • 解耦软件依赖关系。可以独立地对单个容器进行版本控制、重新构建和重新部署。Kubernetes 有一天甚至可能支持单个容器的实时更新。
  • 易用性。用户不需要运行他们自己的进程管理器、也不用担心信号和退出代码传播等。
  • 效率。因为基础结构承担了更多的责任,所以容器可以变得更加轻量化。

为什么不支持基于亲和性的容器协同调度?

这种处理方法尽管可以提供同址,但不能提供 pod 的大部分好处,如资源共享、IPC、有保证的命运共享和简化的管理。

3. 使用 pod

尽管 pod 可以单独被创建,但是实际使用过程中,不会直接使用。因为以 pod 为单独,不方便用户管理而且服务不可自愈。 Kubernetes 提供了更高级别的抽象 控制器 来管理 pod。

控制器可以创建或者管理多个 Pod,他提供了集群范围内的自愈机制,比如:一个节点宕机了,控制器会把副本调度到其它节点,保证服务可用。

常见的 pod 控制器有:Deployment, StatefulSet, DaemonSet 等。控制器通常使用用户提供的 pod 模板来创建 pod。模板样例:

apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
  labels:
    app: myapp
spec:
  containers:
  - name: myapp-container
    image: busybox
    command: ['sh', '-c', 'echo Hello Kubernetes! && sleep 3600']

上面提供了 pod 容器的名称、镜像和启动命令。

4. 终止 pod

4.1. 优雅终止

优雅终止 pod 是很重要的,用户可以设置容器的 宽限期 ,默认的时间是 30 秒。流程如下:

在用户请求删除 Pod 时,同时进行下面几步:

  • Pod 会向容器主进程中发送 TERM 信号,然后等待 宽限期 。 如果过了限制时间,就会发送 KILL 信号,然后就从 API 服务器上删除 Pod。
  • Pod 状态就会设置为 "Terminating",然后同步开始关闭 Pod 流程:
    1. 如果 pod 定义了 preStop Hook,就在 Pod 内部调用它。 如果宽限期结束了,但是 preStop Hook 还在运行,会有一个小的宽限期(2 秒)
    2. 给 pod 内的进程发送 TERM 信号。请注意:并不是所有的 Pod 容器同时收到 TERM 信号
  • 从 services 的 endpoints 中删除 pod,即从负载均衡器中删除

最后,宽限期到时,依然运行的所有进程都会发出 SIGKILL 信号。kubelet 通过设置宽限期为 0 来完成 API server 中删除 pod 的操作。

站在用户(应用方)视角理解:

  • 设置的宽限期是给容器中服务优雅终止的时间。如果服务没有合理的处理 TERM 信号,或者宽限期内服务没有终止完成。Pod 是不会关心的;
  • 请求关闭 pod 之后的关闭 pod 逻辑和不对外提供服务是同时发生的;
  • 宽限期时间不是固定的,会根据 preStop 微调,但是宽限期时间倒计时到 0 时,会强制清理所有资源(发送 SIGKILL 信号);

4.2. 强制删除

强制删除指的是:从集群状态和 etcd 中立即删除 Pod;API 服务器不会等待 kubelet 的确认信息(该 Pod 是否被所运行的节点终止)。

强制删除对某些 Pod 可能具有潜在危险,应谨慎执行

5. Pod 的持久性(或稀缺性)

不能将 pod 视为持久的实体。 它们无法在调度失败、节点故障或其他驱逐策略(例如由于缺乏资源或在节点维护的情况下)中生存。

Pod 可以当做一个应用实例,但不能当成虚拟机的定位。它们的逻辑是完全不同的:

  • 虚拟机中运行的服务宕了之后只会重启服务自身,但不会重启虚拟机
  • Pod 中运行的服务宕机,不会重启服务,也不会重启 pod,而是重新创建一个 pod,然后销毁掉之前的 pod

这种情况下 pod 的更新策略也叫 滚动更新。使用 Kubernetes 理解这一层至关重要。

尽管按照上面说的 pod 的不会被直接使用,但还是会暴露 Pod 原语,目的是方便:

  • 调度器和控制器可插拔性
  • 支持 Pod 级别的操作,而不需要通过控制器 API 「代理」 它们
  • Pod 生命与控制器生命的解耦,如自举
  • 控制器和服务的解耦 — 端点控制器只监视 Pod
  • kubelet 级别的功能与集群级别功能的清晰组合 — kubelet 实际上是「Pod 控制器」
  • 高可用性应用程序期望在 Pod 终止之前并且肯定要在 Pod 被删除之前替换 Pod,例如在计划驱逐或镜像预先拉取的情况下

总结一句话, 其实就是为了组件解耦和方便管理

6. Pod 的状态标识

Pod 的 status 字段是一个 PodStatus 对象,其中包含 phaseconditions 用来标识 pod 的状态。

phase

Pod 的阶段(phase)是一个简单的,高层次的总结了 Pod 在生命周期中的状态;它并不是对容器或者 Pod 状态的全面检测之后的汇总, 也不是一个全面的状态机。

对阶段的定义有严格的保护,除了下面列举的值之外,不应该有其它的值。

描述
Pending Pod 已被 Kubernetes 接受,但容器镜像还没被创建。这包括了被调度的时间和镜像下载时间,可能有点费时
Running Pod 已经绑定到节点上,所有的容器已经被创建。至少一个容器在运行或者正在启动、重新启动
Succeeded Pod 中所有的容器已成功终止,并且不会重新启动
Failed Pod 中所有容器都已终止,并且至少有一个容器因故障而终止。也就是要么容器非 0 退出,要么被系统终止
Unknown 因为某种原因,无法获得 Pod 的状态,通常是和 Pod 所在主机通信故障

conditions

conditions 是一个数组,来记录 pod 通过或者未通过。每个元素都可能有 6 个可能的字段:

  • lastProbeTime 提供最近一次 Pod 探测的时间
  • lastTransitionTime 提供最近一次状态变换的时间
  • message 人类可读的状态变更的详细信息
  • reason 唯一的驼峰命名的一个单词来描述最后一次状态变换
  • status 字符串,可能的值为 True False Unknown
  • type 字符串,可能的值为:
    • PodScheduled Pod 已经被调度到节点;
    • Ready Pod 已经可以处理请求,应该将它添加到匹配 services 的负载均衡池中;
    • Initialized 所有的初始化容器(Init Containers)已经初始化成功;
    • ContainersReady Pod 中所有的容器都已经就绪。

7. 容器探针(probes)

探针(probes)是 kubelet 对容器的定时诊断。提供三种探测方式:

  • ExecAction 在容器内部执行命令。退出时状态码为 0 表示成功
  • TCPSocketAction TCP 端口监测。判断端口是否存在
  • HTTPGetAction: HTTP Get 请求。回包响应码等于 200 或者小于 400 会被认为执行成功

每种探测方式的结果会有下面三种中的一种:

  • 成功 通过诊断
  • 失败 诊断失败
  • 未知 诊断失败,但什么都不做

kubelet 对已经运行的容器可选的执行下面三种探针:

  • livenessProbe 存活探针,表示容器是否处于运行。如果判断结果为失败,会杀掉容器(优雅终止)
  • readinessProbe 可用探针,表示容器是否可正常对外请求。如果判断结果为失败,会把该 pod 从 services 的 endpoint 列表中移出。 也就是暂时不对外提供服务。
  • startupProbe 启动探针,表示容器中的应用程序是否已经启动。如果使用了启动探针,会禁掉其它的探针,直到启动探针成功为止。 如果启动探针执行失败,kubelet 会杀掉容器(优雅终止)。

上面三种探针都是可选的,如果不配置默认都是成功。

  • 慎用存活探针,服务临时不可用时杀掉服务,未必是你想要的。除非你的需求就是这样。
  • 建议服务都配置可用探针,服务不可用时,临时切掉流量,等恢复了在继续处理请求。 尤其是服务的更新策略是滚动更新时,更加建议配置可用检测,等新的 pod 真正意义的上 ready 了,然后再删除旧的 pod, 这样更平滑一些。
  • 启动探针是 v1.16 版本之后的新功能, 还不是很了解

8. Pod 的重启策略

Pod 有可以设置重启策略,由 pod 所在容器的 kubelet 来判断容器状态(异常退出、存活检测失败),重启策略由 PodSpec 中的 restartPolicy 字段来控制。

  • Always 当容器终止退出之后,自动重启,默认策略;
  • OnFailure 容器异常退出且退出码不为 0 时重启;
  • Nerver 容器退出时,不重启;

在需要重启时,是基于延迟策略的间隔进行重启的(10s、20s、40s),上限是 5 分钟,并且成功执行 10 分钟之后重置。

9. Pod 的 lifetime

Pod 除非是人工或者控制器删除,否则会一直存在。当 pod 的数量超过配置的阈值时(由 kube-controller-manager 配置的 terminated-pod-gc-threshold 决定),控制面板将清理终止的 pod(phase 值为 "success" 或者 "error")。

提供三种类型的控制器:

  • Job 运行的预期会终止的 pods ,比如批量计算。Job 仅适用于 pod restartPolicy 等于 OnFailure 或者 Never。
  • ReplicationController, Replicaset 或者 Deployment 的 pods 预期不会被终止,比如 web 服务。 ReplicationControllers 仅适用于 restartPolicy 为 always 的 pod。
  • DaemonSet 运行的 pods 需要在每台机器上都运行一个,因为它们提供了特定于机器的系统服务。

三种类型的控制器都包含 PodTemplate。建议创建合适的控制器(让它们来创建 pods),而不是手动创建 pods。 因为 pods 本身不能抵御机器故障,但是控制器可以。

如果节点宕掉或者与集群失联,kubernetes 会把所有的 pod phase 设置为 Failed。

First created: 2020-02-08 15:20:07
Last updated: 2022-12-11 Sun 12:49
Power by Emacs 29.0.91 (Org mode 9.6.6)