日期:2026-08-23
这个客户是私有化底座的项目,十几个业务方共用一套推理集群,销售想接自己的话术微调,客服想接自己的政策微调,财务也想有自己的抽取模型。如果每个业务方都起一套独立服务,几十张卡根本不够分,而且基座版本升级要同步十几处,运维直接崩。他们的诉求很明确:一套基座,各自挂自己的微调,互不干扰,还能随时上下线,别为了一个业务方的小改动重启整集群。
我们用的底座推理框架支持在基座上挂多个 LoRA 适配器,关键在工程上把「多租户复用一张卡」做稳,而不是朴素地堆上去,否则一个业务方把显存打满,所有人都跟着超时。
单一基座服务启动后,通过管理接口挂载多个 LoRA 适配器,每个适配器绑定一个租户或业务方。请求带租户标识,网关或路由层把请求导向对应适配器,基座权重共享,只有增量部分按适配器切换。租户间做显存和配额隔离,某个适配器下线不影响别的。新 adapter 上线走热插拔,不重启进程,老适配器下线走 draining,把在途请求跑完再释放。
显存复用是第一个难点。LoRA 增量本身不大,但基座权重占大头,必须共享。我们让基座权重常驻,适配器增量按租户配额切显存池,超配额的新适配器拒绝挂载而不是偷偷挤占,否则就会出现一个业务方批次过大把别人拖垮。
请求到适配器的路由要快,不能每次推理都去查表。我们在请求入口做一次路由解析,把租户标识映射到适配器句柄,推理时直接切换,切换延迟控制在毫秒级,业务方完全无感。
热插拔不断流是最磨人的。新适配器加载要走完权重校验再挂到句柄表,旧适配器下线先把句柄标记为 draining,等在途请求跑完再释放,避免正在跑的请求突然拿到空句柄返回空答案。版本校验也不能省,适配器基座版本错配必须拒绝挂载。
案例片段(已脱敏): 适配器挂载与租户显存配额(示意): ``` adapters: - name: lora_sales tenant: sales vram_quota_mb: 1200 - name: lora_cs tenant: cs vram_quota_mb: 1200 routing: header: x-tenant default: base_only
下线走 draining
unload(lora_cs, mode="draining") # 在途请求跑完再释放 ``` 一次财务适配器上线,加载校验发现基座版本错配,系统拒绝挂载而不是带病上线,避免了一次推理结果串租户。一次销售大批次把显存打满,配额拦截让它溢出而不是挤掉客服的适配器。
单卡承载适配器数从朴素部署的 1 个提到约 6 个,集群 GPU 占用直接砍掉一大半,原本要扩的十几张卡不用买了。适配器切换延迟稳定在 3 到 8 毫秒,业务方无感。租户隔离故障域从「一张卡挂全部」收敛到单个适配器,出问题时影响面小了一个量级,再没出现一个业务方拖垮全集群。热插拔中断次数做到 0,上线新适配器不再挑半夜低峰,白天也能滚,运维终于能正常下班。
多租户共用基座,LoRA 复用是最省卡的做法,但前提是把显存和配额切成硬边界,不能靠自觉,显存这种资源一旦超额就是所有人一起超时。路由解析放在请求入口一次到位,别在推理热路径里反复查,那点延迟累积起来业务方会感觉到。热插拔一定要走 draining,在途请求比「立刻释放」重要,我们当时为了快跳过 draining,结果一批在途客服请求拿到空句柄返回空答案,被业务方截图吐槽了一周。适配器加载前做版本校验,宁可拒绝挂载也别带病上线,串租户是这类系统最丢人的事故,而且排查起来极费时间。
这套多租户方案后来成了我们私有化底座的默认交付形态,新业务方接入从原来的几天缩到小时级。回头看,最值钱的决定是把显存配额做成硬边界,虽然初期被业务方抱怨「为什么我的适配器挂不上去」,但换来的是没人能拖垮集群。还有一点,适配器版本和基座版本的兼容矩阵要提前定义清楚,我们后来专门建了一张兼容表,新适配器上线前先查表,少了很多扯皮。如果客户规模再上一个量级,单卡六适配器可能不够,到时候要考虑跨卡调度,不过那是另一个故事了。
回头看,多租户隔离做扎实之后,反而打开了新玩法。比如我们给某个业务方做了适配器灰度,新微调先在它自己租户内小流量验证,不影响别人,验证完再扩大,这比全局灰度安全得多。还有个收益是成本归因变清楚了,哪个业务方占了多少显存、烧了多少算力,账单直接拆开,之前混在一张卡上根本算不清,财务总怀疑被别的部门蹭了。如果往后做,我会把适配器市场化的思路接进来,业务方自己上传微调、自己管配额,平台只收底座算力费,交付会更轻。当然这涉及权限和合规,得慢慢放。
把这套经验抽象一下,多租户隔离的本质是把共享和边界同时做对,共享底座降成本,边界隔离保稳定,缺一个都不行。我们后来给每个新接入的业务方都先过一遍隔离 checklist,反而比之前自由发挥上线更顺,因为坑都提前填了。架构上的约束,有时候是给团队省麻烦。