1. 多了两块 NVIDIA 卡,为什么运维分分钟想离职?
1.1 大模型服务不是“一个 web 服务”,而是一台持续印钞的机器
做了七八年传统运维的人,第一次接触大模型部署时,普遍会有一个错觉:这不就是在那台装了 Linux 的 GPU 服务器上跑个 Python 服务吗?端口一开,Nginx 一代理,监控一挂,完事了。我当初也是这么想的,直到真正把七个一代的 Llama 模型服务接到内部业务,才发现自己踩进了一个完全不同的运行模式。
传统服务,核心资源是 CPU、内存、磁盘 IO,延迟以毫秒计,一台机器上能塞几十个微服务。大模型服务不一样,它把“算力”当成了主料,每生成一个 token 都要做一整次 Transformer 推理。GPU 是生产工具,也是成本中心,相当于车间里开着一台二十四小时不能停的印刷机,每开机一秒都在烧钱。运维的职责首先要从“保证在线率”变成“保证这张卡别闲着,更别被玩坏”。
模型推理还有明显的“并发放大器”效应:一个用户问了一个复杂的推理题,生成了两千个 token,这期间 GPU 显存、算力、带宽全被占用,相当于一个人占了三张打印纸,后边排队的人只能干等。这就引出了大模型运维里最重要的思维转变——你管理的不再是请求的“并发数”,而是请求的“排队深度”。
1.2 传统运维指标在大模型面前基本“失效”
同样是一次“200 OK”,传统服务里意味着请求被快速处理完,在大模型服务里只代表“我接下了这个生成任务”,真正的响应时间可能是 2 秒,也可能是 30 秒。所以沿用 CPU 使用率、QPS、平均响应时间那套体系来监控大模型,会得到大量误导性数据。
我见过最典型的例子:某台显卡的利用率长期只有 15%,按照传统认知,这是“资源严重闲置,可以再压业务量”。但实际情况是,模型已经通过批处理把所有请求合并进了一个批次,GPU 的显存和计算单元并没有空转,而是被少量大请求占满。如果你在这个状态下去加大并发,看到的不是吞吐量上升,而是 Time To First Token(首 token 延迟)从几百毫秒飙升到几千毫秒。
所以大模型运维的指标体系,至少要换成三层:
- 资源层:GPU 利用率、显存占用率、核心温度、功耗;
- 服务层:QPS、排队长度、TTFT、TPOT(每输出一个 token 的时间)、生成吞吐量(tokens/s);
- 业务层:模型版本、Prompt 的 Token 长度分布、输出 Token 长度分布、错误类型分布。
后面我会逐个展开这些参数怎么用,这里先记住一句话:在大模型运维里,不要拿“平均响应时间”做核心指标,要拿“分位延迟”和“排队深度”做核心指标。
1.3 大模型运维要管的第一个东西不是模型,而是“排队”
很多刚上手的人在模型刚部署成功后特别兴奋,赶紧拿 Postman 发一个请求,哇,出字了,好快。但一到生产,几十个人同时用,立刻卡顿。原因很简单:推理服务的吞吐量不是线性的,它有一个显眼的“拐点”。
大模型的推理是顺序生成的,第 n 个 token 没出来之前,第 n+1 个 token 没法算。单个请求快,不代表并发也快。vLLM 这类框架引入了 continuous batching,把新请求动态插入到正在生成的批次里,尽量填满 GPU 的空闲槽位,这就是为什么它能大幅提升吞吐。但批处理也有天花板——当请求队列长度超过显存能承载的最大 KV Cache 空间,新请求就只能排队等待,服务整体的“最大并发数”就被显存硬性卡住了。
运维必须提前做一道算术题:单张卡的可用显存、KV Cache 预留比例、单请求平均 token 数,决定这台机器能同时在线的“逻辑并发数”。再结合实际请求到达速率和生成速率,算出大概的排队延迟。记住这个公式式的思路:你的系统瓶颈往往不是“每秒能收多少请求”,而是“每秒能吐多少 token”,而每个用户的体感又是一条贪心蛇,总想一口气吃完整段回答。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 花二三十万私有化部署,到底买了个什么回来?
2.1 这笔账不是显卡钱,是整套机房配套
网上经常有人讨论“本地花二三十万买硬件部署大模型会怎样”。我可以负责任地说,这个预算在当下只能配出“勉强能用”的入门级生产环境。别急着算 GPU 价格,先把整台机器的配套账算清:一块 80G 显存的卡,零售价就需要数万元,配套的 CPU、内存、NVMe 固态、双电源、散热套件,加起来又是一大笔。要是还想上两块卡做更复杂的任务,预算一下就顶出去了。
2.2 运维工作量一个都省不掉,而且更重
预算花出去,只是把硬件放在了机柜里。真正细水长流的,是此后无穷无尽的运维工作量。有人以为私有化部署就是把模型下载下来,装个环境,打开服务就万事大吉。实际上,日常要面对的事情包括:
- 显卡驱动的周期性升级与降级,CUDA 版本和 PyTorch、推理框架之间的兼容矩阵维护;
- 模型文件的版本管理,一个 70B 模型动辄 200GB 以上,校验、传输、备份都是体力活;
- 服务器重启后驱动丢失、GPU 掉卡、显存 ECC 报错;
- 推理框架升级后 KV Cache 策略变化,导致原有性能测试数据全部失效;
- 温度、功耗、风扇转速的持续监控,机房空调一坏,显卡直接降频,服务变“龟速”。
这笔体力账,很多人买硬件时压根没算进去。所以我的结论很坚定:私有化部署省的是 API 调用费,省不了运维的脑力,甚至因为硬件完全自持,运维的责任反而更集中。
2.3 选型建议:如果是给生产用,别扣散热和电源
针对这个热门话题,我再给一条很现实的建议:如果预算在二三十万级别,优先考虑“用一张强卡部署中等规模模型”,而不是买两张中端卡跑大模型。因为双卡还需要考虑板卡互联带宽、NVLink 或 PCIe 拓扑、卡间通信效率,这些对推理吞吐的影响极其直接。两张卡如果互联带宽不足,模型并行时通信开销甚至可能抵消增加算力带来的收益。
散热和电源是最容易被砍预算、但翻车伤害最大的部分。GPU 高负载时瞬时功耗可以冲到额定功耗的 1.3 倍,电源余量不足会导致整机掉电;机房温度超过 25 度时,显卡会自动降频保护,模型生成速度肉眼可见地变慢。这种“软故障”最坑人,因为它不报错,只降速,表面上看是模型问题,实际上是热管理问题。
3. 从裸机到能用:一套大模型服务是怎么“被支棱起来”的
3.1 硬件底座的检查清单
大家总是把注意力放在模型框架上,但我接手过太多“框架装好了、模型却跑不起来”的事故,最后查了半天是硬件底层没打牢。所以先按清单过一遍:
- 双电源冗余是否接好,BMC/IPMI 带外管理口是否可用;
- GPU 是否被系统正确识别,用
nvidia-smi看风扇、温度、功耗、PCIe 速率是否正常; - NVLink/桥接是否插好,多卡互联状态下查看拓扑,确认卡间通信走的是高速链路而不是 PCIe 总线;
- 系统盘和数据盘分离,模型文件存独立数据盘,避免 IO 挤占系统盘;
- BIOS 里确认 Above 4G Decoding 和 Resizable BAR 已开启,否则大显存可能无法完整映射。
这些检查只要动过一次机器,就特别值得固化成脚本,至少每季度跑一遍。
3.2 显卡驱动、CUDA、PyTorch 的“版本三角恋”
大模型服务对环境的要求非常苛刻,或者说,非常“洁癖”。驱动、CUDA、PyTorch 三者的版本必须彼此兼容,错一个数字都可能出现奇怪的报错,比如 CUDA error: out of memory 或 no kernel image is available for execution on the device。
我推荐的做法是:先确定推理框架需要的 PyTorch 版本,再根据 PyTorch 的官方 CUDA 预编译版本去选 CUDA,最后把 Linux 驱动的 Major 版本和 CUDA 的最高兼容版本对上。这三层里,驱动是地基,CUDA 是中间层,PyTorch 是应用层,任何一层升级都必须回去重测全部依赖。
实际部署时有一个特别实用的技巧:不要直接在系统环境里装包,而是给服务套一层虚拟环境或 Docker 容器。很多人觉得容器化是额外负担,但对于大模型这种依赖极重的应用,容器能把“环境烂了”的影响范围锁死在单个服务里,不用动不动就重装整台机器。
3.3 推理框架选型:vLLM 为什么是运维的第一选择
当前大模型推理框架,主流选择是 vLLM,比手工用 Transformers 硬跑要快出数倍,核心在于它实现了连续批处理(continuous batching)和高效 KV Cache 管理。运维角度看,vLLM 提供了一套完整的 OpenAI 兼容 API,意味着现有的 API 网关、监控探活脚本、测试工具都可以无缝适配,不用再专门写一套协议适配层。
部署时直接在受控环境中拉镜像或从官方源码源码构建,一条典型启动命令,会把模型路径、GPU 数量、KV Cache 预留显存、最大并发数都控制住:
bash复制vllm serve /data/models/qwen2-14b-instruct \
--tensor-parallel-size 2 \
--max-model-len 16384 \
--gpu-memory-utilization 0.90 \
--max-num-seqs 64 \
--port 8000
解释一下几个关键参数:
--tensor-parallel-size:多卡并行分片数,决定了模型被切到几张卡上跑,换机器时必须重新评估;--gpu-memory-utilization:允许推理框架占用显存的比例,留出低比例给内核和别的进程,太满会导致 OOM;--max-num-seqs:最多同时处理的请求数,这是保护牌,超过之后新请求排队,而不是把显存撑爆。
3.4 部署后的健康检查脚本
服务起没起来,不能只看进程在不在。大模型服务的健康检查有一个关键动作:跑一次真实推理,确认后端模型能被正常加载和生成 token,而不是只返回 HTTP 200。
我习惯写一个 60 秒的超时探活,请求里带一个短命的 Prompt,比如“请回答:1+1=”,然后校验返回 JSON 里的 text 字段包含结果。这个小请求不怎么耗费算力,但能覆盖至少 90% 的部署故障,比如模型文件损坏、显存不足、并行配置错误等。
探活脚本要特别注意超时设置。模型冷启动后第一次请求,需要加载权重到显存,耗时可能长达几十秒。如果探活超时设成 3 秒,服务刚刚启动完就会被误杀,后面再接入网关,就会陷入“重启-探活失败-再重启”的恶性循环。建议把启动探活和稳定运行探活分开:启动探活给 120 秒宽限,稳定运行后的探活用 10 秒以内。
4. 线上巡检,天天要看哪些数字才安心
4.1 必盯指标族:当塔玛、TTFT、TPOT、GPU 利用率
我不主张你一次性把几十个指标都挂上大屏,那样只会产生告警疲劳。真正需要每天看的核心指标,其实一只手数得过来。
TTFT(Time To First Token) 是最能反映“用户打开页面后干等多久”的指标。它包含网络传包、排队等待、预填充(Prefill)计算三个部分。如果 TTFT 突然变高,优先查队列深度和请求语料长度,而不是怀疑模型本身。
TPOT(Time Per Output Token) 反映单个 token 的生成速度,从用户的直观体感上看,可以用“打字速度”来类比。TPOT 稳定,说明 GPU 算力在稳定输出,如果 TPOT 忽高忽低,或者稳定在异常高位,很可能是显存不足导致 KV Cache 频繁换出。
生成吞吐量 是所有并发用户的总输出速度,用 tokens/s 衡量。这个数值直接决定系统一天能承载多少工作负载,也是扩容决策的重要依据。
GPU 利用率 又得细分:计算利用率、显存利用率、显存带宽利用率、功耗利用率。只看一个数值会误判,比如显存利用率很高但计算利用率很低,那说明瓶颈在显存容量或带宽,不在算力。
下表是这些核心指标的直观意义和参考经验值:
| 指标 | 描述 | 参考经验值 |
|---|---|---|
| TTFT | 首 token 延迟 | 个人体验场景建议 < 2s,批量场景可放宽 |
| TPOT | 每 token 生成延迟 | 体验场景建议 < 0.1s,批量场景 < 0.3s |
| 生成吞吐量 | 全服务每秒产出 token 数 | 越高越好,但受显存总量硬约束 |
| 排队深度 | 等待中的请求数量 | 长期超过个位数时需扩容 |
| GPU 功耗利用 | 实际功耗与额定功耗之比 | 生产服务建议稳定在 70%-90%,过高易触发降频 |
4.2 日志设计:请求原数据是“金矿”也是“地雷”
模型服务产生的日志,第一条就是请求的完整原文和生成结果。这些数据对于调试模型行为、定位异常非常有价值,但如果原封不动全量落盘,一方面是存储爆炸,另一方面可能把敏感业务数据埋进日志文件里,成为安全隐患。
我建议在生产环境做两层日志策略:
- 全量离线日志:记录请求 ID、模型版本、请求长度、输出 token 数、TTFT、TPOT、是否超时、错误码,不记录具体正文。
- 抽样业务日志:按比例(比如 10%)保存带正文的请求和回复,专门用于效果审计和版本对比。
这样既满足排障需要,又降低了泄露风险。如果业务有明确的数据安全合规要求,抽样比例还要进一步调低,甚至完全关闭正文日志。
4.3 告警阈值设置:学会对“GPU 忙”说没关系
大模型服务的告警,最怕的就是把“高负载”当成“故障”处理。GPU 利用率持续 95% 以上,对一个吞吐量有要求的服务来说,可能恰恰是预期状态。你真正要告警的是下面几类:
- GPU 显存占满并触发 OOM,而不是显存使用率高;
- TTFT 的 P99 连续 5 分钟超过阈值,而不是偶发尖峰;
- 排队深度超过设定的最大并发数,而不是平均排队时长稍有波动;
- 显卡温度触及 85 度且持续不下,而不是瞬时冲到 80 度。
换句话说,告警策略的核心是“要在业务受损的临界点之前通知人”,而不是把一切异常波动都拉响警报。这需要用一段时间跑基线,跑出正常波动范围,再设置阈值。
4.4 Ansible 批量巡检:几十台机器一分钟全查完
当你负责的 GPU 机器多起来,逐台登录手动巡检根本不现实。Ansible 这种自动化工具的介入,对我来说是在找“真问题”的时间和“看假数据”的时间之间画了一条分界线。
我写过一个简单的巡检 playbook,对所有机器执行一组命令,拉取并汇总 nvidia-smi 的显存占用、进程列表、温度、功耗,同时对关键的 vLLM 服务执行心跳测试。跑完之后,所有节点的健康状态汇总到一张表格里输出,异常节点直接高亮。
这类脚本本身不复杂,但价值很大。它把重复劳动交还给程序,让人力集中在真正的异常处理上。自动化巡检脚本建议坚持“采集-汇总-告警-留档”四步走,不要只在出事时临时写。
5. 版本更新、微调模型上线、流量扩容,哪个最容易翻车
5.1 模型文件是“大胖子”,传输和校验是门学问
传统服务的发版,最多上传几百 MB 的压缩包;大模型一发版,动辄上百 GB。文件在服务器之间拷贝,稍微不注意,就会出现静默损坏,跑起来之后表现忽好忽坏,极其难排查。
我的做法是:模型文件必须设立独立存储目录,按版本建子目录;下载或拷贝完成后,用 md5 或 sha256 做完整性校验;每次发版记录模型名、版本号、大小、校验值、所需显存、依赖框架版本,形成一张可以回溯的清单。
还有一个容易忽略的细节:模型文件不要放在系统盘,也不要和日志盘混在一起。大文件写入会吃满磁盘 IO,导致日志写不进去,监控数据丢失,反过来掩盖真实故障。把模型盘、日志盘、系统盘三者分开,是成本很低但收益很高的运维设计。
5.2 灰度切换:网关做路由,模型做影子
生产环境的模型升级不能“杀旧起新”,否则用户会在一分钟内经历“回答正常-连接失败-再连接-更换口音”的割裂感。我推荐在 API 网关层配置多版本后端,新模型先以“影子流量”接入一小部分测试用户,观察输出质量和性能指标,再逐步放量。
如果用的是 vLLM,可以同时启动两个实例,一个跑旧版本,一个跑新版本,网关根据流量比例分发。这要求同一台 GPU 机器有足够的显存承载双实例,或者你有空闲机器。显存不够时,退而求其次的做法是先启新版本、拉通自测,再整体切换,但必须提前把回滚预案写好。
5.3 微调 Lora/SFT 模型时的运维要点
最近“大模型微调”这个热词背后,运维层面的工作量容易被低估。微调不是简单的“把训练脚本跑起来就完事”,尤其是当微调产物要重新部署到推理服务里,必须处理两件事:
- 基础模型 + LoRA 适配器是两个独立文件,推理框架要正确加载 lora 权重,配置错路径会静默用基础模型输出;
- 微调后模型的“风格漂移”问题。模型参数量巨大,微调可能改变它对某类问题的默认回答,上线前要用一套回归测试集,把和业务相关的典型问题跑一遍原文对比。
我踩过最惨的一次坑,是微调后的模型在逻辑推理题上表现没变,但把公司内部项目名误认为公共名词,导致大量输出出现组织名。这就是缺少回归测试集导致的。
5.4 扩缩容:按 Tokens 排队深度决定,不是按 QPS
传统扩容看 QPS,大模型服务看的是排队深度和总 tokens 吞吐。原因是模型服务的实际负载与请求的复杂度和生成长度强相关,两个请求可能相差 20 倍的计算量。只按 QPS 扩容,可能一个长文本生成请求就把新扩容的机器打满。
有条件的话,可以把“当前排队深度”和“平均 TPOT”两个值组合成一条负载曲线,超过设定的水位线后自动触发扩容,低于水位线再缩容。GPU 机器扩容不像 CPU 容器,拉起一个新模型实例可能要 1-3 分钟,所以阈值要留足缓冲,否则会频繁抖动。冷启动时间长的特性也意味着,扩容策略要偏保守,宁可稍微多预留一台空闲机器,也别在流量高峰时等模型慢慢加载。
6. 三个月里我处理过的模型服务故障,复盘给你看
6.1 故障一:服务器重启后显卡“消失”
现象:机房做例行断电演练,一台 GPU 服务器重启后,nvidia-smi 报 No devices were found,模型服务全部不可用。
排查链路:
- 第一反应是驱动丢了,检查
/proc/driver/nvidia/version,发现内核模块确实没加载; - 执行
dmesg | grep -i nvidia,看到 NVIDIA 模块和当前内核版本不匹配的记录; - 判断是内核升级导致驱动模块失效,重新编译安装匹配当前内核版本的驱动,或者回滚内核版本;
- 重新加载模块后执行
nvidia-smi确认识别成功,再恢复模型服务。
这次故障的核心教训是:GPU 服务器不能随便执行系统级升级,尤其是内核更新。驱动程序与内核版本是强绑定的,所有非必要升级都应该先在灾备机器上验证,再决定是否推广到 GPU 生产节点。
建议安装一个叫 nvidia-persistenced 的服务,让它常驻保持 GPU 的初始化状态。不然每次重启后,第一次调 nvidia-smi 都会触发初始化,耗时长,还容易碰到权限问题。
6.2 故障二:GPU 显存莫名其妙被占满
现象:服务进程明明在运行,但模型推理越来越慢,最后直接 OOM,推理请求全部失败。
排查链路:
nvidia-smi查看显存占用,显示存在大量残留进程,每个占了几 GB 显存;- 这些进程全是过去测试时留下的 Python 僵尸进程,代码里有局部变量没有显式释放;
- 通过
ps -eo pid,ppid,cmd | grep python定位到残留进程,手动 kill 释放显存; - 确认释放成功后,再重启推理服务。
这次暴露的问题不是框架层面,而是工程习惯层面。GPU 服务器的显存是共享且稀缺的资源,任何进程一旦占用,不会因为主程序退出就自动释放干净。建议在推理服务启动脚本里增加前置检查,先列出占显存的进程,确认没有异常残留再启动。
6.3 故障三:GPU 利用率很高,但用户说“卡死了”
现象:监控面板上 GPU 利用率 95%,温度正常,显存占用适中,用户却反馈“等半天不出字”。
排查链路:
- 看着 GPU 高利用率,先排除了“空闲”的可能;
- 查队列深度,发现排队数量已经顶到
max-num-seqs; - 再看平均输出 token 长度,发现最近业务方导入了一批超长文本问答场景,每个请求的输出长度从平均 500 token 涨到 2000 token;
- 用单个长请求做压测,确认 TTFT 和 TPOT 都翻倍了;
- 暂时提高
max-num-seqs并增加 KV Cache 预留,恢复一部分效果,但明确这不是治本方案;
最终解决方向是:拆分配置,把长文本请求路由到独立的服务实例,和短请求隔离。这类“容量规划跟不上业务变化”的故障,在大模型场景里会越来越频繁。因为有模型生成能力加持,业务方会不断尝试更复杂的用例,这本身是好事,但容量评估一定要跟着同步更新,不能再按“平均请求长度”做预估,得按“最大请求长度分布”做评估。
6.4 故障应急止损的顺序原则
处理大模型服务故障时,容易犯的错误是“急着修原因,忘了先止损”。我的应急顺序永远是:
- 先切走流量或扩容,让用户先恢复访问;
- 保留现场证据(日志、进程栈、GPU 状态),再开始排查;
- 复现问题、定位根因;
- 修复后先在影子环境确认,再回切生产;
- 复盘时把检查点写进自动化巡检脚本,让同类问题下次自动发现。
这套流程看着简单,但每次故障启动时都容易操作变形。尤其是 GPU 显存和缓存问题,手一快执行了重启,现场数据全没了,后面定位就得靠猜。
我个人的习惯是给每台 GPU 服务器准备一个专门的“故障取证命令包”,一条命令把 nvidia-smi、dmesg 尾部、最近日志、进程列表全部抓到临时目录并打时间戳。这样无论后续是修还是回滚,都有据可依,不会因为追查“当时到底是什么状态”而浪费大半天。这大概是大模型运维这行里,性价比最高的一件小事——它不解决问题,但能让所有问题的解决路径清晰可见。
