Pod 是可以在 Kubernetes 中创建和管理的、最小的可部署的计算单元。
Pod是豌豆荚的意思,Google大胆的定义Pod为豌豆荚 豌豆就是容器,Pod就是逻辑上的主机,它负责管控,容器的的共享存储,网络以及调度。
实际使用过程中我们不会直接创建pod,而是通过deployment或者job这类工作负载资源上应用pod,如果需要跟踪pod状态,会使用statefulset资源。
每个pod都旨在运行一个单一的实例(单一实例并不是单一容器),如果横向扩容Pod增加实例数量,每个实例使用一个Pod。这在k8s中通常称为副本(Replication)。且我们通常使用一种工作负载资源及其控制器创建管理一组pod副本。
Pod中生成的多个协作容器被安排到同一节点上,方便统一调度,管理。
Pod中网络以及存储是共享资源,所有的服务(容器)都通过基层(存储)上联到网络,提供服务。
在k8s集群中很少一个一个创建pod,甚至单但容器的pod。pod的设计源自是一次性抛出应用,实例,使用即弃的概念。pod创建后,会在节点上持续运行,直到Pod执行结束,pod对象被删除,pod资源不足被驱逐或节点失效。
需要注意的是,重启Pod不能与重启容器相混淆,pod对象创建的一刻起,pod作为容器运行环境会一直运行。直到pod对象被删除。
我们通常使用工作负载资源(控制器)来管理多个pod,
控制器的作用:处理副本的管理、上线、并在pod失效时提供自愈能力。如一个节点的pod处于不健康状态,则控制器就会调度健康的pod创建替换他的工作。
控制器列表:
负载均衡的控制器使用pod模板创建pod:
实际yaml模板中 tmplate是实际的pod描述:示例一个简单的Job清单。
apiVersion: batch/v1
kind: Job
metadata:
name: hello
spec:
template:
# 这里是 Pod 模版
spec:
containers:
- name: hello
image: busybox
command: ['sh', '-c', 'echo "Hello, Kubernetes!" && sleep 3600']
restartPolicy: OnFailure
# 以上为 Pod 模版
修改模板必须删除现有的pod,模板被修改后,也不支持通过模板删除pod。
pod的更新和替换可以通过path和replace这类更新操作有很多限制。官方列出如下:
Pod 的绝大多数元数据都是不可变的。例如,你不可以改变其 namespace、name、 uid 或者 creationTimestamp 字段;generation 字段是比较特别的,如果更新 该字段,只能增加字段取值而不能减少。
如果 metadata.deletionTimestamp 已经被设置,则不可以向 metadata.finalizers 列表中添加新的条目。
Pod 更新不可以改变除 spec.containers[].image、spec.initContainers[].image、 spec.activeDeadlineSeconds 或 spec.tolerations 之外的字段。 对于 spec.tolerations,你只被允许添加新的条目到其中。
在更新spec.activeDeadlineSeconds 字段时,以下两种更新操作是被允许的:
如果该字段尚未设置,可以将其设置为一个正数;
如果该字段已经设置为一个正数,可以将其设置为一个更小的、非负的整数
pod中网络以及存储都为共享使用
一个pod可以设置共享的存储卷。pod中的所有容器都能访问卷,共享数据。
每个pod都有一个ip地址,每个容器都协助,进行通信。
在 Linux 中,Pod 中的任何容器都可以使用容器规约中的 安全性上下文中的 privileged(Linux)参数启用特权模式。 这对于想要使用操作系统管理权能(Capabilities,如操纵网络堆栈和访问设备) 的容器很有用。
如果你的集群启用了 WindowsHostProcessContainers 特性,你可以使用 Pod 规约中安全上下文的 windowsOptions.hostProcess 参数来创建 Windows HostProcess Pod。 这些 Pod 中的所有容器都必须以 Windows HostProcess 容器方式运行。 HostProcess Pod 可以直接运行在主机上,它也能像 Linux 特权容器一样,用于执行管理任务。
静态pod,直接由节点上的kubelet守护进程管理,不连接APIServer。但大多数pod由资源负载控制器调控。
静态 Pod 的 spec 不能引用其他的 API 对象(例如:ServiceAccount、ConfigMap、Secret等)
容器探针用于pod的健康检查,kubelet由三种探针:
Pod的生命周期起始于Pending阶段,至少有一个主容器正常启动,则进去running,之后取决于pod的健康状态或实例进程,更改为succeed或failed状态。
Pod在生命周期内只能被调度一次,一旦pod被分配节点后,就不能再调度。
Pod被认为是临时性的产物。并不是长期持久的,Pod创建后会被赋予一个UID直到pod被删除。Pod不具备自愈能力,也就是所,一旦pod进入不健康的状态,pod本身是无法自愈的,随之资源负载控制器即用来负载调度pod,当pod处于不健康的状态时,控制器随即会创建一个全新的pod来替换这个不健康的pod,新pod可以使名字相同,但UID不能相同。
需要注意的是,当pod因不健康被删除,随之数据卷也会被删除。
Pod 的 status 字段是一个 PodStatus 对象,其中包含一个 phase 字段。
Pod 的阶段(Phase)是 Pod 在其生命周期中所处位置的简单宏观概述。 该阶段并不是对容器或 Pod 状态的综合汇总,也不是为了成为完整的状态机。
Pod 阶段的数量和含义是严格定义的。 除了本文档中列举的内容外,不应该再假定 Pod 有其他的 phase 值。
下面是 phase 可能的值:
取值 | Pending(悬决) |
---|---|
Pending(等待) | Pod 已被 Kubernetes 系统接受,但有一个或者多个容器尚未创建亦未运行。此阶段包括等待 Pod 被调度的时间和通过网络下载镜像的时间, |
Running(运行中) | Pod 已经绑定到了某个节点,Pod 中所有的容器都已被创建。至少有一个容器仍在运行,或者正处于启动或重启状态。 |
Succeeded(成功) | Pod 中的所有容器都已成功终止,并且不会再重启。 |
Failed(失败) | Pod 中的所有容器都已终止,并且至少有一个容器是因为失败终止。也就是说,容器以非 0 状态退出或者被系统终止。 |
Unknown(未知) | 因为某些原因无法取得 Pod 的状态。这种情况通常是因为与 Pod 所在主机通信失败。 |
如果某节点死掉或者与集群中其他节点失联,Kubernetes 会实施一种策略,将失去的节点上运行的所有 Pod 的 phase 设置为 Failed
Kubernetes 会跟踪 Pod 中每个容器的状态,就像它跟踪 Pod 总体上的阶段一样。 你可以使用容器生命周期回调 来在容器生命周期中的特定时间点触发事件。
一旦调度器将 Pod 分派给某个节点,kubelet 就通过 容器运行时 开始为 Pod 创建容器。 容器的状态有三种:Waiting(等待)、Running(运行中)和 Terminated(已终止)。
要检查 Pod 中容器的状态,你可以使用 kubectl describe 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 的 spec 中包含一个 restartPolicy 字段,其可能取值包括 Always、OnFailure 和 Never。默认值是 Always。
restartPolicy 适用于 Pod 中的所有容器。restartPolicy 仅针对同一节点上 kubelet 的容器重启动作。当 Pod 中的容器退出时,kubelet 会按指数回退 方式计算重启的延迟(10s、20s、40s、…),其最长延迟为 5 分钟。 一旦某容器执行了 10 分钟并且没有出现问题,kubelet 对该容器的重启回退计时器执行 重置操作。
https://kubernetes.io/zh/docs/concepts/workloads/pods/pod-lifecycle/
参考文献;
kubelet对容器的健康检查是以探针的形式进行。
三种检查方式
ExecAction: 在容器内执行指定命令。如果命令退出时返回码为 0 则认为诊断成功。
TCPSocketAction: 对容器的 IP 地址上的指定端口执行 TCP 检查。如果端口打开,则诊断被认为是成功的。
HTTPGetAction: 对容器的 IP 地址上指定端口和路径执行 HTTP Get 请求。如果响应的状态码大于等于 200 且小于 400,则诊断被认为是成功的。
每次探测都将获得以下三种结果之一:
Success(成功):容器通过了诊断。
Failure(失败):容器未通过诊断。
Unknown(未知):诊断失败,因此不会采取任何行动。
针对容器,kubelet由以下三种探针(不要与检测方式混淆)
livenessProbe:指示容器是否正在运行。如果存活态探测失败,则 kubelet 会杀死容器, 并且容器将根据其重启策略决定未来。如果容器不提供存活探针, 则默认状态为 Success。
readinessProbe:指示容器是否准备好为请求提供服务。如果就绪态探测失败, 端点控制器将从与 Pod 匹配的所有服务的端点列表中删除该 Pod 的 IP 地址。 初始延迟之前的就绪态的状态值默认为 Failure。 如果容器不提供就绪态探针,则默认状态为 Success。
startupProbe: 指示容器中的应用是否已经启动。如果提供了启动探针,则所有其他探针都会被 禁用,直到此探针成功为止。如果启动探测失败,kubelet 将杀死容器,而容器依其 重启策略进行重启。 如果容器没有提供启动探测,则默认状态为 Success。
如果容器自行崩溃,可使用重启策略restartpolicy进行健康修复。
pod可以拥有一个或多个容器,也可以拥有一个或多个初始化容器,用于应用容器开始之前创建。
Init容器与普通容器的不同:
如果pod的init容器失败,那么kubelet就会重启init容器为健康为止,除非pod的restartpolicy为Never,pod会整个变成失败状态。
init容器不支持使用探针,因为探针也受限于init容器的启动。
我们可以为初始化容器制定一些计划,例如:安装gcc awk等或者sleep 60 让容器等待一段时间等,具体的例子可参考:
https://kubernetes.io/zh/docs/concepts/workloads/pods/init-containers/
实际上这里是高级的k8s概念
你可以使用 拓扑分布约束(Topology Spread Constraints) 来控制 Pods 在集群内故障域 之间的分布,例如区域(Region)、可用区(Zone)、节点和其他用户自定义拓扑域。 这样做有助于实现高可用并提升资源利用率。
在 v1.18 之前的 Kubernetes 版本中,如果要使用 Pod 拓扑扩展约束,你必须在 API 服务器 和调度器 中启用 EvenPodsSpread 特性门控。
拓扑分布约束依赖于node节点的标签。
假设你拥有具有以下标签的一个 4 节点集群:
NAME STATUS ROLES AGE VERSION LABELS
node1 Ready > 4m26s v1.16.0 node=node1,zone=zoneA
node2 Ready > 3m58s v1.16.0 node=node2,zone=zoneA
node3 Ready > 3m17s v1.16.0 node=node3,zone=zoneB
node4 Ready > 2m43s v1.16.0 node=node4,zone=zoneB
然后从逻辑上看集群如下:
你可以复用在大多数集群上自动创建和填充的 常用标签, 而不是手动添加标签。
pod.spec.topologySpreadConstraints 字段定义如下所示:
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
topologySpreadConstraints:
- maxSkew: >
topologyKey: >
whenUnsatisfiable: >
labelSelector:
你可以定义一个或多个 topologySpreadConstraint 来指示 kube-scheduler 如何根据与现有的 Pod 的关联关系将每个传入的 Pod 部署到集群中。字段包括:
你可以执行 kubectl explain Pod.spec.topologySpreadConstraints 命令以 了解关于 topologySpreadConstraints 的更多信息
关于pod的干扰请参考:
https://kubernetes.io/zh/docs/concepts/workloads/pods/disruptions/
通常临时容器用于在容器奔溃时调式使用
临时容器一旦启动就无法再讲容器加入到pod中,取而代之的是通过deployment一受控的方式来删除并替换pod。
临时容器不能使用,端口配置,pod的资源分配也是不可变的。