今天本来只想干一件事:把我的一些本地模型服务做成 Docker 容器,方便后续迁移。
结果真动手之后,才发现这事没想象中那么顺。
我的环境本身就比较复杂:在已经虚拟化的 Ubuntu 系统里又装了 vGPU 驱动。
没容器化之前,服务靠一堆本地依赖勉强维持,尤其是 CUDA 相关版本兼容,环境非常脆弱。
很多时候只要某个依赖被动过一点,模型服务就可能直接起不来。
也正因为这样,才有了这次“必须容器化”的经历。
先说结果
这次改造最直接的好处,不是“跑起来了”这么简单,而是把原来松散、脆弱、难迁移的运行环境,整理成了一套可复用、可打包、可迁移的方案。
另外,镜像体积降下来以后,构建和推送都稳了不少,失败重试的情况也少了很多。
这次主要踩了哪些坑
一开始我以为主要问题会出在容器编排,结果真正花时间的是四件事:
构建内容太大
一些不该进镜像的大文件被带进去了,导致镜像层太大,推送压力一下子变重。GPU 运行环境太敏感
vGPU、CUDA、推理运行时之间版本关系很挑,稍微不合适,就会在运行阶段出问题。超时参数看着配了,但实际没按预期生效
特别是带sudo的执行方式,环境变量继承经常和想的不一样,排查时很容易被带偏。docker push一直 retry,问题不在本地客户端
当时 push 过程反复重试,我先后查了本地环境、网关和 Nexus 服务。
最后定位到网关缓存空间被占满,导致上传过程异常。
处理方式是:网关对 Docker 仓库推送这条路径不做缓存,问题随即消失。
后来是怎么理顺的
真正有效的做法,是不再只盯某条命令对不对,而是把问题分开处理:
- 先把构建内容里不需要的东西排出去,减小镜像层
- 再把 GPU 依赖版本固定住,尽量不要来回变
- 推送失败时,客户端参数和服务端上传问题分开看
这样以后,很多以前混在一起的问题都能看清楚,不会一上来就全盘怀疑。
这次做完之后,留下了什么
这轮工作最有价值的地方,不是一次过关,而是留下了后面还能继续用的经验:
- 一套更轻量的镜像做法
- 一组更稳的 GPU 运行环境组合
- 一种更清楚的排查顺序(构建、运行、推送分开处理)
后面准备怎么做
接下来我会坚持几件事:
- 默认优先用瘦身后的镜像方案
- 控制单层大小,尽量避免超大层带来的上传不稳
- 再遇到推送异常,第一时间看上传相关日志,不只盯客户端
这次最大的收获,是把“能跑”变成了“能迁移、能复用、出问题也知道怎么查”。
对模型服务这种依赖敏感的场景来说,容器化不只是方便部署,更是为了把风险控制在自己能处理的范围里。