做过容器化的人都知道,团队规模一大、业务线一多,Kubernetes资源管理平台就不再是“多装个监控看板”那么简单。我这两年被各种“集群要爆了”“命名空间配额到底谁加的”“这个服务request给得也太离谱”之类的问题来回折腾,最后攒了一套内部代号叫ratel的资源管理平台,专门用来管多集群下的容量、配额、弹性、成本与超卖治理。这篇文章就把我对这套平台的完整拆解写出来:它到底解决了什么、整体怎么设计、核心模块如何落地、踩过哪些坑,希望给正在做集群治理的同学一份能直接“抄作业”的参考。
我会从最原始的痛点讲起,再逐步展开平台架构、资源画像、配额治理、弹性伸缩和成本分析这几个关键环节。不管你是平台组新人、SRE还是给自己团队搭容器云的人,都应该能从里面找到对应的思路。文中的所有配置和步骤都来自我实际跑过的环境,参数我会给出计算逻辑,而不是让你死记一个数字。
1. 项目初衷:为什么需要一套专门的资源管理平台
1.1 集群里的资源到底有多难管
刚接触Kubernetes那会儿,我以为资源管理就是看看CPU、内存使用率,不够就加节点。真正接手上千个Pod、几十个命名空间之后才发现,问题远没有这么简单。
最直观的痛点有三个。第一,request和limit严重失真。业务同学普遍抱着“多要一点不吃亏”的心态,CPU request经常是整个节点核数的几倍,内存 limit 敢往几个G写,实际用量不到一半。这种超卖一旦集中爆发,节点CPU throttling严重,Pod还会因为内存申请过大被调度器“卡死”在某个非预期节点上。第二,多集群口径不统一。不同集群的节点规格、标签、监控埋点都不一样,你没法用同一个命令快速回答“哪个集群的CPU超卖率最高”。第三,配额管理形同虚设。开发团队申请的命名空间资源配额没有自动校验,配额给多了没人回收,给少了业务半夜扩容失败,全靠人工去翻yaml。
于是我们决定做一套资源管理平台,把“资源”这件事变成可观测、可控制、可量化的闭环。这个平台就是ratel。它的目标很直接:在Kubernetes集群之上,构建一个面向资源全生命周期的控制面,覆盖从采集、分析、配额、调度建议到成本分摊的完整链路。
1.2 ratel要解决的核心痛点
ratel这个名字来自一种小型猛兽,意思就是一个“小而狠”的资源治理系统。我们不做大而全的容器平台,只聚焦资源的“管、控、省”三件事。
- 管:统一管理多个集群的节点资源、命名空间配额、工作负载资源水位。
- 控:对资源使用进行约束,防止某个团队无限挤占公共资源池。
- 省:通过画像和成本分析挤出过去被浪费掉的资源,降低整体计算成本。
这套平台面向的使用者有三类:平台运维者,需要快速查看集群健康度和资源水位;业务负责人,需要了解自己服务的资源使用趋势和配额余量;管理层,需要看到各项目成本分摊和优化建议。这三类角色的关注点完全不同,所以ratel在交互和数据呈现上也做了分层,而不是只给一张大盘。
2. 平台整体设计与核心模块
2.1 分层架构与数据流
ratel的整体架构沿用了“采集-存储-分析-控制”四层模型,但每一层都针对Kubernetes资源特性做了定制。采集层通过API Server和节点上的统计组件获取资源元数据与实时指标;存储层采用时序数据库加关系型数据库的组合,指标进时序库,配置和审计记录进关系库;分析层负责资源画像、配额计算、成本分摊;控制层通过控制器和Webhook执行配额校验与弹性动作。
数据流大致是这样的:每个集群部署一个采集组件,它会定期拉取节点、Pod、事件和指标数据,汇总后上报给ratel中央服务。中央服务把原始数据写入存储层,再由分析任务定期生成资源快照和趋势曲线。控制层的调度器和配额控制器会监听配置变更,如果发现配额设置异常,会自动更新Kubernetes里的ResourceQuota,或者通过接口返回给上层业务系统。
要说明的是,ratel并不是替代Kubernetes自身调度器和配额机制,而是把它们变成可编程的、有反馈的治理工具。底层能力全部复用Kubernetes原生能力,ratel更多是在外面套了一层“大脑”。
2.2 资源采集层:从kubelet到指标持久化
很多做监控的人都会说“Kubernetes指标不是现成的吗”。确实,kubelet会暴露Pod和容器的基础指标,指标聚合层也能提供HPA使用的指标。但原生的采集分散在每个节点上,缺乏全局聚合和统一标签体系。
ratel的采集组件在每个集群里以DaemonSet方式运行,它除了收集节点的基础指标,还会额外采集两类数据:一类是Kubernetes资源对象的请求量(request)和限制量(limit),另一类是调度器产生的调度事件和驱逐事件。为什么这两类数据重要?因为纯监控指标只能告诉你“现在用了多少”,而资源管理更需要知道“每个Pod承诺了会用多少”。承诺量决定调度结果,实际用量决定运行质量。
采集后的数据经过一层“标签标准化”再入库。标准化这一步很关键。比如不同集群的节点可能打了不同的可用区标签,我们要统一映射成ratel内部的区域字段;Pod的owner信息可能来自deployment、statefulset或自定义控制器,我们要解析并统一归属到业务应用。
时序数据的保留策略也经过仔细设计:原始指标保留7天用于排查,聚合后的分钟级数据保留30天,资源画像的日快照保留1年。这样既控制了存储成本,又能满足大部分分析和审计需求。
2.3 调度与配额控制层:让规则自动执行
ratel的控制层核心是两个控制器:配额控制器和推荐控制器。
配额控制器会监听命名空间或项目组的配额申请。业务同学提交一份“我需要多少CPU和内存”的申请单,ratel根据该业务的资源画像和历史增长趋势,给出一份建议值,同时标记出可能超卖的风险。管理员审批后,控制器自动通过调用Kubernetes API创建或更新ResourceQuota。
这里有一个很容易踩的坑:直接用原生ResourceQuota只限制总量,不会区分其中的“弹性部分”和“保障部分”。比如某个命名空间配额是32核CPU,如果里面塞了32个各request 1核的Pod,那大部分Pod在突发流量时都会被CPU限流。ratel在配额建议里会额外拆出“可压缩资源余量”和“不可压缩资源余量”,给业务一个更合理的配置基线。
推荐控制器则负责定期扫描Deployment、StatefulSet等工作负载的requests/limits配置,对比历史用量,输出“建议将CPU request下调到多少,内存request上调到多少”的变更建议。它不直接改线上配置,而是生成变更单,由业务或管理员确认后执行。这个设计是为了避免全自动变更引发的稳定性风险。
3. 核心细节解析与实操要点
3.1 资源画像:请求量、使用量、资源水位怎么看
资源画像是ratel所有功能的基础。它的核心是建立“承诺-使用-限制”三个维度的时间序列。我直接说计算逻辑。
请求量(Requested):一个工作负载的CPU请求量等于所有副本数的CPU request总和。比如某个Deployment有20个副本,每个副本CPU request为500m,那总请求量就是10000m,即10核。Kubernetes调度器就是靠这些数字决定Pod放在哪个节点上的。
使用量(Used):来自容器实际运行指标,通常用一段时间内的分位数来表示,比如CPU用P99、内存用P95。为什么要用分位数?因为平均使用量会被低谷拉低,高峰期会失真;而最大值可能只是一次毛刺。ratel默认使用7天P95作为“典型峰值使用量”。
资源水位:节点维度上,资源水位 = (节点可分配资源 - 节点上所有Pod请求总量) / 节点可分配资源。也就是说,不是在算“剩余可用CPU”,而是在算“还能塞下多少满request的Pod”。这很关键,否则只看CPU实际使用率60%就以为还能塞,其实调度器早就因为request量把节点排满了。
画布上我会给每个命名空间展示三个曲线:请求量曲线、实际使用量曲线、配额线。这三条线叠在一起,能很快看出一个团队是资源申请过多、实际用量低(浪费),还是请求量已经逼近配额线(需要扩容或调优)。我习惯把“请求量/使用量比值”超过3倍的命名空间标记为“高度浪费”,超过10倍的直接拉入优化清单。
3.2 配额管理:命名空间与项目组双维度治理
ratel的配额管理不是只建一个ResourceQuota就完事了,而是建立了一套规则。
首先,定义“项目组”的概念。一个项目组可以包含多个命名空间,比如前端组有web前端、客户端API两个命名空间。配额首先分配到项目组,再由项目组内部按命名空间拆分。这样做的好处是,当某个命名空间需要临时增加配额时,可以从项目组内部的“缓冲池”里切换,而不是每次改全局配额。
然后,配额的计算要参考节点余量。如果一个集群整体CPU请求量已经占了所有节点可分配量的90%,此时任何项目组都无法申请新的CPU配额,除非先扩容节点或清理低效负载。ratel会在配额申请时执行一个“模拟调度校验”:把你申请的配额按当前每个节点的空闲请求量做一轮调度模拟,能放进去才通过。这个模拟不依赖实际Pod对象,而是用“配额数值”抽象成虚拟Pod,所以速度很快,几百个节点的集群也能秒级完成。
实操中我强烈建议把ResourceQuota里的requests和limits分开设置。很多人图省事只配limits,结果requests没有约束,业务可以无限超卖。ratel默认会在创建配额时同时写入:
- requests.cpu
- requests.memory
- limits.cpu
- limits.memory
- persistentvolumeclaims
- count/pods
其中pods数量限制是防止某个命名空间创建大量短生命周期Pod,把etcd和API Server打爆。我们线上有真实教训:某个离线任务命名空间一次提交了上万个Pod,直接把API Server拖垮。后来在ratel里强制每个命名空间Pod数量上限,这种事才彻底拦住。
3.3 弹性伸缩与资源预占的策略
quotas和requests只是静态控制,真实业务场景里变化最剧烈的还是流量。ratel的弹性伸缩模块基于Kubernetes原生HPA扩展,但不是简单照搬,而是增加了“预测性伸缩”和“资源预占”两个能力。
预测性伸缩的逻辑:根据过去N天的指标曲线,识别出周期性高峰窗口。比如某个业务每天20点有一波流量,ratel会在19:30提前将副本数调整到高峰需要的数量,而不是等指标真的上去了再扩容。这个机制能显著降低扩容延迟造成的响应时间上涨。但要注意,提前扩容一定不要超过命名空间的request配额上限,所以在扩容前ratel会做一次配额余量检查,如果余量不足,会先发出告警,再选择部分扩容。
资源预占则是这样一个场景:业务为了应对日常负载,申请了20个副本的配额,但平时只需要15个副本。剩下5个副本的请求量被“闲置占用”,别的业务没法使用。ratel允许业务把副本分成“稳态Pod”和“弹性Pod”:稳态Pod的request计入配额并长期运行;弹性Pod只在需要时创建,创建前实时检测集群是否有足够余量,用完后缩容并释放资源。这其实就是一种“资源借贷”机制,能让整体集群的超卖率提升不少,同时又不影响业务SLA。
配置参数上,我给出的建议是:弹性Pod的CPU request可以比稳态Pod高一些,因为弹性Pod本就为峰值准备,内存则要严格控制,避免弹性Pod内存超限导致节点驱逐。
4. 实操过程与核心环节实现
4.1 平台部署环境与前置要求
ratel本身是用一套Web服务加两个控制器实现的,部署方式非常灵活。我这里以一个三集群治理的场景为例:生产集群、预发集群、大数据集群。
前置条件不需要重造轮子,现有Kubernetes环境就能接入。需要提前开通的权限包括:读取节点信息、读取命名空间和工作负载信息、读取Pod指标、创建和更新ResourceQuota、创建和删除HPA对象。这些权限建议用独立的ServiceAccount管理,不要直接使用集群管理员的kubeconfig。
采集组件部署到每个集群时,要注意给它设置资源上限。因为如果采集组件自己占用太多资源,就会干扰资源画像的准确性。我一般给采集组件的CPU limit设置500m,内存limit设置512Mi,超过限制自动重启。
中央服务部署在一台4C8G的节点或者小集群里就够了。它主要跑API服务和定时分析任务,不直接和业务流量打交道。时序数据库建议单独一台高IOPS节点的磁盘,因为资源画像的写入频率比较高,普通的云盘也能跑,但峰值时延会明显上升。
4.2 初始化流程:接入集群与建立基线
初始化ratel时,第一步是接入集群。我建议把“只读模式”和“治理模式”区分开。第一次接入时先开启只读模式,只采集数据不执行任何配额变更,跑3到7天后生成了完整的资源画像,再切换到治理模式。这样做能避免一上来就因为误判把生产集群的配额改坏。
接入流程我贴在下面,方便直接参考:
- 创建集群配置:在ratel控制台填写集群名称、API Server地址、Token和证书。
- 部署采集组件:自动生成一个DaemonSet的yaml,直接apply到目标集群。
- 验证数据连接:确认采集组件上报的数据包含节点列表、Pod列表和指标序列。
- 生成资源基线:运行一次全量资源扫描,生成每个命名空间的请求量、使用量、超卖率基线报告。
- 开启治理开关:基线确认无误后,再打开配额控制器和推荐控制器。
基线报告里有一项“可优化空间”我特别关注。它是通过将所有工作负载的CPU request降至P95用量(保留一定缓冲)后,计算出的可节省核数。我们第一个集群接入后,可优化空间居然达到了集群总核数的35%,也就是说有35%的CPU资源被“纸面申请”占着却根本没人用。
4.3 配额调整与推荐变更的执行流程
当ratel生成资源画像后,管理员会收到一批配额调整建议。这里我讲一个典型的执行场景。
某个命名空间A的画像显示:CPU请求量16核,CPU实际使用量峰值4核,内存请求量64Gi,内存实际使用量峰值30Gi。根据默认策略,ratel给出的建议是:CPU request调整为6核,内存request调整为40Gi,保留50%-100%的缓冲。
执行时,配额控制器会先做两步校验:
- 当前节点剩余可调度资源是否足够覆盖新的配额变化;
- 当前命名空间已经创建的工作负载,其请求总量是否超过新配额。
第二步很容易出问题。如果你把配额从16核降到6核,而现有Pod的request已经占了10核,那这个变更会直接导致Pod无法创建新副本。ratel的处理方式很温和:不会直接强制修改,而是生成一个“过渡方案”,比如先将配额降到10核,提示业务方调整工作负载的request,等负载实际请求量降下来后再执行最终目标值。
推荐变更的执行也类似。它会给出一个变更单,标注“原始配置 -> 建议配置 -> 影响分析”。影响分析里会写清楚变更后可能存在的风险,比如CPU request过低可能导致调度过于集中,内存request上调会导致节点余量紧张。业务方确认后,ratel才会调用API更新工作负载的resources字段。我自己的经验是:变更单一定要支持批量处理,但不能支持全选“一键执行”,必须一条条点,这样能强制你思考每个变更的影响。
4.4 成本分摊模型与资源计量
资源管理做到最后必然会聊到成本。ratel的成本模块用的是“以请求量为主、以实际使用量为辅”的分摊模型,而不是直接按实际使用量计费。
为什么这么设计?因为在K8s集群里,资源是分时复用的。如果一个Pod的request是2核,它平时只用0.5核,但高峰期确实需要2核,那这2核的调度承诺就是占用的,不能因为平时用得少就把成本算成0.5核。按照请求量分摊,各业务才能根据自己的“容量规划”来核算成本,而不是看运气。
具体分摊逻辑:把集群的固定成本(节点费用、存储费用、控制面费用)按每个命名空间在所有命名空间请求量中的占比进行分配。同时,对于节点上的系统组件占用,比如kube-system、日志采集、监控组件,这些成本会从总资源池中扣除后再分摊。如果不扣除,业务团队等于在替平台基础设施买单,长此以往成本数据就没法看了。
ratel的成本报表会输出两层数据:预算成本,按request量计算;弹性成本,按实际使用量超过request的部分计算。一个命名空间如果弹性成本持续偏高,说明它的request配置普遍低于真实需求,应该上调,否则高峰期稳定性无法保障。反过来,如果预算成本远高于实际使用,说明存在明显浪费,建议下调request。
5. 常见问题与排查技巧实录
5.1 为什么明明集群有剩余资源,Pod却调度不上去
这是问得最多的一个问题。表面看节点使用率不高,但Pod一直Pending。ratel的资源画像这时候就能派上用场。
排查技巧:看节点的“请求量水位”而不是“使用率水位”。如果节点的请求量已经达到可分配量的95%以上,调度器会把新的Pod放到其他节点,即便该节点当前CPU平均使用率只有30%。因为调度器只看request,不看实时使用率。
还有一个隐藏原因:节点的端口资源、存储卷数量、本地临时存储余量也可能成为调度瓶颈。ratel的节点详情页会把所有调度维度都展示出来,包括可分配的PID数、本地块设备数、挂载卷数。很多Pod卡住其实是tmpfs或emptyDir用太多,把节点本地存储撑爆了。
5.2 配额改成预期值后,业务却反馈扩容失败
这种问题一般出在两个地方。第一,ResourceQuota虽然改大了,但deployment自身的replicas数和requests总量已经超过新配额,导致新建Pod被拒绝。ratel在改配额时已经做了校验,但如果你绕过ratel手动改配额,就可能踩到这个坑。第二,命名空间下还有限制范围(LimitRange),它会在Pod创建时强制注入默认的request或limit。如果LimitRange里的默认值比你配额申请的值还大,那扩容时实际请求量会“悄悄超过”配额,然后Pod被拒。
排查时一定要先看Event,不要只看配额面板。我遇到过一次,Event里显示“exceeded quota”,但配额明明没超额。后来发现是别的Pod在同一秒钟创建,把配额余量占完了,属于典型的并发竞争问题。ratel里加了“预创建校验”功能后,这种问题大幅减少,但底层并发仍然存在,所以关键业务的扩容最好走hpa变更而不是人工直接调replicas。
5.3 监控指标时延导致弹性伸缩误判
刚开始跑预测性伸缩时,我们发现某些业务明明流量早高峰已经过了,却反而扩容了。追了半天,发现是采集组件上报指标偶尔延迟了5分钟,而预测算法把延迟后的低谷数据当成“即将到来的高峰”。
解决办法我给两条,都非常实用。第一,在指标进预测模型前做一次异常值清洗,把“上一时刻值零、下一时刻值突增20倍”这种明显异常的数据点剔除。第二,预测结果必须跟当前真实负载做交叉验证,如果当前负载还不如预测值的一半,不执行扩容,而是继续观察。这种“预测建议、实测确认”的组合拳,能挡掉大部分监控抖动带来的误操作。
5.4 多集群数据口径对不上,成本怎么分
多集群环境下,问题往往出现在“节点规格不同、单价不同”。同样是8核32G的节点,不同集群可能来自不同的采购渠道或不同代际的机器,价格差很多。ratel的成本模型里维护了一张“节点规格单价表”,按规格、区域、供应类型三个字段来确定单价。接入新集群时,先要维护这张表,否则成本数据会有偏差。
另一个坑是“预留实例”和“按量实例”的比例不均。ratel会把不可变成本(预留)和可变成本(按量)分开显示,而不是只给一个总额。这样管理层能看到,即使业务很省,预留实例的空置成本依然在产生,从而推动节点规格的优化和缩容。我通常建议每月月底跑一次“空置节点报告”,把连续7天CPU使用率和内存使用率都低于10%的节点标记为候选下线节点,这个动作往往能省下一大笔钱。
6. 一些踩过坑之后的心得
ratel能做到现在这个形态,中间走了不少弯路。最开始我们一心追求自动化,想把配额调整全流程自动执行,结果某个关键业务因为request被自动调低,高峰期Pod全部CPU throttling,服务响应时间翻了好几倍。后来我把“强治理”和“弱治理”分开:核心在线业务默认走“建议模式”,只出报告不自动变更;离线任务和测试环境才允许“自动模式”,直接执行推荐结果。
关于超卖率,我也有个新的理解。超卖不是越低越好,也不是越高越好。太低,资源严重浪费;太高,突发流量一来整个集群雪崩。ratel里专门有一个“集群风险分”指标,它结合了节点请求量水位、单节点P95使用率、驱逐事件数量三个子项。风险分超过80时,系统会自动冻结新的配额申请,直到管理员处理完风险项。这个机制救了我们很多次,否则某些集群早就被无限制的扩容请求打挂了。
另外,资源管理平台本质上是一个数据产品,不是工具产品。它最重要的事情不是“能改配额”“能伸缩”,而是让所有人对资源现状有共同的理解。ratel每周会向业务发一份“资源周报”,包含每项工作的请求量变化、实际使用量、优化操作记录、成本趋势。这份周报的价值甚至比平台本身的自动化功能还大,因为它让业务同学自己开始关注request配置是否合理,主动提出优化需求。
如果你也要搭这样一套平台,我建议第一件事不是写代码,而是先把自己的资源基线数据拉出来看看。没有基线,所有“优化”“自动控制”都是无源之水。把基础数据盘清楚了,再做配额治理、弹性伸缩、成本分摊,才真正稳得住。
最后再分享一个小技巧:尽量把权限边界划清楚。ratel只做资源治理,不做发布系统,不碰业务镜像,也不接管日志。很多人做着做着就想“顺便”把别的事情都管起来,最后系统膨胀得一塌糊涂,维护成本比收益还高。资源管理平台就该小而专,守住资源这条线,就足够创造很大的价值了。