AI 应用上线实战:从本地脚本到 Docker 容器化(密钥、健康检查、镜像瘦身)

AI 应用上线实战:从本地脚本到 Docker 容器化(密钥、健康检查、镜像瘦身)

LLM 系列又一篇。垂类 Agent 篇讲了系统架构,Harness 篇讲了开发环境------这篇讲最后一公里:上线。本地跑得好好的 AI 应用,一进容器就环境崩、密钥泄漏、启动超时,这篇把链路走通。

一、AI 应用上线为什么有自己的坑

把 AI 应用从本地脚本变成生产服务,三个坑等着你:

  1. 环境地狱:本地 Python 3.11 + 特定版本依赖,换台机器就崩------「在我电脑上是好的」的容器版
  2. 密钥泄漏:API Key 写死在代码里提交到仓库,或者被打进镜像层(安全篇的信任边界,上线时是最容易失守的一环)
  3. 启动超时:LLM 应用启动要预热(加载配置、建向量索引、测 API 连通),健康检查等不及就反复重启

解法是一套标准的容器化流程:多阶段 Dockerfile → 密钥走环境变量 → 健康检查留宽限 → compose 编排。

二、Dockerfile:多阶段构建

单阶段构建会把开发依赖、测试文件全塞进最终镜像。多阶段的核心思路:构建环境和运行环境分离:

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"]

三个关键细节:

  1. requirements.txt 先 COPY、代码后 COPY------依赖层缓存,改代码不重装依赖,构建从几分钟降到几秒
  2. slim 基础镜像 :从完整 python:3.12 换 3.12-slim,体积直接减七成
  3. 非 root 用户跑:容器逃逸的最后一道防线

三、镜像瘦身:.dockerignore 是免费的

.dockerignore 写严格,构建上下文瘦下来(这些进上下文会被永久固化进镜像层):

复制代码
.git
.venv
__pycache__
*.md
tests/
*.env          # 关键:env 文件绝不进构建上下文
.claude/

*.env 必须在 .dockerignore 里 ------密钥文件一旦进了构建上下文,即使后面删掉,镜像层里还在。API 应用的镜像应该很小(几百 MB),如果你们的镜像上了 GB,先看 .dockerignore。

四、密钥管理:环境变量,绝不写进镜像

安全篇讲过密钥是 P0 级------容器化后规则不变:

yaml 复制代码
# 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"
python 复制代码
# 应用代码:启动时校验,缺了就 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 应用启动慢(预热连接、建索引),默认健康检查等不及就反复杀------冷启动误杀:

yaml 复制代码
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
      interval: 10s
      timeout: 5s
      retries: 12          # 12 × 10s = 120s 启动宽限
      start_period: 60s    # 启动初期不计失败次数

应用侧配一个轻量 /health:

python 复制代码
@app.get("/health")
def health():
    return {"ok": True}    # 只查进程活着,别在 health 里调 LLM(会连锁超时)

/health 只查进程活,/ready 才查依赖通------两个探针分开,health 里调 LLM API 是常见坑:API 一慢,健康检查连锁超时,服务被杀,重启,更慢。

六、compose 编排:应用 + 向量库一把起

知识库问答篇的系统要带向量库,compose 一把编排:

yaml 复制代码
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 / 采集器------别在容器里自己写日志文件(容器一删就没了)。

八、踩坑提醒

  1. 密钥进了镜像层 :COPY 过的文件即使后删也留在层里------env 文件进 .dockerignore,密钥只走运行时注入
  2. healthcheck 里调 LLM:健康检查连锁超时 → 服务被杀重启循环------health 只查进程,ready 才查依赖
  3. 没留启动宽限 :LLM 应用预热慢,默认健康检查会冷启动误杀------start_period + 多 retries
  4. 向量库数据没落盘:容器重建索引全丢------volume 挂载,重建是分钟级成本
  5. 依赖没分层缓存 :代码改一个字全量重装依赖------requirements.txt 先 COPY

总结

环节 一句话
Dockerfile 多阶段构建,依赖层缓存,非 root 跑
瘦身 slim 镜像 + 严格 .dockerignore,env 文件绝不进上下文
密钥 环境变量运行时注入,启动时校验 fast-fail
健康检查 start_period 留宽限,health 只查进程不查依赖
编排 depends_on 等就绪,数据卷落盘
可观测 日志打 stdout,token/延迟/错误三件套照挂

上线是系列的最后一公里:垂类 Agent 的架构、可观测性的三件套、安全篇的密钥边界、Harness 的 fast-fail,全在容器化这一步汇合。本地能跑只是及格线,容器里稳了才算上线。 觉得有用点个关注。

相关推荐
吴声子夜歌1 小时前
Docker入门与实战——Docker数据管理
docker·容器
CopyCode2 小时前
Agent 开发不是模型训练:一个前端的入门认知
前端·llm·agent
流浪0012 小时前
大模型技术全景(十八):RAG 检索增强生成与知识时效性问题
llm·大语言模型·rag
程序员老赵2 小时前
Docker 部署 TeslaMate:轻松搭建特斯拉车辆数据记录平台
docker·容器·开源
java_logo2 小时前
Docker 部署 Emby:轻松搭建家庭影音媒体服务器
服务器·docker·nas·emby·飞牛nas·群晖nas·轩辕镜像
月落汀兰2 小时前
为什么跨网桥容器 ping 不通?Docker 网络模式详解,端口映射、容器访问外网底层实战
网络·docker·容器
jason.zeng@15022072 小时前
(七)「固化 Rest 接口 + Text-to-SQL 灵活查询」双模式 Agent 架构教程
数据库·python·sql·ai·架构·langchain·ai编程
zhanghaha13143 小时前
AI Agent_13 模型里的「多少 B」是什么 详细讲解
ai
xcLeigh3 小时前
本体驱动的AI大模型:方法与实践
人工智能·ai·大模型·agent·提示词·语义建模