⚠️阅读建议本文假定你已有基础K8s和GPU调度经验本文在此基础上深入KEDA事件驱动扩缩容。如果你还没看过本系列的第3、4篇建议先去补一下GPU调度和MIG共享的基础。目录一、凌晨三点我盯着GPU账单沉默了二、先搞清楚HPA为什么搞不定AI场景2.1 三个翻车现场2.2 AI服务的弹性特征三、KEDA核心概念三张牌打天下3.1 ScaledObject持续运行服务的扩缩容3.2 ScaledJob一次性批处理任务3.3 Scaler50种事件源的通用接口四、AI场景三大Scaler你的业务该用哪个4.1 Prometheus Scaler为推理服务质量而生4.2 Kafka Scaler异步推理和训练的最佳拍档4.3 Cron Scaler省钱利器定时缩容五、KEDA vs HPA一张表终结选择困难六、实战一Prometheus驱动GPU推理服务自动扩缩6.1 安装KEDA6.2 部署vLLM推理服务带GPU6.3 配置Prometheus Scaler的ScaledObject6.4 验证扩缩容效果七、实战二Kafka消息积压触发批处理训练ScaledJob7.1 场景描述7.2 Kafka Topic设计7.3 ScaledJob完整配置7.4 训练Job的业务逻辑八、实战三Cron定时缩容——凌晨没流量的GPU白烧钱8.1 精细化时间表配置九、缩容到0与冷启动优化的三大策略策略一镜像预热 模型缓存策略二调整 cooldownPeriod 和 pollingInterval策略三预扩缓冲池Warm Pool十、KEDA GPU节点池联动省钱闭环10.1 架构联动全景10.2 GPU节点池配置10.3 配好后的效果一、凌晨三点我盯着GPU账单沉默了你是否遇到过这种场景凌晨三点线上推理流量几乎为零但GPU集群的8张A100还在全速运转——风扇嗡嗡响电费哗哗流而你的HPA因为CPU/内存指标看起来正常稳如泰山地维持着10个Pod在线。更扎心的是另一头——白天高峰期推理请求把服务打到响应超时HPA吭哧吭哧花了3分钟才从10个Pod扩到15个而流量已经在第2分钟把你所有请求队列塞爆了。HPA不是不好是它压根没想过AI服务的特殊性。网上搜到的HPA教程要么用CPU利用率做例子对GPU服务基本无效要么教你在YAML里写一堆metrics-server的配置KEDA一行配置解决。本文将从原理到实战给你一套生产级的AI服务事件驱动扩缩容方案包含完整YAML、Prometheus/Kafka/Cron三大Scaler配置以及缩容到0的冷启动优化指南。这篇文章的终极目标让你白天扛得住流量洪峰夜里不白烧GPU——一张A100月租一万五缩容到0省下的电费够你吃一年海底捞。二、先搞清楚HPA为什么搞不定AI场景2.1 三个翻车现场翻车一GPU推理服务的指标错位标准的HPA盯着CPU和内存。但一个vLLM推理服务CPU可能稳定在15%而GPU利用率已经95%了——HPA觉得一切正常不会扩容。用户那边已经排队排到骂人了。# 典型HPA配置——对GPU服务形同虚设 apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: inference-hpa spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: llm-inference minReplicas: 2 maxReplicas: 10 metrics: - type: Resource resource: name: cpu # ❌ GPU跑满了CPU还闲着呢 target: type: Utilization averageUtilization: 60 - type: Resource resource: name: memory # ❌ 推理服务内存基本恒定 target: type: Utilization averageUtilization: 70翻车二批处理训练的完就走困境你提交了100个训练任务到消息队列。每个任务跑8分钟吃一张GPU。HPA能做到的是看CPU高了就扩几个Pod。但它不知道怎么数队列里的消息还剩多少。结果要么扩得太多要么扩得不够。翻车三夜间服务还在空转这是最扎心的场景。凌晨2:00到早上8:00推理流量趋近于零。但服务跑在K8s上HPA设置minReplicas2两张GPU通宵亮着。每张A100每小时5美元云上价格6个小时就是60美元。一个月30天1800美元打了水漂。2.2 AI服务的弹性特征AI工作负载和传统Web服务在弹性需求上有本质区别特征传统Web服务AI推理服务AI批处理训练流量模式相对平稳日间高峰高度间歇突发尖刺事件驱动队列堆积单请求耗时毫秒级百毫秒-秒级分钟-小时级资源瓶颈CPU/内存GPU利用率 请求队列GPU显存 队列深度冷启动时间秒级10-60秒模型加载分钟级数据加载能否缩容到0可以但很少做强烈需要必须做扩缩容指标CPU/Mem利用率请求延迟/队列深度消息积压/队列长度⚠️关键认知AI服务不适合以资源定规模适合以需求定规模。有多少请求就启多少Pod没请求就停掉。KEDA设计哲学就是围绕这一点——事件驱动而非指标驱动。三、KEDA核心概念三张牌打天下KEDAKubernetes Event-driven Autoscaling是一个CNCF毕业项目本质上是一个轻量级的事件驱动的自动扩缩容组件。它的核心逻辑就一条监听外部事件源 → 翻译成扩缩容指令 → 操控HPA或Job完成扩缩整个KEDA架构可以用一张图看懂graph TB subgraph External[外部事件源] E1[Prometheusbr/查询延迟指标] E2[Kafkabr/消费组Lag] E3[Cronbr/定时触发] E4[RabbitMQbr/队列深度] E5[Redisbr/List长度] end subgraph KEDA[KEDA Core] K1[Scalerbr/事件适配器br/------------br/50种内置Scaler] K2[Metrics Serverbr/gRPC指标暴露] K3[Operator/Controllerbr/ScaledObject/ScaledJob控制器] end subgraph Target[扩缩目标] T1[Deploymentbr/StatefulSetbr/任意Scale子资源] T2[Jobbr/一次性批处理] end E1 -- K1 E2 -- K1 E3 -- K1 E4 -- K1 E5 -- K1 K1 -- K2 K2 -- K3 K3 --|操控HPAbr/增删副本| T1 K3 --|创建/删除Job| T2 style K1 fill:#339af0,color:#fff style K2 fill:#339af0,color:#fff style K3 fill:#339af0,color:#fff style T1 fill:#51cf66,color:#fff style T2 fill:#fcc419,color:#3333.1 ScaledObject持续运行服务的扩缩容ScaledObject是KEDA最常用的CRD用于长期运行的服务Deployment、StatefulSet。一句话理解它定义一个事件触发器和扩缩边界然后KEDA自动帮你操控HPA。核心字段三要素apiVersion: keda.sh/v1alpha1 kind: ScaledObject metadata: name: inference-scaler spec: scaleTargetRef: name: llm-inference # 要扩缩的Deployment名称 pollingInterval: 15 # 每15秒检查一次事件源 cooldownPeriod: 300 # 缩容冷静期5分钟 minReplicaCount: 0 # 可以缩容到0 maxReplicaCount: 20 # 最多20个Pod triggers: # 触发器列表可以有多个 - type: prometheus # Scaler类型 metadata: # Scaler特定参数 serverAddress: http://prometheus.monitoring.svc:9090 metricName: http_requests_per_second query: | sum(rate( istio_requests_total{ destination_service_namellm-inference }[2m] )) threshold: 100 # 每个Pod处理100请求/秒关键设计注意minReplicaCount: 0。这是HPA做不到的事情——HPA的minReplicas不能设为0因为metrics-server在Pod不存在时拿不到指标形成死循环。KEDA通过自己维护指标绕过了这个问题。3.2 ScaledJob一次性批处理任务ScaledJob用于事件驱动的批处理——每个事件创建一个Job实例。一句话理解Kafka来一条消息就创建一个Job去处理处理完就销毁不占资源。apiVersion: keda.sh/v1alpha1 kind: ScaledJob metadata: name: training-job-scaler spec: jobTargetRef: parallelism: 1 completions: 1 template: spec: containers: - name: trainer image: myrepo/training:latest resources: limits: nvidia.com/gpu: 1 restartPolicy: Never pollingInterval: 10 successfulJobsHistoryLimit: 5 failedJobsHistoryLimit: 10 maxReplicaCount: 8 # 最多同时跑8个训练Job triggers: - type: kafka metadata: bootstrapServers: kafka-broker:9092 consumerGroup: training-consumer topic: training-requests lagThreshold: 1 # 每积压1条消息就启动一个Job⚠️ScaledObject vs ScaledJob 选择决策你的任务是长期运行的服务API、推理→ 用ScaledObject你的任务是跑完就结束的批处理训练、数据清洗→ 用ScaledJob搞反了会出事故ScaledJob在任务完成后不会自动缩容副本它会不停创建新Job直到队列清空3.3 Scaler50种事件源的通用接口KEDA的Scaler体系是它最强大的地方。截止2026年KEDA支持50种内置Scaler覆盖了几乎所有你能想到的外部事件源。对于AI场景以下是最重要的几个Scaler触发条件AI适用场景Prometheus任意PromQL查询结果推理延迟、GPU利用率、请求QPSKafka消费组Lag异步推理队列、训练任务队列Cron定时规则cron表达式夜间缩容、预扩预热RedisList/Stream长度轻量推理缓存、预处理RabbitMQ队列深度企业内部异步推理NATS Streaming订阅堆积事件驱动推理管道四、AI场景三大Scaler你的业务该用哪个用一张图串联起AI推理和训练场景下的KEDA决策路径graph TD START{你的AI负载br/是什么类型} START --|在线推理br/同步API| A1[Prometheus Scalerbr/ ScaledObject] START --|异步推理br/消息队列| A2[Kafka/RabbitMQ Scalerbr/ ScaledObject] START --|批量训练br/离线任务| A3[Kafka Scalerbr/ ScaledJob] START --|定时预热br/夜间缩容| A4[Cron Scalerbr/ ScaledObject] A1 -- B1[监控指标请求延迟P99] A1 -- B2[监控指标GPU利用率] A1 -- B3[监控指标请求QPS] A2 -- C1[监控指标队列深度] A2 -- C2[监控指标消息积压数量] A3 -- D1[每积压N条消息] A3 -- D2[启动1个训练Job] A3 -- D3[训练完成自动销毁] A4 -- E1[工作日8:00预热] A4 -- E2[凌晨2:00缩容到0] B1 -- F[扩缩决策:br/P99500ms → 扩容br/P99100ms → 缩容] B2 -- F B3 -- F style START fill:#ff6b6b,color:#fff style A1 fill:#339af0,color:#fff style A2 fill:#339af0,color:#fff style A3 fill:#fcc419,color:#333 style A4 fill:#fcc419,color:#333 style F fill:#51cf66,color:#fff4.1 Prometheus Scaler为推理服务质量而生这是AI推理场景下最强大的Scaler因为PromQL可以表达任何你想要的扩缩容逻辑。一个实用PromQL示例——按GPU利用率加权扩缩# 每个Pod实际使用的GPU利用率百分比 ( sum by (pod) ( DCGM_FI_DEV_GPU_UTIL{pod~llm-inference-.*} ) ) / 100另一个实用PromQL——按请求队列深度扩缩# vLLM/Pod内积压的请求数 sum( vllm:num_requests_waiting{pod~llm-inference-.*} )实战建议不要用单一的GPU利用率指标。推荐用请求队列深度 GPU利用率做复合指标——任何一个超过阈值都触发扩容。KEDA的ScaledObject支持多个trigger它们之间是OR关系任一触发即扩容但不能做AND。如果需要AND关系你需要把逻辑写到PromQL里。4.2 Kafka Scaler异步推理和训练的最佳拍档Kafka在AI场景有两个经典模式模式一异步推理——用户提交推理请求到Kafka消费者从队列中取请求、跑推理、返回结果。KEDA根据消费组Lag自动扩缩消费者Pod。模式二训练任务调度——训练请求入队ScaledJob每积压N条消息起一个训练JobJob跑完自动销毁。Kafka Scaler的核心参数triggers: - type: kafka metadata: bootstrapServers: kafka-broker.default:9092 consumerGroup: inference-workers # 消费组名 topic: inference-queue # 监控的Topic lagThreshold: 5 # 每5条积压扩1个Pod activationLagThreshold: 2 # 积压≥2时从0扩到1 offsetResetPolicy: latest⚠️注意activationLagThreshold这是KEDA从0扩到1的触发阈值必须小于lagThreshold。如果设成一样可能出现一直在0和1之间震荡的问题。4.3 Cron Scaler省钱利器定时缩容Cron Scaler的用法最直观但最容易被低估triggers: - type: cron metadata: timezone: Asia/Shanghai start: 0 8 * * 1-5 # 工作日早8点预热 end: 0 2 * * 1-5 # 工作日凌晨2点缩容 desiredReplicas: 4⚠️Cron Scaler的一个坑它会叠加在其它trigger之上。如果你同时配了Cron和Prometheus triggerCron的逻辑是把目标副本数设为特定值不是上下限而Prometheus trigger是基于指标动态计算。两者同时生效时KEDA取较大的那个值。五、KEDA vs HPA一张表终结选择困难对比维度HPA (HorizontalPodAutoscaler)KEDA (ScaledObject)扩缩依据CPU/内存利用率内置任意外部事件PromQL、Kafka Lag、Redis长度…指标来源metrics-server有限50 Scaler 自定义缩容到0❌ 不支持✅ 原生支持冷启动N/A支持activation机制扩缩速度依赖metrics-server采集间隔15s-60s可自定义pollingInterval最小1sGPU感知❌ 无法直接使用GPU指标✅ PromQL查询DCGM指标批处理Job❌ 不支持✅ ScaledJob原生支持多触发器支持多个metrics取max支持多个triggers取max复杂度低内置中需安装KEDA Operator适用场景传统Web服务AI推理、流式处理、批处理、GPU服务一句话结论如果你只跑无状态Web APIHPA够用。如果你跑AI推理、GPU训练、流式处理——别纠结直接上KEDA。六、实战一Prometheus驱动GPU推理服务自动扩缩6.1 安装KEDA# Helm安装推荐 helm repo add kedacore https://kedacore.github.io/charts helm repo update helm install keda kedacore/keda \ --namespace keda \ --create-namespace \ --set prometheus.enabledtrue # 启用Prometheus Scaler支持 # 验证安装 kubectl get pods -n keda # NAME READY STATUS RESTARTS AGE # keda-operator-6d8f9c7b4-xxxxx 1/1 Running 0 30s # keda-metrics-apiserver-5d8f9c7b4-xxxxx 1/1 Running 0 30s6.2 部署vLLM推理服务带GPU先上推理Deployment——以vLLM部署Llama-3-8B为例apiVersion: apps/v1 kind: Deployment metadata: name: llm-inference labels: app: llm-inference spec: replicas: 1 selector: matchLabels: app: llm-inference template: metadata: labels: app: llm-inference annotations: prometheus.io/scrape: true prometheus.io/port: 8000 spec: # GPU节点亲和性配合上一篇文章的GPU调度策略 nodeSelector: nvidia.com/gpu.product: NVIDIA-A100-SXM4-40GB tolerations: - key: nvidia.com/gpu operator: Equal value: true effect: NoSchedule containers: - name: vllm image: vllm/vllm-openai:v0.5.4 args: - --model - meta-llama/Meta-Llama-3-8B-Instruct - --tensor-parallel-size - 1 - --max-model-len - 8192 - --gpu-memory-utilization - 0.90 ports: - containerPort: 8000 name: http resources: limits: nvidia.com/gpu: 1 # 每Pod一张A100 memory: 32Gi cpu: 8 requests: nvidia.com/gpu: 1 memory: 16Gi cpu: 4 readinessProbe: httpGet: path: /health port: 8000 initialDelaySeconds: 60 # ⚠️ 模型加载至少需要30-60秒 periodSeconds: 10 --- apiVersion: v1 kind: Service metadata: name: llm-inference spec: selector: app: llm-inference ports: - port: 8000 targetPort: 80006.3 配置Prometheus Scaler的ScaledObject接下来是核心——让KEDA根据GPU利用率请求QPS自动扩缩这个推理服务apiVersion: keda.sh/v1alpha1 kind: ScaledObject metadata: name: llm-inference-scaler namespace: default spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: llm-inference pollingInterval: 15 # 每15秒查询一次Prometheus cooldownPeriod: 300 # 缩容冷静期5分钟 minReplicaCount: 0 # 0流量时缩容到0 maxReplicaCount: 10 # 最多10个GPU Pod triggers: # 触发器1GPU利用率如果利用率70%扩容 - type: prometheus metadata: serverAddress: http://prometheus-k8s.monitoring.svc:9090 metricName: gpu_utilization threshold: 70 query: | avg( DCGM_FI_DEV_GPU_UTIL{ pod~llm-inference-.*, exported_namespacedefault } ) # 触发器2vLLM等待队列中的请求数 - type: prometheus metadata: serverAddress: http://prometheus-k8s.monitoring.svc:9090 metricName: vllm_waiting_requests threshold: 5 query: | sum( vllm:num_requests_waiting{ pod~llm-inference-.* } ) # 触发器3请求速率QPS - type: prometheus metadata: serverAddress: http://prometheus-k8s.monitoring.svc:9090 metricName: http_qps_per_pod threshold: 50 query: | sum( rate( istio_requests_total{ destination_service_namellm-inference, response_code!~5.* }[2m] ) ) / count( count by (pod) ( kube_pod_info{ pod~llm-inference-.* } ) ) # 触发器4Cron定时——凌晨2点强制缩容到0 - type: cron metadata: timezone: Asia/Shanghai start: 0 8 * * 1-5 # 工作日8:00恢复 end: 30 1 * * * # 每天1:30缩到0 desiredReplicas: 0 # ⚡ 触发器5Cron定时——工作日高峰期预热到4个实例 - type: cron metadata: timezone: Asia/Shanghai start: 30 7 * * 1-5 # 7:30预热 end: 59 18 * * 1-5 # 18:59结束 desiredReplicas: 46.4 验证扩缩容效果# 查看ScaledObject状态 kubectl get scaledobject llm-inference-scaler # NAME SCALETARGETKIND SCALETARGETNAME MIN MAX TRIGGERS READY # llm-inference-scaler apps/v1.Deployment llm-inference 0 10 5 True # 查看HPAKEDA自动创建的 kubectl get hpa # NAME REFERENCE TARGETS MINPODS MAXPODS # keda-hpa-llm-inference Deployment/llm-inference 35/70, 2/5, ... 0 10 # 模拟流量——用hey压测 hey -z 60s -c 100 -m POST \ -H Content-Type: application/json \ -d {model:llama-3-8b,messages:[{role:user,content:Hello}]} \ http://llm-inference.default:8000/v1/chat/completions # 观察自动扩容 kubectl get pods -l appllm-inference -w # llm-inference-7d8f9c-xxxxx 0/1 Pending 0 0s # llm-inference-7d8f9c-xxxxx 0/1 ContainerCreating 0 5s # llm-inference-7d8f9c-xxxxx 1/1 Running 0 65s ← 注意加载时间⚠️首次冷启动延迟从0到1的过程不是瞬间的。Pod创建5秒 镜像拉取已有缓存秒级 模型加载到GPU显存30-60秒 首次启动约45-70秒。后面会讲如何优化。七、实战二Kafka消息积压触发批处理训练ScaledJob7.1 场景描述场景一个AI平台用户提交模型微调请求到Kafka队列。每个请求包含训练数据路径、超参数、模型类型。KEDA监听队列积压自动为每个请求创建一个训练Job。7.2 Kafka Topic设计# 创建训练任务Topic kafka-topics.sh --create \ --bootstrap-server kafka-broker:9092 \ --topic training-tasks \ --partitions 10 \ --replication-factor 3 # 训练任务消息格式 # {taskId:uuid,modelType:llama-7b,dataPath:s3://data/finetune/2026,epochs:3,lora:true}7.3 ScaledJob完整配置apiVersion: keda.sh/v1alpha1 kind: ScaledJob metadata: name: training-job-scaler namespace: ai-training spec: pollingInterval: 10 successfulJobsHistoryLimit: 5 # 保留5个成功Job历史 failedJobsHistoryLimit: 10 # 保留10个失败Job历史方便排查 maxReplicaCount: 8 # 最多同时8个训练Job对应8张GPU rolloutStrategy: gradual # 逐步创建Job避免GPU节点瞬间被打满 jobTargetRef: parallelism: 1 # 每个Job只用1个GPU completions: 1 backoffLimit: 2 # 失败重试2次 ttlSecondsAfterFinished: 3600 # 完成后1小时自动清理Job template: metadata: labels: type: finetune-job spec: # GPU亲和性 nodeSelector: nvidia.com/gpu.product: NVIDIA-A100-SXM4-40GB tolerations: - key: nvidia.com/gpu operator: Equal value: true effect: NoSchedule containers: - name: trainer image: myrepo/llm-finetune:v2.1 env: - name: KAFKA_MESSAGE valueFrom: fieldRef: fieldPath: metadata.annotations[keda.sh/kafkaMessage] # ↑ KEDA自动把触发Job的Kafka消息内容注入环境变量 resources: limits: nvidia.com/gpu: 1 memory: 80Gi cpu: 16 requests: nvidia.com/gpu: 1 memory: 64Gi cpu: 8 volumeMounts: - name: training-data mountPath: /data - name: model-output mountPath: /output volumes: - name: training-data persistentVolumeClaim: claimName: training-data-pvc - name: model-output persistentVolumeClaim: claimName: model-output-pvc restartPolicy: Never triggers: - type: kafka metadata: bootstrapServers: kafka-broker.ai-training:9092 consumerGroup: training-consumers topic: training-tasks lagThreshold: 1 # 积压1条消息就启动1个Job activationLagThreshold: 1 offsetResetPolicy: latest # ⚠️ ScaledJob场景中KEDA为每个Job创建独立的消费者不共享consumer group7.4 训练Job的业务逻辑训练容器需要做三件事# trainer.py - 训练Job入口伪代码 import os, json, subprocess def main(): # 1. 从环境变量读取Kafka消息内容 kafka_msg json.loads(os.environ[KAFKA_MESSAGE]) task_id kafka_msg[taskId] data_path kafka_msg[dataPath] epochs kafka_msg.get(epochs, 3) # 2. 下载训练数据 subprocess.run([aws, s3, sync, data_path, /data/]) # 3. 执行训练 subprocess.run([ torchrun, --nproc_per_node1, finetune.py, --model, kafka_msg[modelType], --data, /data/, --output, f/output/{task_id}, --epochs, str(epochs), --lora if kafka_msg.get(lora) else ]) # 4. 上传模型产物 subprocess.run([aws, s3, sync, f/output/{task_id}/, fs3://models/output/{task_id}/]) if __name__ __main__: main()关于rolloutStrategyKEDA支持default一口气创建所有Job和gradual逐步创建每秒最多创建N个。对于GPU训练场景强烈建议用gradual——同时拉起8个训练Job会让GPU节点瞬间OOM而且kubelet可能一次分配不了那么多GPU设备。八、实战三Cron定时缩容——凌晨没流量的GPU白烧钱前面ScaledObject里已经内嵌了Cron trigger这里单独拿出来做一个更精细的配置示例。8.1 精细化时间表配置apiVersion: keda.sh/v1alpha1 kind: ScaledObject metadata: name: inference-cron-schedule spec: scaleTargetRef: name: llm-inference minReplicaCount: 0 maxReplicaCount: 10 triggers: # 工作日早晨预扩——提前30分钟避开冷启动延迟 - type: cron metadata: timezone: Asia/Shanghai start: 30 7 * * 1-5 end: 0 9 * * 1-5 desiredReplicas: 3 # 午间流量高峰——午餐时间刷AI的人最多 - type: cron metadata: timezone: Asia/Shanghai start: 0 11 * * 1-5 end: 30 13 * * 1-5 desiredReplicas: 6 # 晚间省钱模式 - type: cron metadata: timezone: Asia/Shanghai start: 0 22 * * * end: 0 2 * * * desiredReplicas: 1 # 深度睡眠——凌晨2点到早上7点 - type: cron metadata: timezone: Asia/Shanghai start: 0 2 * * * end: 30 7 * * 1-5 desiredReplicas: 0 # 周末——大部分人不上班只保留最低保障 - type: cron metadata: timezone: Asia/Shanghai start: 0 9 * * 0,6 end: 0 22 * * 0,6 desiredReplicas: 1⚠️Cron Scaler的优先级规则如果某个时间段有多个Cron trigger同时生效KEDA取desiredReplicas最大值。这意味着如果你在午间同时有desiredReplicas: 6和 Prometheus trigger算出需要8个——最终会扩到8个Prometheus的值更大。九、缩容到0与冷启动优化的三大策略缩容到0是KEDA的杀手级特性但从0到1的冷启动延迟是个现实问题。以下是生产级的三层优化方案。策略一镜像预热 模型缓存# 在所有GPU节点上预先拉取模型镜像 apiVersion: apps/v1 kind: DaemonSet metadata: name: image-prewarmer spec: selector: matchLabels: app: image-prewarmer template: metadata: labels: app: image-prewarmer spec: nodeSelector: nvidia.com/gpu.present: true initContainers: - name: pull-images image: busybox command: [sh, -c, echo Image prewarming DaemonSet ensures images are cached on GPU nodes] containers: - name: sleep image: busybox command: [sleep, infinity]模型本身可以挂载PVC或利用节点本地SSD缓存避免每次启动都重新下载几十GB的模型文件# 使用hostPath将模型缓存到节点本地NVMe volumes: - name: model-cache hostPath: path: /mnt/nvme/models/llama-3-8b type: DirectoryOrCreate策略二调整cooldownPeriod和pollingIntervalspec: pollingInterval: 10 # 更快响应流量变化默认30s cooldownPeriod: 180 # 缩容冷静期降到3分钟默认300s # 高级配置防止频繁震荡 advanced: horizontalPodAutoscalerConfig: behavior: scaleDown: stabilizationWindowSeconds: 120 # 缩容稳定窗口2分钟 policies: - type: Percent value: 50 # 每次最多缩50% periodSeconds: 60 scaleUp: stabilizationWindowSeconds: 0 # 扩容窗口0秒立即响应 policies: - type: Percent value: 100 # 每次最多翻倍 periodSeconds: 15 - type: Pods value: 4 # 或每次最多加4个Pod periodSeconds: 15 selectPolicy: Max # 取两者中较大的策略三预扩缓冲池Warm Pool对于延迟敏感的推理服务完全缩容到0可能不现实。可以保持1个温Pod兜底spec: minReplicaCount: 1 # 永远保持1个Pod在线 # 但这个Pod不需要是最小规格的——可以是一个低配模型 idleReplicaCount: 1 # KEDA 2.14 支持终极方案分层冷启动——高配A100跑热门模型常驻1个Pod低配T4跑冷门模型按需从0起。通过Istio/Gateway API做流量路由根据模型ID转发到不同backend。十、KEDA GPU节点池联动省钱闭环KEDA负责Pod层级的扩缩容但如果Pod扩了、节点没地方放还是白搭。这时候需要节点层弹性——让KEDA和Cluster Autoscaler或Karpenter联动。10.1 架构联动全景# 1. KEDA ScaledObjectPod从0扩到10 # 2. Pending Pod触发Cluster Autoscaler节点从2台扩到5台 # 3. 新节点加入集群后Pending Pod调度成功 # 4. 流量过后KEDA缩容Pod到0 # 5. 空节点被Cluster Autoscaler回收10.2 GPU节点池配置# Karpenter NodePool推荐比Cluster Autoscaler更快 apiVersion: karpenter.sh/v1beta1 kind: NodePool metadata: name: gpu-inference-pool spec: template: spec: requirements: - key: karpenter.sh/capacity-type operator: In values: [spot] # 推理用Spot实例省钱 - key: node.kubernetes.io/instance-type operator: In values: - g5.2xlarge # 1x A10G - g5.4xlarge # 1x A10G 更多CPU - p3.2xlarge # 1x V100 - key: nvidia.com/gpu.present operator: In values: [true] # GPU节点专用Taint——防止CPU Pod抢占 taints: - key: nvidia.com/gpu value: true effect: NoSchedule # 节点启动后自动打标签 startupTaints: - key: nvidia.com/gpu-startup value: true effect: NoSchedule # 等Device Plugin上报GPU后再接受Pod nodeClassRef: name: gpu-default # KEDA缩容后节点自动回收 consolidation: enabled: true disruption: consolidationPolicy: WhenUnderutilized consolidateAfter: 5m # 空节点5分钟后回收 budgets: - nodes: 20% # 最多同时回收20%的节点10.3 配好后的效果# 凌晨2:000个推理Pod0个GPU节点 kubectl get nodes -l nvidia.com/gpu.presenttrue # No resources found # 早上8:00Cron预热触发KEDA扩到4个Pod # → Pending触发Karpenter启动2台g5.4xlarge # → 60秒后Pod全部Running # 中午12:00Prometheus检测到GPU利用率飙升 # → KEDA扩到8个Pod # → Karpenter再启动2台g5.4xlarge # 凌晨2:00Cron缩容到0 # → 节点空闲5分钟后被Karpenter回收 # → 0个GPU Pod0个GPU节点0美元开销这是KEDAGPU节点池的内核Pod扩缩容KEDA→ Pending触发节点扩缩容Karpenter→ Pod缩到0后节点自动回收。三个组件各司其职形成一个完整的按需付费闭环。十一、总结与系列预告核心要点回顾KEDA解决的不是怎么扩缩容的问题——HPA也能。KEDA解决的是该不该扩缩的问题——当你的决策依据是PromQL查询结果、Kafka消息积压、GPU利用率这些外部信号时HPA无能为力。三个必须记住的数字50KEDA内置Scaler数量覆盖几乎所有事件源0KEDA独有的缩容到0能力AI场景的省钱根基60秒GPU推理服务冷启动的典型延迟通过预热和缓存可降到15秒三个最容易踩的坑❌ 把GPU推理服务配成CPU/内存指标的HPA → 永远扩不上去❌ ScaledJob的lagThreshold设太大 → 队列越积越长永远追不上❌ 冷启动时没有设置足够的initialDelaySeconds → Pod被kubelet反复kill 三件套【源码获取】本文所有YAML配置的完整仓库关注本系列后在公众号后台回复「KEDA实战」获取一键部署脚本。【思考题】如果你的推理服务支撑100个不同模型Prometheus Scaler需要监控100个模型的延迟指标。如何设计一个通用的扩缩容策略避免为每个模型创建单独的ScaledObject欢迎在评论区留下你的方案——我会在下篇文章中分析最佳实践。【系列文章预告】下一篇《NVIDIA MIG深度实战——A100/H100的物理分区与K8s集成》将深入MIG 7种profile的物理切分配置K8s Device Plugin MIG策略MIG vs Time Slicing vs HAMi 终极对比生产环境MIG监控与故障排查觉得有用的话三连支持一下标签KEDA、事件驱动、弹性伸缩、AI推理、GPU、Kubernetes、HPA