欢迎光临
我们一直在努力

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

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

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

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

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

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

    三个关键细节:

  • requirements.txt 先 COPY、代码后 COPY——依赖层缓存,改代码不重装依赖,构建从几分钟降到几秒
  • slim 基础镜像:从完整 python:3.12 换 3.12-slim,体积直接减七成
  • 非 root 用户跑:容器逃逸的最后一道防线
  • 三、镜像瘦身:.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 / 采集器——别在容器里自己写日志文件(容器一删就没了)。

    八、踩坑提醒

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

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

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

    赞(0)
    未经允许不得转载:171主机测评 » AI 应用上线实战:从本地脚本到 Docker 容器化(密钥、健康检查、镜像瘦身)
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址