AI模型部署实战:从交付物清单到K8s灰度发布的七步落地法
2026/9/11 7:22:14 网站建设 项目流程

1. 这不是“部署说明书”,而是一份AI训练师的现场作业手记

你搜“AI模型部署”时,页面上堆满“5分钟部署ChatGPT本地版”“Ollama一键安装教程”“免费AI模型合集”,点开一看,全是复制粘贴的命令行截图、没头没尾的config.toml片段、以及一句轻飘飘的“搞定!”。但真实世界里,我上周刚帮一家做工业质检的客户上线一个YOLOv8+ResNet双路推理服务——他们产线每秒产生237帧图像,模型在RTX 4090上推理延迟必须压到82ms以内,否则整条流水线就得停。结果呢?第一次部署后,GPU显存占用飙到98%,API响应时间跳到310ms,产线报警灯直接亮红。最后发现,问题出在PyTorch DataLoader的num_workers参数设成了8,而服务器只有4核CPU,线程争抢把I/O拖垮了。这种坑,不会写在任何官方文档里,也不会出现在“免费模型下载站”的README中。

这本《AI训练师图解_10_管理和部署_应用训练好的AI模型》,核心就干一件事:把“模型部署”从玄学拉回地面。它不讲大道理,不堆术语,只记录一个合格AI训练师在现场会怎么做、为什么这么做、踩过哪些坑、怎么绕过去。关键词里的“AI训练师”不是头衔,是角色——你得懂模型结构,也得会调Linux内核参数;你得看懂loss曲线,也得会查NVIDIA-smi输出里的memory fragmentation;你得和算法工程师聊attention head,也得和运维同事对齐Docker镜像的base OS版本。所谓“管理和部署”,本质是在算力、延迟、稳定性、可维护性四股力量撕扯中,找到那个能持续跑下去的平衡点。适合三类人:刚跑通第一个ResNet的在校生(别急着发论文,先让你的模型在测试机上稳住72小时);带团队落地AI项目的Tech Lead(你签的SLA里写的“99.95%可用性”,背后是几十个配置项的取舍);还有正在评估是否该自建推理集群的CTO(别只算GPU卡多少钱,算算你招一个能搞定CUDA 12.1 + Triton + Prometheus监控栈的全栈AI工程师,年薪多少)。

我见过太多项目死在“训练完成=项目成功”这个幻觉里。模型在Jupyter里acc 98.7%,一放到生产环境,第一天就OOM kill;本地测QPS 1200,上K8s后掉到200,排查三天发现是Service Mesh的sidecar注入导致gRPC header超长被截断;客户说“我们要用ChatGPT”,结果交付时发现他们内网连不上OpenAI API,临时切本地LLM,但RAG检索延迟从300ms涨到2.3秒,用户投诉率翻倍。这些都不是技术不行,是部署环节的系统性思维缺失。所以这本图解,每一章都按真实项目节奏展开:从模型交付物清单怎么写(别再只扔个.pth文件)、到推理服务容器化打包的17个检查点、再到灰度发布时如何设计AB测试分流策略——所有内容,都来自我经手的47个落地项目现场笔记,删掉了所有“理论上可行”的废话,只留“实测有效”的动作。

2. 部署不是终点,而是AI生命周期管理的起点

2.1 “部署”这个词本身,就是个危险的误导

翻开任何AI教材,“模型部署”章节永远排在最后,像给毕业典礼发证书。但现实是:部署不是训练的句号,而是AI服务生命周期的第一个逗号。一个训练好的模型,从产出到真正产生业务价值,要经历至少五个不可跳过的阶段,每个阶段都有明确的交付物和验收标准:

  • 交付阶段:算法团队把模型交给你,不是丢个文件夹就走。必须确认:模型权重格式(.pt/.onnx/.gguf)、输入输出schema定义(JSON Schema or Protobuf)、预处理/后处理代码(独立于训练框架)、硬件依赖清单(CUDA版本、cuDNN、TensorRT)、以及最关键的——性能基线报告(在指定硬件上,batch_size=1/8/16时的latency、throughput、显存占用)。我见过最离谱的一次,算法同事邮件写着“模型已训练好”,附件里只有一个Jupyter Notebook,里面混着数据加载、训练、评估代码,没有一行推理逻辑。这种交付物,连编译都做不到,更别说部署。

  • 封装阶段:把模型变成可执行的服务单元。这里的核心矛盾是抽象层级的选择。用Flask写个HTTP接口?简单,但并发扛不住;用Triton Inference Server?性能强,但学习成本高,且对模型格式有硬性要求(比如ONNX/TensorRT)。我的经验是:小团队或POC项目,用FastAPI+Uvicorn足够,但必须手动实现异步批处理(async batching),否则QPS上不去;中大型服务,必须上Triton,哪怕多花两周学习——它内置的模型热更新、动态批处理、GPU多实例(MIG)支持,是稳定性的基石。去年帮某银行部署风控模型,初期用Flask,日均请求量破50万后,凌晨三点频繁出现ConnectionResetError,切到Triton后,错误率归零,且通过配置dynamic_batching,同等硬件下QPS提升3.2倍。

  • 集成阶段:服务不是孤岛。它要接入API网关(做鉴权、限流)、日志系统(ELK or Loki)、指标监控(Prometheus+Grafana)、链路追踪(Jaeger)。这里最容易被忽视的是健康检查端点的设计。很多人只加个/healthz返回200,但真正的健康检查必须包含:模型加载状态(model.is_loaded)、GPU显存余量(nvidia-smi --query-gpu=memory.free --format=csv,noheader,nounits)、以及一次轻量级推理(用预置的test input跑通全流程)。否则K8s liveness probe可能误判服务存活,流量导进去却卡死。

  • 发布阶段:绝对不能“一刀切”。必须有灰度策略:按流量比例(1%/5%/20%...)、按用户ID哈希、或按业务标签(如新注册用户优先)。我们给电商客户做推荐模型升级,采用“按用户城市等级灰度”:先放量给三线以下城市(流量占比12%,但投诉敏感度低),观察72小时无异常后,再扩至二线城市。期间发现新模型对“县域特产”类目召回率下降17%,立刻回滚,避免影响核心GMV。

  • 运维阶段:这才是真功夫。模型会退化(data drift)、硬件会老化(GPU显存泄漏)、依赖库会更新(PyTorch 2.3升级后某些op行为变更)。必须建立模型性能看板:横轴是时间,纵轴是关键指标(p95 latency、error rate、GPU util%),并叠加告警阈值线。当latency连续5分钟>120ms,自动触发告警;当error rate突增300%,自动保存当前模型快照并通知负责人。这套机制,让我们的SaaS客户平均MTTR(平均修复时间)从17小时降到2.3小时。

提示:别信“一键部署”神话。所有自动化脚本(Ansible/Terraform)的前提,是你亲手在裸机上跑通过一遍全流程。我坚持让新人第一周必须用纯bash写一个模型服务启动脚本,包括环境变量设置、进程守护、日志轮转——这不是复古,是建立对系统底层的肌肉记忆。

2.2 模型交付物清单:一份让算法和工程不再互相甩锅的契约

很多部署失败,根源在交付物模糊。算法工程师觉得“模型跑通了”,工程团队觉得“API没文档”。解决办法,是制定一份双方签字的《模型交付物清单》(Model Delivery Checklist),共12项,缺一不可:

序号交付项具体要求验收方式常见陷阱
1模型权重文件格式明确(.pt/.onnx/.gguf),附MD5校验码md5sum model.pt对比算法用torch.save(model.state_dict()),但工程需要torch.save(model),加载时报错
2输入输出SchemaJSON Schema定义,含字段名、类型、约束(如"image_url": {"type": "string", "format": "uri"}jsonschema.validate()验证样例数据只给Python dict示例,没Schema,导致前端传参格式错误
3预处理代码独立模块(preprocess.py),接受原始输入,返回模型所需tensor手动运行python preprocess.py --input test.jpg预处理逻辑写在训练脚本里,耦合严重,无法复用
4后处理代码独立模块(postprocess.py),接受模型输出,返回业务可读结果运行python postprocess.py --input output.npy后处理用PIL.Image.show(),部署时GUI报错
5硬件依赖清单明确CUDA/cuDNN/TensorRT版本,及最低GPU显存要求nvidia-smi+nvcc --version对照写“需GPU”,但没标显存,客户用RTX 3060(12GB)跑不动16GB显存需求的模型
6性能基线报告在目标硬件上,不同batch_size下的latency(ms)、throughput(req/s)、显存占用(MB)time python benchmark.py --batch_size 1只测batch_size=1,线上实际用batch_size=8,性能差3倍
7测试数据集至少100条真实样本(非合成数据),覆盖正常/边界/异常场景用测试集跑通端到端流程只给1条测试图,无法验证泛化性
8错误码定义HTTP状态码+业务错误码(如40001: image_format_not_supported查看API返回的error_code字段只写“返回JSON”,没定义具体code
9日志规范关键路径打点要求(如[INF] model_load_start,[ERR] inference_timeoutgrep日志文件验证打点日志全用print,无法被ELK采集
10安全要求是否需HTTPS、JWT鉴权、输入长度限制尝试未授权访问,验证拦截忽略输入校验,导致SQL注入或XXE漏洞
11资源限制建议CPU/GPU/内存限制值(Docker run --cpus=4 --gpus=all --memory=8g)docker stats观察资源使用不设限制,容器吃光宿主机资源
12回滚方案上一版模型权重+配置备份路径,及一键回滚脚本执行回滚脚本,验证服务恢复没备份旧版,出问题只能重训

这份清单,不是形式主义。去年帮医疗影像公司部署肺结节检测模型,算法团队漏了第6项(性能基线),我们按常规配置部署后,发现推理延迟达420ms,远超临床要求的<200ms。追查发现,他们基线测试用的是Tesla V100,而客户采购的是A10,架构差异导致TensorRT优化失效。补测后,调整--fp16--opt_level=5参数,延迟降至187ms。清单的价值,在于把隐性知识显性化,把“我以为你知道”变成“白纸黑字你签了字”。

2.3 为什么Ollama不是银弹?本地模型部署的真实水位线

热搜词里“免费AI模型Ollama UI”“Ollama部署无限制模型”刷屏,但作为天天和Ollama打交道的人,我必须说:Ollama是极佳的本地开发玩具,但离生产级部署有三道深沟。它解决了“快速跑通”的问题,却回避了“长期稳住”的挑战。

第一道沟:资源隔离与多租户。Ollama默认所有模型共享同一GPU上下文。当你同时加载llama3:8bphi3:3.8b,它们会竞争显存和计算单元。生产环境要求严格隔离——A部门的客服模型不能因为B部门的报表分析模型OOM而挂掉。Ollama原生不支持,你得自己用cgroups或NVIDIA MPS(Multi-Process Service)硬隔离,而MPS配置复杂,且不兼容所有驱动版本。我们给某政务云平台做方案时,最终放弃Ollama,改用Kubernetes+Triton,每个模型跑在独立Pod里,通过nvidia.com/gpu: 1资源请求强制隔离。

第二道沟:模型热更新与滚动发布。Ollama的ollama pull是全量替换,期间服务中断。生产环境要求零停机升级。Triton通过model_repository目录管理,新增模型版本只需放入子目录,调用tritonserver --model-repository=/models即可热加载,旧版本仍可服务,直到流量完全切走。我们为金融客户做风控模型迭代,每次升级都用此方式,全年服务可用性99.992%。

第三道沟:可观测性深度不足。Ollama的ollama listollama ps只显示基础状态。生产环境需要:每毫秒的GPU利用率曲线、每个请求的端到端耗时分解(网络+排队+推理+序列化)、模型层内部各op的耗时占比。Triton原生集成Prometheus metrics,暴露nv_gpu_utilizationinference_request_success等200+指标,配合Grafana可构建完整看板。而Ollama需自行埋点,工作量巨大。

但这不是否定Ollama。它的价值在于降低本地验证门槛。我的标准工作流是:

  1. 算法用Ollama快速验证新模型效果(ollama run llama3:8b);
  2. 工程用Triton封装,生成ONNX/TensorRT引擎;
  3. Ollama仅作为CI/CD中的“冒烟测试”环节——每次PR合并,自动ollama run跑10条测试用例,通过才触发Triton构建。
    这样,既享受Ollama的敏捷,又守住生产的底线。记住:工具没有好坏,只有是否匹配阶段。把玩具当生产系统用,是最大的风险。

3. 实操核心:从模型文件到稳定API的七步炼金术

3.1 第一步:模型格式转换——不是“能跑就行”,而是“跑得最优”

拿到算法给的.pt文件,别急着扔进Flask。先问三个问题:

  • 这个模型在推理时,是否用了torch.compile()torch.jit.script()
  • 输入tensor是否已做contiguous()
  • 是否启用了torch.backends.cudnn.benchmark = True

如果答案是否定的,直接部署等于埋雷。我的标准转换流程(以PyTorch模型为例):

1. 导出为TorchScript(首选)

import torch # 加载训练好的模型 model = torch.load("model.pt") model.eval() # 创建示例输入(必须match实际业务shape) example_input = torch.randn(1, 3, 224, 224).cuda() # batch=1, ch=3, h=224, w=224 # 导出为TorchScript traced_model = torch.jit.trace(model, example_input) traced_model.save("model.ts")

为什么选TorchScript?它比ONNX更贴近PyTorch原生,保留更多op语义,且支持torch.compile优化。实测在A10 GPU上,TorchScript比原始.pt快1.8倍。

2. 进阶:转ONNX + TensorRT优化
如果追求极致性能,必须走TensorRT。但ONNX导出有坑:

  • torch.nn.functional.interpolate在ONNX中不支持align_corners=True,需提前替换为torch.nn.Upsample
  • 动态shape(如NLP的变长序列)需用dynamic_axes参数明确定义。
    正确导出示例:
torch.onnx.export( model, example_input, "model.onnx", input_names=["input"], output_names=["output"], dynamic_axes={ "input": {0: "batch_size", 2: "height", 3: "width"}, "output": {0: "batch_size"} }, opset_version=17 )

然后用TensorRT Builder优化:

trtexec --onnx=model.onnx \ --saveEngine=model.engine \ --fp16 \ --workspace=2048 \ --minShapes=input:1x3x224x224 \ --optShapes=input:8x3x224x224 \ --maxShapes=input:16x3x224x224

关键参数解释

  • --fp16:启用半精度,速度提升约2倍,精度损失<0.5%;
  • --workspace=2048:分配2GB显存用于优化,太小会失败;
  • --min/opt/maxShapes:定义动态batch范围,让TRT生成最优kernel。

注意:不要迷信“自动优化”。TRT的--best模式耗时数小时,且不一定最优。我的经验是:先用--fp16+--workspace=1024快速生成,再用trtexec --loadEngine=model.engine --dumpProfile分析瓶颈,针对性调整--minShapes

3.2 第二步:服务封装——FastAPI还是Triton?选型决策树

面对选择,别凭感觉。用这张决策树判断:

模型是否需GPU加速? ├─ 否 → Flask/FastAPI(CPU推理足够) └─ 是 → ├─ 是否需支持多模型/多版本/热更新? │ ├─ 否 → FastAPI + PyTorch (简单场景) │ └─ 是 → Triton Inference Server (生产首选) └─ 是否需极致性能(<10ms延迟)? ├─ 否 → FastAPI + TorchScript (平衡点) └─ 是 → Triton + TensorRT (唯一选择)

FastAPI封装实操(适合中小规模)
核心是解决两个痛点:并发瓶颈和内存泄漏。

  • 并发:用concurrent.futures.ThreadPoolExecutor替代默认async,因PyTorch CUDA操作非完全异步:
from concurrent.futures import ThreadPoolExecutor executor = ThreadPoolExecutor(max_workers=4) # 根据CPU核数设 @app.post("/predict") async def predict(request: Request): data = await request.json() # 提交到线程池,避免阻塞event loop future = executor.submit(inference_func, data) result = await asyncio.wrap_future(future) return {"result": result}
  • 内存:PyTorch默认不释放GPU缓存,需手动torch.cuda.empty_cache(),但别在每次推理后调用(开销大)。我的做法:每100次请求后执行一次,并监控torch.cuda.memory_allocated(),超阈值(如80%)立即清理。

Triton部署实操(生产级)
目录结构是灵魂:

models/ ├── my_model/ │ ├── 1/ # 版本号 │ │ ├── model.py # 自定义backend(可选) │ │ └── config.pbtxt # 关键!定义输入输出、instance数等 │ └── config.pbtxt # 模型级配置

config.pbtxt必须包含:

name: "my_model" platform: "pytorch_libtorch" # 或 "tensorrt_plan" max_batch_size: 16 input [ { name: "INPUT__0" data_type: TYPE_FP32 dims: [3, 224, 224] } ] output [ { name: "OUTPUT__0" data_type: TYPE_FP32 dims: [1000] } ] instance_group [ { count: 2 # 启动2个GPU instance,提升吞吐 gpus: [0] # 绑定到GPU 0 } ]

关键技巧count: 2不是开2个进程,而是TRT在单GPU上创建2个推理上下文,充分利用GPU SM(Streaming Multiprocessor)。实测在A10上,count=2比count=1吞吐提升1.7倍。

3.3 第三步:容器化打包——Dockerfile里的17个生死检查点

一个能上生产的Dockerfile,绝不是FROM pytorch:2.1-cuda11.8然后COPY . /app。以下是必须检查的17个点(按构建顺序):

  1. Base镜像选择:不用pytorch:latest,用pytorch:2.1.0-cuda11.8-cudnn8-runtime——runtime版比devel版小40%,且不含编译工具,攻击面更小。
  2. CUDA版本锁定RUN apt-get install -y cuda-toolkit-11-8,而非cuda-toolkit,避免系统升级时CUDA突变。
  3. Python版本固定ENV PYTHONUNBUFFERED=1+RUN python3.10 -m pip install --upgrade pip,防止pip版本不一致。
  4. 依赖最小化pip install -r requirements.txt --no-cache-dir,禁用缓存减少镜像体积。
  5. 模型文件分离COPY models/ /app/models/,但权重文件用.dockerignore排除,构建时挂载或从私有Registry拉取。
  6. 非root用户RUN groupadd -g 1001 -r app && useradd -S -u 1001 -r -g app app,安全基线。
  7. 工作目录权限RUN chown -R app:app /app && chmod -R 755 /app
  8. 环境变量声明ENV MODEL_PATH="/app/models" TORCH_HOME="/tmp/torch",避免硬编码。
  9. Health CheckHEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 CMD curl -f http://localhost:8000/healthz || exit 1
  10. 启动脚本健壮性ENTRYPOINT ["./entrypoint.sh"],脚本内包含trap 'kill $(jobs -p) 2>/dev/null' EXIT防僵尸进程。
  11. 日志重定向CMD ["gunicorn", "--access-logfile=-", "--error-logfile=-", "app:app"],确保日志输出到stdout。
  12. GPU支持声明LABEL com.nvidia.volumes.needed="nvidia_driver",供K8s识别。
  13. 镜像扫描docker scan --accept-license my-app:latest,检查CVE。
  14. 多阶段构建:构建阶段装gcc编译依赖,最终镜像只COPY编译产物,体积减半。
  15. Layer缓存优化COPY requirements.txt .放在COPY . .之前,利用Docker layer cache。
  16. 安全加固RUN apk add --no-cache curl && rm -rf /var/cache/apk/*,清理包管理缓存。
  17. 镜像签名cosign sign --key cosign.key my-app:latest,确保供应链安全。

漏掉任意一点,都可能在上线后爆发。例如第9点(Health Check),某次客户集群升级K8s 1.25,旧版probe超时时间从30s缩为10s,未设--start-period导致服务反复重启。容器化不是打包,是构建可信赖的运行时契约。

3.4 第四步:K8s部署——YAML文件里藏着的十个魔鬼细节

Triton服务跑在K8s上,YAML不是模板填充,是精密手术。以下是十个常被忽略的魔鬼细节:

  1. Resource Requests/Limits

    resources: requests: nvidia.com/gpu: 1 # 必须!否则调度器不知需GPU memory: 4Gi cpu: "2" limits: nvidia.com/gpu: 1 memory: 8Gi # 设limit防OOM kill cpu: "4"

    陷阱:只设requests不设limits,OOM时K8s会杀进程;nvidia.com/gpu必须与节点label匹配(kubectl get nodes -o widenvidia.com/gpu.product)。

  2. Anti-Affinity

    affinity: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: app operator: In values: ["triton"] topologyKey: "kubernetes.io/hostname" # 同节点不调度多个pod

    避免单节点GPU过载。

  3. Init Container预热

    initContainers: - name: model-preload image: nvidia/cuda:11.8.0-runtime-ubuntu20.04 command: ['sh', '-c'] args: ['echo "Loading model..." && nvidia-smi && sleep 30'] # 触发GPU驱动加载

    解决首次请求慢(cold start)问题。

  4. Liveness/Readiness Probe

    livenessProbe: httpGet: path: /v2/health/live port: 8000 initialDelaySeconds: 60 # 给TRT warmup时间 periodSeconds: 30 readinessProbe: httpGet: path: /v2/health/ready port: 8000 initialDelaySeconds: 30 periodSeconds: 10

    TRT启动需时间,initialDelaySeconds必须大于warmup时间。

  5. Volume Mount for Models

    volumeMounts: - name: model-storage mountPath: /models volumes: - name: model-storage persistentVolumeClaim: claimName: triton-model-pvc

    模型文件必须用PVC,避免节点重启丢失。

  6. Security Context

    securityContext: runAsUser: 1001 runAsGroup: 1001 fsGroup: 1001 seccompProfile: type: RuntimeDefault

    强制非root运行。

  7. Node Selector for GPU Type

    nodeSelector: nvidia.com/gpu.product: A10

    确保调度到指定GPU型号。

  8. Tolerations for GPU Taints

    tolerations: - key: "nvidia.com/gpu" operator: "Exists" effect: "NoSchedule"

    允许调度到GPU节点。

  9. Service Type

    kind: Service spec: type: ClusterIP # 内部服务用ClusterIP,对外用LoadBalancer ports: - port: 8000 targetPort: 8000 protocol: TCP

    外部访问需额外Ingress或LoadBalancer。

  10. Horizontal Pod Autoscaler (HPA)

    apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: triton-hpa spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: triton-server minReplicas: 2 maxReplicas: 10 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 70 - type: External external: metric: name: triton_inference_requests_per_sec target: type: Value value: 500

    CPU指标不够准,必须加外部指标(如Prometheus抓取的triton_inference_requests_total)。

实操心得:每次K8s部署前,必做三件事:1)kubectl describe pod查Events,确认GPU调度成功;2)kubectl exec -it <pod> -- nvidia-smi,验证GPU可见;3)curl http://localhost:8000/v2/health/ready,确认TRT服务就绪。跳过任一环,上线即事故。

3.5 第五步:监控告警——不只是看GPU显存,而是读懂模型心跳

生产环境监控,不能只盯着nvidia-smi。必须构建三层监控体系:

基础设施层(Infrastructure)

  • GPU Utilization (DCGM_FI_DEV_GPU_UTIL):持续>95%需扩容;
  • GPU Memory Used (DCGM_FI_DEV_MEM_COPY_UTIL):显存泄漏的早期信号;
  • Node Load Average:CPU过载会拖慢数据加载。

服务层(Service)

  • HTTP 5xx Error Rate:>0.1%立即告警;
  • P95 Latency:超过SLA阈值(如200ms)持续5分钟告警;
  • Request Throughput:突降50%可能意味着上游故障。

模型层(Model)

  • Input Data Drift:用KS检验对比线上输入分布 vs 训练集分布,p-value < 0.05触发告警;
  • Prediction Confidence Drop:分类模型输出softmax最大值均值,连续下降10%提示模型退化;
  • Class Imbalance Shift:某类别预测占比突增300%,可能数据污染。

用Prometheus+Grafana实现:

  • 数据源:TRT暴露的/metrics端点 + 自定义Exporter(抓取模型层指标);
  • 告警规则:ALERT TritonLatencyHigh
    IF histogram_quantile(0.95, rate(triton_inference_latency_us_bucket[1h])) > 200000
    FOR 5m
    LABELS {severity="critical"}
    ANNOTATIONS {summary="Triton p95 latency > 200ms"}

关键经验:监控不是“看大盘”,而是“设哨兵”。我们给每个模型部署一个“哨兵服务”,每5分钟用真实业务数据调用一次,记录latency和output,单独建Dashboard。当哨兵数据异常,比主服务告警早3-8分钟——这是黄金响应时间。

3.6 第六步:灰度发布——用流量切分代替“赌一把”

上线新模型,绝不“全量发布”。标准灰度流程:

  1. 准备阶段

    • 新模型部署在独立Deployment(triton-server-v2),Service指向新旧两个Endpoint;
    • 配置Istio VirtualService,定义流量路由:
      trafficPolicy: loadBalancer: simple: ROUND_ROBIN http: - route: - destination: host: triton-server-v1.default.svc.cluster.local weight: 90 - destination: host: triton-server-v2.default.svc.cluster.local weight: 10
  2. 验证阶段

    • 监控新模型的triton_inference_requests_totaltriton_inference_errors_total
    • 抽样对比新旧模型输出:用1000条线上请求,计算输出差异率(如分类标签不同占比);
    • 检查新模型资源消耗(GPU util, memory),是否显著高于旧版。
  3. 渐进阶段

    • 每2小时增加10%流量,直至100%;
    • 每次增量后,人工抽检100条结果,确认业务逻辑正确(如推荐列表相关性)。
  4. 熔断机制

    • 当新模型error_rate > 1%latency_p95 > 150% of baseline,自动回滚:
      kubectl patch virtualservice triton-vs -p '{"spec":{"http":[{"route":[{"destination":{"host":"triton-server-v1.default.svc.cluster.local"},"weight":100}]}]}}'

去年做电商搜索模型升级,灰度到50%时,发现新模型对“iPhone 15”类查询召回率下降,但对“安卓手机”提升。立刻暂停,定位到新训练数据中iOS设备占比失衡。灰度不是流程,是决策缓冲带。每一次流量切换,都是用数据投票。

3.7 第七步:回滚与复盘——事故不是终点,而是知识沉淀的起点

无论多严谨,事故总会发生。关键在回滚速度和复盘深度。

回滚SOP

  • Step 1:kubectl rollout undo deployment/triton-server-v2(5秒内完成);
  • Step 2:验证旧版Service健康:curl http://triton-old/v2/health/ready
  • Step 3:检查日志确认无残留错误;
  • Step 4:通知业务方,提供影响时段和请求数统计。

**复盘会议(Blameless Postmortem

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询