news 2026/9/4 1:28:15

Day63—AI服务的K8s弹性伸缩:基于GPU利用率的HPA

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Day63—AI服务的K8s弹性伸缩:基于GPU利用率的HPA

AI 推理服务有个让运维抓狂的特质:它吃 GPU,但 Kubernetes 原生的 HPA 只看 CPU 和内存。结果就是——流量打进来,GPU 已经 99% 占满,HPA 却因为 CPU 才 30% 而纹丝不动,请求在队列里越积越多,P99 从 300ms 飙到 30 秒。

这篇文章不聊概念,直接给你一套能落地的组合拳:DCGM Exporter 采 GPU 指标 → Prometheus Adapter 转成 Custom Metrics → HPA v2 按 GPU 利用率伸缩 → 预热池化解模型冷启动 → 限流 + 降级兜底。每一段配置都能直接抄。


一、为什么 CPU HPA 在 AI 服务上失灵

先说清楚一个根因,不然后面的配置你会「知其然不知其所以然」。

传统 Web 服务是CPU-bound:请求来了,吃 CPU,CPU 高了扩,CPU 低了缩,很自然。但 AI 推理服务是GPU-bound:一次 forward 推理,CPU 只是打打杂(预处理、tokenize),真正干活的是 GPU 的 CUDA Core 和显存。

结果就是三种「指标错配」:

场景CPUGPU原生 HPA 反应
流量小、单请求大低(15%)高(95%)不扩,请求排队
流量大、模型小高(80%)低(40%)乱扩,浪费 GPU
显存被打满显存 100%不扩,直接 OOM

结论:AI 服务必须用 GPU 指标做伸缩,CPU 最多当辅助。下面按「采集 → 暴露 → 伸缩」三步走。


二、GPU 指标采集:DCGM Exporter + Prometheus Adapter

K8s 自带的 metrics-server 只提供 CPU/Memory,压根不认识 GPU。要拿到 GPU 利用率、显存占用、温度,业界标准做法是 NVIDIA 的DCGM(Data Center GPU Manager)+DCGM Exporter

2.1 部署 DCGM Exporter(DaemonSet)

DCGM Exporter 以 DaemonSet 跑在每个 GPU 节点上,通过 NVIDIA 驱动提供的 NVML 库读取 GPU 状态,暴露成 Prometheus 格式的指标。

# dcgm-exporter.yaml —— 每个 GPU 节点跑一个,采集该节点所有 GPU 指标 apiVersion: apps/v1 kind: DaemonSet metadata: name: dcgm-exporter namespace: monitoring spec: selector: matchLabels: app: dcgm-exporter template: metadata: labels: app: dcgm-exporter spec: # 关键:需要 hostPID 才能访问 NVML,且挂载设备 hostPID: true hostNetwork: true tolerations: - operator: Exists # 容忍 GPU 节点的污点,保证能调度上去 containers: - name: dcgm-exporter image: nvcr.io/nvidia/k8s/dcgm-exporter:3.3.5-3.4.0-ubuntu22.04 env: - name: NVIDIA_VISIBLE_DEVICES value: "all" ports: - name: metrics containerPort: 9400 volumeMounts: - name: pod-gpu-resources mountPath: /var/lib/kubelet/pod-resources readOnly: true volumes: - name: pod-gpu-resources hostPath: path: /var/lib/kubelet/pod-resources --- # 对应的 Service + ServiceMonitor,让 Prometheus 自动抓取 apiVersion: v1 kind: Service metadata: name: dcgm-exporter namespace: monitoring spec: selector: app: dcgm-exporter ports: - name: metrics port: 9400 targetPort: 9400

部署完,访问http://<节点IP>:9400/metrics,你会看到DCGM_FI_DEV_GPU_UTIL(GPU 利用率)、DCGM_FI_DEV_FB_USED(显存已用)这类指标。DCGM_FI_DEV_GPU_UTIL就是我们伸缩的核心指标。

小提示:阿里云 ACK 上如果不想自己搭这套,可以直接装「云原生 AI 套件 ack-ai-dev-console」,它内置了 GPU 指标采集和 Dashboard,开箱即用。但自建 DCGM Exporter 的好处是:任何云/自建机房行为一致,不锁厂商。

2.2 Prometheus Adapter 把 GPU 指标变成 Custom Metrics

Prometheus 里有指标还不够,HPA 只认Custom Metrics API。这一步由Prometheus Adapter负责:它把 PromQL 查询结果包装成custom.metrics.k8s.io的 API,HPA 就能直接引用。

# prometheus-adapter 的自定义规则:把 GPU 指标翻译成 HPA 能懂的 custom metrics # 关键:seriesQuery 从 Prometheus 找原始指标,metricsQuery 定义最终暴露的名字
rules: default: false custom: - seriesQuery: 'DCGM_FI_DEV_GPU_UTIL{exported_namespace!="", exported_pod!=""}' resources: overrides: exported_namespace: { resource: "namespace" } exported_pod: { resource: "pod" } name: matches: "DCGM_FI_DEV_GPU_UTIL" as: "gpu_utilization" # 对同一个 Pod 的多卡取平均,得到「这个 Pod 的平均 GPU 利用率」 metricsQuery: 'avg by (<<.GroupBy>>) (avg_over_time(<<.Series>>{<<.LabelMatchers>>}[1m]))'

配置完重启 Prometheus Adapter,kubectl get --raw /apis/custom.metrics.k8s.io/v1beta1 | grep gpu能看到pods/gpu_utilization这个指标,就说明通了。


三、HPA v2:按 GPU 利用率伸缩

指标就位,HPA 配置反而最简单——就是标准 HPA v2 语法,把 metrics 从resource换成pods类型的自定义指标。

# hpa.yaml —— 按 Pod 平均 GPU 利用率伸缩,目标 70% apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: ai-inference-hpa namespace: ai spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: llm-inference minReplicas: 2 maxReplicas: 20 metrics: # 核心:GPU 利用率,目标 70%(留 30% 缓冲应对突发) - type: Pods pods: metric: name: gpu_utilization target: type: AverageValue averageValue: "70" # 辅助:显存占用,防止利用率不高但显存先爆的情况 - type: Pods pods: metric: name: gpu_memory_utilization target: type: AverageValue averageValue: "80" behavior: scaleUp: stabilizationWindowSeconds: 60 # 扩容:60 秒窗口,GPU 紧张要快扩 policies: - type: Percent value: 100 # 每次最多翻一倍,防止误判打爆 periodSeconds: 60 - type: Pods value: 4 # 或每次最多加 4 个 periodSeconds: 60 selectPolicy: Max scaleDown: stabilizationWindowSeconds: 300 # 缩容:5 分钟窗口,模型冷启动贵,别急着缩 policies: - type: Pods value: 1 # 每次只缩 1 个,慢缩 periodSeconds: 120 selectPolicy: Min

这里有两个老兵经验值得记住:

  1. 目标值别设太满。GPU 利用率 70% 不是拍脑袋——留 30% 缓冲,是因为 GPU 负载是「突发式」的,一个 batch 突然变大,如果没有缓冲,HPA 扩容的那几分钟里队列已经爆了。

  2. 扩容快、缩容慢。这是 AI 服务的铁律。扩容慢 = 用户等;缩容快 = 频繁冷启动(几十秒的模型加载),反而更伤。所以上面scaleUp用 60 秒窗口、scaleDown用 300 秒窗口。


四、模型冷启动:预热 + 预热池

GPU 伸缩有个 Web 服务没有的「天坑」:模型冷启动慢。一个 7B 的模型,从 Pod 起来到把权重从磁盘/对象存储 load 进显存,少则几秒,多则几十秒。这几十秒里 Pod 是「Ready 但不 Serve」,流量打进来全是超时。

解决思路两条:预热(warm-up)预热池(warm pool)

4.1 预热:启动即加载,加载完才报 Ready

startupProbe+ 一个「模型加载完成」的健康端点,把「进程起来了」和「模型能用了」区分开:

// Spring Boot AI 推理服务:暴露模型加载状态,配合 startupProbe @RestController public class ModelReadyController { ​ // 原子变量标记模型是否已加载进显存 private static final AtomicBoolean MODEL_LOADED = new AtomicBoolean(false); ​ @PostConstruct public void warmUp() { // 启动后立即加载模型(权重 → 显存),加载完才置 true Thread.ofVirtual().start(() -> { try { // 触发模型加载 + 跑一次 dummy 推理,让 CUDA 完成初始化 llmService.loadModel("qwen2.5-7b-instruct"); llmService.predict("ping"); // 预热一次,把 kernel 编译缓存也热了 MODEL_LOADED.set(true); } catch (Exception e) { log.error("模型预热失败", e); } }); } ​ // startupProbe 会反复访问这个端点,直到返回 200 才算启动成功 @GetMapping("/actuator/ready") public ResponseEntity<String> ready() { return MODEL_LOADED.get() ? ResponseEntity.ok("ready") : ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).body("loading"); } }

配合 Deployment 里的探针:

# deployment.yaml 片段 —— 三探针各司其职 spec: template: spec: containers: - name: llm-inference image: myrepo/llm-inference:1.0 resources: limits: nvidia.com/gpu: 1 # 关键:申请 1 张 GPU,调度器才会把它放到 GPU 节点 startupProbe: # 启动探针:给足模型加载时间,最多等 5 分钟 httpGet: path: /actuator/ready port: 8080 failureThreshold: 30 # 30 * 10s = 最多等 5 分钟 periodSeconds: 10 readinessProbe: # 就绪探针:加载完成后,才把流量引进来 httpGet: path: /actuator/ready port: 8080 periodSeconds: 5 livenessProbe: # 存活探针:进程还活着就行,别用 ready 当 live httpGet: path: /actuator/health/liveness port: 8080 periodSeconds: 10

划重点:startupProbe 和 readinessProbe 都指向/actuator/ready,但含义不同。startupProbe 给模型加载「兜底时间」,readinessProbe 决定「流量什么时候进来」。如果只配 liveness/readiness,模型还没加载完,readiness 失败次数多了 Pod 会被直接干掉重启,陷入「加载 → 被杀 → 再加载」的死循环。

4.2 预热池:预启动 N 个副本,温热待命

预热解决了「单个 Pod 冷启动」的问题,但没解决「扩容时新 Pod 依然要冷启动」的问题。预热池的思路是:永远保持比实际需求多 1~2 个「温热副本」,一旦 HPA 要扩容,直接从池子里拿,而不是从零加载。

最简单的做法是把minReplicas设成 2,并配合「定时预热」:业务低谷期定时给所有副本发一个 dummy 请求,保持模型常驻显存(长时间无请求,驱动可能把权重换出显存)。

// 定时预热任务:每隔一段时间发个空请求,防止模型被换出显存
@Component public class ModelKeepWarmTask { ​ @Scheduled(fixedDelay = 4 * 60 * 1000) // 每 4 分钟一次 public void keepWarm() { // 发一个最小代价的请求,让 GPU 保持活跃、权重常驻显存 String resp = llmService.predict("ping"); log.debug("keep-warm: {}", resp); } }

生产上更完整的做法是KEDA + 预热:KEDA 支持基于「队列长度」的伸缩(比 GPU 利用率更贴近业务),配合一个常驻的预热副本,就能做到「流量一到,池子顶上,同时后台悄悄加载新副本」。


五、降级 + 限流:GPU 打满时的最后防线

弹性伸缩再好,也有一段时间差(扩容快也要 1~2 分钟)。在这段「GPU 已经打满、新副本还没起来」的空窗期,光靠 HPA 是不够的,必须上两道保险:限流(挡住过量请求)和降级(GPU 不可用时切到备选路径)。

5.1 限流:保护 GPU 集群不被冲垮

AI 服务限流不能用简单的 QPS——因为「1 个短请求」和「1 个长推理请求」资源消耗差百倍。更好的维度是并发数(in-flight 请求数)Token 配额

// 基于 Semaphore 的并发限流:同时只允许 N 个推理请求进入 GPU @Component public class GpuConcurrencyLimiter { ​ // GPU 显存能同时容纳的最大并发推理数,压测出来的,不是拍脑袋 private final Semaphore semaphore = new Semaphore(16); ​ public String predictWithLimit(String prompt) { // tryAcquire 拿不到就立刻拒绝,别排队——排队只会让后面的请求更慢 if (!semaphore.tryAcquire()) { throw new GpuBusyException("GPU 繁忙,请稍后重试"); } try { return llmService.predict(prompt); } finally { semaphore.release(); } } }

在网关层(Spring Cloud Gateway 或 Sentinel)也配一道「预热期熔断」:当检测到多个 Pod 处于 loading 状态时,直接熔断,返回兜底话术「服务繁忙,请稍候」,而不是让请求无意义地等。

5.2 降级:GPU 不可用时切 CPU 或小模型

这是很多团队忽略的「性价比之王」:不是所有请求都值得占 GPU。简单问题用 CPU 推理或小模型,只有复杂问题才走大模型 GPU。

// 模型路由 + 降级链:复杂问题走 GPU 大模型,简单问题走 CPU 小模型 @Service public class ModelRouterWithFallback { ​ public String route(String prompt) { // 第一步:意图分类(轻量,CPU 就能跑) boolean complex = intentClassifier.isComplex(prompt); ​ try { if (complex) { return gpuBigModel.predict(prompt); // GPU:qwen2.5-14b } else { return cpuSmallModel.predict(prompt); // CPU:qwen2.5-0.5b,延迟高但免费 } } catch (GpuBusyException | TimeoutException e) { // 第二步:降级——GPU 打满时,复杂问题也先甩给 CPU 小模型兜底 log.warn("GPU 不可用,降级到 CPU 小模型: {}", e.getMessage()); return cpuSmallModel.predict(prompt); } } }

这套「大模型 GPU + 小模型 CPU 兜底」的组合,实测能把 GPU 峰值压力削掉 30%~40%,因为大量「打招呼、简单问答」根本不需要 14B 的大模型。


实战建议(照抄版)

  1. 先把 GPU 指标接进来,再谈伸缩。别急着写 HPA。先确认kubectl get --raw /apis/custom.metrics.k8s.io/v1beta1 | grep gpu能查到指标,这一步不通,后面全是空中楼阁。自建就用 DCGM Exporter + Prometheus Adapter,托管就用 ACK 云原生 AI 套件,二选一别混着用。

  2. HPA 目标值用压测定,别用经验值。70% 是我给的经验值,但你的模型、你的 batch size、你的显卡型号都不一样。正确姿势是:压测出「GPU 利用率到多少时 P99 开始劣化」,这个临界值减 10% 就是你的 target。没有压测数据的伸缩阈值,都是玄学。

  3. 冷启动和伸缩是同一个问题,必须一起设计。只做 HPA 不做预热池,等于「修了油门忘了打火」。最省事的组合是:startupProbe 兜底加载时间 + minReplicas=2 保底温热 + 定时 keep-warm 任务,三件套上齐,冷启动对用户的伤害能降到最低。别在「GPU 要不要留缓冲」上抠,留的 30% 缓冲省下来的,是无数个深夜的告警。


CPU 时代的伸缩是「水涨船高」,GPU 时代的伸缩是「量体裁衣」——指标选错,扩得越多,死得越快。

下篇预告:第 9 周「云原生与 AI 工程化」收官,从下一篇 Day 64 起我们正式杀进大模型的内核——《后端工程师的大模型第一课:Transformer 通俗理解》。不需懂高数,我会用「图书馆检索」的直觉把 Self-Attention 讲明白,让你彻底搞懂这些天一直在调的大模型到底是怎么「看懂」一句话的。

往期回顾:

Day 62 - AI云服务企业级集成:百炼/混元/方舟平台接入对比。

Day 61 | Docker部署AI推理服务:Ollama + Open WebUI生产实践。

Day60-Serverless:函数计算改写传统Spring Boot。

Day59-阿里云ACK实战:生产级K8s集群部署与运维。

90篇JAVA高级深度分析与实践文章:做AI的主人,代码直接可用,附完整目录

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

ruwebframe再评估

ruwebframe 技术评估报告 基于对 Gitee 仓库 及关联项目 gowebframe3 的深度分析&#xff0c;以下是综合评估。 一、项目定位与背景 表格 维度详情名称ruwebframe语言Rust&#xff08;基于 actix-web&#xff09;作者leijmdas&#xff08;与 gowebframe3 同作者&#xff09;…

作者头像 李华
网站建设 2026/9/4 16:16:49

Kimi 进阶 LeetCode 29. 两数相除 Rust实现

LeetCode 29. 两数相除 - Rust 实现 核心思路 这道题要求不使用乘法、除法和取模运算符来实现整数除法。标准解法是倍增法&#xff08;位移模拟&#xff09;&#xff1a; 处理符号&#xff1a;记录结果的符号&#xff0c;将两个数转为同号处理倍增逼近&#xff1a;将除数不断左…

作者头像 李华
网站建设 2026/9/4 8:11:12

基于SpringBoot的养老服务平台设计与实现(毕业设计项目源码+文档)

温馨提示&#xff1a;本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;本人主页置顶文章(点我)开头有 CSDN 平台…

作者头像 李华
网站建设 2026/9/4 8:26:03

美团算法策略岗春招笔试复盘:从题型拆解到备战思路

2026年春招的号角比往年吹得更早&#xff0c;我投的是美团算法策略岗&#xff0c;收到第一批笔试通知的时候愣了一下——距离我在官网提交简历才过去四天。当天晚上和几个同批进笔试的同学聊了聊&#xff0c;发现大家都有类似感受&#xff1a;算法岗的筛人逻辑变了&#xff0c;…

作者头像 李华
网站建设 2026/9/4 8:25:29

从宇树机器人争议看模型预测控制(MPC)实战:Python实现倒立摆平衡

最近在机器人圈子里&#xff0c;一个话题讨论得挺热闹&#xff1a;宇树科技&#xff08;Unitree&#xff09;的机器人新品发布后&#xff0c;市场反馈似乎没有预想中那么热烈&#xff0c;甚至有声音说它“丢掉了王座”。作为长期关注机器人开发与实战的技术博主&#xff0c;我觉…

作者头像 李华
网站建设 2026/9/3 19:51:55

HTTP中URL,状态码,HTTPS证书加密

bit::Shadow✧(≖ ◡ ≖✿ 目录 浏览器服务器模式&#xff08;B/S模式&#xff09; URL https://news.qq.com/rain/a/20260829A0D52600?adChannelIdnews分析其构成 从IP到域名的历程 幕后机制&#xff1a;DNS&#xff08;域名系统&#xff09;自动转换 URL实例 状态码 …

作者头像 李华