自研轻量调度系统rea:让集群资源利用率翻倍的实战

发布时间:2026/10/21 13:00:20
自研轻量调度系统rea:让集群资源利用率翻倍的实战 先聊个真实场景某天下午我在机房巡查时发现一个怪现象——新上线的服务占满了三台机器但相邻机架上另一批机器CPU负载长年趴在5%以下。问了值守的同事答复是业务部门提需求我们连夜扩容从来没人关心资源到底够不够用。那段时间团队每周都要被这类低效调度问题折磨新服务上线前要手动梳理资源、改配置、盯部署出了故障还得靠人肉翻日志定位。于是我们抽了两个人用下班时间写了套内部工具代号就叫rea。rea的全称是Resource Efficient Automation核心就一句话把资源怎么分配、任务怎么跑、跑完怎么回收这件事从人肉决策变成规则驱动。它能自动感知集群里的空闲算力把任务派到负载最低的机器上跑完自动释放还能在无人值守时按队列执行批量作业。如果你也经常被这类问题缠住——机器利用率不均、重复性部署操作靠手工、临时任务占着资源不放——那这篇东西值得你花十分钟看完。下面我会把rea从立项到落地的完整过程包括架构选型、任务模型设计、调度策略、以及线上踩过的几个大坑全部摊开讲。1. 为什么自研而不是买现成方案对现有工具的失望与取舍刚开始我们确实考虑过直接用市面上成熟的任务编排和调度工具毕竟自己造轮子风险不小。调研了一圈发现一个尴尬的事实那些重量级平台普遍依赖一套完整的基础设施装监控、配权限、接告警、做UI前置学习成本足够让一个小团队忙活一个月。更麻烦的是它们擅长的是长期稳定运行的长服务编排而不是我们这种临时性、短平快、资源敏感的批量计算任务。1.1 现有工具的三个死穴第一资源感知滞后。很多调度器只认容器是否活着CPU、内存的真实波动完全不管。我们的业务里经常出现IO密集和CPU密集任务混跑前者把磁盘带宽吃满后者把核数打满调度器却一脸茫然继续往满载节点派任务。第二短任务的调度粒度太粗。我们测试过一次任务的调度决策周期通常在十秒级别可很多内部脚本任务运行时间只有几秒。等调度器做完决策任务都快跑完了排队等待的时间反而比执行时间还长。第三回收机制缺失。用完的临时容器、临时目录、占用的端口资源系统不会自动清。隔几天就得手动清理一次否则新任务会因为端口冲突或磁盘占满而失败。1.2 自研空间的判断我们把需求收敛成三个核心词轻量、实时、自动化。rea不需要管容器生命周期不需要做服务发现也不需要完整的控制面UI——它只负责回答三个问题哪个节点现在最闲、把任务发给谁、任务结束后怎么把资源还回去。确定了这个边界之后自研的难度其实一下就降下来了。不需要跟集群基础设施深度绑定只需要用一套轻量代理采集每台机器的实时负载再由一个中央调度器按规则做决策。整个项目最关键的技术点不在分布式一致性而在资源指标的处理逻辑和调度策略的取舍。这个决定后来被证明是对的。如果当时贪大求全搞一套完整的编排平台我们可能现在还陷在配置权限和踩坑调试里根本走不到任务自动调度这一步。2. rea的核心架构三个组件和一条任务流水线rea的架构不复杂总共三个组件每台机器上部署一个采集代理collector一台中心调度服务器scheduler还有一个带Web界面的控制端console。所有通信走内部网络的自定义短连接协议没有引入独立的消息队列因为任务量还没大到需要异步削峰。2.1 采集代理用10秒窗口评估节点负载collector每10秒采集一次节点的CPU使用率、内存占用率、磁盘IO等待时间、1分钟平均负载以及当前正在运行的rea任务数。它会把这份数据压缩成一条JSON通过HTTP POST到调度服务器。调度服务器维护一张全量节点状态表内存里存最近60个窗口的指标也就是10分钟的滑动窗口。调度时不是看瞬时值而是看趋势这样能避免任务恰好撞上负载毛刺导致的误判。我们遇到过一种情况某节点瞬时CPU冲到90%但平均下来只有30%。如果只看瞬时值这台机器会被误判为高负载丧失大量调度机会。改用滑动窗口均值之后误判率下降非常明显。2.2 调度器三类任务和执行队列设计调度器是整个系统的核心决策模块。最初我们只设计了一种任务类型后来扩展到三种按资源占用和时长区分任务类型典型场景单任务资源上限超时回收时间短任务日志清洗、指标聚合1核 CPU / 512MB 内存5分钟中任务数据转换、特征提取4核 CPU / 8GB 内存30分钟长任务模型训练、报表生成16核 CPU / 64GB 内存2小时每种任务进入独立的执行队列。队列不是单纯的FIFO而是带优先级的长任务优先级最高因为这类任务通常有对外承诺的交付时间短任务其次它们执行快插队影响不大中任务排最后。这套设计初期被不少同事质疑但上线后证明非常适合我们的实际业务比例——短任务数量占80%以上但它们对整体算力占用不到15%。2.3 控制端让运维不再需要SSHconsole提供三块面板节点资源看板展示每台机器的实时负载曲线任务管理页能查看所有任务的执行状态、日志、调度记录策略配置页调节三类任务的资源上限、队列并发数、节点选择倾向。我们刻意没有做命令行入口而是把所有操作都封装成按钮和表单为的是让不太熟悉命令行的同事也能自助提交任务把运维从重复操作里解放出来。任务提交的流程是这样的用户在console粘贴一段shell脚本或Python代码选择任务类型指定运行节点数量。rea会先把脚本下发到调度器选中的节点由该节点上的runner进程执行。runner捕获stdout和stderr实时回传日志任务结束后把临时工作目录整个清理掉。3. 任务调度策略设计从随机分配到智能打分调度策略是rea整个项目最花心思的部分。第一版我们用的策略简单粗暴——轮询把任务轮流发给各个节点。上线第一天就出了问题节点配置并不相同几台老机器被塞满任务新机器反而空闲。后来改成选当前负载最低的节点但很快发现这样会导致任务在节点间频繁切换小文件缓存完全失效数据读取效率变低。3.1 节点打分机制负载加权与亲和性惩罚最终版我们实现了一个打分函数。调度器看到新任务时对所有可用节点做三件事计算基础负载得分权重依次为CPU使用率40%、内存占用30%、磁盘IO等待20%、1分钟负载10%归一化到0到100分。根据任务的资源规格对节点做过滤资源不足直接淘汰。比如短任务永远不会被派到内存只剩300MB的节点。计算亲和性惩罚分。如果某个节点最近30分钟内执行过同类任务且节点上还留有该任务的数据缓存则给一个减分项。这样做是希望任务尽量留在同一批节点上减少数据搬运。调度器把每个节点的最终得分排序取最高分的节点执行。同时做一个简单的并发控制每类任务的全局并发数可配置默认短任务50、中任务10、长任务5防止某一类任务瞬间打满整个集群。def score_node(node, task_type): cpu node.cpu_usage * 0.4 mem node.mem_usage * 0.3 io node.io_wait * 0.2 load node.load_avg1 * 0.1 base_score (cpu mem io load) * 100 if node.available_mem task_type.mem_required: return -1 # 不可用 if node.available_cpu task_type.cpu_required: return -1 affinity_penalty 0 if task_type in node.recent_task_types: affinity_penalty 10 # 惩罚跨节点调度 return base_score affinity_penalty3.2 为什么用打分而不是经典调度算法这里我想解释一下设计取舍。经典的分布式调度算法比如最不请求优先Least Request、一致性哈希、甚至更复杂的竞价算法都能在很多场景下给出更优解但它们要么实现成本高要么对业务特征依赖强。我们这些任务的资源需求相对固定运行时间相对可控打分机制已经能覆盖绝大多数的调度质量问题。实际跑下来集群整体CPU利用率从改造前的平均约30%提升到60%以上资源空转的情况少了很多。当然打分机制也有明显的短板它完全不考虑节点间的网络延迟也不感知机架拓扑。这对以批处理为主、跨节点通信量小的场景问题不大但如果你有一个强依赖节点间频繁通信的应用这种策略就不合适了。这也是rea从一开始就定位为任务调度器而不是分布式计算框架的原因。3.3 调度器本身的容错处理调度器单点故障是逃不掉的问题。我们做了两个层面的兜底。第一调度服务器本身由同机房两台机器组成主备模式主节点宕机后备用节点自动接管故障切换时间大约30秒。第二每个collector在本地缓存最近一轮任务下发结果调度器恢复后可以重新拉取对账避免任务发了但没执行的灰色状态。这两个兜底机制看起来简单却让rea的整体可用性稳稳上了三个九。4. 落地实施中的几个关键细节从代码到稳定运行前面讲的都是架构和理论真正开始落地时还碰到一堆细节问题。我挑几个影响最大的展开讲。4.1 任务日志的收集与统一起初每个节点上的runner直接写本地日志排查问题时得挨个节点去找效率极低。后来改成所有日志通过HTTP实时回传到consoleconsole按任务ID分文件存储。日志量大的任务会占掉不少带宽我们就加了压缩和采样策略短任务日志只保留最后200行中任务保留全部日志但限制单条大小长任务保留全部原日志方便事后分析。这个调整从账面上看是小事但实际排障时非常关键。有一次某个数据转换任务在跑了20分钟后突然失败靠console里完整保留的长日志我们花了一个小时就锁定了是某个字段的类型解析错误如果还像之前那样去节点上翻日志可能得折腾一上午。提示任务日志存储的保留策略一定要提前定好。正式环境里日志量会快速增长按任务粒度做打包归档比按节点切割再检索要高效得多。4.2 资源回收与超时强制清理任务执行完成后runner会执行三步清理删除临时目录、关闭所有子进程、释放端口占用。但总有一些僵尸任务因为脚本异常无法正常退出这时候就需要强杀机制。我们在runner里内置了一个看门狗进程每5秒检查一次任务进程的CPU和内存占用超过任务类型上限时连续警告3次第4次直接kill -9。超时强制清理放在调度器侧。任务运行时长超过该类型的软上限时调度器通知runner先发SIGTERM给10秒做优雅退出超过硬上限直接SIGKILL。这个两级超时机制非常实用软超时给任务留了清理现场的机会硬超时确保资源不会被无限期占用。4.3 节点下线与维护模式的切换运维经常需要给机器打补丁、重启或扩容。我们给每台机器的collector加了一个状态字段可以手动切换为drain模式。处于drain模式的节点不再接收新任务正在运行的任务最多等2分钟跑完就彻底闲置。切换上线过程全程不用改调度器配置collector状态实时同步到调度器内存维护窗口期的工作量减少了很多。4.4 任务模板和参数管理随着使用的人变多重复提交的任务脚本越来越多。我们给console增加了模板功能。用户可以创建任务模板比如每日Hive数据导入周报自动化生成模型训练一百轮模板里可以定义参数占位符。提交任务时只需要填参数不需要重新写脚本。这个功能极大减少了误操作概率之前手动复制脚本经常出现改坏参数的问题模板化之后这类问题基本绝迹。5. 线上踩过的三个坑完整的排查与修复过程任何项目都不可能在设计阶段把所有问题都想到。rea上线后我踩过几个记忆深刻的坑这里详细复盘一下希望能帮你少走弯路。5.1 坑一节点负载假高导致任务排队停滞上线第二周就遇到了诡异现象集群整体显示很闲但新任务却一直提示找不到可用节点。拉出节点状态表仔细看发现所有节点的CPU使用率都超过了90%。可我们从监控面板看这些节点的进程列表里明明全是低负载进程。排查过程先怀疑是采集数据出错拉出最近100个采集窗口的原始数据发现CPU使用率的计算公式有bug——把进程的用户态时间和内核态时间直接相加但没有除以采样周期导致数值被放大了10倍以上。修复后CPU使用率恢复正常任务排队现象立刻消失。这个坑给我们的教训是实时指标的计算公式一定要在模拟环境里压测验证不能只核对瞬时值。我们从那次之后加了一个指标合理性校验步骤新指标上线时会对比同期监控系统的数据偏差超过15%就自动告警。5.2 坑二任务重复执行导致的数据不一致某天数据团队反馈报表里出现了重复数据。追查后发现某个长任务因为节点假死触发了一次重试但重试前原任务已经执行了一半导致同一批数据被处理了两次而且两次的写入操作不是幂等的。排查过程先梳理rea的重试机制发现当时的实现是任务失败后自动重新调度没有判断失败阶段。有的任务在中间步骤失败重新执行没问题有的任务在结果写入阶段失败重新执行就会产生脏数据。修复方案给任务增加执行约束配置调用方可以声明任务的幂等性。非幂等任务失败时默认不自动重试而是进入人工确认队列。幂等任务可以安全重试。这个改动上线后重复数据的问题没有再发生过。5.3 坑三并发任务数突增导致的雪崩某个业务月初集中提交了上千个短任务瞬时并发远超默认配置。调度器同时向大量节点发起任务下发collector没做并发限制结果节点上的runner进程数暴增内存被打满反而拖垮了整个集群。排查过程现象最先出现在console的性能监控里节点响应时间从几十毫秒涨到几百毫秒接着一部分健康的服务也开始报错。翻调度器日志发现任务下发接口在短时间内的调用量比平时高了几百倍节点侧的runner还没来得及启动就被新的下发请求打满。修复方案在collector端加了基于信号量的并发控制每台节点同时最多拉起5个runner进程。调度器端也做了任务下发速率限制每分钟最多下发500个任务。这两个限流措施组合之后即便突增任务量系统也能平滑处理而不是被压垮。提示任何分布式系统都要在关键接口埋限流熔断的开关。我们后来还加了一个全局开关一键暂停所有调度动作用于线上大故障时的应急止血。6. rea的能力边界与后续扩展思考rea到目前为止满足了团队的大部分调度需求但它的能力边界也相当清楚。它不处理任务间的依赖关系不提供DAG编排也不关心任务执行结果的数据血缘。当初刻意没做这些是因为团队没有一个任务依赖关系足够复杂的场景。如果未来业务发展到需要多阶段串联任务我们可能不得不在rea之上再叠加一层工作流引擎。6.1 从调度走向自动伸缩下一步比较明确的方向是接到弹性伸缩能力上。现在节点数基本是静态的双十一、月底结账这类大流量来临时还是要靠运营手动确认扩容。如果让rea进一步承担弹性扩容的决策——感知队列积压情况动态拉起新节点空闲后自动回收——从成本和人效两个角度看都会很有价值。6.2 调度策略从固定公式走向数据驱动目前的打分公式依赖人工指定的权重未必是最优配置。我在想一个实验方案把每次调度的决策结果和任务执行质量记录成样本跑一个小的离线回归模型用历史数据寻找更优的权重组合。不过这个方向工作量不小短期内未必能落地但至少说明rea的架构给了我们后续迭代的空间。6.3 从内部工具到可复用的开源启发团队内部一直有人讨论是否把rea整理成一个开源项目。我的看法是核心调度引擎的部分可以剥离出来配合统一的节点接入API做成一个通用轻量调度组件应该能帮到不少跟我们有类似痛点的小团队。但UI、权限、多租户这类企业级功能开发成本会陡增至少不是当前最优先要解决的问题。写在最后关于工具生命周期的一点个人看法做rea这个项目给我最大的感触是一个小工具的价值不在于技术多炫而在于它是否真正解决了当下的资源浪费问题。我们完全没有追求一套复杂的大系统很多设计决策都是基于够用、可维护、能扩展的原则。实际收益非常直观机器利用率翻倍团队处理重复操作的时间从每周十几个小时降到两三个小时线上因资源不足引起的故障也基本绝迹。最后分享一个小技巧如果你也在做类似的内部工具一定要把资源回收当成一级需求来设计它比调度策略更能决定这个系统是帮忙还是添乱。没有可靠的回收机制用不了多久这个工具就会变成另一个需要人肉维护的资源黑洞。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询