Skip to content

企业私有化大模型部署落地全流程:从硬件评估到服务上线 ​

给一个金融企业客户做私有化大模型部署,核心诉求很明确:数据绝对不能出内网,模型和推理服务全部部署在客户机房。从硬件评估到最终上线交付,花了三周。这篇完整复盘全流程。

私有化部署的核心约束 ​

跟云端调用 API 完全是两个世界:

┌──────────────────────────────────────────────────────────────┐
│              云端 API vs 私有化部署                           │
├────────────────────────┬─────────────────────────────────────┤
│     云端 API            │       私有化部署                    │
├────────────────────────┼─────────────────────────────────────┤
│ 按量付费,用多少花多少  │ 一次性硬件投入,固定成本             │
│ 弹性扩缩容              │ 硬件固定,算力天花板明确             │
│ 模型随便换              │ 部署后换模型成本高                   │
│ 运维云厂商负责          │ 运维全靠自己                        │
│ 数据上传到云端          │ 数据不出内网                        │
│ 模型效果直接用最好的    │ 受硬件限制,模型可能要量化压缩       │
└────────────────────────┴─────────────────────────────────────┘

私有化部署的核心矛盾是:在有限的硬件预算下,跑出接近云端 API 的效果。

第一步:硬件评估 ​

这是整个项目的基础,算错了要么浪费钱,要么跑不起来。

显存需求计算公式 ​

模型显存占用 = 模型参数权重 + KV-Cache + 激活值

┌─────────────────────────────────────────────────────────────┐
│  13B 模型显存计算示例(FP16 精度)                           │
│                                                             │
│  模型参数:13B × 2 bytes = 26 GB                            │
│  KV-Cache(每路并发)≈ 1.5 GB                               │
│  激活值 + 碎片 ≈ 2 GB                                       │
│                                                             │
│  基础占用:26 + 2 = 28 GB                                   │
│  每增加 1 路并发:+ 1.5 GB                                  │
│                                                             │
│  目标 10 路并发:28 + 10 × 1.5 = 43 GB → A100 80G 够用      │
│  目标 20 路并发:28 + 20 × 1.5 = 58 GB → A100 80G 够用      │
│  目标 30 路并发:28 + 30 × 1.5 = 73 GB → A100 80G 刚好      │
│                                                             │
│  如果用 INT4 量化:参数 26GB → 6.5 GB                       │
│  基础占用降到 8.5 GB,同卡可扛 40+ 路并发                    │
└─────────────────────────────────────────────────────────────┘

硬件选型方案 ​

根据客户预算和性能要求,给了三档方案:

方案GPU显存适配模型并发能力大致成本
高配1× A100 80G80 GB13B FP16 / 70B INT420-30 路~15 万
中配1× A10 24G24 GB7B FP16 / 13B INT48-12 路~4 万
低配1× RTX 409024 GB7B FP16 / 13B INT48-12 路~1.5 万

客户选了中配方案:A10 24G + 13B 模型 INT4 量化。原因:金融场景对效果有要求但不能接受 A100 的成本,13B INT4 是效果和成本的平衡点。

容易忽略的硬件细节 ​

  • CPU:推理服务 CPU 占用不高,但 tokenizer 编码和数据预处理需要算力。建议 8 核以上
  • 内存:至少是显存的 2 倍。模型加载时需要先把权重读进内存再传到显存
  • 磁盘:SSD 必须。模型文件动辄几十 GB,HDD 加载慢到怀疑人生
  • 网络:内网千兆够用,但如果多卡分布式推理需要万兆

第二步:模型选型与量化 ​

模型选型 ​

金融场景对中文理解和专业术语要求高,候选模型:

模型参数量中文能力金融领域显存(FP16)
Qwen2-13B-Chat13B优秀中等26 GB
Qwen2-7B-Chat7B优秀中等14 GB
ChatGLM3-6B6B优秀较弱12 GB
Baichuan2-13B13B优秀中等26 GB

最终选 Qwen2-13B-Chat:中文能力最强,社区生态好,vLLM 支持完善。

量化方案对比 ​

A10 24G 跑 13B FP16 显存不够(26 GB > 24 GB),必须量化。

量化方案对比:

精度        显存(13B)    效果损失    推理速度    推荐场景
─────────────────────────────────────────────────────────
FP16        26 GB        基准        基准        显存够就用
INT8        13 GB        ~2%        略快        显存不够但想保效果
INT4(AWQ)   6.5 GB       ~5%        快          显存紧张、要并发
INT4(GPTQ)  6.5 GB       ~6%        快          同上,AWQ 替代
GGUF Q4     7 GB         ~5%        较慢        Ollama 本地跑

选了 AWQ INT4 量化:效果损失最小(约 5%),推理速度比 FP16 还快(计算量小),显存降到 6.5 GB,24G 显存卡能扛 10+ 路并发。

量化操作 ​

直接用社区预制的 AWQ 量化模型,不用自己量化(自己量化需要校准数据集,耗时长且效果不可控):

bash
# 下载 AWQ 量化模型
huggingface-cli download Qwen/Qwen2-13B-Chat-AWQ \
    --local-dir /models/Qwen2-13B-Chat-AWQ

# 验证模型完整性
ls -lh /models/Qwen2-13B-Chat-AWQ/
# 总大小约 7-8 GB

第三步:推理服务搭建 ​

部署架构 ​

┌─────────────────────────────────────────────────────────────┐
│                    客户内网环境                              │
│                                                             │
│  ┌──────────┐     ┌──────────────┐     ┌───────────────┐   │
│  │ Nginx    │────→│ API 网关     │────→│ vLLM 推理服务  │   │
│  │ (443)    │     │ (FastAPI)    │     │ (GPU 推理)    │   │
│  └──────────┘     └──────┬───────┘     └───────┬───────┘   │
│                          │                     │           │
│                   ┌──────┴───────┐      ┌──────┴───────┐   │
│                   │ Redis        │      │ GPU: A10     │   │
│                   │ (会话缓存)   │      │ 显存: 24GB   │   │
│                   └──────────────┘      └──────────────┘   │
│                                                             │
│  ┌──────────────────────────────────────────────────────┐  │
│  │              监控 & 运维                              │  │
│  │  Prometheus + Grafana (GPU/服务监控)                  │  │
│  │  日志收集 (Loki / 文件日志)                           │  │
│  └──────────────────────────────────────────────────────┘  │
│                                                             │
│  所有数据在内网流转,不访问任何外部网络                      │
└─────────────────────────────────────────────────────────────┘

vLLM 推理服务 ​

用 Docker 部署,保证环境一致性:

dockerfile
FROM vllm/vllm-openai:latest

# 拷贝模型文件(内网无法从 HuggingFace 下载)
COPY ./models/Qwen2-13B-Chat-AWQ /models/Qwen2-13B-Chat-AWQ

# 启动命令
CMD ["--model", "/models/Qwen2-13B-Chat-AWQ", \
     "--quantization", "awq", \
     "--max-model-len", "4096", \
     "--max-num-seqs", "12", \
     "--gpu-memory-utilization", "0.9", \
     "--host", "0.0.0.0", \
     "--port", "8000"]
yaml
# docker-compose.yml
version: '3.8'
services:
  vllm:
    build: .
    container_name: vllm-server
    restart: always
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: 1
              capabilities: [gpu]
    ports:
      - "8000:8000"
    volumes:
      - ./models:/models:ro
    environment:
      - NVIDIA_VISIBLE_DEVICES=0

  gateway:
    image: python:3.11-slim
    container_name: api-gateway
    restart: always
    depends_on:
      - vllm
    ports:
      - "8080:8080"
    # 网关代码...

  redis:
    image: redis:7-alpine
    container_name: redis
    restart: always
    ports:
      - "6379:6379"

vLLM 兼容 OpenAI API 格式 ​

vLLM 默认提供 OpenAI 兼容的 API 接口,业务代码不用改:

python
from openai import OpenAI

# 指向内网 vLLM 服务
client = OpenAI(
    base_url="http://localhost:8000/v1",
    api_key="internal-key"  # 内网也需要鉴权
)

response = client.chat.completions.create(
    model="/models/Qwen2-13B-Chat-AWQ",
    messages=[{"role": "user", "content": "解释一下资产负债率"}],
    temperature=0.3,
    max_tokens=512
)

第四步:安全加固 ​

金融客户对安全的要求极其严格,以下几项必须做到:

1. 网络隔离 ​

nginx
# Nginx 只开放 443 端口,强制 HTTPS
server {
    listen 443 ssl;
    server_name llm.internal.company.com;

    ssl_certificate     /etc/ssl/internal.crt;
    ssl_certificate_key /etc/ssl/internal.key;

    # 只允许内网网段访问
    allow 10.0.0.0/8;
    deny  all;

    location / {
        proxy_pass http://gateway:8080;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        # SSE 流式响应配置
        proxy_buffering off;
        proxy_read_timeout 120s;
    }
}

2. API 鉴权 ​

即使内网也不能裸奔,加一层 API Key 鉴权 + 速率限制:

python
from fastapi import FastAPI, Request, HTTPException
from fastapi.middleware.cors import CORSMiddleware
import time

app = FastAPI()

VALID_KEYS = {"internal-service-key-xxx"}

# 速率限制:每分钟最多 60 次
rate_limit = {}

@app.middleware("http")
async def auth_and_rate_limit(request: Request, call_next):
    # 鉴权
    api_key = request.headers.get("Authorization", "").replace("Bearer ", "")
    if api_key not in VALID_KEYS:
        raise HTTPException(401, "Unauthorized")

    # 速率限制
    client_ip = request.client.host
    now = time.time()
    window = rate_limit.get(client_ip, [])
    window = [t for t in window if now - t < 60]  # 清理 60 秒外的记录
    if len(window) >= 60:
        raise HTTPException(429, "Rate limit exceeded")
    window.append(now)
    rate_limit[client_ip] = window

    return await call_next(request)

3. 内容安全过滤 ​

金融场景不能输出违规内容,加一层输入/输出过滤:

python
SENSITIVE_PATTERNS = [
    "股票代码", "买入", "卖出", "涨停",  # 投资建议类
    "身份证", "银行卡号",               # 隐私信息类
]

def filter_content(text: str) -> str:
    """过滤敏感内容"""
    for pattern in SENSITIVE_PATTERNS:
        if pattern in text:
            return f"[内容已过滤:包含敏感词「{pattern}」]"
    return text

# 在生成前后都做过滤
def safe_chat(prompt: str) -> str:
    # 输入过滤
    if filtered := filter_content(prompt):
        if filtered != prompt:
            return "抱歉,您的输入包含敏感内容"
    # 调用模型
    response = call_llm(prompt)
    # 输出过滤
    return filter_content(response)

第五步:监控与运维 ​

GPU 监控 ​

部署 dcgm-exporter 采集 GPU 指标:

yaml
# Prometheus 采集 GPU 指标
scrape_configs:
  - job_name: 'gpu'
    static_configs:
      - targets: ['dcgm-exporter:9400']
    metrics_path: '/metrics'

核心监控指标:

指标告警阈值说明
GPU 显存使用率> 95%即将 OOM
GPU 利用率< 10% 持续 5min服务可能挂了
推理延迟 P99> 10s排队过长
请求错误率> 5%服务异常
队列深度> 20并发过高

日志收集 ​

python
import logging
import json

logger = logging.getLogger("llm-service")

def log_request(request_id: str, prompt: str, response: str, usage: dict):
    """结构化日志,方便后续分析"""
    logger.info(json.dumps({
        "request_id": request_id,
        "prompt_length": len(prompt),
        "response_length": len(response),
        "prompt_tokens": usage.get("prompt_tokens"),
        "completion_tokens": usage.get("completion_tokens"),
        "latency_ms": usage.get("latency_ms"),
        "timestamp": time.time()
    }, ensure_ascii=False))
    # 注意:不记录 prompt 和 response 原文(金融合规要求)

上线效果 ​

最终交付的实测数据:

指标目标实测达标
首 token 延迟< 2s0.8s✅
完整回答延迟< 10s4.5s✅
并发支持10 路12 路✅
显存占用< 90%78%✅
数据外泄00✅

复盘总结 ​

企业私有化部署和云端调 API 是完全不同的工程。核心区别在于:你要对从硬件到应用的每一层负责。

关键经验清单:

环节核心决策经验
硬件评估显存 = 参数 + KV-Cache × 并发留 20% 余量
模型选型中文场景优先 Qwen / Baichuan看社区生态
量化方案AWQ INT4 是性价比之王效果损失约 5%
推理引擎vLLM 生产首选Continuous Batching
安全加固鉴权 + 限流 + 内容过滤内网也不能裸奔
监控运维GPU 显存 + 延迟 + 错误率提前配告警

最后一句:私有化部署不是「把模型跑起来」就完事,而是交付一个稳定、安全、可运维的完整服务。硬件评估、量化选型、安全加固、监控运维,缺一不可。

📖本文阅读--次|📊全站访问--次|👥访客--人