前言:那个让我凌晨3点修环境的夜晚

去年双十一前夕,同事小张在本地开发机上跑通了一套完整的 RAG 系统:Ollama 做模型推理、FastAPI 做接口、ChromaDB 做向量存储、Nginx 做反向代理。本地测试一切正常,信心满满地交给运维上线。

然后——

plaintext
1
2
3
4
5
6
7
8
9
小张:“在我电脑上能跑啊!”
运维:“服务器上 CUDA 版本不对。”
小张:“那装一下嘛。”
运维:“Python 版本也不对,你用的 3.10,服务器是 3.8。”
小张:“……”
运维:“依赖包也有冲突,torch 2.1 和 transformers 版本不兼容。”
小张:“我本地明明好好的……”
运维:“行吧,今晚一起调试。”

凌晨3点,问题终于解决。但所有人都知道:同样的事情还会再发生。

这就是为什么我们需要 Docker。

今天这篇文章,是这个专栏的收官之作。我们将从最基础的 Docker 概念讲起,一步步完成 3个实战项目的容器化部署,最后打通 CI/CD 和监控链路。

你将学到:

✅ 5分钟掌握 Docker 核心概念
✅ Ollama + WebUI 一键容器化部署
✅ vLLM 生产级 GPU 部署(多卡、健康检查、资源限制)
✅ RAG 全栈应用容器化(FastAPI + ChromaDB + Nginx + HTTPS)
✅ GitHub Actions 自动化部署流水线
✅ Prometheus + Grafana 监控方案
✅ 生产环境避坑指南

适合人群: 有 Python 基础、了解 AI 应用开发、想掌握容器化部署的开发者。

一、为什么必须容器化?

1.1 "在我电脑上能跑"的三大惨案

在 AI 开发领域,环境问题尤其严重。我总结了三大高频惨案:

表格
惨案类型 具体表现 根因
CUDA 地狱 本地 CUDA 11.8,服务器 CUDA 12.1,torch 版本冲突 GPU 驱动、CUDA Toolkit、PyTorch 版本强耦合
依赖地狱 transformers 4.35 和 vLLM 0.2.x 不兼容 Python 包版本冲突,系统级依赖不同
配置漂移 开发、测试、生产三套环境,配置逐渐分化 手动配置无法保证一致性

1.2 Docker 解决的三大痛点

plaintext
1
2
3
4
5
6
7
8
9
10
┌─────────────────────────────────────────────────────┐
│ Docker 容器化 │
├─────────────┬──────────────┬────────────────────────┤
│ 环境一致性 │ 一键部署 │ 弹性伸缩 │
│ │ │ │
│ 开发=测试 │ docker up │ 秒级启动新实例 │
│ =生产 │ 一键启动 │ 横向扩展无压力 │
│ │ 全部服务 │ │
└─────────────┴──────────────┴────────────────────────┘

痛点一:环境一致性

Docker 把应用 + 依赖 + 配置 + 系统库全部打包成镜像,保证 “Build once, run anywhere”。你的 CUDA 11.8 + Python 3.10 + torch 2.1 在本地能跑,在任何服务器上也能跑。

痛点二:部署效率

以前部署一套 RAG 系统需要:装驱动 → 装 CUDA → 装 Python → 装依赖 → 配 Nginx → 配数据库……至少半天。现在一条命令:

bash
1
2
docker compose up -d

痛点三:弹性伸缩

流量翻倍?以前需要重新配置一台服务器。现在只需要:

bash
1
2
docker compose up --scale api-server=4

4个实例瞬间拉起,负载均衡自动分配。

1.3 部署方式对比

表格
部署方式 环境一致性 扩展性 运维成本 启动速度 GPU支持 适用场景
裸机部署 ❌ 差 ❌ 低 🔴 高 🐢 分钟级 手动配置 个人开发
Docker ✅ 完美 ⚡ 中 🟢 低 🚀 秒级 原生支持 中小团队
K8s ✅ 完美 🚀 高 🟡 中 🚀 秒级 需插件 大规模集群
Docker + K8s ✅ 完美 🚀🚀 极高 🟡 中 🚀 秒级 原生支持 企业级生产

💡 建议:中小团队先用 Docker Compose,当服务数量超过 10 个、需要多机编排时再上 K8s。不要为了用 K8s 而用 K8s。

二、Docker 基础速通(5分钟)

如果你已经熟悉 Docker,可以跳过这节。

2.1 核心概念

plaintext
1
2
3
4
5
6
镜像(Image):应用的"模板",包含代码 + 依赖 + 系统环境
↓ docker run
容器(Container):镜像的运行实例,相互隔离
↓ docker compose
编排(Compose):多个容器的协调运行

类比理解:

镜像 = 一张光盘(里面刻好了操作系统 + 软件)
容器 = 用这张光盘启动的一台虚拟机
Dockerfile = 光盘的刻录方案
docker-compose.yml = 一次放入多台光盘的播放列表

2.2 第一个 Dockerfile

dockerfile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

基础镜像:Python 3.10 + CUDA 11.8

FROM nvidia/cuda:11.8.0-base-ubuntu22.04

安装 Python

RUN apt-get update && apt-get install -y python3.10 python3-pip

设置工作目录

WORKDIR /app

复制依赖文件(利用缓存层)

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

复制应用代码

COPY . .

暴露端口

EXPOSE 8000

启动命令

CMD [“python3”, “-m”, “uvicorn”, “main:app”, “–host”, “0.0.0.0”, “–port”, “8000”]

2.3 docker-compose 速览

yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

docker-compose.yml - 多服务编排示例

version: ‘3.8’

services:
api: # 服务名
build: . # 使用当前目录的 Dockerfile
ports:
- “8000:8000” # 宿主机端口:容器端口
environment:
- MODEL_PATH=/models # 环境变量
volumes:
- ./data:/app/data # 挂载目录(数据持久化)
depends_on:
- db # 依赖关系:先启动 db

db:
image: chromadb/chroma:latest
volumes:
- chroma-data:/chroma/db # 命名卷(Docker 管理)

volumes: # 声明命名卷
chroma-data:

2.4 常用命令速查

bash
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

构建镜像

docker build -t my-app:v1 .

运行容器

docker run -d -p 8000:8000 --gpus all my-app:v1

启动所有服务

docker compose up -d

查看日志

docker compose logs -f api

进入容器

docker compose exec api bash

停止所有服务

docker compose down

查看资源使用

docker stats

三、实战一:Ollama + WebUI 容器化部署

最简单的入门实战:用 Docker Compose 一键部署本地大模型 + 对话界面。

3.1 架构概览

plaintext
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
┌──────────────────────────────────────────┐
│ Docker Network │
│ │
│ ┌──────────┐ ┌──────────────────┐ │
│ │ Ollama │◄────│ Open WebUI │ │
│ │ :11434 │ │ :3000 │ │
│ │ │ │ │ │
│ │ 本地大模型│ │ Web 对话界面 │ │
│ └──────────┘ └──────────────────┘ │
│ │ │ │
│ ┌────┴─────┐ ┌─────┴──────┐ │
│ │ models/ │ │ webui-data/│ │
│ │ (volume) │ │ (volume) │ │
│ └──────────┘ └────────────┘ │
└──────────────────────────────────────────┘

3.2 docker-compose.yml

yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
version: ‘3.8’

services:
ollama:
image: ollama/ollama:latest
container_name: ollama
ports:
- “11434:11434”
volumes:
- ollama-data:/root/.ollama
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: [gpu]
restart: unless-stopped
networks:
- ai-network

open-webui:
image: ghcr.io/open-webui/open-webui:main
container_name: open-webui
ports:
- “3000:8080”
environment:
- OLLAMA_BASE_URL=http://ollama:11434
- WEBUI_AUTH=false
volumes:
- webui-data:/app/backend/data
depends_on:
- ollama
restart: unless-stopped
networks:
- ai-network

volumes:
ollama-data:
webui-data:

networks:
ai-network:
driver: bridge

3.3 一键启动

bash
1
2
3
4
5
6
7
8
9
10
11
12

启动服务

docker compose up -d

等待 Ollama 就绪后,拉取模型

docker compose exec ollama ollama pull qwen2.5:7b

查看模型列表

docker compose exec ollama ollama list

访问 WebUI

浏览器打开 http://localhost:3000

💡 提示:ollama-data 使用命名卷持久化模型文件,即使容器删除,模型也不会丢失。

四、实战二:vLLM 生产级部署

Ollama 适合开发测试,但生产环境推荐 vLLM——它的吞吐量是 HuggingFace Transformers 的 24倍。

4.1 Dockerfile(Python + CUDA + vLLM)

dockerfile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
FROM vllm/vllm-openai:latest

设置环境变量

ENV MODEL_PATH=/models
CUDA_VISIBLE_DEVICES=0,1
VLLM_WORKER_MULTIPROC_METHOD=spawn

安装额外依赖(如需要)

RUN pip install --no-cache-dir
prometheus-client
tiktoken

创建非 root 用户(安全最佳实践)

RUN useradd -m -u 1000 vllm-user
USER vllm-user

模型目录(运行时通过 volume 挂载)

WORKDIR /models

EXPOSE 8000

健康检查:每30秒检查一次

HEALTHCHECK --interval=30s --timeout=10s --start-period=120s --retries=3
CMD curl -f http://localhost:8000/health || exit 1

启动 vLLM 服务(多GPU + 高性能配置)

ENTRYPOINT [“python3”, “-m”, “vllm.entrypoints.openai.api_server”]
CMD [“–model”, “/models/Qwen2.5-7B-Instruct”,
“–tensor-parallel-size”, “2”,
“–max-model-len”, “8192”,
“–gpu-memory-utilization”, “0.90”,
“–enable-chunked-prefill”,
“–disable-log-requests”,
“–host”, “0.0.0.0”,
“–port”, “8000”]

4.2 docker-compose.yml(生产配置)

yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
version: ‘3.8’

services:
vllm:
build:
context: ./vllm
dockerfile: Dockerfile
container_name: vllm-server
ports:
- “8000:8000”
volumes:
- model-cache:/models
- vllm-logs:/var/log/vllm
environment:
- CUDA_VISIBLE_DEVICES=0,1
- HF_TOKEN=HFTOKEN−VLLMAPIKEY={HF_TOKEN} - VLLM_API_KEY=HFTOKENVLLMAPIKEY={VLLM_API_KEY:-sk-vllm}
- NVIDIA_VISIBLE_DEVICES=all
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 2
capabilities: [gpu]
# 资源限制
mem_limit: 64g
cpus: 16
# 日志管理
logging:
driver: json-file
options:
max-size: “100m”
max-file: “3”
# 重启策略
restart: unless-stopped
# 共享内存(PyTorch 多进程需要)
shm_size: ‘8gb’
healthcheck:
test: [“CMD”, “curl”, “-f”, “http://localhost:8000/health”]
interval: 30s
timeout: 10s
retries: 3
start_period: 120s
networks:
- ai-network

volumes:
model-cache:
driver: local
vllm-logs:
driver: local

networks:
ai-network:
driver: bridge

4.3 关键参数说明

表格
参数 值 说明
tensor-parallel-size 2 张量并行 GPU 数量,7B 模型 2 卡足够
max-model-len 8192 最大上下文长度,根据显存调整
gpu-memory-utilization 0.90 GPU 显存利用率上限,留 10% 余量
shm_size 8gb 共享内存,PyTorch DataLoader 必需
max-size/max-file 100m/3 日志滚动策略,防止磁盘打满
start_period 120s 健康检查启动宽限期,模型加载需要时间

五、实战三:RAG 全栈应用容器化

这是最贴近生产的实战——把一个完整的 RAG 系统容器化部署。

5.1 系统架构

plaintext
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
HTTPS (443)

┌────┴────┐
│ Nginx │ ← SSL终止 + 反向代理 + 静态资源
│ :443/80 │
└────┬────┘

┌─────────┼─────────┐
│ │ │
┌────┴───┐ ┌───┴────┐ ┌─┴────────┐
│FastAPI │ │FastAPI │ │ ChromaDB │
│ :8000 │ │ :8000 │ │ :8000 │
│(API-1) │ │(API-2) │ │(向量数据库)│
└────┬───┘ └───┬────┘ └────┬─────┘
│ │ │
└────┬────┘ │
│ │
┌────┴────────────────┴───┐
│ vLLM (模型推理) │
│ :8001 │
└─────────────────────────┘

5.2 FastAPI 应用 Dockerfile

dockerfile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
FROM python:3.10-slim

WORKDIR /app

系统依赖

RUN apt-get update && apt-get install -y --no-install-recommends
curl build-essential
&& rm -rf /var/lib/apt/lists/*

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

COPY ./app /app/app

EXPOSE 8000

CMD [“uvicorn”, “app.main:app”, “–host”, “0.0.0.0”, “–port”, “8000”, “–workers”, “4”]

5.3 Nginx 配置

nginx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69

nginx/nginx.conf

upstream api_backend {
# 负载均衡:轮询
server api-1:8000;
server api-2:8000;
}

HTTP → HTTPS 重定向

server {
listen 80;
server_name ai.example.com;
return 301 https://servernameserver_nameservernamerequest_uri;
}

HTTPS 主配置

server {
listen 443 ssl http2;
server_name ai.example.com;

# SSL 证书
ssl_certificate     /etc/nginx/ssl/cert.pem;
ssl_certificate_key /etc/nginx/ssl/key.pem;
ssl_protocols       TLSv1.2 TLSv1.3;
ssl_ciphers         HIGH:!aNULL:!MD5;


# 安全头
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header Strict-Transport-Security "max-age=31536000" always;


# 请求体大小限制(上传文档)
client_max_body_size 50M;


# API 路由 → FastAPI 集群
location /api/ {
    proxy_pass http://api_backend;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    
    # SSE 流式输出支持
    proxy_buffering off;
    proxy_cache off;
    proxy_read_timeout 300s;
}


# vLLM 模型推理(单独路由)
location /v1/ {
    proxy_pass http://vllm:8001;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_buffering off;
}


# 静态资源
location /static/ {
    alias /app/static/;
    expires 7d;
    add_header Cache-Control "public, immutable";
}


# 健康检查端点
location /health {
    proxy_pass http://api_backend/health;
    access_log off;
}

}

5.4 完整 docker-compose.yml

yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
version: ‘3.8’

services:

========== API 服务(2个实例)==========

api-1:
build:
context: ./api
dockerfile: Dockerfile
container_name: rag-api-1
environment:
- VLLM_BASE_URL=http://vllm:8001/v1
- CHROMA_HOST=chromadb
- CHROMA_PORT=8000
- REDIS_URL=redis://redis:6379/0
depends_on:
chromadb:
condition: service_healthy
vllm:
condition: service_healthy
networks:
- ai-network
restart: unless-stopped

api-2:
build:
context: ./api
dockerfile: Dockerfile
container_name: rag-api-2
environment:
- VLLM_BASE_URL=http://vllm:8001/v1
- CHROMA_HOST=chromadb
- CHROMA_PORT=8000
- REDIS_URL=redis://redis:6379/0
depends_on:
chromadb:
condition: service_healthy
vllm:
condition: service_healthy
networks:
- ai-network
restart: unless-stopped

========== 向量数据库 ==========

chromadb:
image: chromadb/chroma:latest
container_name: rag-chromadb
volumes:
- chroma-data:/chroma/chroma
environment:
- ANONYMIZED_TELEMETRY=False
- ALLOW_RESET=True
healthcheck:
test: [“CMD”, “curl”, “-f”, “http://localhost:8000/api/v1/heartbeat”]
interval: 15s
timeout: 5s
retries: 3
networks:
- ai-network
restart: unless-stopped

========== 模型推理 ==========

vllm:
image: vllm/vllm-openai:latest
container_name: rag-vllm
volumes:
- model-cache:/models
environment:
- CUDA_VISIBLE_DEVICES=0,1
- HF_TOKEN=${HF_TOKEN}
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 2
capabilities: [gpu]
shm_size: ‘8gb’
healthcheck:
test: [“CMD”, “curl”, “-f”, “http://localhost:8001/health”]
interval: 30s
timeout: 10s
retries: 3
start_period: 180s
command: >
–model /models/Qwen2.5-7B-Instruct
–tensor-parallel-size 2
–max-model-len 8192
–gpu-memory-utilization 0.90
–host 0.0.0.0
–port 8001
networks:
- ai-network
restart: unless-stopped

========== 反向代理 ==========

nginx:
image: nginx:alpine
container_name: rag-nginx
ports:
- “80:80”
- “443:443”
volumes:
- ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf:ro
- ./nginx/ssl:/etc/nginx/ssl:ro
- ./static:/app/static:ro
depends_on:
- api-1
- api-2
networks:
- ai-network
restart: unless-stopped

========== 缓存 ==========

redis:
image: redis:7-alpine
container_name: rag-redis
command: redis-server --maxmemory 512mb --maxmemory-policy allkeys-lru
volumes:
- redis-data:/data
networks:
- ai-network
restart: unless-stopped

volumes:
chroma-data:
model-cache:
redis-data:

networks:
ai-network:
driver: bridge

5.5 启动与验证

bash
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

创建 SSL 证书目录(生产环境使用真实证书)

mkdir -p nginx/ssl

启动全部服务

docker compose up -d

查看服务状态

docker compose ps

查看启动日志

docker compose logs -f

验证各服务健康状态

curl -k https://localhost/health

测试 RAG API

curl -X POST https://localhost/api/chat
-H “Content-Type: application/json”
-d ‘{“query”: “什么是RAG?”, “stream”: true}’

六、GPU 容器化:NVIDIA Container Toolkit

Docker 默认不支持 GPU 透传,需要安装 NVIDIA Container Toolkit。

6.1 安装步骤

bash
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

1. 确认 NVIDIA 驱动已安装

nvidia-smi

2. 添加 NVIDIA 仓库

curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey
| sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg

curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list
| sed ‘s#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g’
| sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list

3. 安装

sudo apt-get update
sudo apt-get install -y nvidia-container-toolkit

4. 配置 Docker 运行时

sudo nvidia-ctk runtime configure --runtime=docker

5. 重启 Docker

sudo systemctl restart docker

6. 验证

docker run --rm --gpus all nvidia/cuda:11.8.0-base-ubuntu22.04 nvidia-smi

6.2 GPU 资源分配策略

yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

方式1:使用所有 GPU

deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: [gpu]

方式2:指定 GPU 数量

deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 2
capabilities: [gpu]

方式3:指定 GPU ID(精确控制)

environment:

  • NVIDIA_VISIBLE_DEVICES=0,2 # 只使用第0和第2号 GPU

⚠️ 注意:deploy.resources.reservations.devices 只能在 docker compose up 时生效,docker compose run 需要额外加 --gpus all 参数。

七、CI/CD 集成:GitHub Actions 自动化

手动构建推送镜像太low了,让我们用 GitHub Actions 实现自动化。

7.1 Workflow 文件

yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70

.github/workflows/deploy.yml

name: Build and Deploy AI App

on:
push:
branches: [main]
tags: [‘v*’]
pull_request:
branches: [main]

env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}/rag-app

jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write

steps:
  - name: Checkout code
    uses: actions/checkout@v4


  - name: Set up Docker Buildx
    uses: docker/setup-buildx@v3


  - name: Login to Container Registry
    uses: docker/login-action@v3
    with:
      registry: ${{ env.REGISTRY }}
      username: ${{ github.actor }}
      password: ${{ secrets.GITHUB_TOKEN }}


  - name: Extract metadata
    id: meta
    uses: docker/metadata-action@v5
    with:
      images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
      tags: |
        type=ref,event=branch
        type=semver,pattern={{version}}
        type=semver,pattern={{major}}.{{minor}}
        type=sha


  - name: Build and push
    uses: docker/build-push-action@v5
    with:
      context: ./api
      push: true
      tags: ${{ steps.meta.outputs.tags }}
      labels: ${{ steps.meta.outputs.labels }}
      cache-from: type=gha
      cache-to: type=gha,mode=max

deploy:
needs: build
runs-on: self-hosted # 使用自建 Runner(GPU 服务器)
if: github.ref == ‘refs/heads/main’

steps:
  - name: Deploy to server
    run: |
      cd /opt/rag-app
      git pull origin main
      docker compose pull
      docker compose up -d --remove-orphans
      docker image prune -f

7.2 镜像体积优化

构建优化前后的对比:

表格
优化手段 优化前 优化后 节省
多阶段构建 - ✅ 镜像只包含运行时
–no-cache-dir - ✅ ~200MB
.dockerignore - ✅ 排除无关文件
slim 基础镜像 1.2GB 380MB 68%
合并 RUN 层 15层 6层 减少层数

.dockerignore 示例:

plaintext
1
2
3
4
5
6
7
8
9
.git
pycache
*.pyc
.env
.venv
tests/
docs/
*.md

八、监控与运维:Prometheus + Grafana

部署上线只是开始,监控才是运维的核心。

8.1 Prometheus 配置

yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

prometheus/prometheus.yml

global:
scrape_interval: 15s
evaluation_interval: 15s

scrape_configs:

vLLM 指标

  • job_name: ‘vllm’
    static_configs:
    • targets: [‘vllm:8001’]
      metrics_path: /metrics
      scrape_interval: 10s

FastAPI 指标(需集成 prometheus-fastapi-instrumentator)

  • job_name: ‘fastapi’
    static_configs:
    • targets: [‘api-1:8000’, ‘api-2:8000’]
      metrics_path: /metrics

Nginx 指标

  • job_name: ‘nginx’
    static_configs:
    • targets: [‘nginx-exporter:9113’]

8.3 添加到 docker-compose.yml

yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

========== 监控栈 ==========

prometheus:
image: prom/prometheus:latest
container_name: rag-prometheus
volumes:
- ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro
- prometheus-data:/prometheus
command:
- ‘–config.file=/etc/prometheus/prometheus.yml’
- ‘–storage.tsdb.retention.time=30d’
ports:
- “9090:9090”
networks:
- ai-network
restart: unless-stopped

grafana:
image: grafana/grafana:latest
container_name: rag-grafana
ports:
- “3001:3000”
environment:
- GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD:-admin123}
volumes:
- grafana-data:/var/lib/grafana
depends_on:
- prometheus
networks:
- ai-network
restart: unless-stopped

8.4 关键监控指标

表格
指标 PromQL 查询 告警阈值 含义
请求延迟 histogram_quantile(0.95, rate(vllm_request_duration_seconds_bucket[5m])) > 5s P95 延迟
GPU 利用率 nvidia_gpu_utilization > 95% GPU 过载
显存使用 nvidia_gpu_memory_used_bytes / nvidia_gpu_memory_total_bytes > 90% 显存即将耗尽
请求队列 vllm_num_requests_waiting > 50 并发过高
错误率 rate(vllm_request_errors_total[5m]) > 5% 服务异常

💡 Grafana Dashboard:推荐导入 vLLM 官方 Dashboard(ID: 20234),开箱即用。

九、生产环境避坑指南

这是我踩过的坑,希望你不要再踩。

9.1 CUDA 版本对齐

问题:容器内 CUDA 版本与宿主机驱动不兼容。

plaintext
1
2
错误:CUDA driver error: CUDA_ERROR_UNSUPPORTED_PTX_VERSION

解决规则:

plaintext
1
2
3
4
5
6
宿主机 NVIDIA 驱动版本 ≥ 容器 CUDA 版本所需的最低驱动版本

CUDA 11.8 → 驱动 ≥ 520.61
CUDA 12.1 → 驱动 ≥ 525.60
CUDA 12.4 → 驱动 ≥ 550.54

检查命令:

bash
1
2
3
4
5
6

宿主机驱动版本

nvidia-smi

容器内 CUDA 版本

docker run --rm --gpus all nvidia/cuda:11.8.0-base-ubuntu22.04 nvcc --version

9.2 共享内存问题

问题:PyTorch DataLoader 多进程时报 Unexpected bus error。

根因:Docker 默认 shm 只有 64MB,PyTorch 需要更多。

解决:

yaml
1
2
3
4
5
6
7
8

docker-compose.yml

services:
vllm:
shm_size: ‘8gb’ # 必须设置!

或者使用 host IPC

docker run --ipc=host …

9.3 显存泄漏

问题:运行一段时间后显存持续增长,最终 OOM。

排查:

bash
1
2
3
4
5
6
7
8
9
10
11
12
13

实时监控显存

watch -n 1 nvidia-smi

查看容器内进程

docker exec vllm-server nvidia-smi

查看 Python 内存分配

docker exec vllm-server python3 -c "
import torch
print(f’Allocated: {torch.cuda.memory_allocated()/10243:.2f} GB’)
print(f’Cached: {torch.cuda.memory_reserved()/1024
3:.2f} GB’)
"

解决:

bash
1
2
3
4
5

vLLM 启动参数优化

–gpu-memory-utilization 0.90 # 限制显存使用比例
–max-model-len 8192 # 限制最大序列长度
–enable-prefix-caching # 启用前缀缓存,减少重复计算

9.4 镜像体积优化清单

dockerfile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

✅ 多阶段构建

FROM python:3.10-slim AS builder
RUN pip install --user -r requirements.txt

FROM python:3.10-slim
COPY --from=builder /root/.local /root/.local
ENV PATH=/root/.local/bin:$PATH

✅ 合并 RUN 指令,减少层数

RUN apt-get update && apt-get install -y --no-install-recommends
curl build-essential
&& rm -rf /var/lib/apt/lists/*

✅ 使用 .dockerignore 排除无关文件

.git, pycache, .env, tests/, docs/

9.5 日志管理

问题:容器日志无限增长,撑爆磁盘。

解决:

yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

全局配置(/etc/docker/daemon.json)

{
“log-driver”: “json-file”,
“log-opts”: {
“max-size”: “100m”,
“max-file”: “3”
}
}

或单个服务配置

services:
api:
logging:
driver: json-file
options:
max-size: “50m”
max-file: “5”

9.6 数据安全

yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

✅ 敏感信息用 secrets 管理,不要写死在 compose 文件里

secrets:
hf_token:
file: ./secrets/hf_token.txt

services:
vllm:
secrets:
- hf_token
environment:
- HF_TOKEN_FILE=/run/secrets/hf_token

✅ 使用非 root 用户运行

Dockerfile:

RUN useradd -m -u 1000 app-user

USER app-user

✅ 只读文件系统(防止容器内写入)

services:
nginx:
read_only: true
tmpfs:
- /tmp
- /var/cache/nginx

十、总结与专栏回顾

10.1 容器化部署 Checklist

plaintext
1
2
3
4
5
6
7
8
9
10
11
✅ Docker + NVIDIA Container Toolkit 已安装
✅ Dockerfile 使用多阶段构建 + 非 root 用户
✅ docker-compose.yml 配置健康检查 + 重启策略
✅ 日志滚动策略已配置(防止磁盘打满)
✅ 敏感信息使用 secrets 管理
✅ SSL 证书已配置(HTTPS)
✅ Prometheus + Grafana 监控已部署
✅ CI/CD 流水线已打通(GitHub Actions)
✅ 镜像推送至私有仓库(版本管理)
✅ 数据卷备份策略已制定

10.2 专栏回顾

至此, 「AI 全栈开发实战:从大模型部署到 Agent 工程化」 专栏全部完结。让我们回顾这 7 篇的内容:

表格
篇目 主题 核心内容
第1篇 大模型本地部署 Ollama / vLLM / llama.cpp 部署对比
第2篇 API 服务封装 FastAPI + OpenAI 兼容接口
第3篇 RAG 系统构建 向量数据库 + 检索增强生成
第4篇 Agent 工程化 LangChain Agent + 工具调用
第5篇 性能优化 量化、KV Cache、批处理
第6篇 安全加固 API鉴权、输入过滤、日志脱敏
第7篇 容器化部署 Docker + CI/CD + 监控(本篇)

从一行代码到一套完整的生产系统,我们走完了 AI 应用从开发到上线的全链路。

但这只是开始。 技术日新月异,大模型能力还在快速进化。保持学习,持续实践,才是工程师最大的竞争力。

📌 作者说:如果这个系列对你有帮助,欢迎点赞、收藏、关注。你的支持是我持续创作的动力。有任何问题欢迎评论区交流,我会逐一回复。

🔗 完整代码:GitHub 仓库(含本篇所有配置文件)

📚 系列导航:专栏目录

本专栏所有代码均经过实际项目验证,配置文件可直接用于生产环境。文中涉及的品牌和产品名称均为其各自所有者的商标。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐