ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

CKA备考:PriorityClass与Pod抢占机制实战解析

CKA备考:PriorityClass与Pod抢占机制实战解析 CKA备考练到Pod调度这块时PriorityClass优先级这个知识点很容易被一带而过。很多人觉得它就是给Pod分个三六九等考试时背个yaml就完事了。但根据2025年CKA新题的走向PriorityClass不仅单独出题还经常跟资源配额、节点亲和、驱逐策略组合在一起考用来考查你对调度器完整决策链路的理解。从题目变化来看光会创建PriorityClass已经不够了你得真正理解它什么时候生效、怎么和抢占配合、优先级数值设成多少才合理以及当系统报出NoPreemptionVictims这类事件时该怎么收场。这篇文章不适合零基础从头看起更适合已经了解Pod基本调度逻辑、正在搭CKA练习环境刷题的考生。我会从机制原理讲到实操验证再到排错经验全程用我在练习环境里实际踩过的坑来说话尽量把这块内容一次讲透。1. 为什么PriorityClass突然成了CKA热门考点先说一个观察近几年CKA题库迭代的速度明显加快尤其是调度相关的题目从“能创建资源”进化到了“能控制资源如何被调度”。PriorityClass在旧题库里确实不算主角顶多是考你kubectl apply一个yaml然后在Pod里指定priorityClassName。但2025年新题明显变了味道——它开始考察你对抢占机制的理解甚至要求你在集群资源紧张的情况下通过调整优先级来触发Pod驱逐。1.1 调度器眼中“排序”这件事到底有多重要Kubernetes调度器本质上是一个排队系统。集群里几百上千个Pending的Pod排队等着被调度到合适的节点上调度器不可能随机乱挑它要按某种规则排序。这个排序规则就是优先级。没有优先级时所有Pod在调度队列里都是平起平坐有了PriorityClass高优先级的Pod就能插队。但“插队”只是表面效果内部实际发生过两件事调度队列排序高优先级Pod进入调度队列后排到更靠前的位置。抢占式调度如果队列前面的Pod找不到可用节点比如资源不够调度器不会干等着而是直接尝试干掉节点上低优先级的Pod把资源腾出来给高优先级Pod用。CKA新题考的就是这个第二点。以前的题目只要你写出“优先级高的Pod会被先调度”这种理论答案就能拿分现在它可能给你一个资源已经占满的节点再提交一个高优先级Pod问你接下来会发生什么或者让你通过kubectl describe去排查为什么这个高优先级Pod还是Pending。1.2 题目从“会写yaml”转向“会排错”我备考时刷到一道模拟题创建了一个PriorityClass并指定给一个Pod但Pod始终Pending。用kubectl describe pod查看事件发现了No preemption victims found for incoming pod这样的错误提示。当时第一反应是“优先级设得不够高”后来仔细排查才发现节点上的现存Pod全是静态Pod和DaemonSet托管的Pod根本不参与抢占。这就是新题出题的方向不只是问你怎么做而是给你一个已经做好的环境让你去发现问题并修复。这跟你实际工作中维护生产集群的场景非常接近——线上Pod调度不上去你总不能只看一眼yaml就交差得会看事件、查调度器日志、分析节点资源。1.3 PriorityClass在考试大纲里对应的知识点集群从CKA官方大纲来看PriorityClass归属于“调度、超卖与驱逐”这一大块。涉及的子知识点包括调度器如何根据优先级排序PodPriorityClass的定义、修改和删除globalDefault的影响范围抢占机制与preemptionPolicy设置与ResourceQuota、LimitRange的协作关系节点资源压力下的驱逐顺序这些知识点在练习环境里是可以串成一个完整实验的。下文我会从搭建环境开始一步步带你完成从创建PriorityClass到触发抢占的完整链路。2. PriorityClass的核心机制优先级、抢占和调度器三者怎么配合想练好这个实验先得把底层机制嚼碎。PriorityClass看起来只是个简单的key-value配置但它背后牵动着调度器、kubelet和API Server三方的协作。2.1 PriorityClass资源的本质一个全局的“价目表”PriorityClass是一个集群级别的资源不属于任何Namespace。它的yaml长这样apiVersion: scheduling.k8s.io/v1 kind: PriorityClass metadata: name: high-priority value: 1000000 globalDefault: false description: 用于生产环境核心业务的高优先级类 preemptionPolicy: PreemptLowerPriority几个关键字段逐一解释value取值范围1到1000000000十亿数值越大优先级越高。globalDefault布尔值标记这个PriorityClass是否是集群默认优先级。一个集群里最多只能有一个globalDefault: true如果创建了多个为true的后创建的会被API Server拒绝掉。注意当集群里不存在任何globalDefault为true的PriorityClass时Pod如果不指定priorityClassName优先级默认是0。preemptionPolicy默认值是PreemptLowerPriority表示这个优先级支持抢占低优先级的Pod改成Never则只提高调度队列位置不触发抢占。这里插一句很多生产场景为了避免高优先级Pod把节点上的业务Pod全干掉会把preemptionPolicy设为Never只让优先级参与调度排序。这个细节在CKA新题里出过我当时就差点写错。2.2 调度器排队priority只是排序规则之一调度器为Pod打分排序时优先级不是唯一标准但它是第一层过滤器。有心人可以看一下调度器源码里PrioritySort这个插件它实现了最基础的排序逻辑——把待调度的Pod按优先级从高到低排列再一个个送入调度管线。这里的重点是排序发生在调度管线之前。高优先级Pod会被更早地尝试调度但并不意味着它一定能被调度成功。如果集群没有任何节点能满足它的资源要求它还是一样会Pending。优先级只能保证“优先尝试”不能保证“一定成功”。2.3 抢占真正让优先级“硬起来”的机制当高优先级Pod进入调度管线后如果没有节点满足它的资源需求调度器会尝试寻找一个节点通过抢占该节点上低优先级Pod来腾出资源给高优先级Pod。抢占流程大致分四步选受害者调度器在所有节点上找“部分Pod”可以被抢占后、剩余资源刚好满足高优先级Pod的节点。模拟驱逐选出受害者Pod后调度器会先模拟一遍删除这些Pod后节点是否满足需求。提前占坑调度器会立刻在API Server里抢占这个节点的资源配额通过nodebinding.kubernetes.io/taint这类污点机制防止其他Pod“截胡”。真正驱逐受害者Pod被优雅终止高优先级Pod随后完成调度。整个过程看起来比较粗暴但设计上有一个关键底线不会抢占比待调度Pod优先级更高的Pod。也就是说如果节点上有同优先级或更高优先级的Pod这些Pod不会成为受害者。这个规则考试里经常拿来出判断题。写到这里就不得不提No preemption victims found for incoming pod这个报错。它的完整含义是高优先级Pod确实尝试抢占但当前所有节点上找不到可以被抢占的低优先级Pod。常见原因有三个节点上的Pod都是同优先级或更高优先级节点上的Pod全是DaemonSet、静态Pod这类un-daemon的Pod调度器默认不抢占它们抢占虽然找到了受害者但模拟驱逐后发现资源仍然不够遇到这个事件你先别急着改PriorityClass的数值应该先排查受害者的候选对象判断是不是节点上根本没有低优先级Pod可选。2.4 kubelet层面的驱逐另一种“优先级”这里还要区分清楚调度器的抢占和kubelet的驱逐。前者发生在调度阶段解决的是“Pod还没跑起来、找不到位置”的问题后者发生在节点运行阶段解决的是“节点内存或磁盘资源耗尽、需要牺牲部分Pod保集群稳定”的问题。当节点内存或磁盘触发驱逐阈值时kubelet会按Pod的优先级排序优先杀掉低优先级Pod。这个优先级也是来自Pod的.spec.priority字段而该字段正是由Pod指定的PriorityClass换算出来的。所以你在kubectl get pod xxx -o yaml里看到的priority: 1000000就是这么来的。3. 练习环境准备搭一套多节点集群来验证优先级行为PriorityClass的很多行为尤其是抢占单节点minikube玩不出效果因为抢占需要“另一个Pod占着资源不撒手”。我建议至少准备一个两节点的集群你可以在任意一台Linux机器上用kubeadm现搭一套最简集群或者用云厂商的托管集群都行。我自己的练习环境是1台2C8G的控制节点2台2C4G的工作节点Kubernetes版本选的1.29.4稳定版。3.1 准备两个不同优先级的PriorityClass先把实验要用的PriorityClass一次性准备好。# low-priority.yaml apiVersion: scheduling.k8s.io/v1 kind: PriorityClass metadata: name: low-priority value: 100 globalDefault: false description: 低优先级测试类 preemptionPolicy: PreemptLowerPriority # high-priority.yaml apiVersion: scheduling.k8s.io/v1 kind: PriorityClass metadata: name: high-priority value: 1000 globalDefault: false description: 高优先级测试类用于CKA练习 preemptionPolicy: PreemptLowerPriority数值差距拉开一个量级就够了不要一上来就设成十万、百万级虽然系统允许但不利于观察行为差异。在练习环境里100和1000的差值足够触发抢占。创建并验证kubectl apply -f low-priority.yaml -f high-priority.yaml kubectl get priorityclasses正常能看到两个PriorityClass加上Kubernetes自带的system-cluster-critical和system-node-critical一共四个。3.2 如何正确查看Pod绑定后的优先级创建完PriorityClass后写一个引用它的Pod试试apiVersion: v1 kind: Pod metadata: name: test-high-priority spec: priorityClassName: high-priority containers: - name: nginx image: nginx:1.25应用后查看细节kubectl get pod test-high-priority -o yaml | grep -A2 priority你会在spec里看到priority: 1000和priorityClassName: high-priority两个字段。如果写的是describe命令输出里也会直接显示PriorityClass名称和Priority数值。3.3 globalDefault的认知盲区别在这里翻车CKA新题很喜欢把globalDefault作为一个陷阱点。我在一个练习群看到有同学问“我创建了一个名称为high-priority的PriorityClass设置了globalDefault为true为什么集群里已有的Pod优先级还是0”原因其实很简单globalDefault只在Pod创建时生效。已存在的Pod如果要变更优先级只能重建不能动态更新。另外如果集群中本来就有globalDefault: true的Policy你新创建的另一个globalDefault: true会被API Server直接拒绝。这道题我在模拟考试时做错过后来自己在环境里试了两次才记住。还有更隐蔽的一点globalDefault为true的优先级作用于所有没有显式指定priorityClassName的Pod。但如果你同时存在globalDefault: true和Pod显式指定了某个PriorityClass显式指定的优先级优先。这是个基本规则但混在一起就容易出错。4. 完整实操创建一个被抢占的实验场景概念说完了进入正题。下面我在练习环境里从头走一遍“低优先级Pod占资源高优先级Pod触发抢占”的完整实验。这套实验脚本算是我备考CKA时觉得用途最大的一套素材。4.1 用Deployment创造资源占用首先创建一个能占住节点资源的低优先级Deployment用来充当“被抢占者”。我这里用的是一个requests配置得很死板的容器镜像# resource-hungry.yaml apiVersion: apps/v1 kind: Deployment metadata: name: resource-hungry namespace: default spec: replicas: 2 selector: matchLabels: app: hungry template: metadata: labels: app: hungry spec: priorityClassName: low-priority containers: - name: stress image: polinux/stress command: [stress] args: [--cpu, 2, --timeout, 600s] resources: requests: cpu: 2 memory: 4Gi这里有两个细节需要留意必须写requests调度器只认Pod的requests不认limits。你写limits再大调度器也不关心因为limits只是运行时限制。所以想要把节点的可分配资源占满就要把requests写足。replicas数量要匹配节点资源我这里的测试节点每台有4C8G但这个Deployment创建在单节点上请求2×24个CPU和2×48G内存可以直接吞掉一个小节点的大部分资源。我会人为指定Pod调度到某个特定节点方便观察后续行为。操作方式是在Pod模板里加nodeSelectornodeSelector: kubernetes.io/hostname: worker-1如果不知道节点名先跑一下kubectl get nodes --show-labels确认。4.2 观察低优先级Pod的调度结果应用Deployment后确认Pod处于Running状态kubectl get pods -o wide你会看到两个resource-hungry-xxxx都调度到了worker-1节点。这个节点剩余的可分配资源应该被压得很低。你可以用kubectl describe node worker-1 | grep -A10 Allocated resources来确认。这里再分享一个我实践时用的小技巧直接在describe node里看Allocated resources下面的CPU Requests和Memory Requests。它会显示占用的百分比如果接近100%说明节点的资源确实已经被吃掉了。如果百分比低于预期检查一下是否还有其他系统组件占用了或者你的requests数字没有写对。4.3 提交高优先级Pod触发抢占接下来创建要抢占的高优先级Pod# winner.yaml apiVersion: v1 kind: Pod metadata: name: winner spec: priorityClassName: high-priority nodeSelector: kubernetes.io/hostname: worker-1 containers: - name: nginx image: nginx:1.25 resources: requests: cpu: 1 memory: 1Gi创建后立刻查看kubectl get pods -o wide正常情况下你会看到resource-hungry有一个Pod被终止Terminating或直接消失而winner变成了Running。这就是抢占成功的效果。让我解释一下为什么被杀的是一个Pod而不是全部高优先级Pod需要1个CPU和1G内存节点上只要挤掉一个低优先级Pod就能腾出2个CPU和4G内存调度器不会多杀。它会选择最少“牺牲”的那个Pod来驱逐。4.4 用事件和历史记录复盘整个抢占过程如果你想知道到底发生了什么不要只盯get pods。要看Eventskubectl get events --sort-by.lastTimestamp | tail -30 kubectl describe pod winnerdescribe pod里会有类似这样的事件记录Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal Preempting 3s default-scheduler Preempting other pods based on priority Normal Scheduled 3s default-scheduler Successfully assigned default/winner to worker-1 Normal Pulling 2s kubelet Pulling image nginx:1.25 Normal Pulled 2s kubelet Successfully pulled image nginx:1.25这行Preempting other pods based on priority就是调度器替你执行抢占动作的痕迹。实际生产环境中这种事件一旦出现在核心业务Pod上说明集群资源已经到了相当紧张的地步该考虑扩容或给低优先级任务降配了。4.5 注意抢占不是瞬时的很多人做这个实验会有一个错觉以为提交高优先级Pod的瞬间低优先级Pod就立刻消失。实际上抢占过程有几步要走API Server记录Pod更新调度器发现无法正常调度调度器计算抢占方案并驱逐低优先级Podkubelet异步处理Pod删除整个过程通常在秒级完成但如果节点负载高或者API Server响应慢可能会持续几十秒。我在练习时偶尔会碰到winner卡在Pending状态长达一分钟的情况。排查方法还是看事件确认它是在等待抢占还是真的无法调度。5. 常见报错与排查链路NoPreemptionVictims不是世界末日备考进入刷题模式后我遇到最多的报错就是No preemption victims found for incoming pod。这里单独开一节讲排查链路因为这个问题非常典型而且CKA的新题大概率会以它作为“故障场景”来出。5.1 完整排查链路从事件到根因的五步走当Pod无法被调度时我的排查顺序固定是第一步看Pod状态和事件kubectl describe pod pod-name关注Events区域有没有类似FailedScheduling或UnexpectedAdmissionError的信息。如果出现No preemption victims found for incoming pod说明调度器已经尝试过抢占但失败了。第二步看节点资源是否真的不足kubectl describe node重点看Allocated resources这一栏。有时候你的高优先级Pod其实只要0.1个CPU就能跑但节点确实一丁点资源都不剩了抢占了也没用。第三步看目标节点上有没有可被抢占的对象列出节点上所有Pod筛掉DaemonSet和静态Podkubectl get pods --all-namespaces -o wide --field-selector spec.nodeNameworker-1 kubectl get daemonsets --all-namespaces如果节点上的Pod全是kube-system里由DaemonSet管理的调度器默认不会抢占这些Pod。原因很简单它们是集群基础设施的一部分优先级很高system-cluster-critical或system-node-critical普通PriorityClass设计的Priority值根本不会超过它们。第四步看是否因为preemptionPolicy: Never禁止了抢占有些考生会把PriorityClass的preemptionPolicy误设成Never然后又期望它触发抢占。这本身不矛盾但如果你明确指定了preemptionPolicy: Never调度器只会参与队列排序绝不会去抢占任何Pod。这时候即使节点满负荷高优先级Pod也只会老老实实Pending。第五步看调度器日志高级排查如果上述步骤都查不出问题直接去控制平面容器里看调度器日志kubectl logs -n kube-system $(kubectl get pods -n kube-system | grep kube-scheduler | awk {print $1}) --tail200CKA考试环境一般允许你查看控制平面Pod日志这个排错动作本身就是题目考察点之一。5.2 我踩过的一个典型案例有一回我在练习环境里模拟一个“高优先级Pod抢占低优先级任务”的场景。无论我PriorityClass的value调到多高Pod始终Pending。折腾了快40分钟最后发现问题是节点被加了一个NoSchedule污点而我创建的Pod没有指定对应的容忍度。这个案例特别适合作为New Question的素材因为它把污点容忍度和优先级两个知识点混在一起考。调度器是先检查污点容忍再检查资源最后才轮到抢占逻辑。你优先级再高如果容忍度不匹配连调度管线都挤不进去。5.3 学会看优先级类型的Owner还有一个小细节容易被忽略——system-cluster-critical和system-node-critical这两个内置PriorityClass是不能删除的强行删除会导致API Server报错。考试时有道题让我确认某核心组件的PriorityClass我一开始以为是自己创建的后来kubectl get priorityclass system-cluster-critical -o yaml才发现它的owner是kube-apiserver自己。这提醒我们生产环境里动内置PriorityClass要格外谨慎先确认owner再操作。6. 备考建议PriorityClass怎么和场景结合出题单会创建PriorityClass只是入门CKA新题更看重的是“在一个完整场景里解决问题”的能力。所以我建议在练习环境里多做这几个方向。6.1 组合实验一PriorityClass ResourceQuota在某个Namespace里创建一个ResourceQuota单位为requests.cpu总量设置为1。然后在同一个Namespace里分别创建三个Deployment优先级从低到高。观察高优先级Pod能否抢占配额内的资源还是直接被Quota拒绝。这个实验考察的核心是PriorityClass决定调度顺序ResourceQuota决定能不能创建。两者不是同一个层面的机制。很多CKA考生在这个地方犯迷糊。正确的理解是ResourceQuota在你创建Pod的时候就会校验跟调度器的优先级排序没有直接关系。即使PriorityClass再高如果超出该Namespace的QuotaPod也会直接被拒绝连调度的机会都没有。6.2 组合实验二PriorityClass PodDisruptionBudget创建一个PDB设置minAvailable: 1关联到某个工作负载的Pod selector。然后触发节点排水kubectl drain看看PDB限制下的驱逐行为会不会因为优先级而区别对待。CKA新题里PDB不会阻止抢占是一个容易踩的坑。PDB定义的是“自愿驱逐”时的最低可用Pod数但PriorityClass的抢占是一种非自愿性操作不遵循PDB限制。举个例子你有个低优先级Deployment3副本PDB设置minAvailable为2。当一个高优先级Pod要抢占时调度器可以把这3个副本全部干掉只为了给高优先级Pod腾地方。PDB在这时候是不提供保护的。这个设计逻辑很多人觉得不合理但Kubernetes就是这么工作的。6.3 组合实验三PriorityClass Taint与Toleration给一个节点添加dedicatedhigh-priority:NoSchedule污点同时给高优先级Pod添加对应的容忍低优先级Pod不添加容忍。观察调度结果和抢占行为。这个实验可以帮你建立“容忍度是第一道门槛”的直觉。如果高优先级Pod没有容忍度它永远无法被调度到这个节点上无论PriorityClass有多高。6.4 从考试角度看PriorityClass的“反向出题”2025年CKA新题还有一个趋势反向出题。不再问“这个PriorityClass会让Pod怎么样”而是给你一个生产环境的现象描述让你反推需要创建什么优先级的PriorityClass。比如场景一kube-system里有个核心组件经常被低优先级Pod挤掉资源需要保证它永远优先调度。→ 解决方案给该Pod关联system-cluster-critical或更高优先级的自定义PriorityClass。场景二批处理任务拥进来把节点资源全部吃光导致有状态服务无法调度。→ 解决方案给有状态服务设置高优先级PriorityClass。场景三测试环境的开发Pod优先级不能太高但也不能被别人随便抢占还想让它们参与调度排序。→ 解决方案优先级中等preemptionPolicy: Never。备考到最后我会这样检验自己不看答案直接在白纸上把“高优先级Pod → 调度器排队 → 资源不足 → 查找受害者 → 模拟驱逐 → 真正的驱逐 → 调度成功”这条链路画一遍每个环节说出涉及的组件和可能的失败原因。能完整画下来说明这部分内容是真的掌握了。7. 最后再分享两个小技巧实验做到这里该掌握的知识点都过了一遍。最后补充两个我自己练习中使用频率最高的操作习惯对CKA备考和日常排查都有用。第一养成看Pod最终yaml里priority字段的习惯。很多人创建了PriorityClass却不知道Pod是否真的生效直接在kubectl get pod -o yaml里搜priority两个词就能确认比反复看describe更直观。第二练习结束后记得清理环境。如果创建了globalDefault为true的PriorityClass清理时要格外小心直接删除PriorityClass不会影响已经运行的Pod但新创建的未指定优先级的Pod会回到0。练习环境无所谓生产环境就必须先在低峰期操作确认没有Pod依赖这个默认值再动手。PriorityClass不是什么复杂机制但它处在Pod调度的关键路径上值得你在练习环境里多花半个小时把抢占行为、事件日志、边界条件都摸透。CKA考试里遇到相关题目至少能保证不慌。
返回列表