一、背景介绍
pod作为k8s的核心资源,可以说一切其他资源都是为pod而服务,给他提供了诸如网络、存储、监控功能,所以了解pod的运行机制与逻辑对掌握张个k8s集群显得非常重要,本文通过pod的生命周期,管理接口等方面介绍pod的各种特性,为了降低篇幅量,本文尽量减少命令演示,多采用语言描述。
二、应用容器与pod资源
1. linux名称空间
在开始之前,需要先说明下名称空间的概念,名称空间可以看成一个作用域,不同的名称空间中的资源相互之间隔离,linux系统本身包含了6中不同的名称空间,如下表:
USER |
用户 |
IPC |
internal procedure call,进程间通信 |
UTS |
UNIX Time-sharing System,主机名和域名 |
MOUNT |
挂载文件系统 |
PID |
进程 id |
NETWORK |
网络,主要用于实现协议栈的隔离 |
2. pod资源介绍
2.1. 什么是pod
pod是一组容器的集合,也是在k8s上的最小调度单元,除了强关联的服务,一个pod中只运行一个应用容器。说起pod就不得不提到他的工作特性:结束即销毁,之所以如此,原因就是pod中运行的都是一个执行特定服务的应用容器,该应用的pid为1,且可以接受信号传递,当这个服务终止时,相当于结束了这个pid为1的进程,容器自然就不再存在。
2.2. pod的组成
前文得知,linux又6种名称空间,pod本质上是共享network,ipc,uts,ipc名称空间以及存储资源的容器集合,但mount和user名称空间却各自独立。pod在创建时,首选会创建一个pause的基础容器,然后再创建出与基础容器指定共享空间的业务容器,如下图:
2.3. pod的资源需求与限制
pod运行时需要消耗所在物理节点的资源,每一个物理节点的资源都是有限的,其上资源该如何分配给pod,当资源发生争用时按照哪种方法进行避让,这就需要对pod的资源有一定的了解,pod创建时资源按照显式可分为两种:
- requests资源需求:指定至少需要为pod保留的资源
- limits资源限制:指定pod使用资源的上限
当创建一个pod时,如果并未指定需求与限制,他理论上可以使用整个物理节点的所有资源,按照这两个参数条件,可以分为以下几种场景:
- 只设定requests,pod在资源充沛时可以使用超出requests的资源,当资源紧张时归还超出资源
- 只设定limits,pod最大使用limits设定的资源,当发生资源紧张时,由于没有requests设置,不会预留资源
- 同时设定requests和limits,pod资源必须满足requests且不能超过limits,资源紧张时requests预留资源不会被抽走
2.4. pod服务质量类别
通过上面描述,在生产环境中,建议为每一个pod都要设置requests预留,避免资源紧张时该pod的资源被全部抽走,但是如果pod有设置requests或limits,每一个pod内的容器运行时(runc)在物理节点上都是以一个进程的方式存在,当内存资源不足时,会触发oom机制,随机kill掉进程,此处就引入了质量类别的概念,pod的质量类别由低到高可以分为:
- besteffort:不设置任何requests,limits参数,这类pod服务质量属于最低,发生资源争用时,这类pod不保证任何资源预留,最先被关闭,换来的就是当资源充沛时,不设置使用上限
- burstable:只设定requests,这类pod服务质量属于中级,要求运行时至少满足requests的值,并可以超出,将besteffort关闭后资源仍得不到满足,会关闭此类
- guarantee:同时设置requests和limits,这类pod服务质量属于高级,在besteffort和burstable全部关闭前,此类资源不会被关闭
上述3个不通服务质量级别中,如果同等级pod在oom的时候,会将内存实际使用值与requests的值进行比较,kill掉百分比大的pod,如下图所示:
假设有两个服务质量都为burstable的pod,尽管pod1的实际使用值40小于pod2的实际使用值60,但pod1与requests的比值为80%,大于pod2的比值72.5%,所以优先关掉的是pod1。
3. pod资源管理
3.1. pod创建过程
生产环境中,经常会有人拿虚拟机和容器进行对比,相对于虚拟机,容器的寿命会显得特别的短,运维时的态度也大有不同,运维虚拟机时,大多将他当成一个物理主机来使用,排错是主要的运维手段,但这种方法在运维容器时就不合适,得益于容器“停止即销毁”的特性,运维时直接销毁掉故障的容器,控制器会自行拉起新的容器,下图就是一个pod的创建过程:
创建步骤如图所示,不再赘述,需要说明的一点是:当pod创建完成后,后期由于人为或其他因素导致的pod销毁,控制器为何会再次主动拉起新的pod,这就要引出pod中的spec和status两个字段。
- 用户通过api server将期望创建的资源请求存放在etcd中
- 由于调度器scheduler实时监控api server,会获取到该请求,并指定一个物理节点创建资源
- 同样监控api server的kubelet看到后会根据需求在本地创建出pod,并记录下pod的状态信息
- 此时pod中同时存在请求的期望信息spec和创建出的实际信息status,status会无限逼近于spec以满足客户需求
如上所述,正因如此,status发生变化时,他才能对比spec自动的增加或减少对应的pod
3.2. pod的生命周期
了解pod的创建过程后,再来看pod在运行时的生命周期,如下图所示:
周期过程不再赘述,其中需要着重说明的是周期中的几个概念,其特点如下:
- init容器
- 一个或多个先于应用容器启动的 Init 容器
- 如果为一个 Pod 指定了多个 Init 容器,这些容器会按顺序逐个运行
- 每个 Init 容器必须运行成功,下一个才能够运行。当所有的 Init 容器运行完成时, Kubernetes 才会为 Pod 初始化应用容器并像平常一样运行
- Init 容器能以不同的文件系统视图运行。因此,Init 容器可以被赋予访问应用容器不能访问的 Secret 的权限
- Init 容器可以安全地运行实用程序或自定义代码,而在其他方式下运行这些实用程序或自定义代码可能会降低应用容器镜像的安全性。 通过将不必要的工具分开,你可以限制应用容器镜像的漏洞范围
- 容器回调
- PostStart
这个回调在容器被创建之后立即被执行。 但是,不能保证回调会在容器入口点(ENTRYPOINT)之前执行。 没有参数传递给处理程序。
- PreStop
在容器因 API 请求或者管理事件(诸如存活态探针、启动探针失败、资源抢占、资源竞争等) 而被终止之前,此回调会被调用。 如果容器已经处于已终止或者已完成状态,则对 preStop 回调的调用将失败。 在用来停止容器的 TERM 信号被发出之前,回调必须执行结束。 Pod 的终止宽限周期在 PreStop 回调被执行之前即开始计数, 所以无论回调函数的执行结果如何,容器最终都会在 Pod 的终止宽限期内被终止。 没有参数会被传递给处理程序。
- 检测探针
- startupProbe
指示容器中的应用是否已经启动。如果提供了启动探针,则所有其他探针都会被 禁用,直到此探针成功为止。如果启动探测失败,kubelet 将杀死容器, 而容器依其重启策略进行重启。 如果容器没有提供启动探测,则默认状态为 Success。
- livenessProbe
指示容器是否正在运行。如果存活态探测失败,则 kubelet 会杀死容器, 并且容器将根据其重启策略决定未来。如果容器不提供存活探针, 则默认状态为 Success。
- readinessProbe
指示容器是否准备好为请求提供服务。如果就绪态探测失败, 端点控制器将从与 Pod 匹配的所有服务的端点列表中删除该 Pod 的 IP 地址。 初始延迟之前的就绪态的状态值默认为 Failure。 如果容器不提供就绪态探针,则默认状态为 Success。
3.3. pod和容器状态
在通过命令kubectl get pods或者命令kubectl describe pods/${pod名称}时,可以看到pod和容器的状态信息,分别如下表所示:
- pod状态
取值 |
描述 |
Pending |
Pod 已被 Kubernetes 系统接受,但有一个或者多个容器尚未创建亦未运行。此阶段包括等待 Pod 被调度的时间和通过网络下载镜像的时间。 |
Running |
Pod 已被 Kubernetes 系统接受,但有一个或者多个容器尚未创建亦未运行。此阶段包括等待 Pod 被调度的时间和通过网络下载镜像的时间。 |
Succeeded |
Pod 中的所有容器都已成功终止,并且不会再重启。 |
Failed |
Pod 中的所有容器都已终止,并且至少有一个容器是因为失败终止。也就是说,容器以非 0 状态退出或者被系统终止。 |
Unknown |
因为某些原因无法取得 Pod 的状态。这种情况通常是因为与 Pod 所在主机通信失败。 |
- 容器状态
取值 |
描述 |
Waiting |
如果容器并不处在 Running 或 Terminated 状态之一,它就处在 Waiting 状态。 处于 Waiting 状态的容器仍在运行它完成启动所需要的操作:例如, 从某个容器镜像仓库拉取容器镜像,或者向容器应用 Secret 数据等等。 当你使用 kubectl 来查询包含 Waiting 状态的容器的 Pod 时,你也会看到一个 Reason 字段,其中给出了容器处于等待状态的原因。 |
Running |
Running 状态表明容器正在执行状态并且没有问题发生。 如果配置了 postStart 回调,那么该回调已经执行且已完成。 如果你使用 kubectl 来查询包含 Running 状态的容器的 Pod 时, 你也会看到关于容器进入 Running 状态的信息。 |
Terminated |
处于 Terminated 状态的容器已经开始执行并且或者正常结束或者因为某些原因失败。 如果你使用 kubectl 来查询包含 Terminated 状态的容器的 Pod 时, 你会看到容器进入此状态的原因、退出代码以及容器执行期间的起止时间。如果容器配置了 preStop 回调,则该回调会在容器进入 Terminated 状态之前执行。 |
以上大体上就是一个pod的运行过程与生命周期的大致介绍,当我们创建出一个pod后进入pod中的容器执行top命令看到的资源是高物理节点本身的可用资源,查看当前pod资源的downloadapi功能与实现pod安全的security context功能后续在进行介绍。