2
DCU技术达人
新人贴
从单卡 Demo 到八卡生产集群:海光 DCU 多模态服务架构实战(8×vLLM + 手写反向代理)
2026-10-10 09:40:46
32
在这台 8 卡[海光 K100\_AI](https://zhida.zhihu.com/search?content_id=282117030&content_type=Article&match_order=1&q=%E6%B5%B7%E5%85%89+K100_AI&zd_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ6aGlkYV9zZXJ2ZXIiLCJleHAiOjE3OTE3NjkyMTQsInEiOiLmtbflhYkgSzEwMF9BSSIsInpoaWRhX3NvdXJjZSI6ImVudGl0eSIsImNvbnRlbnRfaWQiOjI4MjExNzAzMCwiY29udGVudF90eXBlIjoiQXJ0aWNsZSIsIm1hdGNoX29yZGVyIjoxLCJ6ZF90b2tlbiI6bnVsbH0.aaDLcEr0VVNRJ0IWAHkMBqsP6CioR98ofWguuaRkNgI&zhida_source=entity)(DCU)服务器上,[多模态模型](https://zhida.zhihu.com/search?content_id=282117030&content_type=Article&match_order=1&q=%E5%A4%9A%E6%A8%A1%E6%80%81%E6%A8%A1%E5%9E%8B&zd_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ6aGlkYV9zZXJ2ZXIiLCJleHAiOjE3OTE3NjkyMTQsInEiOiLlpJrmqKHmgIHmqKHlnosiLCJ6aGlkYV9zb3VyY2UiOiJlbnRpdHkiLCJjb250ZW50X2lkIjoyODIxMTcwMzAsImNvbnRlbnRfdHlwZSI6IkFydGljbGUiLCJtYXRjaF9vcmRlciI6MSwiemRfdG9rZW4iOm51bGx9.7r8T2m7KZwGfvqWZITsWwA4E8P44gy16VSH50oLPIAY&zhida_source=entity)从单卡 Demo 跑到了正式上线。但「跑通」和「能扛生产」之间还差着一大截——真实业务不会温柔地一次发一张图,它是一天几万张、高峰几十并发的洪流。 这一篇记录整套多模态服务的\*\*[集群架构](https://zhida.zhihu.com/search?content_id=282117030&content_type=Article&match_order=1&q=%E9%9B%86%E7%BE%A4%E6%9E%B6%E6%9E%84&zd_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ6aGlkYV9zZXJ2ZXIiLCJleHAiOjE3OTE3NjkyMTQsInEiOiLpm4bnvqTmnrbmnoQiLCJ6aGlkYV9zb3VyY2UiOiJlbnRpdHkiLCJjb250ZW50X2lkIjoyODIxMTcwMzAsImNvbnRlbnRfdHlwZSI6IkFydGljbGUiLCJtYXRjaF9vcmRlciI6MSwiemRfdG9rZW4iOm51bGx9.TpURBYDOGxgF_NUo8yCR43VAFvfbMS6nc7j4Xo6wxCw&zhida_source=entity)与生产化过程\*\*:为什么选 8×单卡实例而不是 TP8、没有 [sudo](https://zhida.zhihu.com/search?content_id=282117030&content_type=Article&match_order=1&q=sudo&zd_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ6aGlkYV9zZXJ2ZXIiLCJleHAiOjE3OTE3NjkyMTQsInEiOiJzdWRvIiwiemhpZGFfc291cmNlIjoiZW50aXR5IiwiY29udGVudF9pZCI6MjgyMTE3MDMwLCJjb250ZW50X3R5cGUiOiJBcnRpY2xlIiwibWF0Y2hfb3JkZXIiOjEsInpkX3Rva2VuIjpudWxsfQ.RonoH6to69JDHzPTIeI6IMNTGQ87c6HIvqmjFbLZ8fk&zhida_source=entity) 权限怎么做[负载均衡](https://zhida.zhihu.com/search?content_id=282117030&content_type=Article&match_order=1&q=%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1&zd_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ6aGlkYV9zZXJ2ZXIiLCJleHAiOjE3OTE3NjkyMTQsInEiOiLotJ_ovb3lnYfooaEiLCJ6aGlkYV9zb3VyY2UiOiJlbnRpdHkiLCJjb250ZW50X2lkIjoyODIxMTcwMzAsImNvbnRlbnRfdHlwZSI6IkFydGljbGUiLCJtYXRjaF9vcmRlciI6MSwiemRfdG9rZW4iOm51bGx9.PqMeyH-UaA9MkhAIaDZafT3gfbBk0oGLdULjWivCjb8&zhida_source=entity)、以及 stop 脚本漏杀进程导致显存泄漏这种只有亲自运维才会踩的坑。 ## 第一个架构决策:8×TP1,而不是 TP8 8 张卡跑一个大模型,教科书式的第一反应是[张量并行](https://zhida.zhihu.com/search?content_id=282117030&content_type=Article&match_order=1&q=%E5%BC%A0%E9%87%8F%E5%B9%B6%E8%A1%8C&zd_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ6aGlkYV9zZXJ2ZXIiLCJleHAiOjE3OTE3NjkyMTQsInEiOiLlvKDph4_lubbooYwiLCJ6aGlkYV9zb3VyY2UiOiJlbnRpdHkiLCJjb250ZW50X2lkIjoyODIxMTcwMzAsImNvbnRlbnRfdHlwZSI6IkFydGljbGUiLCJtYXRjaF9vcmRlciI6MSwiemRfdG9rZW4iOm51bGx9.0G_-LMD059Sk08KOfGpAhQC1SeUT2uLavRiPvgUcl-Q&zhida_source=entity)(TP=8)。我们最终选的是\*\*每张卡一个完整的独立实例(TP=1),共 8 个实例\*\*,前面挂负载均衡。理由: | | 8×TP1(选用) | 1×TP8 | |---|---|---| | [故障隔离](https://zhida.zhihu.com/search?content_id=282117030&content_type=Article&match_order=1&q=%E6%95%85%E9%9A%9C%E9%9A%94%E7%A6%BB&zd_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ6aGlkYV9zZXJ2ZXIiLCJleHAiOjE3OTE3NjkyMTQsInEiOiLmlYXpmpzpmpTnprsiLCJ6aGlkYV9zb3VyY2UiOiJlbnRpdHkiLCJjb250ZW50X2lkIjoyODIxMTcwMzAsImNvbnRlbnRfdHlwZSI6IkFydGljbGUiLCJtYXRjaF9vcmRlciI6MSwiemRfdG9rZW4iOm51bGx9.06m1xrqgY2y8fAFa9fAsCffvyi8tg9x9dsG3-1h-o1k&zhida_source=entity) | \*\*单实例崩,只废一张卡\*\*,其余 7 张照常服务 | 任一 rank 出问题,整个服务宕 | | 吞吐 | 8 份 KV cache 各自独立,聚合并发 = 8×max\_seqs | 单实例调度,KV cache 被 TP 切分 | | 请求延迟 | 小模型单卡放得下,TP 通信(RCCL all-reduce)反而是纯开销 | 大模型单卡放不下时才必要 | | 启动/运维 | 实例可单独重启、单独升级 | 牵一发动全身 | 一句话:\*\*模型单卡装得下(4.5 的 17GB 权重 vs 64GB [显存](https://zhida.zhihu.com/search?content_id=282117030&content_type=Article&match_order=2&q=%E6%98%BE%E5%AD%98&zd_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ6aGlkYV9zZXJ2ZXIiLCJleHAiOjE3OTE3NjkyMTQsInEiOiLmmL7lrZgiLCJ6aGlkYV9zb3VyY2UiOiJlbnRpdHkiLCJjb250ZW50X2lkIjoyODIxMTcwMzAsImNvbnRlbnRfdHlwZSI6IkFydGljbGUiLCJtYXRjaF9vcmRlciI6MiwiemRfdG9rZW4iOm51bGx9.IqhR_4jzHrpoXOsgp2Of2wU-9mANhNUMrxLOaQ8NP5Q&zhida_source=entity)),[数据并行](https://zhida.zhihu.com/search?content_id=282117030&content_type=Article&match_order=1&q=%E6%95%B0%E6%8D%AE%E5%B9%B6%E8%A1%8C&zd_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ6aGlkYV9zZXJ2ZXIiLCJleHAiOjE3OTE3NjkyMTQsInEiOiLmlbDmja7lubbooYwiLCJ6aGlkYV9zb3VyY2UiOiJlbnRpdHkiLCJjb250ZW50X2lkIjoyODIxMTcwMzAsImNvbnRlbnRfdHlwZSI6IkFydGljbGUiLCJtYXRjaF9vcmRlciI6MSwiemRfdG9rZW4iOm51bGx9.klCdiohhM9EUQ5O6m-VhXboJiVBbFZk4jBIVHKtjVDs&zhida_source=entity)就优于张量并行\*\*。TP 是「装不下」时的妥协,不是默认选项。顺带一提,多模态还有个额外好处——视觉塔不用跨卡切,预处理逻辑简单。 ## [集群拓扑](https://zhida.zhihu.com/search?content_id=282117030&content_type=Article&match_order=1&q=%E9%9B%86%E7%BE%A4%E6%8B%93%E6%89%91&zd_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ6aGlkYV9zZXJ2ZXIiLCJleHAiOjE3OTE3NjkyMTQsInEiOiLpm4bnvqTmi5PmiZEiLCJ6aGlkYV9zb3VyY2UiOiJlbnRpdHkiLCJjb250ZW50X2lkIjoyODIxMTcwMzAsImNvbnRlbnRfdHlwZSI6IkFydGljbGUiLCJtYXRjaF9vcmRlciI6MSwiemRfdG9rZW4iOm51bGx9.lH0lwKlhacRPoN1d-5hbrLzSACAm3GbrPtUt3dcf7XY&zhida_source=entity) ```text ┌─────────────────────────┐ 业务客户端 ────────►│ aiohttp 反向代理 :8000 │ 统一入口 └───────────┬─────────────┘ 最少连接 + 健康检查 + SSE 透传 ┌──────┬──────┬──────┬───┴──┬──────┬──────┬──────┐ ▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼ :8002 :8003 :8004 :8005 :8006 :8007 :8008 :8009 (vLLM 实例) GPU0 GPU1 GPU2 GPU3 GPU4 GPU5 GPU6 GPU7 ``` 规则极简:**实例 i → GPU i → 端口 8002+i**。每实例独占一卡,`--max-num-seqs 4`,集群总并发 32。每个响应带 `X-Backend`[响应头](https://zhida.zhihu.com/search?content_id=282117030&content_type=Article&match_order=1&q=%E5%93%8D%E5%BA%94%E5%A4%B4&zd_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ6aGlkYV9zZXJ2ZXIiLCJleHAiOjE3OTE3NjkyMTQsInEiOiLlk43lupTlpLQiLCJ6aGlkYV9zb3VyY2UiOiJlbnRpdHkiLCJjb250ZW50X2lkIjoyODIxMTcwMzAsImNvbnRlbnRfdHlwZSI6IkFydGljbGUiLCJtYXRjaF9vcmRlciI6MSwiemRfdG9rZW4iOm51bGx9.A1AdsB27mwMpWRXz2gLeciKtvEz4ur8cha70_5fcaas&zhida_source=entity),标明命中了哪个后端——排查问题时 invaluable。 ### 启动脚本里的三个现实问题 启动逻辑看着简单(for 循环拉 8 个 vllm),但生产化要处理三个细节: **① 错峰启动。** 8 个实例同时读 17GB 权重,磁盘 IO 直接打满,加载时间反而变长。实例间隔 5 秒启动,8 实例全 ready 约 2–3 分钟——比单实例加载没慢多少。 **② [幂等](https://zhida.zhihu.com/search?content_id=282117030&content_type=Article&match_order=1&q=%E5%B9%82%E7%AD%89&zd_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ6aGlkYV9zZXJ2ZXIiLCJleHAiOjE3OTE3NjkyMTQsInEiOiLluYLnrYkiLCJ6aGlkYV9zb3VyY2UiOiJlbnRpdHkiLCJjb250ZW50X2lkIjoyODIxMTcwMzAsImNvbnRlbnRfdHlwZSI6IkFydGljbGUiLCJtYXRjaF9vcmRlciI6MSwiemRfdG9rZW4iOm51bGx9.2_ZyZAYb1T8U49C9JN6cSTsm0vomgxdmox-YhM9uxr8&zhida_source=entity)。** 脚本先 `ss -ltn` 检查端口,已被占用的实例直接 skip 而不是报错退出。这样局部重启实例时可以无脑跑同一个脚本。 **③ [环境变量](https://zhida.zhihu.com/search?content_id=282117030&content_type=Article&match_order=1&q=%E7%8E%AF%E5%A2%83%E5%8F%98%E9%87%8F&zd_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ6aGlkYV9zZXJ2ZXIiLCJleHAiOjE3OTE3NjkyMTQsInEiOiLnjq_looPlj5jph48iLCJ6aGlkYV9zb3VyY2UiOiJlbnRpdHkiLCJjb250ZW50X2lkIjoyODIxMTcwMzAsImNvbnRlbnRfdHlwZSI6IkFydGljbGUiLCJtYXRjaF9vcmRlciI6MSwiemRfdG9rZW4iOm51bGx9.WYIk5AQHQix_bACKEvribruGSzdHfcdw239rN3o8sm4&zhida_source=entity)参数化。** 实例数 `N`、起始端口 `BASE_PORT`、上下文 `MAX_LEN`、并发 `MAX_SEQS`、是否 eager 全部可覆盖——验证新参数时不用改脚本: \`\`\`bash ./start\_cluster.sh # 全量 8 实例 N=4 ./start\_cluster.sh # 只起 4 卡 MAX\_SEQS=1 ./start\_cluster.sh # 降到确定性串行排查问题 \`\`\` ## 没有 sudo:100 行 aiohttp 手写反向代理 统一入口 \`:8000\` 需要一个反向代理。标准答案是 [nginx](https://zhida.zhihu.com/search?content_id=282117030&content_type=Article&match_order=1&q=nginx&zd_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ6aGlkYV9zZXJ2ZXIiLCJleHAiOjE3OTE3NjkyMTQsInEiOiJuZ2lueCIsInpoaWRhX3NvdXJjZSI6ImVudGl0eSIsImNvbnRlbnRfaWQiOjI4MjExNzAzMCwiY29udGVudF90eXBlIjoiQXJ0aWNsZSIsIm1hdGNoX29yZGVyIjoxLCJ6ZF90b2tlbiI6bnVsbH0.vQiHDnsLAQQlRDlONrhVCAbiEznP6gB9eg5QlXgx_JM&zhida_source=entity),但当前账号没有免密 sudo——装不了、也改不了配置。于是用 aiohttp 手写了一个(aiohttp 恰好已在 vllm-serve 环境里),核心逻辑不到 100 行: \`\`\`python def pick\_backend(): """最少连接;平局按轮询。无健康后端返回 None。""" global \_rr cand = [u for u in BACKEND\_URLS if healthy[u]] if not cand: return None min\_n = min(active[u] for u in cand) tied = [u for u in cand if active[u] == min\_n] u = tied[\_rr % len(tied)] \_rr += 1 return u \`\`\` 功能清单:最少连接调度(平局轮询打散)、周期健康检查、SSE 流式透传、\`X-Backend\` 头、推理超时 300s、单请求体上限 64MB(容纳多图 base64)。后端 8002–8009 的地址不变,\*\*将来拿到 root 权限可以原样换成 nginx,客户端零感知\*\*——这是无 sudo 方案最重要 design goal:它得是可退役的。 这个「土法」代理在生产里跑了几周,期间按真实故障迭代出了三个关键修复,每个都值得单独讲。 ### 修复一:健康检查必须用 \`/health\`,不能用 \`/v1/models\` vLLM 有两个看起来都能探活的端点,但语义完全不同:\`/v1/models\` 只反映 API Server 层,\*\*EngineCore 进程崩了它照样返回 200\*\*——负载均衡会继续把请求往尸体上分发。\`/health\` 直接反映引擎状态,模型加载中或 EngineCore 崩溃时返回 503。切换之后,「请求打到已死实例」的报错消失了。 ### 修复二:计数竞态——一行代码的位置问题 最少连接调度依赖 \`active[backend]\` 计数。最初的写法是「选完后端 → 读完请求 body → 计数 +1」。高并发下这中间的[时间窗](https://zhida.zhihu.com/search?content_id=282117030&content_type=Article&match_order=1&q=%E6%97%B6%E9%97%B4%E7%AA%97&zd_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ6aGlkYV9zZXJ2ZXIiLCJleHAiOjE3OTE3NjkyMTQsInEiOiLml7bpl7TnqpciLCJ6aGlkYV9zb3VyY2UiOiJlbnRpdHkiLCJjb250ZW50X2lkIjoyODIxMTcwMzAsImNvbnRlbnRfdHlwZSI6IkFydGljbGUiLCJtYXRjaF9vcmRlciI6MSwiemRfdG9rZW4iOm51bGx9.yS1Yd-48CAZVM-cOmdQIc-hmMf9LySyYeddzVapWhTo&zhida_source=entity)足够让几十个请求都看到同一个后端「当前最闲」,然后一起涌过去——实测最大堆积 22 个请求挤在一张卡上(\`max\_seqs=4\`,其余全在排队),而别的卡在睡觉。修法是把计数提前到「选中后端之后、读 body 之前」,用 \`try/finally\` 保证减计数。改完后最大堆积降到 \~5。 ### 修复三:结果日志是排查的生命线 生产环境里业务方报「你们的接口老报错」,你第一件想做的事是什么?\*\*看每一个请求到底发生了什么\*\*。vLLM 的 access 日志只有状态码没有 body,400 的具体原因它不记。于是给代理加了一层结果日志 \`req\_results.log\`:每条 chat/completions 记一行——业务追踪 ID(\`user\` 字段)、状态码、\`finish\_reason\`、completion\_tokens、是否含 \`\`、内容片段;非 200 的请求把 vLLM 的报错正文完整抓下来。 这行日志立过大功。业务端反馈「大量请求失败」,从vLLM 日志里只能看到一堆 \`400 Bad Request\` 没有原因;翻代理的结果日志,对比成功请求和失败请求的差别,几分钟定位:\*\*业务方把追踪 ID 当整数塞进了 \`user\` 字段,vLLM 的 Pydantic 校验要求字符串,100% 拒绝\*\*。修复就是客户端一行 \`user: str(orid)\`。后来结果日志本身也出了次事故——忘了做轮转,悄悄涨到 769MB,于是补上 \`RotatingFileHandler\`(50MB×3 份)。 ## 坑:stop 脚本杀不干净,显存越停越少 这是整个[集群运维](https://zhida.zhihu.com/search?content_id=282117030&content_type=Article&match_order=1&q=%E9%9B%86%E7%BE%A4%E8%BF%90%E7%BB%B4&zd_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ6aGlkYV9zZXJ2ZXIiLCJleHAiOjE3OTE3NjkyMTQsInEiOiLpm4bnvqTov5Dnu7QiLCJ6aGlkYV9zb3VyY2UiOiJlbnRpdHkiLCJjb250ZW50X2lkIjoyODIxMTcwMzAsImNvbnRlbnRfdHlwZSI6IkFydGljbGUiLCJtYXRjaF9vcmRlciI6MSwiemRfdG9rZW4iOm51bGx9.sd6vfdyfjPRw_F_FWT06sRFPAQUkJTOZEwJQy8XNylU&zhida_source=entity)里最阴的一个坑,从 2.6 时代一路踩到 4.5。 \*\*症状\*\*:\`stop\_cluster.sh\` 跑完,\`[pgrep](https://zhida.zhihu.com/search?content_id=282117030&content_type=Article&match_order=1&q=pgrep&zd_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ6aGlkYV9zZXJ2ZXIiLCJleHAiOjE3OTE3NjkyMTQsInEiOiJwZ3JlcCIsInpoaWRhX3NvdXJjZSI6ImVudGl0eSIsImNvbnRlbnRfaWQiOjI4MjExNzAzMCwiY29udGVudF90eXBlIjoiQXJ0aWNsZSIsIm1hdGNoX29yZGVyIjoxLCJ6ZF90b2tlbiI6bnVsbH0.rORQO9kniw-cgwCZ0hiTfLg1VcojV76n-YGaBTfR1sU&zhida_source=entity)\` 看不到 vllm 进程了,端口也释放了,但 \`hy-smi\` 一看——8 张卡的显存还是满的。重启集群就开始报 \`Free memory on device (x/64 GiB) is less than desired\`。 \*\*根因\*\*:vLLM 的[多进程架构](https://zhida.zhihu.com/search?content_id=282117030&content_type=Article&match_order=1&q=%E5%A4%9A%E8%BF%9B%E7%A8%8B%E6%9E%B6%E6%9E%84&zd_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ6aGlkYV9zZXJ2ZXIiLCJleHAiOjE3OTE3NjkyMTQsInEiOiLlpJrov5vnqIvmnrbmnoQiLCJ6aGlkYV9zb3VyY2UiOiJlbnRpdHkiLCJjb250ZW50X2lkIjoyODIxMTcwMzAsImNvbnRlbnRfdHlwZSI6IkFydGljbGUiLCJtYXRjaF9vcmRlciI6MSwiemRfdG9rZW4iOm51bGx9.j9FFoFFMtr4gfCWOyR9lv__EAMFQGCilRerqARBhCEw&zhida_source=entity)里,EngineCore/worker 是主进程用 multiprocessing \*\*spawn 出来的子进程\*\*,这些子进程:①不监听任何端口(按端口 kill 杀不到);②命令行是 \`python -c "..."\` 形式,\*\*不含 "vllm" 字样\*\*(\`pgrep -f vllm\` 也杀不到)。两个常规手段全部漏杀,[子进程](https://zhida.zhihu.com/search?content_id=282117030&content_type=Article&match_order=3&q=%E5%AD%90%E8%BF%9B%E7%A8%8B&zd_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ6aGlkYV9zZXJ2ZXIiLCJleHAiOjE3OTE3NjkyMTQsInEiOiLlrZDov5vnqIsiLCJ6aGlkYV9zb3VyY2UiOiJlbnRpdHkiLCJjb250ZW50X2lkIjoyODIxMTcwMzAsImNvbnRlbnRfdHlwZSI6IkFydGljbGUiLCJtYXRjaF9vcmRlciI6MywiemRfdG9rZW4iOm51bGx9.jefuslFxmLPB3HSCF1tnzy2NsQynyQcqvhZ03FaF6xs&zhida_source=entity)就这么一直占着显存。再叠加 DCU 的[显存回收](https://zhida.zhihu.com/search?content_id=282117030&content_type=Article&match_order=1&q=%E6%98%BE%E5%AD%98%E5%9B%9E%E6%94%B6&zd_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ6aGlkYV9zZXJ2ZXIiLCJleHAiOjE3OTE3NjkyMTQsInEiOiLmmL7lrZjlm57mlLYiLCJ6aGlkYV9zb3VyY2UiOiJlbnRpdHkiLCJjb250ZW50X2lkIjoyODIxMTcwMzAsImNvbnRlbnRfdHlwZSI6IkFydGljbGUiLCJtYXRjaF9vcmRlciI6MSwiemRfdG9rZW4iOm51bGx9.3GKbzujaYuVTCJIa06u4b0cytSObMiPQ5nz4GRt9VOw&zhida_source=entity)本来就慢(进程死后 HIP 内存要等一段时间才归还驱动),现场更加扑朔迷离。 \*\*修复\*\*:stop 脚本改成五步——①按端口 kill [主进程](https://zhida.zhihu.com/search?content_id=282117030&content_type=Article&match_order=2&q=%E4%B8%BB%E8%BF%9B%E7%A8%8B&zd_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ6aGlkYV9zZXJ2ZXIiLCJleHAiOjE3OTE3NjkyMTQsInEiOiLkuLvov5vnqIsiLCJ6aGlkYV9zb3VyY2UiOiJlbnRpdHkiLCJjb250ZW50X2lkIjoyODIxMTcwMzAsImNvbnRlbnRfdHlwZSI6IkFydGljbGUiLCJtYXRjaF9vcmRlciI6MiwiemRfdG9rZW4iOm51bGx9.fHrosxQq8QVO_BfMCVAQpZPOmwhuXYowEJ3Q3-dZBdQ&zhida_source=entity);②\`[pkill](https://zhida.zhihu.com/search?content_id=282117030&content_type=Article&match_order=1&q=pkill&zd_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ6aGlkYV9zZXJ2ZXIiLCJleHAiOjE3OTE3NjkyMTQsInEiOiJwa2lsbCIsInpoaWRhX3NvdXJjZSI6ImVudGl0eSIsImNvbnRlbnRfaWQiOjI4MjExNzAzMCwiY29udGVudF90eXBlIjoiQXJ0aWNsZSIsIm1hdGNoX29yZGVyIjoxLCJ6ZF90b2tlbiI6bnVsbH0.bnjDrNQfyw8jChWUYsUZdUB4pSeW6HufPgVg392dojk&zhida_source=entity) -9 -f vllm\` 兜底;③睡 8 秒等显存回收;④\*\*用 \`fuser /dev/kfd /dev/dri/renderD\* /dev/dri/card\*\` 按设备占用抓残留进程强杀\*\*——spawn 子进程再怎么伪装,只要占着设备文件就无所遁形;⑤\`hy-smi\` 验证 8 卡显存归零。 \`fuser /dev/kfd\` 这一步是点睛之笔:\*\*当进程的「名字」和「[端口](https://zhida.zhihu.com/search?content_id=282117030&content_type=Article&match_order=8&q=%E7%AB%AF%E5%8F%A3&zd_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ6aGlkYV9zZXJ2ZXIiLCJleHAiOjE3OTE3NjkyMTQsInEiOiLnq6_lj6MiLCJ6aGlkYV9zb3VyY2UiOiJlbnRpdHkiLCJjb250ZW50X2lkIjoyODIxMTcwMzAsImNvbnRlbnRfdHlwZSI6IkFydGljbGUiLCJtYXRjaF9vcmRlciI6OCwiemRfdG9rZW4iOm51bGx9.EH-XHthHAxj6_xjctZr9XREJTE76yGj8NoV0gp_vhSo&zhida_source=entity)」都不可信时,还有「它占着什么设备」这个物理事实可以依赖\*\*。修复后实测,8 实例 stop 后 fuser 抓到 8 个残留 PID,强杀,8 卡显存全部归零。 ## [压测](https://zhida.zhihu.com/search?content_id=282117030&content_type=Article&match_order=1&q=%E5%8E%8B%E6%B5%8B&zd_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ6aGlkYV9zZXJ2ZXIiLCJleHAiOjE3OTE3NjkyMTQsInEiOiLljovmtYsiLCJ6aGlkYV9zb3VyY2UiOiJlbnRpdHkiLCJjb250ZW50X2lkIjoyODIxMTcwMzAsImNvbnRlbnRfdHlwZSI6IkFydGljbGUiLCJtYXRjaF9vcmRlciI6MSwiemRfdG9rZW4iOm51bGx9.hyOJndvlnH20hyb5o1sS0XgrXuJXGXZmhFy-zryurT8&zhida_source=entity):32 路真并行的验证 架构好不好,数字说话。经 \`:8000\` 入口压测(脚本 \`concurrent\_test\_45.py\`,4.5 集群,关 think): \*\*短问答(max\_tokens=64):\*\* | 并发 | 成功率 | 总耗时 | 吞吐 | |---|---|---|---| | 8 | 100% | 0.3s | 25.5 r/s | | 16 | 100% | 0.4s | 44.4 r/s | | 32 | 100% | 0.5s | 67.3 r/s | | 48 | 100% | 0.8s | 60.2 r/s | | 64 | 100% | 1.1s | 60.5 r/s | \*\*详细描述(max\_tokens=384):\*\* | 并发 | 成功率 | 总耗时 | 吞吐 | |---|---|---|---| | 8 | 100% | 3.5s | 2.3 r/s | | 16 | 100% | 4.3s | 3.7 r/s | | 32 | 100% | 4.9s | 6.6 r/s | 三个结论: 1. \*\*32 并发的总耗时 ≈ 单请求延迟\*\*(短问答 0.5s)。如果是串行,32 个请求至少要 10 秒往上——这证明 8 卡 × \`max\_seqs=4\` = 32 个并发槽是真并行,不是排队 serial number; 2. \*\*吞吐在并发 48 附近触顶(\~60 r/s)\*\*,64 并发不再上涨只增加排队。想突破只能调高服务端 \`max\_seqs\`,加大客户端并发没有意义——这个「客户端并发 vs [服务端](https://zhida.zhihu.com/search?content_id=282117030&content_type=Article&match_order=2&q=%E6%9C%8D%E5%8A%A1%E7%AB%AF&zd_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ6aGlkYV9zZXJ2ZXIiLCJleHAiOjE3OTE3NjkyMTQsInEiOiLmnI3liqHnq68iLCJ6aGlkYV9zb3VyY2UiOiJlbnRpdHkiLCJjb250ZW50X2lkIjoyODIxMTcwMzAsImNvbnRlbnRfdHlwZSI6IkFydGljbGUiLCJtYXRjaF9vcmRlciI6MiwiemRfdG9rZW4iOm51bGx9.O4o348zMKgL8hq8BBXM3kr45I2_9ZhLzJhT1eXVvquI&zhida_source=entity)容量」的区分,是压测报告里最容易被误读的地方; 3. 压测时采样 [HCU](https://zhida.zhihu.com/search?content_id=282117030&content_type=Article&match_order=1&q=HCU&zd_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ6aGlkYV9zZXJ2ZXIiLCJleHAiOjE3OTE3NjkyMTQsInEiOiJIQ1UiLCJ6aGlkYV9zb3VyY2UiOiJlbnRpdHkiLCJjb250ZW50X2lkIjoyODIxMTcwMzAsImNvbnRlbnRfdHlwZSI6IkFydGljbGUiLCJtYXRjaF9vcmRlciI6MSwiemRfdG9rZW4iOm51bGx9.rQADMJFA3XRQYVrUlBnk0oxQX-_77AqZkeeD6yx0zQo&zhida_source=entity) 利用率中位 71%、显存 79%——算力没打满,\`max\_seqs\` 从 4 提到 6 理论上还有 15–30% 空间,代价是单请求延迟上升。 换算成日处理量(持续满载 × 0.75 折扣):短问答约 \*\*390 万张/天\*\*,详细描述约 \*\*43 万张/天\*\*,带 think 推理约 10–13 万张/天。 ## 小结 这套架构没有任何一个组件是「高级」的:for 循环启动、aiohttp 代理、fuser 杀进程。但它们合在一起扛住了每天几十万张图的真实流量,而且每个组件都可独立替换(nginx 到手就换、max\_seqs 一改就扩)。回头看,生产化这件事的核心不是选多强的技术,而是:**故障路径和正常路径一样认真设计**——怎么停、怎么排错、怎么定位一个具体请求,和怎么启动同样重要。 把「怎么停、怎么排错、怎么定位一个具体请求」和「怎么启动」放在同等地位去设计,是这次生产化最值得复制的经验——技术选型可以朴素,故障路径不能省略。 --- *环境信息:海光 K100\_AI × 8 / DTK 26.04 / vLLM 0.11.0(DCU 定制版)/ [MiniCPM](https://zhida.zhihu.com/search?content_id=282117030&content_type=Article&match_order=1&q=MiniCPM&zd_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ6aGlkYV9zZXJ2ZXIiLCJleHAiOjE3OTE3NjkyMTQsInEiOiJNaW5pQ1BNIiwiemhpZGFfc291cmNlIjoiZW50aXR5IiwiY29udGVudF9pZCI6MjgyMTE3MDMwLCJjb250ZW50X3R5cGUiOiJBcnRpY2xlIiwibWF0Y2hfb3JkZXIiOjEsInpkX3Rva2VuIjpudWxsfQ.2Q_X6zlyrts5qvUz_s6LBZdKGzQcoee6NQuQXhCwHDQ&zhida_source=entity)-V 4.5(BF16,17GB)/ aiohttp 3.14 反向代理。压测数据实测于 2026-08-11。*
全部评论(0)
只看楼主
暂无数据
--
楼主
Level.1
0 帖子
楼主热帖
暂无数据