AI 应用上线实战:从本地脚本到 Docker 容器化(密钥、健康检查、镜像瘦身)
LLM 系列又一篇。垂类 Agent 篇讲了系统架构,Harness 篇讲了开发环境——这篇讲最后一公里:上线。本地跑得好好的 AI 应用,一进容器就环境崩、密钥泄漏、启动超时,这篇把链路走通。
一、AI 应用上线为什么有自己的坑
把 AI 应用从本地脚本变成生产服务,三个坑等着你:
解法是一套标准的容器化流程:多阶段 Dockerfile → 密钥走环境变量 → 健康检查留宽限 → compose 编排。
二、Dockerfile:多阶段构建
单阶段构建会把开发依赖、测试文件全塞进最终镜像。多阶段的核心思路:构建环境和运行环境分离:
# 阶段 1:依赖安装(利用层缓存,代码改动不重装依赖)
FROM python:3.12-slim AS deps
WORKDIR /app
COPY requirements.txt .
RUN pip install –no-cache-dir –user -r requirements.txt
# 阶段 2:最终运行镜像
FROM python:3.12-slim
RUN apt-get update && apt-get install -y –no-install-recommends \\
libgomp1 && rm -rf /var/lib/apt/lists/* # 只装运行时必需
COPY –from=deps /root/.local /root/.local
ENV PATH=/root/.local/bin:$PATH
WORKDIR /app
COPY src/ ./src/
RUN useradd -m -u 1000 aiuser && chown -R aiuser /app
USER aiuser # 别用 root 跑(安全篇老话)
EXPOSE 8000
CMD ["uvicorn", "src.main:app", "–host", "0.0.0.0", "–port", "8000"]
三个关键细节:
三、镜像瘦身:.dockerignore 是免费的
.dockerignore 写严格,构建上下文瘦下来(这些进上下文会被永久固化进镜像层):
.git
.venv
__pycache__
*.md
tests/
*.env # 关键:env 文件绝不进构建上下文
.claude/
*.env 必须在 .dockerignore 里——密钥文件一旦进了构建上下文,即使后面删掉,镜像层里还在。API 应用的镜像应该很小(几百 MB),如果你们的镜像上了 GB,先看 .dockerignore。
四、密钥管理:环境变量,绝不写进镜像
安全篇讲过密钥是 P0 级——容器化后规则不变:
# docker-compose.yml
services:
app:
build: .
env_file: .env # 密钥走 env 文件,不进镜像
environment:
– LLM_BASE_URL=${LLM_BASE_URL}
– LLM_API_KEY=${LLM_API_KEY} # 运行时注入,镜像里只有变量名
ports:
– "8000:8000"
# 应用代码:启动时校验,缺了就 fast-fail
import os
def require_env(key: str) -> str:
v = os.environ.get(key)
if not v:
raise RuntimeError(f"缺少环境变量 {key},检查 .env") # 启动即失败,别等第一个请求
return v
LLM_API_KEY = require_env("LLM_API_KEY")
启动时校验密钥,别等第一个请求才发现——配置错误在启动时暴露,比线上第一个请求 500 便宜得多(Harness 篇的 fast-fail 老话)。
五、健康检查:给启动留宽限
LLM 应用启动慢(预热连接、建索引),默认健康检查等不及就反复杀——冷启动误杀:
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
interval: 10s
timeout: 5s
retries: 12 # 12 × 10s = 120s 启动宽限
start_period: 60s # 启动初期不计失败次数
应用侧配一个轻量 /health:
@app.get("/health")
def health():
return {"ok": True} # 只查进程活着,别在 health 里调 LLM(会连锁超时)
/health 只查进程活,/ready 才查依赖通——两个探针分开,health 里调 LLM API 是常见坑:API 一慢,健康检查连锁超时,服务被杀,重启,更慢。
六、compose 编排:应用 + 向量库一把起
知识库问答篇的系统要带向量库,compose 一把编排:
services:
app:
build: .
env_file: .env
depends_on:
vectordb:
condition: service_healthy # 等向量库就绪再起应用
ports: ["8000:8000"]
vectordb:
image: pgvector/pgvector:pg17
environment:
– POSTGRES_PASSWORD=${DB_PASSWORD}
volumes:
– pgdata:/var/lib/postgresql/data # 数据落盘,容器重建不丢索引
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
retries: 10
volumes:
pgdata:
两个细节:depends_on 配 condition: service_healthy(等就绪不是等启动)、数据卷落盘(容器重建不丢向量索引——重建一次索引是分钟级,别让容器重启给清了)。
七、上线后的可观测
可观测性篇的三件套原样挂上:token 追踪(每次调用记 prompt/completion tokens)、延迟分位数(p95 而不是平均值)、错误分类(限流/超时/内容拒绝分开计数)。容器化后加一条:日志打到 stdout,交给 docker logs / 采集器——别在容器里自己写日志文件(容器一删就没了)。
八、踩坑提醒
总结
| Dockerfile | 多阶段构建,依赖层缓存,非 root 跑 |
| 瘦身 | slim 镜像 + 严格 .dockerignore,env 文件绝不进上下文 |
| 密钥 | 环境变量运行时注入,启动时校验 fast-fail |
| 健康检查 | start_period 留宽限,health 只查进程不查依赖 |
| 编排 | depends_on 等就绪,数据卷落盘 |
| 可观测 | 日志打 stdout,token/延迟/错误三件套照挂 |
上线是系列的最后一公里:垂类 Agent 的架构、可观测性的三件套、安全篇的密钥边界、Harness 的 fast-fail,全在容器化这一步汇合。本地能跑只是及格线,容器里稳了才算上线。 觉得有用点个关注。

