💡

文章摘要

从模型导出到生产服务,掌握 ML 模型的部署策略

1部署模式概述:批处理、在线与流式

模型部署不是把训练好的文件丢到服务器上就完事了。生产环境中的推理服务有三种基本模式,选择哪种取决于业务对延迟吞吐量和成本的综合要求。

批处理模式适合离线场景,比如每天凌晨跑一次用户画像更新或推荐系统召回。它的优势在于可以最大化 GPU 利用率,一次加载模型处理整批数据,单位推理成本最低。在线推理则要求实时响应,用户发一个请求,系统必须在几百毫秒内返回结果,这对服务的可用性和延迟稳定性提出了极高要求。流式推理介于两者之间,适用于连续数据源,比如传感器数据流或日志分析管道,模型需要持续消费数据并输出结果。

选择部署模式时,关键指标是 P99 延迟吞吐量。在线服务通常要求 P99 低于 200ms,批处理则关注每小时能处理多少条记录。流式场景需要同时考虑端到端延迟和背压处理能力。

图表加载中…
python
# 批处理推理:最大化 GPU 利用率
import torch
from torch.utils.data import DataLoader

def batch_inference(model, dataset, batch_size=256, device="cuda"):
    model.eval()
    loader = DataLoader(dataset, batch_size=batch_size, shuffle=False)
    predictions = []

    with torch.no_grad():
        for batch in loader:
            batch = batch.to(device)
            preds = model(batch)
            predictions.append(preds.cpu().numpy())

    return np.concatenate(predictions, axis=0)
python
# 在线推理:低延迟单条处理
class OnlineInferenceService:
    def __init__(self, model_path):
        self.model = torch.jit.load(model_path)
        self.model.eval()
        self.model.to("cuda")
        self._warmup()

    def _warmup(self):
        dummy = torch.randn(1, 768, device="cuda")
        for _ in range(10):
            _ = self.model(dummy)

    def predict(self, input_tensor):
        with torch.no_grad():
            return self.model(input_tensor.to("cuda"))
模式延迟要求吞吐量典型场景

批处理

无实时要求

极高

离线推荐、报表

在线推理

P99 < 200ms

中等

搜索、对话

流式推理

P99 < 50ms

持续

监控、异常检测

💡 一句话理解

在线服务一定要做模型预热,GPU 冷启动的前几次推理延迟可能高达正常值的 5 倍以上。

⚠️ 常见踩坑

批处理看似简单,但数据量增长时容易遭遇内存瓶颈,务必使用 DataLoader 分批加载而非一次性读入。

2模型格式:ONNX、TorchScript 与 SavedModel

训练框架的 checkpoint 不能直接用于生产PyTorch 的 pth 文件依赖完整的 Python 环境和源代码,TensorFlow 的 SavedModel 虽然自包含但体积庞大。生产部署需要将模型转换为优化后的通用格式。

ONNX 是目前最通用的中间表示格式,支持 PyTorchTensorFlow、scikit-learn 等多种框架的模型导出。它的核心价值在于跨框架互操作性和推理引擎兼容性。TorchScript 则是 PyTorch 生态内的序列化方案,通过 trace 或 script 将模型转换为独立于 Python 的格式。TensorFlow SavedModel 是 TF 的原生部署格式,与 TensorFlow Serving 深度集成。

选择格式时主要考虑三个因素:目标推理引擎、是否需要跨框架迁移、以及对动态图特性的依赖程度。如果只用 PyTorch 生态,TorchScript 最方便;如果需要部署到多平台,ONNX 是首选;如果是 TensorFlow 项目,SavedModel 配合 TF Serving 是标准方案。

图表加载中…
python
# PyTorch 导出 ONNX
import torch

model = MyModel()
model.load_state_dict(torch.load("model.pth"))
model.eval()

dummy_input = torch.randn(1, 3, 224, 224)
torch.onnx.export(
    model,
    dummy_input,
    "model.onnx",
    input_names=["input"],
    output_names=["output"],
    dynamic_axes={
        "input": {0: "batch_size"},
        "output": {0: "batch_size"},
    },
    opset_version=17,
)
python
# PyTorch 导出 TorchScript
import torch

# 方式一:trace(适合无控制流的模型)
model = MyModel().eval()
traced = torch.jit.trace(model, torch.randn(1, 768))
traced.save("model_traced.pt")

# 方式二:script(支持控制流)
class ScriptableModel(torch.nn.Module):
    def forward(self, x):
        if x.dim() > 2:
            return self.encoder(x)
        return self.decoder(x)

scripted = torch.jit.script(ScriptableModel())
scripted.save("model_scripted.pt")
格式框架支持推理引擎动态图

ONNX

多框架

ONNX Runtime

有限支持

TorchScript

PyTorch

LibTorch / C++

Script 支持

SavedModel

TensorFlow

TF Serving

完整支持

GGUF

LLM 专用

llama.cpp

不支持

💡 一句话理解

导出 ONNX 后务必用 onnx.checker.check_model() 验证格式正确性,再用 onnxruntime 做一次推理对比,确保精度无损。

⚠️ 常见踩坑

TorchScript 的 trace 模式无法捕获控制流(if/for),如果你的模型有动态逻辑,必须使用 script 模式或重构模型。

3FastAPI 构建推理 API

FastAPI 已经成为 Python 领域构建 ML 推理 API 的事实标准。它基于 ASGI 异步架构,天然支持高并发请求处理,同时自动生成 OpenAPI 文档,让前后端协作变得极其简单。

构建推理 API 的核心挑战不是写路由函数,而是管理模型生命周期。模型加载是重量级操作,必须在应用启动时完成一次,而不是每个请求都加载。此外,输入验证、批量推理、异步处理和错误处理都是生产级服务必须考虑的要素。

FastAPI 的依赖注入系统非常适合管理模型实例,通过 lifespan 上下文管理器可以优雅地处理启动和关闭逻辑。配合 Pydantic 模型做输入验证,可以在请求到达推理代码之前就拦截格式错误的数据,避免模型报错。

图表加载中…
python
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from contextlib import asynccontextmanager
import onnxruntime as ort
import numpy as np

class InferenceRequest(BaseModel):
    features: list
    request_id: str | None = None

class InferenceResponse(BaseModel):
    prediction: list
    request_id: str | None = None
    latency_ms: float

@asynccontextmanager
async def lifespan(app: FastAPI):
    app.state.session = ort.InferenceSession("model.onnx")
    yield
    app.state.session = None

app = FastAPI(lifespan=lifespan)

@app.post("/predict", response_model=InferenceResponse)
async def predict(req: InferenceRequest):
    import time
    t0 = time.time()
    try:
        input_data = np.array([req.features], dtype=np.float32)
        result = app.state.session.run(
            None, {"input": input_data}
        )
        return InferenceResponse(
            prediction=result[0].tolist(),
            request_id=req.request_id,
            latency_ms=(time.time() - t0) * 1000,
        )
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))
bash
# 启动服务并配置 Workers
# Uvicorn 多 worker 模式适合 CPU 推理
uvicorn main:app \
    --host 0.0.0.0 \
    --port 8000 \
    --workers 4 \
    --log-level info

# 测试 API
curl -X POST http://localhost:8000/predict \
    -H "Content-Type: application/json" \
    -d '{"features": [0.5, 1.2, -0.3], "request_id": "test-001"}'

# 查看自动生成的文档
# 浏览器打开 http://localhost:8000/docs
组件作用关键配置

lifespan

模型生命周期管理

启动加载、关闭释放

Pydantic Model

请求/响应验证

类型检查、默认值

Uvicorn Workers

并发处理

CPU 推理用多 worker

Middleware

请求日志/鉴权

计时、限流、认证

💡 一句话理解

对于 GPU 推理服务,不要用多 worker 模式,一个 worker 独占 GPU 即可,多 worker 会导致显存竞争和 OOM。

⚠️ 常见踩坑

不要在路由函数中加载模型!每次请求都加载模型会导致延迟从几毫秒飙升到数秒,并且迅速耗尽内存。

4Docker 容器化部署

Docker 容器化是 ML 服务从开发环境走向生产环境的关键一步。它解决了"在我机器上能跑"这个经典问题,确保开发、测试和生产环境的一致性。

ML 服务的 Docker 镜像有几个特殊考量:基础镜像的选择、GPU 驱动的支持、模型文件的打包策略。NVIDIA 提供的 CUDA 基础镜像是 GPU 推理的首选,它预装了 CUDA Toolkit 和 cuDNN,省去了大量环境配置工作。对于 CPU 推理,使用 slim 版本的 Python 镜像可以显著减小镜像体积。

模型文件通常很大,不应该直接打包进镜像。更优雅的方案是在容器启动时从对象存储(S3、OSS)下载模型,或者挂载外部存储卷。这样更新模型时只需要替换文件而不用重新构建镜像。

图表加载中…
dockerfile
# GPU 推理服务的 Dockerfile
FROM nvidia/cuda:12.1.0-runtime-ubuntu22.04

WORKDIR /app

# 安装 Python 和依赖
RUN apt-get update && apt-get install -y \
    python3.11 python3.11-venv && \
    rm -rf /var/lib/apt/lists/*

RUN python3.11 -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY app/ ./app/
# 模型文件不打包进镜像,启动时从 S3 下载
COPY scripts/download_model.sh ./

EXPOSE 8000

CMD ["sh", "download_model.sh", "&&", "uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
bash
# 构建和运行
docker build -t inference-service:v1 .

# CPU 模式运行
docker run -d --name inference \
    -p 8000:8000 \
    -e MODEL_URL=s3://bucket/model.onnx \
    inference-service:v1

# GPU 模式运行(需要 nvidia-container-toolkit
docker run -d --gpus all --name inference-gpu \
    -p 8000:8000 \
    -e MODEL_URL=s3://bucket/model.onnx \
    -e CUDA_VISIBLE_DEVICES=0 \
    inference-service:v1

# 查看容器日志
docker logs -f inference-gpu
镜像类型大小适用场景GPU 支持

nvidia/cuda:runtime

~3GB

GPU 推理

原生支持

python:3.11-slim

~200MB

CPU 推理

不支持

nvidia/cuda:devel

~10GB

训练+推理

完整 CUDA

onnxruntime:latest

~500MB

ONNX 推理

可选

💡 一句话理解

使用多阶段构建可以大幅减小镜像体积,把编译工具和依赖安装放在第一阶段,只拷贝最终产物到第二阶段的生产镜像。

⚠️ 常见踩坑

容器内不要用 root 用户运行服务,创建专用用户并设置最小权限,这是生产环境的基本安全要求。

5Kubernetes 部署

当单一容器无法应对流量规模时,Kubernetes 成为了事实上的容器编排标准。它提供了服务发现、自动重启、滚动更新、配置管理等核心能力,让 ML 服务能够以集群规模可靠运行。

在 K8s 上部署 ML 服务有几个关键资源对象需要理解:Deployment 管理 Pod 的生命周期和副本数,Service 提供稳定的网络入口和负载均衡,ConfigMap 和 Secret 管理配置和敏感信息,HorizontalPodAutoscaler 实现自动扩缩容。

GPU 节点的管理是 K8s ML 部署中最复杂的部分。需要配置 NVIDIA Device Plugin 让 K8s 能够感知和调度 GPU 资源,通过 resource requests 和 limits 精确控制每个 Pod 的 GPU 分配。对于多 GPU 服务器,还可以使用 MIG 技术将一张物理 GPU 切分为多个逻辑实例。

图表加载中…
yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: inference-service
spec:
  replicas: 3
  selector:
    matchLabels:
      app: inference
  template:
    metadata:
      labels:
        app: inference
    spec:
      containers:
        - name: inference
          image: registry.example.com/inference:v1
          ports:
            - containerPort: 8000
          resources:
            requests:
              cpu: "2"
              memory: "4Gi"
              nvidia.com/gpu: 1
            limits:
              cpu: "4"
              memory: "8Gi"
              nvidia.com/gpu: 1
          env:
            - name: MODEL_URL
              value: "s3://bucket/model.onnx"
          readinessProbe:
            httpGet:
              path: /health
              port: 8000
            initialDelaySeconds: 30
            periodSeconds: 10
yaml
apiVersion: v1
kind: Service
metadata:
  name: inference-service
spec:
  selector:
    app: inference
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8000
  type: ClusterIP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: inference-ingress
spec:
  rules:
    - host: api.example.com
      http:
        paths:
          - path: /predict
            pathType: Prefix
            backend:
              service:
                name: inference-service
                port:
                  number: 80
资源对象作用关键配置

Deployment

管理 Pod 副本

replicas、strategy

Service

网络入口+LB

type: ClusterIP/LoadBalancer

Ingress

外部路由

host、path、TLS

HPA

自动扩缩容

min/max replicas、metrics

💡 一句话理解

使用 readinessProbe 而不是 livenessProbe 来检测服务是否就绪,避免模型加载期间被误杀。livenessProbe 的超时时间要设置得足够长。

⚠️ 常见踩坑

GPU 资源请求和限制必须一致(requests = limits),否则 K8s 调度器可能将多个 Pod 调度到同一 GPU 上导致 OOM。

6自动扩缩容与负载均衡

生产环境的流量从来不是均匀的。早高峰、营销活动、突发事件都可能导致流量激增。自动扩缩容确保服务在流量峰值时不会崩溃,在低谷时不会浪费资源。

Kubernetes HPA 基于 CPU、内存或自定义指标自动调整 Pod 副本数。对于 ML 服务,CPU 使用率往往不是最好的扩缩容指标,更合理的是使用请求队列长度、P99 延迟或 GPU 利用率。Kubernetes 的自定义指标 API 配合 Prometheus 可以实现基于业务指标的扩缩容。

负载均衡方面,除了 K8s Service 自带的轮询策略,生产环境通常需要更智能的策略:最少连接数可以避免某些 Pod 因为处理慢请求而积压,一致性哈希可以确保同一个用户的请求路由到同一个 Pod,利用模型缓存提高响应速度。

图表加载中…
yaml
# 基于自定义指标的 HPA
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: inference-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: inference-service
  minReplicas: 2
  maxReplicas: 20
  metrics:
    - type: Pods
      pods:
        metric:
          name: inference_queue_depth
        target:
          type: AverageValue
          averageValue: "10"
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 70
  behavior:
    scaleUp:
      stabilizationWindowSeconds: 60
      policies:
        - type: Pods
          value: 4
          periodSeconds: 60
    scaleDown:
      stabilizationWindowSeconds: 300
      policies:
        - type: Percent
          value: 10
          periodSeconds: 120
python
# Prometheus 自定义指标导出器
from prometheus_client import Histogram, Counter, Gauge
import time

# 推理延迟直方图
INFERENCE_LATENCY = Histogram(
    "inference_latency_seconds",
    "Inference latency",
    buckets=[0.01, 0.05, 0.1, 0.2, 0.5, 1.0],
)

# 请求队列深度
QUEUE_DEPTH = Gauge(
    "inference_queue_depth",
    "Number of pending requests",
)

# 请求计数器
REQUEST_COUNT = Counter(
    "inference_requests_total",
    "Total inference requests",
    ["status"],
)

@INFERENCE_LATENCY.time()
def run_inference(input_data):
    REQUEST_COUNT.labels(status="success").inc()
    return model.predict(input_data)
扩缩容指标优点缺点适用场景

CPU 使用率

简单、原生支持

不反映 ML 负载

CPU 推理

请求队列深度

直接反映压力

需自定义指标

在线推理

P99 延迟

面向用户体验

指标波动大

SLA 严格的服务

GPU 利用率

GPU 推理最佳

需 GPU 监控插件

GPU 推理服务

💡 一句话理解

缩容策略的 stabilizationWindowSeconds 要设置得比扩容长(建议 300s 以上),避免流量波动导致频繁的扩缩容震荡。

⚠️ 常见踩坑

HPA 的扩缩容不是即时的,新 Pod 从创建到就绪通常需要 30-60 秒。如果流量突增非常剧烈,需要配合 KEDA 的预测性扩缩容或使用保底副本数。

7实战:完整部署流水线

理论讲完了,现在把所有环节串起来,构建一条从模型训练完成到生产服务上线的完整部署流水线。这条流水线涵盖模型导出、镜像构建、自动化测试、灰度发布和回滚机制。

一条成熟的 ML 部署流水线应该包含以下阶段:首先是 CI 阶段,在代码提交时自动运行单元测试和模型精度验证,确保新代码没有破坏推理逻辑。然后是构建阶段,将模型和代码打包为 Docker 镜像,推送到容器镜像仓库。接下来是 CD 阶段,通过 GitOps 工具(如 ArgoCD)将新镜像部署到 K8s 集群。最后是验证阶段,运行集成测试和金丝雀分析,确认服务指标正常后完成全量发布。

灰度发布是 ML 服务上线的关键安全措施。先用 5% 的流量测试新版本,对比新旧版本的延迟和准确率指标,如果一切正常再逐步提升比例到 50%、100%。整个过程可以完全自动化,一旦检测到异常立即回滚。

图表加载中…
yaml
# GitHub Actions CI/CD Pipeline
name: ML Deploy Pipeline
on:
  push:
    branches: [main]

jobs:
  test-and-build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Run model tests
        run: |
          pip install -r requirements.txt
          pytest tests/ -v
          python scripts/validate_model.py

      - name: Build and push
        run: |
          docker build -t registry/inference:${{ github.sha }} .
          docker push registry/inference:${{ github.sha }}

  deploy:
    needs: test-and-build
    runs-on: ubuntu-latest
    steps:
      - name: Update K8s manifest
        run: |
          sed -i "s|image:.*|image: registry/inference:${{ github.sha }}|" k8s/deployment.yaml
          kubectl apply -f k8s/

      - name: Wait and verify
        run: |
          kubectl rollout status deployment/inference-service --timeout=300s
          python scripts/smoke_test.py
yaml
# Argo Rollouts 金丝雀发布配置
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: inference-rollout
spec:
  replicas: 5
  strategy:
    canary:
      steps:
        - setWeight: 5
        - pause: { duration: 5m }
        - setWeight: 25
        - pause: { duration: 5m }
        - setWeight: 50
        - pause: { duration: 10m }
        - setWeight: 100
      analysis:
        templates:
          - templateName: inference-analysis
        startingStep: 1
---
apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
  name: inference-analysis
spec:
  metrics:
    - name: error-rate
      interval: 1m
      successCondition: result[0] < 0.01
      provider:
        prometheus:
          query: |
            sum(rate(inference_requests_total{status="error"}[1m]))
            /
            sum(rate(inference_requests_total[1m]))
流水线阶段工具产出物关键检查点

CI 测试

pytest

测试报告

单元测试 + 精度验证

镜像构建

Docker

容器镜像

镜像扫描、大小检查

CD 部署

ArgoCD

K8s 资源

滚动更新策略

金丝雀验证

Argo Rollouts

分析报告

错误率 < 1%

💡 一句话理解

每次模型更新前保留旧版本镜像至少 7 天,这样即使新版本上线后发现问题,回滚只需要切回旧镜像,几秒钟就能完成。

⚠️ 常见踩坑

不要在金丝雀阶段使用有状态的服务实例,灰度流量可能不均匀导致部分用户的数据状态不一致。确保推理服务是无状态的。

8更新于 2026-05-19 — 2026 年模型部署新趋势

本轮更新追加了2026 年模型部署新趋势的内容,包括推理引擎的演进、边缘部署的突破和 AIOps 在模型运维中的应用。推理引擎的 2026 年演进:
vLLM 已成为 LLM 推理的事实标准。PagedAttention 技术和连续批处理吞吐量比 Hugging Face Transformers 高出 24 倍。2026 年,vLLM 新增了自动张量并行、 speculative decoding 集成和多模型路由等功能,使其在生产环境中更加可靠。 Ollama 本地部署生态爆发。Ollama 支持超过 100 个开源模型,一键下载、量化、运行。2026 年新增的 Ollama Server 功能使其可以作为轻量级推理服务运行在局域网内,适合团队内部使用。 边缘部署的突破:
手机和笔记本本地运行 LLM 成为现实。 Google 的 Gemini Nano、Apple 的 Apple Intelligence、高通的 Cloud AI 100 等方案,使大模型可以在终端设备上运行,无需云端连接。这种
端侧推理
模式在隐私保护和延迟敏感的应用中尤为重要。AIOps 在模型运维中的应用:
自动异常检测和根因分析。当模型服务的延迟飙升或错误率上升时,AIOps 系统可以自动分析日志、指标和追踪数据,定位问题根源(是模型本身的问题、GPU 资源不足、还是网络延迟),并提出修复建议。 预测性扩缩容。基于历史流量模式的机器学习模型,预测未来的负载变化,提前进行扩缩容,而不是被动响应。

图表加载中…

💡 一句话理解

关注 vLLMOllama 的最新发展——它们代表了 2026 年模型部署的两个方向:生产级推理服务和本地快速原型。

⚠️ 常见踩坑

端侧推理虽然前景广阔,但仍受限于设备算力和散热。对于复杂任务(如长文本生成),云端推理仍然是最佳选择。