Kubernetes 资源编排系列之一: Pod YAML 篇
作者:周虚(应金挺)
SREWorks 的开源吸引了大量用户来尝试部署和使用我们的产品,其中不乏一些初次接触 Kubernetes 的朋友。随着 SREWorks 云原生运维平台使用的持续深入,部分用户对于其中的原理和概念还存在一些困惑。因此,我们特推出 《Kubernetes 资源编排系列》 ,从底层的 Pod YAML 开始,逐步递进地讲解相关内容,希望能够解答大家对于 Kubernetes 的一些疑问,让用户对于云原生相关技术有更深入的了解。
1.Pod 整体结构
Pod YAML 的整体结构,可以初步分为 Resource(资源)、Object(元数据)、Spec(规范)、Status(状态)。 本文将会围绕这四部分一一展开。
Resource:定义资源类型与版本, 作为从 Rest API 中获取资源必带的属性。
Object:资源的元数据属性,明确资源的基本标识。
Spec / Status:
Spec:定义资源的期望状态,包括用户提供的配置、系统扩展的默认值,以及周边系统初始化或者更改值(scheduler、hpa 等)。
Status:定义资源的当前状态,从而基于 Spec 定义的申明式配置,使 pod 不断朝期望状态靠近。
2.Resource(资源)-Rest API
k8s 资源按照 Scope 可以分为 Namespace 资源、Cluster 资源,Namespace 在 k8s 可以认为是软租户的效果,实现资源层面的隔离,Pod 资源就是属于 Namespace 资源,而 Namespace 不光体现在 YAML 参数中,也表现在 k8s Rest API 中。
Rest API 的整体结构,以 Pod 举例
基于上述 YAML,可以明确出 namespace 为 default, name 为 test-pod 的 Pod 资源对象,也就是明确出 Pod 为 Namespace 资源,该 Pod 资源对象对应的 apiVersion 为 v1,后续 k8s 自内联相关的 Group 为/api,自然而然,我们就将该对象的数据分离出来了:
group:api
apiVersion:v1
kind:Pod
name:test-pod
namespace:default
基于上述的数据展示,apiserver 自然而然会相应的注册出下列 rest api,
/api/{apiVersion}/{kind}
:该 kind 下的所有资源列表/api/{apiVersion}/namespace/{namespace}/{kind}/
:该 kind 下当前 namespace 的所有资源列表/api/{apiVersion}/namespace/{namespace}/{kind}/{name}
:该 kind 下当前 namespace 且名为 name 的资源/api/{apiVersion}/namespace/{namespace}/{kind}/{name}/{subresource}
:该 kind 下当前 namespace 且名为 name 的资源下子资源操作
后续基于扩展,我们就需要明确出 method,这样一个真正完整的 Rest API 就诞生了。
3.Object(元数据)
在 rest api 中明确了 Resource 的 kind、apiVersion, 也确定了 Object 的 namespace、name,作为凡是 k8s 资源对象都会引用的公共结构,自然也存在很多公共机制供使用。
观察上述 YAML,我们将其整理一下,有这样一些字段:
namespace:常规来说,Namespace 资源才会使用该资源对象
name:代表资源实例名称
uid:是资源的唯一标识,可以区别已删除与重新创建的同名资源实例
resourceVersion:是 k8s 的内部版本,具备时间属性,基于此就能明确该资源对是什么时候发生改变的,也是保证 k8s list-watch 核心机制
creationTimestamp: 资源实例创建时间
deleteTimestamp: 资源实例删除时间,后续会在 pod 的生命周期内讲到对该字段应用
ownerReferences: 资源从属对象,从上面 yaml 可知,该 Pod 资源从属于名为 testdemo-5bb759f78, ownerReferences 内部是没有 namespace 参数,也就是 ownerReferences 不允许跨 namespace, 将资源由下到上能够建立起来
labels :标签, k8s 内的服务发现以及相应的软关联,都是围绕 label 运作的,比如 testdemo-5bb759f78 replicaset 的 labelselector(标签筛选器)能够筛选到当前 Pod 的 label,保证两者关联由上到下的建立
annotations: 注释,通常来说会是作为额外字段供应给周边系统使用,比如当前
k8s.aliyun.com/pod-eni="true"
是提供网络系统使用
label & labelSelector
Deployment 会根据自己的 labelseletor:app=taihao-app-cluster 以及计算出 podtemplate 的 hash lable:pod-template-hash: 5b8b879786 , 筛选出出符合的 replicaset, replicaset 再根据自己的 labelselector 去筛选出符合的 pods, 相应的服务发现 service,也是通过 labelselector 去筛选出符合的 Pod
Owner & GC(垃圾回收)
基于 Pod 的 metadata.ownerReferences 找寻到对应的 replicaset,replicaset 基于自身的 metadata.ownerReferences 找寻到 deploy;当 deployment 被删除后,基于原有 owner 构建的树状,回收原有的 rs 与 pod。
Deploy & Replicaset
基于 label&labelselector,明确了从上到下的筛选归纳;基于 owner&GC,明确了关联资源的回收流程。
replicaset.spec.replicas: 实例数,rs 控制下的 Pod 个数
replicaset.spec.selector:基于 label 筛选出对应的 Pod
replicaset.spec.template:replicaset 创建的 Pod 会基于 podtemplate
replicaset.status:replicaset 当前管理 Pod 的状态
deploy.spec.replicas: deploy 期望的 pod 实例格式
deploy.spec.revisionHistoryLimit:deploy 管理 replicaset 的保留三个月
deploy.spec.selector:deploy 筛选符合标签
deploy.spec.strategy:deploy 的升级策略
deploy.template:deploy 基于此模版要创建的 pod 格式
4.Spec(规范)
Spec 作为 Pod 的期望状态,一定程度上也覆盖了 Pod 完整生命周期的逻辑,Pod 的生命周期分为以下阶段
Pending:代表 Pod 处于未调度阶段
Creating:节点上的 kubelet 已经发现了 Pod,处于创建阶段
Running:至少一个容器运行完毕,kubelet 这会发起健康监测
Terminating:Pod 处于删除状态,kubelet 开始回收容器
Terminated: Pod 销毁完成
Pod 生命周期: Pending
Pod 资源创建完毕后,处于还未调度阶段,这个时候 scheduler(调度器)基于 pod yaml 本身的配置与节点资源状态情况,来进行调度。
scheduler 会去分析 podyaml,将其中的策略提取出来,与节点组中的节点配置进行匹配,若匹配成功后,会选出最佳节点,重新修改 pod yaml,将 spec.nodeName 更新掉,完成整个调度环节
资源策略
资源策略表明 Pod 运行需要的资源情况,以 demo 为例,Pod 需要 2 核 4G 的资源,那么调度过去的节点也需要有 2 核 4G 的资源剩余,Pod 才能运行在该节点上
节点标签筛选策略
节点标签筛选策略,筛选节点是否存在topology.kubernetes.io/region: cn-hangzhou
亲和策略
亲和策略,有节点亲和与 Pod 亲和(Pod 所在节点优先调度),常规来说可以优先满足亲和的节点上,当前例子就是节点亲和,满足标签disk-type=aaa
或者disk-type=bbb
污点策略
污点策略,当节点上配置了污点,若 Pod 没有容忍该污点的策略,则 Pod 不允许调度到该节点上
Pod 生命周期: Creating
当 Pod 调度完毕后,开始创建阶段,kubelet 会基于 pod.spec 期望状态来创建出 Pod
kubelet 在创建 Pod 阶段,总共大致经历以下过程
Group 配置:主要是为了容器配置 cgroup,里面涉及了对容器资源限制,比如不允许超过 cpu、memory 配置,这里涉及到 Pod 的 qos 级别判定
初始化环境配置:主要是对相关 Pod 数据存储目录进行配置,涉及到 volume,则会去引用 CSI 协议,也会去获取镜像 secret,为了后续拉取镜像进行准备工作
创建 pause 容器:创建 pause 容器,该容器主要是为了后续配置容器网络,配置容器网络会去调用 CNI
创建 Pod 容器:基于 imagesecret 拉取业务镜像,在创建 Pod 容器阶段,也会将相应的 Pod YAML 配置传输进去,在启动 Pod 容器完毕后,会基于 poststart 进行相关的回调
上述阶段,会选择部分关键概念进行详细说明
image
imagePullSecrets: 拉取镜像的密钥,保证能够拉取 image:testdemo:v1,尤其在镜像库是私有库的阶段
imagePullPolicy:镜像拉取策略
Always:总是拉取镜像
IfNotPresent:本地若有则使用本地镜像,不进行拉取
Never:只使用本地镜像,不拉取
containers
注意这个 containers 用的是复数,可以填多个容器镜像: 比如可以放 nginx 和 业务容器。这样做的好处是可以尽量减少业务容器中与业务无关的代码或进程。
container 涉及很多配置,其中有涉及到 volume、env、dnsconfig、host 等基础配置
env:配置 Pod 的环境变量
dnsConfig:配置 Pod 的域名解析
hostALiases:配置/etc/hosts 文件内容
volume/volumeMount: 配置文件挂载到容器内,也可以配置文件存储系统挂载到容器内
postStart
当前 poststart demo 是发起 command 命令,也可以发起 http 请求,主要作用可以作为资源部署以及环境准备。
Pod 生命周期: Running
在 Pod running 阶段的时候,Pod 就迎来对其健康的检查,当前 kubelet 提供三种方式判定
readiness:检查 Pod 是否为健康
liveness:件看 Pod 是否正常,若检查失败,则重启容器
readinessGate:提供给第三方组件健康验证,第三方组件验证不过,则 Pod 不为健康
readiness 与 liveness 检查参数都是一致的
httpGet / tcpSocket:都是检查方式,一种是 http 请求验证,一种是 tcpSocket,其中也有 exec 执行命令,以及 grpc 形式验证
initialDelaySeconds:延迟多久开始检查,原因在于容器启动的时候,通常需要过段时间进行验证
periodSeconds:检验时间周期
failureThreshold:连续几次失败,则代表这轮检验失败
successThreshold:连续几次成功,则代表这轮检验成功
timeoutSeconds:代表检验超时时间,若检验在该配置时间内没有返回,则认为检验失败
readiness、liveness 虽然参数不一样,但对检验的结果行为不一致。
readiness 默认状态下为 false,也就是 Pod 为不健康,直到检查通过,才将 Pod 变为健康
liveness 默认状态下为 true,不会在刚开始就将 Pod 重启,只有等检查不通过后,才会进行容器重启操作
readinessGate 是 Pod 健康的扩展,kubelet 会基于此,默认在 pod.status.conditions 上配置对应的 condition, 比如当前例子 readinessGate 为conditionType: TestPodReady
, 则相应就会有 conditions
当该 condition.status 为 false 时,则 Pod 就会一直是不健康,哪怕 readiness 检查通过,直到第三方系统去操作更新 Pod 该 condition.status 为 true,才可以将 Pod 变为健康,这样就可以接入更多的 Pod 健康指标。
Pod 生命周期: Terminating
client 在发起请求删除 Pod 的时候,实际上是配置
pod.metadata.deletionTimestamp,kubelet 感知到后,开始进行 Pod 回收流程
整个 Pod 的回收周期,常规来说 preStop—>SIGTERM—>SIGKILL
当 kubelet 进行 preStop 后,开始发起 SIGTERM 给容器内进程,若超过总默认耗时 30S(metadata.DeletionGracePeriodSeconds),则强制发起 SIGKILL 给容器,也就是 prestop+SIGTERM 总耗时不允许超过 30s。
5.Status(状态)
基于上述 YAML 样例,将 Pod status 状态拆建出来分析一下:
conditions: conditions 是作为一种更详尽的状态报告,其本身也是一种扩展机制,其他的扩展字段也可以放入其中,比如可以表明网络状况,其中 readinessGate 就是这种扩展机制的表现,但决定 Pod 是否 ready,永远只看 type: Ready 是否为 true
containerStatuses: Pod 内各容器的状态
hostIP: Pod 所在节点 ip 地址
phase: Pod 的生命周期状态
Pending:代表 Pod 有一个容器或者多个容器还未运行,其中包括 Pod 调度到节点之前以及拉取镜像
Running:代表 Pod 已绑定到节点上,至少有一个容器运行或在重启
Successed:代表 Pod 所有容器已终止
Failed:代表 Pod 内至少有一个容器终止失败
Unknown:代表无法获取 Pod 状态
podIP / podIPs:Pod 的 IP 地址,假如有 ipv4、ipv6,则可以在 podIPs 上配置
qosClass:代表 kubernetes 服务等级
Guaranteed:resource.requests 与 resource.limits 一致
Burstable:resource.requests 与 resource.limits 不一致
BestEffort:没有配置 resource.requests 与 resource.limits
startTime:启动时间
通过以上 Pod 四个部分拆解,我们基本搞清了一个 Pod 在 k8s 下“从哪里来”的这个问题。本系列的后续的文章会对“到哪里去”这个问题继续展开:Kubernetes 的魅力在于不仅仅是拉起一个工作负载,而是能够召之即来挥之即去地编排海量工作负载。
后续文章均会发布在我们的公众号“阿里智能运维”上,请大家持续关注~也欢迎大家在公众号后台留言想了解的内容和感兴趣的相关话题,与 SREWorks 团队进行交流。
评论