ARTICLE DETAIL

资讯详情

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

AI推理服务如何用好Kubernetes HPA:原理、调优与排错实战

AI推理服务如何用好Kubernetes HPA:原理、调优与排错实战 先说个反直觉的结论一个AI推理服务你给它配了20个PodCPU常年只用15%但它还是会在某天半夜被用户骂上热搜。原因很简单AI服务的流量不是匀速长河而是脉冲式的。真正的问题是——当脉冲来的时候你手动敲kubectl scale的速度根本追不上它。这就是为什么我坚持把HPAHorizontalPodAutoscaler水平Pod自动扩缩容当成所有AI服务上线的标配。它本质上就是个智能资源管家盯着业务水位自动帮你加人、减人不需要半夜爬起来看告警。这篇文章不整文档式科普就把我自己从手动扩容累到怀疑人生到HPA用得像老司机的完整路径拆给你看原理、配置、AI场景调优、排错链路全都有。适合刚上手Kubernetes的AI应用开发者也适合已经配了HPA但经常不按预期工作的同学。1. AI服务的流量脾气决定了你绕不开HPA1.1 一次被人流打懵的教训扩容永远慢过流量去年底我把一个基于大模型的图片理解服务部署到Kubernetes上线第二周赶上一次营销活动。凌晨1点47分告警群开始刷屏gateway timeout率一路冲到87%。我当时的应对路径是这样的先登录监控面板看请求量发现是某个合作方的H5页面瞬间引入了上千并发然后去查Deployment当前副本数才4个Pod接着手动执行扩容把副本数改到12等Kubernetes调度完、拉完镜像、加载完模型全部Pod就绪已经是25分钟之后。用户不会等你25分钟。那次事故之后我把完整的操作时间线复盘了一遍发现问题用了3分钟决定扩容用了1分钟但真正吞掉时间的是调度、拉镜像、容器启动、模型加载这个链条。手动扩容的致命伤不是我反应慢而是Kubernetes的冷启动链路天然长。想要追平瞬时流量必须在流量刚露头的那几十秒就做出扩容动作这是人手做不到的。HPA的评估周期默认只有15秒。也就是说它可以在你刚看到告警的时候就已经把副本数提上去了。扩容速度不一定快多少但它不会睡过头不会犹豫不会在凌晨两点的时候因为再等等看而错过窗口。1.2 推理服务的三个不听话特征AI服务的负载模型和传统Web服务有本质区别这也是为什么它特别需要HPA同时也特别难配好。第一个特征是请求烈度极不均匀。传统Web接口每个请求的成本基本恒定但大模型推理请求的成本可能差出几个数量级。一个你好和一个请帮我写一篇五千字的报告放在同一个batch里GPU算力开销完全不是一个量级。单看QPS根本推断不出真实的资源压力。第二个特征是服务端batch机制扭曲了资源利用率指标。vLLM这类推理框架会把并发请求攒起来批量推理GPU利用率可以长时间维持高位但此时队列可能还在堆积反过来GPU利用率不高的时候队列也可能已经堵了。CPU和内存指标在这种场景下更能严重失真。第三个特征是单副本贵。一个GPU节点动辄几万块一年你不可能靠常年开着50个副本来应对5分钟的流量峰值。成本约束逼着你必须动态扩缩容。1.3 谁真的适合HPA先给服务对号入座不是所有服务都适合HPA。我见过不少团队把HPA当成万金油给什么服务都贴一份结果带来的麻烦比省下的事还多。这里列一个我常用的判断表业务类型流量特征是否适合HPA原因多轮对话ChatBot早晚高峰明显闲时很低很合适潮汐效应明显低成本时段长在线推理API突发性强由外部调用方驱动很合适手动扩根本来不及内部开发测试环境恒定低负载常驻2个Pod就够不建议缩容没有收益反而增加抖动批处理任务由任务队列驱动运行时长固定看情况需要按队列长度扩而不是按CPU扩离线离线处理负载本身就受调用方节奏控制不建议计算资源可以预留一句话总结有潮汐、有突发、单副本又贵的服务是HPA的主场。AI推理服务三个特征全占基本属于头号适用对象。2. HPA的决策逻辑指标、公式、冷却窗口一锅端2.1 指标从哪来一条数据管线的完整链路HPA本身不采集任何指标它只是各种指标管线的下游消费者。理解这条管线排错时能少走一半弯路。Kubernetes里有三类指标能供HPA使用资源指标Resource MetricsCPU、内存这种内置指标来自每个节点上的kubelet和cAdvisor由metrics-server汇总暴露在metrics.k8s.io这个API下。自定义指标Custom Metrics应用自身暴露的业务指标比如请求队列长度、每秒推理请求数通过聚合层暴露在custom.metrics.k8s.io下。外部指标External Metrics和Pod没有直接绑定关系的指标比如消息队列积压量暴露在external.metrics.k8s.io下。你可以这样理解HPA是个只管下订单的管家它不看锅里的菜只看抄表员递给它的数字。架构上所有指标请求都通过Kubernetes API服务器的聚合层转发链路是应用和节点产生数据 → 采集器metrics-server、Prometheus等汇总 → 注册为APIService → HPA控制器周期性拉取评估。常见的误区是以为装了Prometheus就算能自动扩缩容了。不是的Prometheus只是数据源还需要一层适配器比如prometheus-adapter把Prometheus的查询结果翻译成HPA能读的API。KEDA这类控制器本质上也承担了这个翻译和托管工作后面讲自定义指标时会细说。2.2 副本数公式拆解HPA心里那杆秤HPA每隔15秒做一次要不要调副本数的计算。它算的不是直觉是一个确定公式desiredReplicas ceil[currentReplicas × (currentMetricValue / desiredMetricValue)]翻译一下现在的副本数乘以实际指标值除以目标指标值向上取整。举个具体例子你就明白了。现在有4个PodCPU平均利用率50%HPA目标利用率配的60%。代入公式4 × (50/60) 3.33向上取整是4。结果不变所以副本数保持不变。这就是为什么很多人的HPA看起来不动——不是坏了而是当前负载压根没越过阈值。换一组数字当前5个Pod平均CPU利用率90%目标60%5 × (90/60) 7.5向上取整是8。HPA一次评估就可能把副本从5调到8。如果配置了多个指标HPA会分别计算每个指标对应的期望副本数取最大值。这是很合理的设计任何一个维度的压力都不允许被忽视。还有一个隐藏参数叫容忍度tolerance默认0.1。意思是实际指标和目标的比值落在0.91.1之间时控制器不动作防止微小波动导致副本数来回改。这就是为什么前面的例子算出来3.33取整为4看起来明明低于目标但没缩容——除了取整容忍度也提供了缓冲。2.3 稳定窗口和速率控制防过山车的两把闸理解了公式你会立刻想到一个问题如果流量出现一个秒级毛刺HPA是不是马上把副本数拉满等毛刺过去又立刻缩回来答案是如果没有稳定窗口确实会。这也是新手HPA抖动最频繁的原因。Kubernetes在HPA里引入了stabilizationWindowSeconds和behavior两个机制。缩容默认有300秒的稳定窗口HPA评估发现需要缩容时不会立刻执行而是先等5分钟看负载是不是真的持续走低。扩容的稳定窗口默认是0秒因为扩容通常希望快。Behavior可以对扩容和缩容的速度分别做策略限制。我最常用的一组策略是扩容时每60秒最多增加当前副本数的100%缩容时每60秒最多减少2个Pod并且缩容前保持5分钟观察。这样既保证流量陡增时能以翻倍速度拉起又避免几分钟后的缩容把服务打回原形。这两把闸本质上是拿短暂的不精确换长期的稳定。用生活类比就是空调温度到了设定值不会立刻停机而是要再吹一会儿才压压缩机如果一过温度就停机一超温度就开机压缩机早废了。3. 快速上手把HPA配置到你的推理服务上3.1 动手前先确认三件事我见过太多人对着HPA文档抄一份YAML就kubectl apply结果三天后发现TARGETS列全是unknown。配置前请先确认三件事能帮你省掉大量排查时间。第一Kubernetes版本够不够新。HPA的autoscaling/v2API从Kubernetes 1.23开始进入稳定版。如果你的集群还是1.20、1.21请用autoscaling/v2beta2字段大部分兼容但别直接抄新版配置。第二metrics-server是否正常。执行kubectl get apiservice | grep metrics.k8s.io确认状态是True而不是False或Unknown。再执行kubectl top node能返回节点用量才算通。很多云厂商的托管集群默认不带metrics-server这一步挂了后面全白搭。第三Deployment容器必须声明资源requests。这是最容易踩的坑。HPA的CPU利用率是拿当前使用量除以requests里声明的CPU算出来的如果容器没写requests这个除法根本无从做起HPA就显示unknown。注意写limits不写requests也没用必须显式给requests。3.2 写第一份CPU和内存HPA我建议所有AI服务先跑通最简单的CPU、内存HPA再上自定义指标。第一份YAML可以直接抄apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: llm-inference-hpa spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: llm-inference minReplicas: 2 maxReplicas: 20 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 70 - type: Resource resource: name: memory target: type: Utilization averageUtilization: 80重点看scaleTargetRef它告诉HPA管哪个DeploymentminReplicas和maxReplicas给副本数划了边界。CPU目标利用率70%内存80%任何一个超过自己阈值HPA都会按前面说的公式去扩容。运行kubectl apply -f hpa.yaml之后执行kubectl get hpa -w实时观察。正常情况下TARGETS列会显示70%/70%这种实际和目标的比例过一会儿如果流量上来会变成95%/70%随后副本数开始上涨。3.3 让AI帮你讲一遍HPA快速扫盲既然标题是AI快速掌握HPA我分享一个我现在带新人常用的方法利用AI对话助手把HPA的机制快速理解一遍。不需要多高级随便一个你用得顺手的AI助手都行。我一般会给这样的指令请用最通俗的语言解释Kubernetes HPA的副本计算公式。举例当前有4个副本CPU平均使用率50%目标利用率60%计算后最终副本数是多少再举一个使用率90%的例子。最后解释为什么第一个例子计算结果虽然是3.33但取整后是4。AI通常会把比例关系、取整逻辑讲得比较清楚尤其适合补充那些你不好意思问人的基础问题。还可以把kubectl describe hpa的输出贴给AI让它帮你读Events里的报错提示。但我要提醒一点AI生成的HPA配置不一定是当前集群的API版本。我就见过AI输出autoscaling/v2beta2的旧格式还有把averageValue和averageUtilization混用的。AI在这里是随身教练不是最终裁判。所有它给的结论都以kubectl api-resources | grep autoscaling和你集群实际运行情况为准。3.4 验证配完不等于完事HPA配置完必须压测不能只盯着YAML自我感动。我的验证套路分三步。第一步准备一个压测工具wrk、hey都行对着推理服务的Service发起请求wrk -t4 -c200 -d120s http://your-service-address/v1/chat第二步观察HPA动作。压测开始后30秒内kubectl get hpa -w应该能看到TARGETS百分比上升再过12分钟REPLICAS开始增长。如果压了3分钟副本纹丝不动停掉压测回头看指标源和阈值。第三步验证缩容。压测结束后不要急着等缩容因为缩容有5分钟稳定窗口。你可以趁这个时间观察日志里是否有Pod被终止、新Pod是否正常清理连接。整个过程走完才敢说这个HPA是可用的。4. AI推理场景的进阶调优GPU、队列和冷启动4.1 为什么CPU指标对GPU推理服务不灵跑基础HPA只是及格线真正麻烦的是AI推理服务的特点瓶颈在GPU而你最方便拿到的CPU指标偏偏和GPU负载不匹配。现象很典型vLLM服务在压测时GPU利用率已经95%CPU占用却只有30%。这时候如果你只配了CPU的HPA副本数纹丝不动但用户已经在排队了。反过来有些预处理任务密集的服务CPU很高但GPU很闲照CPU扩容纯粹是浪费显卡。原因在于推理框架的batch机制。GPU吃的是批量矩阵计算CPU只负责调度、tokenize、采样这些边角活。想用指标反映真实压力你得往业务层走。4.2 用排队长度当扩缩容信号对大模型推理服务最直观的用户可感知压力指标不是资源利用率而是请求排队长度。vLLM会在metrics端点暴露类似vllm:num_requests_waiting的队列指标TGI也有对应的tgi_queue_size具体名称跟随版本变化但思路一致。实现上有两条路一是用KEDA这种控制器直接用Prometheus查询触发扩缩容二是通过prometheus-adapter把指标注册成external metrics再用原生HPA引用。我推荐优先试KEDA因为对你屏蔽了大量指标适配细节配置更短。一个典型的ScaledObject长这样apiVersion: keda.sh/v1alpha1 kind: ScaledObject metadata: name: llm-queue-scaler spec: scaleTargetRef: name: llm-inference minReplicaCount: 2 maxReplicaCount: 20 triggers: - type: prometheus metadata: serverAddress: http://prometheus.monitoring.svc.cluster.local:9090 query: sum(vllm:num_requests_waiting{namespacedefault}) threshold: 5 metricType: AverageValue注意metricType那行。AverageValue表示所有匹配Pod平均排队数不超过5适合判断整体是否过载如果你更关心全局总排队量可以换Value。KEDA控制器会为这个ScaledObject在背后生成一个HPA对象所以你仍然可以用kubectl get hpa看到它它的核心逻辑还是HPA那套。这里query里的PromQL语义很关键别聚合错了维度否则指标失真。4.3 扩容要快、缩容要稳behavior配置之道默认HPA的扩容策略是没有速率限制的看起来爽快但遇到突刺会拉满一堆Pod。我的线上配置一定给behavior明确写两套策略behavior: scaleUp: stabilizationWindowSeconds: 0 policies: - type: Percent value: 100 periodSeconds: 15 scaleDown: stabilizationWindowSeconds: 300 policies: - type: Pods value: 2 periodSeconds: 60扩容不设稳定窗口每15秒最多翻一倍保证突发流量能在几个评估周期内拉出足够副本。缩容设300秒稳定窗口并且每60秒最多减少2个Pod给业务留出退潮观察期。这个配法尤其适合模型加载慢的服务。但就算扩容再快你还要面对一个残酷事实大模型服务的冷启动时间是分钟级的。一个推理镜像动辄10GB起步模型权重可能有十几GB新Pod光是加载模型就够一分钟。即使HPA瞬间把副本从2扩到10新Pod也要慢慢ready。对策有几个minReplicas不要抠得太死留23个热副本兜底镜像做分层缓存配合startupProbe给足启动时间别让readiness探针在模型加载期就把新Pod踢出服务。4.4 一个必须破除的误解PDB拦不住HPA缩容很多文章会建议配PodDisruptionBudgetPDB来防止缩容把在跑的长请求掐断。这是个非常常见的误解我专门说清楚。PDB约束的是自愿驱逐eviction比如节点维护、滚动更新时Kubernetes主动把Pod赶走。但HPA缩容不是驱逐它是直接通过ReplicaSet把副本数调小然后ReplicaSet删掉多余的Pod——这个流程完全不经过PDB的准入检查。所以别指望只配一个PDB就能保护长请求。真正对HPA缩容有效的是这几件事给容器配置优雅停止preStop钩子加一段等待时间再配合terminationGracePeriodSeconds让正在处理的推理请求有收尾时间。适当拉长缩容稳定窗口等于给所有长请求一段安全撤退时间。在Service层面做多副本流量分散避免缩容瞬间把连接集中在少数Pod上。PDB在节点维护、集群升级时依然很有用但它管不了HPA两者别混为一谈。4.5 HPA和ClusterAutoscaler的联动HPA管的只是够不够Pod至于节点够不够那是ClusterAutoscalerCA的事。AI服务扩容到一定规模后集群里所有空闲节点都调度完了新Pod会卡在Pending。这时候CA会观察到Pending Pod自动申请新节点。联动关系看着很美但有个现实问题云厂商的新节点从申请到可用通常要25分钟比Pod冷启动还慢。所以在AI场景里HPACA适合应对持续数分钟以上的负载上升不适合应对秒级突刺。我的习惯是应对秒钟到分钟级突发靠HPA的minReplicas和热副本应对长时间业务潮汐靠CA伸缩节点省钱。两者配合才算真正的资源管家。5. 排错链路HPA不扩容、乱扩容时怎么查5.1 TARGETS显示unknown先查指标管线如果kubectl get hpa里TARGETS列显示unknown先别怀疑HPA本身十有八九是指标没采集上来。按这条链路排查kubectl get hpa kubectl describe hpadescribe输出里的Events很关键。如果看到FailedGetResourceMetric说明HPA根本没拿到指标。接着查APIService状态kubectl get apiservice | grep metrics.k8s.io状态必须是True。然后直接测数据kubectl top node kubectl top pod -l appllm-inference如果top pod返回空或者只有少数Pod检查这些Pod是否处于Running状态、是否配置了requests、metrics-server是否收集到数据。最阴间的坑是Pod里只写了limits没写requestsHPA算不了利用率没有就会一直显示unknown没有任何报错因为这不是错误是没法算。如果是自定义指标链路更复杂一层查kubectl get apiservice | grep custom.metrics确认聚合API可用再看Prometheus里那个query能否查询出数据。Prometheus里查不到就往上查应用metrics端点Prometheus里有但HPA读不到就查适配器配置。5.2 配了HPA但就是不扩容阈值和实例数问题指标正常但HPA一直按兵不动是第二常见的问题。我的排查顺序是看当前实际用量kubectl top pod -l appllm-inference确定CPU是不是真的超过目标值。检查目标值是不是设太高了。averageUtilization: 90在个别服务上可能永远到不了因为90%已经是极端水位了。回忆公式和容忍度当前利用率50%、目标60%算出期望副本数不变90%、目标60%时如果当前副本数33 × 1.5 4.5取整是5也才加2个。你要是压测量不够HPA可能真的认为不需要动。最后才怀疑behavior配置。有些人把scaleUp的stabilizationWindowSeconds设成600秒等于扩个容先犹豫10分钟看起来就像完全不扩。5.3 副本数像过山车全是毛刺惹的祸另一个典型症状是副本数忽上忽下比如5分钟内从3扩到8又缩回4再扩到7。根因通常是指标毛刺某个秒级采样刚好冲到高位触发扩容下一轮采样平复又触发缩容。HPA默认对缩容有5分钟稳定窗口但扩容的稳定窗口是0所以毛刺最容易放大扩容动作。对策分几个层次。最推荐的是让指标本身平滑如果是自定义指标在PromQL里套一层窗口函数比如avg_over_time(metric[3m])滤掉秒级毛刺。其次是把目标阈值调高一点用小波动换低抖动。如果还不行就给scaleUp也加一个稳定窗口比如60秒HPA就不会因为单次采样立刻扩容behavior: scaleUp: stabilizationWindowSeconds: 60但代价是扩容响应变慢AI场景要权衡清楚。5.4 扩容了但依旧超时最后一公里没走完最让人崩溃的是HPA确实扩容了副本数从2涨到10但业务依然超时。这时候别骂HPA它已经做了它该做的。查两个地方。第一kubectl get pods看新Pod是不是Ready。大模型服务最典型的坑是启动阶段模型加载很慢readiness探针配置不当的话新Pod会被Kubernetes判定为不健康永远不接入Service后端。我通常同时配startupProbe和readinessProbestartupProbe给足模型加载时间readinessProbe负责加载完成后的健康检查。第二看新Pod是否真的在接收流量。用kubectl logs看新副本有没有真实请求进来。有时候Service的endpoint因为某些原因没有把新Pod加进去或者流量亲和性把连接都粘在旧Pod上副本翻倍了但压力完全没分担。一句话总结HPA告诉你需要更多但让新副本真正可用是应用启动设计、探针配置、网络层三者共同的事。HPA是资源管家不是性能保险。6. 几个我踩坑后养成的HPA实操习惯最后分享几个我长期养成的习惯不一定能让你立刻变高手但一定能让HPA少出幺蛾子。第一凡是新服务上线HPA必须压测验证扩容和缩容两个方向。只验扩容不验缩容容易在深夜被缩过头背刺。压测停止后耐心等满5分钟的稳定窗口再确认副本数降到了合理水位。第二监控告警不要盯HPA是否在扩要盯HPA是否已经顶到maxReplicas且持续处于目标值之上超过15分钟。前者会频繁误报后者才是真正需要你出面的信号——说明副本上限设低了或者流量超出了当前集群容量。第三Deployment里的requests必须好好写。很多人随手写cpu: 500m结果HPA的利用率基于500m计算这个数漂不漂直接决定你的扩缩容准不准。用kubectl top pod观察一段真实用量再反过来定requests比拍脑袋靠谱。第四用AI辅助写HPA配置时让AI生成YAML后自己至少核对apiVersion、metric类型、behavior三个点再翻一遍kubectl api-resources。AI生成的东西可以当草稿但它对你的集群版本和业务指标一无所知最终判断必须是你自己。对我来说HPA不是配完就不管的组件它更像一个需要偶尔校正的智能管家。它帮你半夜不用爬起来扩缩容但也需要你在白天花点时间理解它的脾气。把这个管家调教好了AI服务上线之后你终于能睡个整觉了。
返回列表