拓冰建站拓冰建站
首页 / 资讯中心 / 正文

AI Infra实战05:用Helm在K8s中部署vLLM,从安装到压测全流程

AI Infra实战05用Helm在K8s中部署vLLM,从安装到压测全流程本篇配套的全部脚本、YAML、Helm Chart、预检工具和 Terraform 配置已开源在 GitHub:https://github.com/Jich1123/gpu-k8s-lab 。clone 下来按00-lab/execution-checklist.md的顺序执行即可复现全部实验(仓库不含任何真实 IP、密钥或账号信息)。本篇目标用自己编写的 Helm Chart 在 K8s 中部署 vLLM 推理服务,跑通 OpenAI 兼容 API,用 2500 次请求压测拿到真实性能数据,并在 Grafana 上看到 GPU 从空闲到满载的完整曲线。学完本篇你将掌握:如何把 vLLM 封装成 Helm Chart(Deployment、Service、PVC、ServiceMonitor)单 GPU 环境下的更新策略选择(Recreate vs RollingUpdate)T4 上 vLLM 的真实性能表现(吞吐、延迟分布、显存分配)压测方法和结果解读T4 的架构限制(V0 引擎回退、XFormers 替代 FlashAttention)证据等级:B级实验复现。本篇所有命令、输出和性能数据来自一次真实执行(AWS g4dn.xlarge / T4),压测 2500 请求零失败。实操环境配置值平台AWS EC2 g4dn.xlargeGPUTesla T4(16GB显存)系统Ubuntu 22.04.5 LTSk3sv1.34.10k3s1GPU Operator已部署(实验01)监控kube-prometheus-stack 已部署(实验02)vLLMv0.10.1.1模型Qwen/Qwen2-1.5B-Instruct费用约 $0.526/小时,本篇实操约 $0.5(含模型下载和压测)一、为什么用 Helm 部署,而不是直接 kubectl apply前面第03篇(vLLM部署实战)是在裸机上直接跑vllm serve,适合快速验证。但在 K8s 环境中,你需要:Deployment(管理 Pod 生命周期、重启策略)Service(提供稳定的集群内访问入口)PVC(模型缓存持久化,避免每次重启都重新下载几个 GB 的模型)ServiceMonitor(让 Prometheus 自动发现 vLLM 暴露的 /metrics)可配置的 values.yaml(不同环境改参数,不改模板)这些打包成一个 Helm Chart,一条helm install全部部署,一条helm uninstall全部清理。二、Chart 核心设计完整 Chart 在配套资料的05-vllm-helm/vllm-chart/中,这里说几个关键设计决策。更新策略:Recreatestrategy:type:Recreate单 GPU 环境必须用 Recreate。因为nvidia.com/gpu: 1是独占资源,如果用 RollingUpdate,新 Pod 和旧 Pod 同时申请同一张 GPU,新 Pod 会一直 Pending。Recreate 先杀旧再起新,代价是升级时有短暂中断,但避免了 GPU 争抢死锁。模型缓存 PVCvolumes:-name:model-cachepersistentVolumeClaim:claimName:vllm-vllm20Gi PVC 挂载到/root/.cache/huggingface,模型权重下载一次就缓存住。Pod 被删重建后,PVC 还在,不用重新下载。GPU 资源申请resources:requests:nvidia.com/gpu:1limits:nvidia.com/gpu:1requests 和 limits 都写 1,确保 Pod 独占这张 T4。启动命令与参数Deployment 模板里,容器的启动命令就是vllm serve,后面跟一串通过 values 注入的参数:command:[vllm,serve,Qwen/Qwen2-1.5B-Instruct]args:---host-0.0.0.0---port-8000---dtype-float16---max-model-len-4096---gpu-memory-utilization-0.90---max-num-seqs-64---tensor-parallel-size-1几个和 T4 相关的关键参数:--dtype float16:T4 不支持 bfloat16 的高效计算,用 float16。vLLM 启动时会自动把模型的 bfloat16 权重转成 float16。--gpu-memory-utilization 0.90:让 vLLM 用 90% 的显存(剩 10% 给系统和碎片)。这个值直接决定 KV Cache 能拿多少显存,进而决定最大并发。--max-model-len 4096:单请求最大 token 数,越大越占显存。--tensor-parallel-size 1:单卡,不做张量并行。多卡才需要调大。三个探针模板给容器配了 startup / readiness / liveness 三个探针,都打/health:startupProbe:{httpGet:{path:/health,port:http},failureThreshold:180}readinessProbe:{httpGet:{path:/health,port:http}}livenessProbe:{httpGet:{path:/health,port:http}}startupProbe 的 failureThreshold 给得很大(180)是刻意的:vLLM 首次启动要下载模型 加载权重 初始化引擎,可能好几分钟。startup 探针没过之前,readiness 和 liveness 不生效,避免容器还在加载模型就被 liveness 判定失败重启,陷入启动-被杀-重启的死循环。这是部署大模型推理服务的一个通用技巧。三、部署bashinstall.sh关键输出:Release vllm does not exist. Installing it now. STATUS: deployed NAMESPACE: ai-inferencePod 启动过程安装后 Pod 经历Pending → ContainerCreating → Running三个阶段:ContainerCreating(约4分钟):拉取 vLLM 镜像(vllm/vllm-openai:v0.10.1.1,较大)Running 但 0/1(约1-2分钟):容器启动了但 readiness 探针还没过,vLLM 在下载模型和初始化引擎Running 1/1:服务就绪vLLM 启动日志中的关键信息模型加载成功后,日志里有一段非常有价值的显存分配信息:total_gpu_memory (14.56GiB) x gpu_memory_utilization (0.90) 13.11GiB model weights take 2.89GiB non_torch_memory takes 0.05GiB PyTorch activation peak memory takes 0.48GiB the rest of the memory reserved for KV Cache is 9.70GiB Maximum concurrency for 4096 tokens per request: 88.64x这段话翻译一下:T4 总显存 14.56 GiB,vLLM 用了 90% 13.11 GiB其中模型权重 2.89 GiB(Qwen2-1.5B 很小)KV Cache 拿到了 9.70 GiB,这是 vLLM 高并发的关键:它用 PagedAttention 动态分配 KV Cache,这 9.7G 能同时服务 88 个并发请求(4096 tokens/请求)T4 上的特殊行为:T4 是 Turing 架构(Compute Capability 7.5),vLLM 的 V1 引擎不支持,自动回退到 V0 引擎;同时 FlashAttention-2 不支持 Turing,改用 XFormers 后端。这不影响正确性,但性能不如 A100/H100 上的 V1 FlashAttention-2。这是 T4 上的固有行为,文章里应该说清楚,避免读者用 T4 复现时困惑。四、验证:API 真的能推理bashscripts/verify.sh查询模型{data:[{id:qwen2-1.5b-instruct,object:model,max_model_len:4096}]}真实对话问:什么是 Kubernetes?{choices:[{message:{content:Kubernetes是一个开源的容器编排和自动化管理系统用于管理和部署容器化应用程序。},finish_reason:stop}],usage:{prompt_tokens:26,completion_tokens:20,total_tokens:46}}推理成功,finish_reason 是 stop(自然结束,不是截断)。Helm TestTEST SUITE: vllm-vllm-test-connection Phase: Succeeded五、压测:2500 请求的真实性能数据这是本篇最有价值的部分。用配套的 benchmark.sh,发 2500 次相同请求(单轮对话,max_tokens 50),并发 10:CONCURRENCY10REQUESTS2500bashscripts/benchmark.sh脚本的压测逻辑不复杂,核心是用后台进程控制并发:每发一个请求就放到后台,当在跑的请求数达到CONCURRENCY上限时就等一个完成再发下一个,始终维持固定并发。每个请求用curl -w %{http_code} %{time_total}记录状态码和耗时,最后用 Python 算出 P50/P95/P99。它不依赖任何压测框架,一个 bash 脚本就能拿到可信的延迟分布,方便在任何机器上复现。压测结果总请求: 2500 成功: 2500 失败: 0 总耗时: 156730 ms 吞吐量: 15.95 req/s 平均延迟: 0.576 s P50延迟: 0.574 s P95延迟: 0.593 s P99延迟: 0.609 s怎么解读这些数据零失败:2500 个请求全部 HTTP 200,vLLM 在持续负载下没有崩溃、超时或拒绝。P50 和 P99 只差 35ms(0.574 vs 0.609):延迟分布极其集中,几乎没有长尾。这正是 vLLM Continuous Batching 的价值:它把多个请求合并到一个 GPU batch 里一起算,每个请求的延迟不会因为排在别人后面而差很多。吞吐 15.95 req/s:对 T4 1.5B 模型来说合理。如果换成更大的模型(7B/14B),吞吐会下降,延迟会上升。压测期间的 GPU 状态压测过程中用nvidia-smi抓到的实时数据:时间点GPU-Util功耗温度显存压测前0%14W32C0 用压测中(峰值)92%70W(满额)50C13409 MiB压测后0%27W40C13409 MiB(常驻)注意:压测结束后 GPU-Util 回到 0%,但显存不释放(13409 MiB),因为 vLLM 的模型权重和 KV Cache 预留是常驻的。这是正常行为,不是内存泄漏。Grafana 对比(配合实验02)在实验02的 Grafana 面板上,压测前后的对比截图清晰记录了 GPU 从空闲到满载再回落的完整过程。利用率从 0% 跳到 92%,功耗从 14W 顶到 71W(T4 TDP 上限),温度升了 19 度。详细对比图见实验02。六、升级与回滚配套脚本包含了 Recreate 策略下的升级回滚验证:bashscripts/upgrade-rollback.sh因为策略是 Recreate,升级时会:杀掉旧 Pod启动新 Pod(重新加载模型)中间有约1-2分钟的中断这是单 GPU 环境的固有代价。生产环境如果有多张 GPU,可以改用 RollingUpdate,新旧 Pod 各占一张卡,实现无中断升级。七、踩过的坑小结坑现象处理T4 回退 V0 引擎启动日志 WARNING: Compute Capability 8.0T4 是 Turing(7.5),V1 不支持,自动回退 V0,正常FlashAttention-2 不可用Cannot use FlashAttention-2 for Turing改用 XFormers 后端,不影响正确性ContainerCreating 很久Pod 卡在 ContainerCreating 4分钟镜像大,正常;如果更久检查网络和镜像源显存压测后不释放压测结束显存仍 13.4GBvLLM 模型KV Cache 常驻,正常行为小结这一篇完成了从裸机跑 vLLM到K8s 里用 Helm 管理 vLLM的跨越。Helm Chart 让部署可重复、可配置、可回滚;压测数据让你对 T4 上的 vLLM 性能有了真实的数字感;监控联动让你能看到推理负载下 GPU 的真实状态。到这里,GPU 基础(01)、监控(02)、推理服务(05)三篇都跑通了。剩下的实验07(KServe)会在下次 GPU 实例上完成,届时你将看到 Serverless 推理、自动扩缩容和灰度发布的真实验证。参考链接本系列开源仓库(脚本 Helm Chart Terraform):https://github.com/Jich1123/gpu-k8s-labvLLM 官方文档:https://docs.vllm.ai/en/latest/Helm 官方文档:https://helm.sh/docs/Kubernetes 探针配置(startup/readiness/liveness):https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门