Kubernetes 工作负载 - Pod

Table of Contents

1 概览

1.1 理解 Pod

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

Pod 封装了应用程序容器、存储、唯一网络 IP 以及控制器运行选项。

一个 Pod 表示部署单元:Kubernetes 中应用程序的单个实例。一个 Pod 包含的容器个数:

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

一个 Pod 表示单个实例,如果希望横向扩展应用程序,应该创建多个 Pod(而不是一个 Pod 多个容器)。 在 Kubernetes 中,这通常称之为 副本(replication) 。一组副本通常由抽象的控制器来创建和管理。

1.1.1 Pod 是如何管理多个容器的

同一个 Pod 的容器会自动安排到集群中的同一个节点上,一起调度;容器可以共享资源、依赖、彼此通信、协调终止方式。

有些 Pod 具有 初始容器 和 应用容器。初始容器会在应用容器之前运行并完成。

Pod 为组成容器提供了两种资源:网络和存储。

网络

每个 Pod 分配一个 IP 地址。内部容器共享网络命名空间,包括 IP 地址和网络端口。内部容器可以使用 localhost 互相通信。

存储

一个 Pod 可指定一组共享存储卷。内部容器可以访问共享卷,允许容器共享数据。

1.2 使用 Pod

Pod 通常不会被单独使用,不方便用户管理而且本身不能自愈。Kubernetes 通常使用一个更高级别的抽象成为 控制器 来管理。

1.2.1 Pod 和控制器

控制器可以创建和管理多个 Pod:处理副本和在集群范围内提供自愈功能。比如:一个节点失败了,控制器在不同的节点上调度副本。

包含一个或者多个 Pod 的控制器的示例包括:

  • Deployment
  • StatefulSet
  • DaemonSet

控制器通常使用用户提供的 Pod 模板来创建它负责的 Pod。

1.2.2 Pod 模板

控制器使用 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']

2 Pods

2.1 Pod 是什么?

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

2.2 设计 Pod 的目的

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

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

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

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

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

这两个问题可以合并到一起:为什么需要 Kubernetes 需要抽象一个 Pod 出来,而不是直接使用容器?

2.3 使用 Pod

通常,不会用单个 Pod 来运行同一应用程序的多个实例。

2.4 Pod 的持久性(或稀缺性)

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

一般情况下,Pod 不会被直接使用。大都通过控制器创建(即便是只有一个实例),比如 Deployments。控制器提供集群范围的自修复以及副本数和滚动管理。 StatefulSet 还支持有状态的 Pod。

即便这样,还是会暴露 Pod 原语,目的是方便:

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

其实就是为了组件解耦和方便管理。

2.5 Pod 的终止

Pod 中包含容器进程,Pod 终止时,优雅的终止容器中的服务是至关重要的:用户可以设置容器的 宽限期 ,默认的时间是 30 秒。

在用户请求删除 Pod 时,Pod 会向容器主进程中发送 TERM 信号,然后等待 宽限期 。 如果过了限制时间,就会发送 KILL 信号,然后就从 API 服务器上删除 Pod。

当用户发送命令删除时,Pod 状态就会被显示为 「Terminating」,然后同步开始关闭 Pod 流程:

  1. 如果 Pod 定义了 preStop Hook,就在 Pod 内部调用它。如果宽限期结束了,但是 preStop Hook 还在运行,会有一个小的宽限期(2 秒)
  2. 给 Pod 内的进程发送 TERM 信号。请注意:并不是所有的 Pod 容器同时收到 TERM 信号

与此同时(设置 Terminating 状态时),从服务的 Endpoint 列表中删除 Pod,Pod 也不再会被视为副本控制器的运行状态的 Pod 集的一部分 (负载均衡器会从列表删除)。

当宽限期到时,扔在 Pod 中运行的所有进程都会被 SIGKILL 信号杀死。kubelet 将通过设置宽限期为 0 (立即删除)来完成 API 服务器中删除 Pod 的操作。

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

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

2.5.1 Pod 强制删除

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

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

2.6 API 对象

3 Pod 的生命周期

3.1 Pod phase

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

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

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

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

3.2 Pod conditions

Pod 包含一个 PodStatus 对象,包含一个数组来记录 Pod 通过或者未通过。其中的每个元素都有六个可能的字段:

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

3.3 TODO 容器探针(probes)

3.4 TODO Pod 和容器状态

3.4.1 TODO 容器状态

3.4.2 TODO 容器 readiness gate

3.4.3 TODO 重启策略

3.5 Pod 的一生(lifetime)

Date: 2020-02-08 15:20:07

Author: JerryZhang