多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

Kubernetes HPA实战:构建视频业务弹性伸缩与资源治理方案

Kubernetes HPA实战:构建视频业务弹性伸缩与资源治理方案 “video-use”标题看似简短实则是我们生产环境里一套完整的 Kubernetes 资源治理方案。当时摆在我们面前的局面很现实业务流量一天内有多个明显波峰波谷白天人力高峰和晚间活动高峰交错出现固定规格的节点池要么在高峰期被打满要么在低谷期大量浪费。视频转码、推流网关、审核服务这些核心应用对资源的需求各不相同手动调整副本数根本来不及。这套方案落地后我们真正实现了“流量涨实例自动涨流量退实例自动退”资源利用率和管理效率都上了一个台阶。这篇文章我把设计思路、核心参数、灰度过程和故障排查完整拆解出来给同样被资源问题折磨的运维、SRE 和后端架构同学一个可直接参考的样本。1. 资源治理的整体思路与方案选型1.1 为什么最终选了 Kubernetes 原生的 HPA先说结论我们不是没试过其他方案正是因为对比过才坚定选了 HPA。市面上能实现弹性伸缩的产品不少有云厂商自带的 CACluster Autoscaler有各种第三方组件也有人把业务层的自愈逻辑直接写在代码里。但它们要么绑定特定云厂商的基础设施要么和业务耦合太深要么需要在业务代码里侵入式地实现一套伸缩规则改造和维护成本都很高。HPA 不一样。它是 Kubernetes 控制面内置的控制器天生就和 Pod、Deployment、Service 这些核心资源属于同一套体系。我们需要的不只是“能扩容”而是“能按照业务的实时负载自动扩容”。HPA 直接通过 Metrics Server 采集 CPU、内存指标也可以扩展自定义指标在控制循环里不断计算当前指标值和期望副本数的偏差再通过调整 Deployment 的 replicas 来收敛偏差。整条链路没有任何外部强依赖监控组件挂了也不影响已有 Pod 的运行这对视频业务要求的稳定性来说非常重要。HPA 的算法也是开箱即用的。它基于当前副本数、当前指标值、目标值三者的比例关系计算出期望副本数。举一个具体例子某个 Deployment 当前有 4 个副本当前 CPU 使用率是 80%目标利用率是 50%那么期望副本数 4 × (80 / 50) 6.4向上取整得到 7。如果某一个 Pod 的指标值特别高它会先做“单 Pod 指标值 / 目标值”的判断当结果大于 1 时直接按这个 Pod 的指标值参与计算防止单个热点 Pod 被平均数据掩盖。这个机制在处理视频转码、推流合成这类典型的高 CPU 场景时尤为有效不会因为某个实例异常就把整个服务的伸缩决策带偏。1.2 配额、优先级与节点池资源治理的三根支柱只靠 HPA 扩容其实解决不了“资源从哪里来”的问题。我们最初上线的时候HPA 能把一个服务的副本数从 2 扩到 20但集群里如果只剩几台低配节点扩容出来的 Pod 就只能挤在一起或者一直卡在 Pending 状态。这时候真正需要的是配额、优先级、节点池三个层面的配合。配额解决的是“资源总量”的问题。我们把整个集群的资源按 Namespace 划分成不同资源池每个池子有独立的 CPU、内存上限服务只能在配额范围内伸缩不会出现一个业务把整个集群资源吃光的情况。比如视频转码服务所在的 Namespace 配额是 32 核 CPU、64GB 内存即使 HPA 因为异常指标一直扩容到达配额上限就会被 API Server 拒绝创建新 Pod 的请求天然形成一道保护屏障。优先级解决的是“资源竞争规则”的问题。通过 PriorityClass关键业务在资源紧张时优先获得调度而可延后的批处理任务可以随时被抢占。我们给视频推流网关设置了最高的优先级比如 1000000给离线转码任务设置了较低优先级比如 1000一旦资源池出现竞争调度器会优先保证高优先级 Pod 运行。这在高峰期特别有用不会出现因为几个大任务占满节点、网关 Pod 反而被挤掉线的异常情况。节点池解决的是“资源放在哪里”的问题。我们的 GPU 节点单独划池视频转码这类需要 GPU 的任务固定调度到 GPU 池普通 API 服务走 CPU 池两类任务互不干扰。这种隔离不只是性能上的考虑也有成本上的考量GPU 节点价格高出普通节点数倍如果不做隔离一个普通的 CPU 密集型服务调度到 GPU 节点上不仅浪费了昂贵的 GPU 资源还会影响真正需要 GPU 的转码任务。这三根支柱配合起来才构成了一套完整的资源治理闭环。HPA 负责算“需要多少副本”配额负责限制“最多能用多少资源”PriorityClass 负责决定“资源不够时谁先上”节点池负责确保“上去之后有合适的机器”。缺少任何一根都会在实际运行中露出短板。1.3 多集群资源池的共享策略业务规模扩大到一定程度后单一集群已经很难满足所有需求。我们在 video-use 的架构里加入了多集群管理的思路。每个业务集群独立承载自己的服务但我们构建了一个统一调度层把各个集群的空闲资源汇聚到一个共享资源池中。当某个集群出现流量高峰、本地资源不够时调度层会把溢出的工作负载调度到有空闲能力的兄弟集群上。这里的关键在于“资源池的共享并不是无条件的”。第一我们给每个集群设置了水位线比如 CPU 使用率超过 70% 就视为高水位不再接收溢出的负载。为什么要设水位线因为跨集群调度会带来额外的网络开销和数据同步延迟如果源集群本身已经高负载再把任务调度过去反而会让整个系统雪上加霜。第二每个集群在共享池中能借用的资源也有上限这个上限会根据该集群的历史峰值和业务优先级动态调整。核心业务集群的借用上限设得高一些非核心业务集群设得低一些避免一个集群的突发流量把整个共享池的资源都借走影响其他集群的基本运行。2. 核心配置解析与关键参数的调优过程2.1 Metrics Server 的部署与指标采集链路HPA 依赖指标来源默认情况下使用的是 Metrics Server。这个组件的原理并不复杂它通过 kubelet 的 Summary API 采集每个节点上所有容器的 CPU、内存使用数据再通过 Metrics API 暴露给 HPA 调用。但有一个细节很多人容易忽略Metrics Server 采集的是容器在上一个采集周期内的平均使用量而不是瞬时值。采集周期默认是 15 秒HPA 默认每 15 秒同步一次指标如果业务容器的负载波动非常快就会出现“指标还没采集到负载峰值已经过去了”的情况HPA 的响应自然就不够灵敏。我们在 video-use 中把 Metrics Server 的采集周期调整为 10 秒同时优化了 HPA 的--horizontal-pod-autoscaler-sync-period参数让伸缩决策的响应更快。但这里也要提醒一句缩短采集周期会增加 API Server 和 kubelet 的压力。尤其是集群规模达到上千节点的时候所有节点的指标汇总会对 API Server 产生不小的请求量需要评估监控链路的承载能力。我们目前的做法是核心链路的采集周期保持 10 秒非核心服务继续用默认值这样既保证了关键业务的响应速度又不会让整个集群的监控链路过度负载。2.2 HPA 核心参数minReplicas、maxReplicas 与 targetAverageUtilizationHPA 配置里的三个核心参数每一个都需要根据业务特点仔细敲定不能照抄文档示例。minReplicas的意义在于保证服务的基础吞吐能力。对视频 API 网关来说哪怕深夜没有任何流量也必须保持一定数量的副本随时接收请求否则睡梦中来一个突发请求冷启动会直接导致高延迟。我们的生产配置是把网关类服务的最小副本数设为 3既保留了冗余又不至于太多浪费资源。对于更核心的推流网关minReplicas 设到了 5因为这类服务一旦出现请求积压用户体验会立刻下降容不得半点意外。maxReplicas决定了一个服务最多可以扩展到多少副本。这个值不能拍脑袋定它受限于命名空间的配额、节点池的容量以及下游依赖如数据库连接数的可承受上限。我们把视频转码服务的 maxReplicas 设定为 12就是经过压测确认过的数据库连接池在 12 个副本并行写入时达到最佳吞吐再多反而会因为连接争抢导致性能下降。这个“最优副本数”需要通过真实的压测数据得出来盲目的数字只会埋下隐患。targetAverageUtilization是触发扩缩容的阈值。这个值的设定要考虑业务负载的特征和副本数变化的惯性。如果一个服务在高峰期的负载曲线非常陡峭阈值可以适当调低让扩容提前发生如果负载曲线比较平缓阈值可以调高一些避免服务频繁伸缩。视频转码服务我们用的是 60%因为转码任务对 CPU 的消耗非常稳定60% 的阈值可以在负载爬升早期就触发扩容。网关服务用的是 50%它的负载波动更频繁阈值稍低一些能让预留空间更大。批处理任务用的是 80%这类任务对延迟不敏感可以把资源利用得越满越好。2.3 自定义指标扩展不止 CPU 和内存CPU 和内存指标在大部分场景下够用但视频业务有几个场景必须使用自定义指标。最典型的就是队列深度。我们有一个视频审核服务任务从消息队列里拉取拉取速度直接决定业务吞吐。当队列积压越多说明消费能力不足这时单纯看 CPU 可能毫无反应因为任务都在等待 I/OCPU 根本跑不满。我们通过 Prometheus Adapter 把队列积压数量暴露为自定义指标HPA 根据积压数量实时计算副本数才彻底解决这个问题。自定义指标接入 HPA 的流程其实并不复杂核心是两步第一步在 Prometheus 中定义并采集业务指标例如通过 exporter 将 MQ 的队列深度暴露成mq_queue_depth第二步通过 Prometheus Adapter 的配置把指标名称映射成 Kubernetes 的 Custom Metrics API 资源HPA 就能像使用 CPU 指标一样使用它了。这里要特别留意自定义指标的值通常是一个绝对值比如当前积压 5000 条消息HPA 对绝对值的处理方式是按每个副本的期望处理能力来换算的。例如我们设置单副本期望处理能力为 1000 条积压当前积压 5000当前副本数 3期望副本数就是 5000 / 1000 5。这个设计思路要求你对业务指标的物理意义有清晰的理解否则指标配置得再花哨也只是一堆没有指导意义的数字。3. 从预发到生产video-use 的灰度落地过程3.1 灰度策略的选定与发布流程我们把 video-use 部署到生产环境的路径可以说是稳扎稳打先在预发集群全量验证再在正式集群按 10% 的流量逐步放量。为什么是 10%因为我们想让真实流量来验证 HPA 的弹性伸缩对业务延迟的影响而不是靠压测模拟。10% 的流量足够刺激一次真实的扩容和缩容动作但即使出问题爆炸半径也可控。这个方法看起来很保守但收益非常高因为生产环境的流量特征和预发环境存在本质差异只有真实流量才能暴露出那些在测试中根本发现不了的问题。灰度期间我们重点观察三个指标Pod 的启动时间、扩容触发到副本就绪的延迟、以及缩容后是否出现流量抖动。视频推流网关是最先灰度验证的业务因为它的流量特征最明显早高峰和晚高峰的请求量差距能达到 5 倍以上HPA 的每一次伸缩决策都能被清晰观察到。灰度通过后我们再把这个配置推广到其他业务。每个业务的灰度周期至少观察 3 个完整的业务波峰波谷确保伸缩策略在峰值和低谷期都不会出问题。3.2 灰度期间踩过的坑与应急预案第一次灰度我们就踩了坑。某个服务在 HPA 生效后频繁出现副本震荡原因很直白指标采集周期和 HPA 同步周期叠加产生了一个控制回路上的振荡。HPA 每次检测到 CPU 高于目标值就扩容扩容后 CPU 短暂下降下一轮检测又触发缩容结果是副本数在 4 和 8 之间来回跳服务后端的负载均衡器被频繁变化的节点列表搅得焦头烂额部分请求甚至出现短暂的 502。排查这个问题的时候我们发现光看 HPA 的事件列表根本看不出规律因为事件只记录了“扩容了多少副本”没有记录“为什么扩容”。于是我们同时抓了三份数据Metrics Server 的原始指标曲线、HPA 控制器的决策日志、Deployment 的副本数变更历史。三条数据放在一起对比振荡的规律立刻清楚了指标数据在高位和低位之间快速交替HPA 的反应总是慢半拍于是形成了扩了又缩、缩了又扩的死循环。解决办法有两个一是调大 HPA 的--horizontal-pod-autoscaler-tolerance参数默认值是 0.1我们调到 0.2让更小幅度的指标波动不再触发扩缩容。这个参数的本质是设置一个“死区范围”指标偏差在容忍度范围内就不做任何操作。二是给 Deployment 加上 scaleDown 的stabilizationWindowSeconds参数延长缩容冷静期防止刚扩容完就立刻缩容。我们设的是 300 秒意思是缩容决策一旦做出在 5 分钟内不能再次缩容。这两个调整叠加之后副本数曲线变得平滑多了震荡问题彻底消失。3.3 正式放量与容量压测的核对方法灰度验证完成后正式放量之前我们还做了一次严谨的容量核对。方法并不复杂但在生产环境非常有效先把预发集群的流量入口切到正式集群然后把正式集群的副本数人工固定在某个水位逐步增加模拟流量直到出现资源瓶颈记录瓶颈点对应的 QPS 和 Pod 数。有了这组数据再结合 HPA 的算法公式我们就能算出当前配置的理论最大吞吐然后反推是否需要调整对外承诺的 SLA。这组数据还有一个很重要的用途验证 HPA 的扩容上限是否合理。如果压测显示 8 个副本就能扛住 2 倍峰值流量而 maxReplicas 设的是 16那我们就知道还有一半的冗余可以应对更极端的场景。如果 16 个副本都扛不住那就得重新评估节点池容量和配额设置了。这个过程是数据驱动的每一步都留有记录之后复盘时也能搞清楚当初为什么做这样的决策。用数据说话灰度落地才不是碰运气。4. 常见问题、故障实录与运维建议4.1 扩容成功但 Pod 一直 Pending 怎么办这个问题出现的频率不低尤其容易在业务流量突然暴涨的时候出现。表象是 HPA 已经把副本数从 5 扩到了 15但新增的 Pod 一直处于 Pending 状态后续扩容出来的 Pod 白白占用了配额请求还是大量超时。这时候如果只看 HPA 状态你会以为扩容已经完成但实际上服务根本没有真正扩容到位。我们排查这类问题时先看了节点的资源水位发现可用 CPU 明明足够于是怀疑是节点亲和性或污点问题。最后定位到的根因是新增服务需要挂载一个本地数据卷而满足数据卷要求的节点只有两个扩容出来的多个 Pod 都挤在这两个节点上资源不够自然就 Pending 了。解决方案是在 Deployment 里声明了拓扑分布约束让扩容出来的 Pod 尽量分散到不同的可用区同时给集群补充了支持该数据卷的节点。这起故障给我们的教训是HPA 扩容只是“创建 Pod 请求”Pod 能否成功调度还取决于调度器的规则和节点实际资源状态。排查故障时先把视角从 HPA 切换到调度器问题往往一目了然。4.2 缩容比扩容更难冷数据与优雅退出的处理很多新手只关注扩容时副本数蹭蹭上涨的爽快感却忽略了缩容才是真正考验产品架构设计的地方。我们的视频点播服务在缩容时就遇到过问题Pod 被删除后正在处理的请求被硬生生中断。按照 Kubernetes 的默认行为删除 Pod 时会先发送 SIGTERM 信号默认等待 30 秒后发送 SIGKILL。但问题在于我们的服务端代码根本没有处理 SIGTERM 信号收到信号后直接退出了正在跑的任务自然就断了。处理这个问题需要在业务代码里监听 SIGTERM 信号收到后先停止接收新请求等存量请求处理完毕再主动退出。我们给每个服务设置了一个最大等待时间比如 30 秒超过这个时间就强制退出避免容器长时间不终止导致节点资源泄漏。同时要配合调整terminationGracePeriodSeconds参数默认是 30 秒如果业务请求的平均处理时间比较长这个值需要相应调大。另外还有一个隐蔽的坑如果服务使用了消息队列消费缩容时一定要手动关闭消费者组里的当前消费者而不是完全依靠 Pod 删除时自动触发否则同一个消息会被多个消费者重复消费造成数据不一致。缩容的优雅退出做得好业务的稳定性才算真正稳了。4.3 最后的运维建议告警、日志与容量规划video-use 上线后我们把运维团队的核心精力集中到了三类事情上告警、日志、容量规划。告警规则的核心不是监控 HPA 本身而是监控副本数和实际业务指标的关系。我们保留了几条非常有代表性的告警规则比如“副本数已经达到 maxReplicas 但队列积压仍在上涨”这条是严重告警说明当前容量已经不够了需要立刻介入再比如“副本数在 10 分钟内伸缩超过 5 次”这条是性能告警说明伸缩策略可能存在震荡或者配置不合理。这两类告警一个盯容量瓶颈一个盯策略健康度比单纯监控 CPU 使用率有意义得多。日志层面我们给 HPA 控制器开启了详细日志同时把 HPA 的每一次决策动作无论是扩容、缩容还是跳过都记录到独立的日志文件里方便事后审计。这类日志通常不会太多但每一次扩容背后的原因、当时的指标值、目标副本数都有据可查。线上出现问题时翻这些日志能节省大量排查时间。容量规划上我们每个季度都会做一次资源复盘结合业务增长曲线、各服务的峰值流量、以及 HPA 的实际伸缩记录推导下一季度的节点池扩容计划。这个过程看似繁琐但没有它任何弹性伸缩方案都只是表面的自动化资源池只会越用越乱。我们曾经遇到过某个月流量的增长远超预期但因为复盘及时提前一个星期加了节点没有影响到用户体验。容量规划不是临时抱佛脚而是持续性的例行工作。5. 实操过程中的其他心得与扩展思路5.1 配置模板化的收益在多个业务复用 video-use 方案时我们把 HPA、PriorityClass、ResourceQuota 的配置做成了一套可复用的模板。每个业务方只要通过一个简单的参数文件声明自己的资源画像包括最小副本数、最大副本数、目标利用率等就能自助完成接入。这样带来的好处非常直观平台团队不再需要每天帮不同业务调配置业务方也能根据自己的实际情况灵活修改参数。模板化的核心是把“不变的部分”和“变化的部分”拆开。不变的部分包括 Metrics Server、Prometheus Adapter 这些基础设施的部署方式变化的部分只有每个业务的几个数字参数。我们使用 Helm Chart 来管理这些模板业务方提交一个 values.yaml 就能完成整个接入流程。之前需要一整天才能完成的新服务接入现在压缩到了半小时以内效率提升非常明显。5.2 成本控制与资源利用率的平衡最后想聊一下成本。资源治理做到最后其实都是在成本和稳定性之间找平衡。HPA 帮你把副本数控制在刚好满足需求的水平这是节省成本的基本盘。但我们也发现很多服务缩容后 CPU 利用率依然长期偏低说明这些服务的 minReplicas 设置得过高了或者业务本身已经不再需要那么大的基础容量。于是我们定期分析所有服务的 CPU 使用率中位数把那些长期低于 20% 的服务筛选出来逐个调整它们的 minReplicas直接从源头降低资源占用。这个工作每个季度执行一次效果非常显著。最近一次调整我们把 40 多个服务的 minReplicas 平均降低了约 30%每月节省下来的资源成本相当可观。而且因为目标利用率阈值没有变服务的响应延迟完全没有受到影响线上服务质量稳如磐石。省下来的成本反手就投入到节点池的扩容上既保证了稳定性又优化了成本结构这是我认为这套方案最划算的地方。资源治理不是把资源压得越紧越好而是在保证业务服务质量的前提下让每一份资源都花在刀刃上。5.3 给后来者的一些实操建议如果让我给准备落地类似方案的同学一些建议我会强调几点先从小流量业务开始验证不要一上来就治理核心链路监控体系必须先于弹性伸缩建立否则出了问题连排查的方向都没有HPA 的参数不是设一次就能一劳永逸的随着业务流量特征的变化需要定期复盘和调整。这套方案真正跑起来之后你会发现它带来的不只是资源利用率的提升更重要的是运维同学终于不用在凌晨三点被扩容短信炸醒了。那种“系统自己在正确的时间做了正确的事”的感觉值得你花时间去打磨。
返回列表