news 2026/10/12 2:11:57

descheduler PodLifeTime 插件实战:基于 Pod 生命周期与状态迁移的精细驱逐策略

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
descheduler PodLifeTime 插件实战:基于 Pod 生命周期与状态迁移的精细驱逐策略
  • 云原生
  • 运维

【免费下载链接】descheduler

Descheduler for Kubernetes

项目地址:https://gitcode.com/gh_mirrors/de/descheduler
点击查看免费下载

导读

PodLifeTime 是 Kubernetes descheduler 框架中一个高度灵活的 Deschedule 插件,它不依赖节点资源利用率,而是完全以 Pod 自身的"年龄 + 状态 + 状态迁移时间"为判断依据,决定哪些 Pod 该被驱逐。本文以插件官方文档(pkg/framework/plugins/podlifetime/README.md)为主体,结合仓库源码、示例配置与单元/E2E 测试,完整讲解其过滤链组合逻辑、全部参数语义、经典使用场景与可直接复用的 DeschedulerPolicy 配置,帮助你在集群中实现"到期轮换、残留清理、故障回收"三类最常见的 Pod 生命周期治理。

插件定位:在 Descheduler 框架中的角色

PodLifeTime 被注册为Deschedule 扩展点插件。在 pkg/descheduler/setupplugins.go 中,插件以podlifetime.PluginName(即"PodLifeTime")注册进默认插件注册表,并绑定了参数类型PodLifeTimeArgs、校验函数ValidatePodLifeTimeArgs与默认值函数SetDefaults_PodLifeTimeArgs:

pluginregistry.Register(podlifetime.PluginName, podlifetime.New, &podlifetime.PodLifeTime{}, &podlifetime.PodLifeTimeArgs{}, podlifetime.ValidatePodLifeTimeArgs, podlifetime.SetDefaults_PodLifeTimeArgs, registry)

因此在使用时,PodLifeTime 必须出现在策略文件的plugins.deschedule.enabled列表中,而不是balance或filter等其他扩展点。其核心实现位于 pkg/framework/plugins/podlifetime/pod_lifetime.go,参数类型定义在 pkg/framework/plugins/podlifetime/types.go。

工作原理:过滤链的 AND/OR 组合语义

PodLifeTime 的判定逻辑可以概括为一句话:所有非空的过滤类别之间是 AND 关系(Pod 必须满足每一个已配置的过滤条件才会被驱逐);每个过滤类别内部是 OR 关系(命中其中任意一项即可满足该类别)。

源码中 New() 通过podutil.WrapFilterFuncs把各个条件逐一串成一条过滤链:

  • 命名空间过滤(include/exclude)与labelSelector是插件级的通用前置过滤,与驱逐器的Filter/PreEvictionFilter一并组合;
  • maxPodLifeTimeSeconds:Pod 年龄大于该秒数才通过;
  • states:任一匹配即通过(OR);
  • ownerKinds:按 OwnerReference 的 Kind 做 include/exclude;
  • conditions:任一条件过滤器命中即通过(OR);
  • exitCodes:任一容器终止退出码匹配即通过(OR)。

最终所有过滤器叠加后,Pod 必须全部通过才能进入驱逐候选列表。该语义与项目根文档 README.md 中"All non-empty filter categories are ANDed…Within each category, items are ORed"的描述完全一致。

排序与驱逐:最老优先、限额即止

候选 Pod 确定后,插件调用 SortPodsBasedOnAge(pkg/descheduler/pod/pods.go)按CreationTimestamp升序原地排序,保证最老的 Pod 先被驱逐;随后在 Deschedule() 中逐个调用handle.Evictor().Evict()执行驱逐。

驱逐循环对两种限额错误做了专门处理(pkg/framework/plugins/podlifetime/pod_lifetime.go#L179-L193):

  • EvictionNodeLimitError:单节点驱逐上限已满,continue跳过当前节点继续尝试其他节点上的 Pod;
  • EvictionTotalLimitError:全局总驱逐上限已满,直接停止整个插件执行。

这些限额(maxPodsToEvictPerNode、maxPodsToEvictPerNamespace、maxPodsToEvictTotal)由驱逐器在 pkg/descheduler/evictions/evictions.go 中统一强制(total 与 per-node 上限在EvictPod内检查并返回对应错误类型),PodLifeTime 只需消费它们即可,无需自己实现限额逻辑。

注:Pod 的 PDB(Pod Disruption Budget)约束同样由驱逐器层负责,PodLifeTime 不绕过 PDB,驱逐请求会正常受 PDB 保护。

参数总览

下表完整列出了插件支持的配置参数(字段名、语义与 types.go 一致):

参数说明类型必填默认
maxPodLifeTimeSeconds年龄超过该秒数的 Pod 才会被驱逐uint否*nil
states按 Pod phase、Pod status reason、容器 waiting/terminated reason 过滤,命中任一即匹配[]string否nil
conditions仅驱逐满足状态条件(见 PodConditionFilter)的 Pod[]PodConditionFilter否nil
exitCodes仅驱逐存在匹配容器终止退出码的 Pod[]int32否nil
ownerKinds按 OwnerReference 的 Kind 做 include/excludeOwnerKinds否nil
namespaces限定驱逐的命名空间(include 或 exclude)Namespaces否nil
labelSelector仅驱逐匹配这些标签的 Podmetav1.LabelSelector否nil
includingInitContainers将 state/exitCode 过滤扩展到 init 容器bool否false
includingEphemeralContainers将 state 过滤扩展到 ephemeral 容器bool否false

* 至少需要指定一个过滤条件(maxPodLifeTimeSeconds、states、conditions或exitCodes之一),否则参数校验失败。

这条"至少一个过滤条件"的硬性校验在 validation.go 中实现,同时校验还包括:namespaces 与 ownerKinds 的 include/exclude 互斥、labelSelector 合法性、states 取值白名单、conditions 每条至少设置一个字段。defaults.go(pkg/framework/plugins/podlifetime/defaults.go)目前保持各字段默认 nil/false 不变,即所有过滤维度默认关闭,未配置即不参与判定。

states 字段详解:四类状态的 OR 匹配

states是 PodLifeTime 最核心的过滤维度。它按OR语义同时匹配四类状态,任一命中即通过(见 pod_lifetime.go):

类别取值示例
Pod phaseRunning、Pending、Succeeded、Failed、Unknown
Pod status reasonNodeAffinity、NodeLost、Shutdown、UnexpectedAdmissionError
容器 waiting reasonCrashLoopBackOff、ImagePullBackOff、ErrImagePull、CreateContainerConfigError、CreateContainerError、InvalidImageName、PodInitializing、ContainerCreating
容器 terminated reasonOOMKilled、Error、Completed、DeadlineExceeded、Evicted、ContainerCannotRun、StartError

上述全部取值由 validation.go 中的podLifeTimeAllowedStates白名单强制约束,states 中不允许出现白名单之外的值,否则校验直接报错。

底层匹配由 pkg/descheduler/pod/pods.go 中的HasMatchingContainerWaitingState/HasMatchingContainerTerminatedState辅助函数完成:逐条检查容器的State.Waiting.Reason与State.Terminated.Reason是否命中集合。

当includingInitContainers为true时,InitContainerStatuses的 waiting/terminated reason 也会参与匹配;当includingEphemeralContainers为true时,EphemeralContainerStatuses同样参与。单元测试 pod_lifetime_test.go 验证了:未开启这两个开关时,init/ephemeral 容器的CreateContainerError不会被匹配(预期驱逐数为 0),开启后则能被驱逐(预期驱逐数为 1)。

conditions:按状态条件与迁移时间精细过滤

conditions允许你针对pod.status.conditions[]做精确匹配,非常适合"清理已 Succeeded 但残留过久"的场景。

PodConditionFilter 字段

单个条件过滤器内的字段级匹配是AND(所有已设置字段必须同时命中),未设置的字段不参与检查;多个条件过滤器之间是OR——Pod 的任一 condition 满足任意一个过滤器即被驱逐(实现见 pod_lifetime.go 的matchesAnyPodConditionFilter与matchesConditionFields):

字段说明
type条件类型(如Ready、Initialized、ContainersReady)
status条件状态(True、False、Unknown)
reason条件原因(如PodCompleted)
minTimeSinceLastTransitionSeconds要求匹配条件的lastTransitionTime距今至少该秒数

校验规则(validation.go):每条过滤器至少设置type/status/reason/minTimeSinceLastTransitionSeconds之一,否则校验失败。

迁移时间语义(重点)

当设置了minTimeSinceLastTransitionSeconds时,Pod 的条件必须同时满足 type/status/reason 字段匹配,且其lastTransitionTime必须足够久远;若该 condition 没有lastTransitionTime(零值),则视为不匹配。源码逻辑(pod_lifetime.go):

if f.MinTimeSinceLastTransitionSeconds != nil { if cond.LastTransitionTime.IsZero() { continue } idle := metav1.Now().Sub(cond.LastTransitionTime.Time) if idle < 0 || uint(idle.Seconds()) < *f.MinTimeSinceLastTransitionSeconds { continue } } return true

测试 TestTransitionTimeFiltering 覆盖了三个关键行为:2 小时前的旧迁移时间可驱逐、1 分钟前的新迁移时间不可驱逐、迁移时间检查只作用于匹配字段命中的那条 condition(另一条 reason 不匹配的 condition 即便迁移时间很新也不影响判定)。

ownerKinds:按控制器类型定向驱逐

ownerKinds通过 Pod 的 OwnerReference 的Kind字段做 include/exclude(见 pod_lifetime.go):

字段说明
include仅驱逐由这些 Kind 拥有的 Pod
exclude不驱逐由这些 Kind 拥有的 Pod

include与exclude最多只能设置一个(validation.go 强制)。测试 TestOwnerKindsFiltering 验证:exclude: [Job]时 Job 拥有的 Failed Pod 不被驱逐、非 Job Pod 正常驱逐;include: [Job]时只有 Job Pod 被驱逐。

典型价值:Job 控制器自身会清理已结束的 Pod,因此驱逐器通常应该排除 Job 拥有的 Pod,避免与 Job 的清理逻辑重复;而 ReplicaSet/DaemonSet 等长期运行的控制器则希望被驱逐后由控制器重建。

典型使用场景(可直接复用)

以下场景配置全部来自官方 README,字段语义与前述参数一一对应。

场景一:清理闲置过久的 Succeeded Pod

args: states: [Succeeded] conditions: - reason: PodCompleted status: "True" minTimeSinceLastTransitionSeconds: 14400 # 4 hours

仅当 Pod 处于Succeeded阶段,且存在reason=PodCompleted、status=True的 condition,且该 condition 的迁移时间距今超过 4 小时,才会被驱逐。

场景二:驱逐 Failed Pod 但排除 Job 拥有的

args: states: [Failed] exitCodes: [1] ownerKinds: exclude: [Job] maxPodLifeTimeSeconds: 3600 includingInitContainers: true

Failed Pod 同时满足"存在退出码为 1 的已终止容器(含 init 容器)"且"年龄超过 1 小时"且"不属于 Job"才被驱逐。exitCodes匹配实现在 pod_lifetime.go,测试 TestExitCodesFiltering 验证了命中与未命中两种情形。

场景三:资源泄漏缓解——定期重启长驻 Pod

args: maxPodLifeTimeSeconds: 604800 # 7 days states: [Running]

长期运行的 Pod 可能积累内存泄漏,本配置让运行超过 7 天的 Running Pod 被驱逐并由控制器重建,实现"到期轮换"。

场景四:清理 CrashLoopBackOff / ImagePullBackOff 卡死 Pod

args: states: [CrashLoopBackOff, ImagePullBackOff]

states列表内部是 OR 语义,命中任一 waiting reason 即可被驱逐,用于快速回收陷入镜像拉取/启动循环的故障 Pod。

场景五:仅驱逐特定控制器拥有的 Pod

args: states: [Succeeded, Failed] ownerKinds: include: [Job] maxPodLifeTimeSeconds: 600

只对 Job 拥有的 Succeeded/Failed Pod 生效,且要求其存活超过 600 秒,适合对"已完成但未及时清理"的 Job Pod 做兜底回收。

完整 DeschedulerPolicy 配置示例

示例 A:按年龄 + 状态过滤驱逐(1 天轮换)

apiVersion: descheduler/v1alpha2 kind: DeschedulerPolicy profiles: - name: default plugins: deschedule: enabled: - name: PodLifeTime pluginConfig: - name: PodLifeTime args: maxPodLifeTimeSeconds: 86400 # 1 day states: - Running namespaces: include: - default

示例 B:基于状态迁移时间的精细驱逐(Succeeded 残留 4 小时)

apiVersion: descheduler/v1alpha2 kind: DeschedulerPolicy profiles: - name: default plugins: deschedule: enabled: - name: PodLifeTime pluginConfig: - name: PodLifeTime args: states: - Succeeded conditions: - reason: PodCompleted status: "True" minTimeSinceLastTransitionSeconds: 14400 namespaces: include: - default

该配置的效果:default命名空间中,处于Succeeded阶段、带有PodCompleted=True条件、且该条件最后迁移时间距今超过 4 小时的 Pod 会被驱逐。

仓库还提供了两个可直接套用的独立示例文件:examples/pod-life-time.yml(7 天年龄 + Pending/PodInitializing 状态)与 examples/pod-life-time-transition.yml(Succeeded + PodCompleted + 4 小时迁移阈值),均以descheduler/v1alpha2API 版本编写,可作为--policy-config-file的输入。

命令行运行与验证

按 docs/user-guide.md 的"Balance Cluster By Pod Age"用例,可通过 CLI 直接加载策略文件运行:

descheduler -v=3 --evict-local-storage-pods --policy-config-file=pod-life-time.yml

该命令以-v=3开启详细日志,--evict-local-storage-pods允许驱逐挂载本地存储的 Pod,并加载包含maxPodLifeTimeSeconds: 604800的PodLifeTime策略文件(即上述"7 天轮换"配置)。文档同时建议:为每个业务应用配置 Pod Disruption Budget(PDB),以保证驱逐不会导致应用可用性受损。

测试与可靠性依据

  • 单元测试:pkg/framework/plugins/podlifetime/pod_lifetime_test.go(约 1367 行)覆盖年龄阈值边界(605 秒驱逐 / 595 秒不驱逐,见TestPodLifeTime_AgeThreshold)、各 phase 状态、全部 waiting reason、Pod status reason、init/ephemeral 容器开关、条件过滤、迁移时间过滤、ownerKinds、exitCodes、组合过滤以及驱逐限额(per-node / per-namespace / total)下的"最老优先"行为。
  • 参数校验测试:validation_test.go 验证了"无任何过滤条件时报错""非法 state 报错并列出完整白名单""include/exclude 互斥""空 condition 过滤器报错"等规则。
  • 默认值测试:defaults_test.go 确认空参数时各字段保持 nil。
  • E2E 测试:test/e2e/e2e_podlifetime_test.go 在真实集群中用 Job 制造 Failed/Succeeded Pod,验证States: [Failed]可驱逐、OwnerKinds.Exclude: [Job]不驱逐 Job Pod、Conditions命中与不命中两种结果。

这些测试共同锁定了本文所述的所有参数语义与边界行为,可作为你自行验证配置时的参照。

小结

PodLifeTime 的价值在于把"时间"这一维度引入了驱逐决策:从简单的按年龄驱逐,到精确到"某个状态条件已迁移超过 N 秒"的细粒度回收,再到按控制器类型、命名空间、标签、退出码的组合筛选,它都能在不触碰运行良好 Pod 的前提下完成集群的"新陈代谢"。配置时请牢记三条准则:至少指定一个过滤条件、类别间 AND / 类别内 OR、为关键应用配置 PDB。

  • 云原生
  • 运维

【免费下载链接】descheduler

Descheduler for Kubernetes

项目地址:https://gitcode.com/gh_mirrors/de/descheduler
点击查看免费下载

相关推荐

上一篇:Thornvigil 战斗动画实战参考:运动文件布局、回归测试套件与实机测量工具链
下一篇:原神数据本地化管理:胡桃工具箱 Snap.Hutao 完整使用指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/12 2:07:10

.NET接入钉钉开放平台实战:从Token缓存到事件订阅

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/12 2:06:17

ANSYS远程图形显示配置:Exceed v10与X Server桥接实践

简介&#xff1a;Exceed.v10是运行于Windows上的X窗口系统服务器&#xff0c;面向需要跨平台访问Unix/Linux远程主机的ANSYS仿真工程师及IT运维人员&#xff0c;可在本地图形界面中直接操作远程ANSYS计算任务。压缩包内共有1197个文件&#xff0c;约37.32MB&#xff0c;以dll、…

作者头像 李华
网站建设 2026/10/12 2:05:44

STM32最小系统点灯实操:90秒完成三步硬件启动

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华