Skip to content

生产部署指南 ​

本文档说明如何使用 deploy/ 目录部署 web-presentation。所有正式部署模板默认只拉取 CI/CD 已发布镜像;SQLite 轻量版面向个人或小团队,不需要 PostgreSQL 与 Redis。该版把 Backend、Runtime 和 Gateway 放在同一个 platform-lite 容器中。

部署形态收敛口径(两种方式 / 三个模板):

  • Lite 轻量版(单镜像单容器):deploy/compose/compose.sqlite-lite.yml 与 NAS 图形界面(群晖 / 飞牛)完全同构,镜像内置 Backend、Runtime、Renderer(内置 Chromium)与 Gateway 四个长期进程;支持「零必填变量、零密钥文件」一键启动,截图在容器内部完成,不需要独立 Renderer 服务,也不需要手工凭据文件。
  • 生产版(多容器拓扑):生产提供 compose.prod.yml(小团队单 Runtime 全角色)与 compose.runtime-roles.yml(分角色 + 资源隔离)两个模板,采用独立的 web-presentation-renderer 容器执行截图,通过共享密钥与 Backend 通信。详情见环境变量治理与现行执行计划。

文档导航 ​

文档内容
Compose 部署说明两种部署方式、三个 compose 模板(Lite 单容器与生产分角色/小团队)、启动方式和访问关系
部署环境变量production env 版变量分组和关键约束
多 Backend 与密钥一致性多 Backend 共享存储/密钥前提、签名密钥轮换、旧票据语义、AI Run 停机语义与协调器幂等边界
版本兼容矩阵N/N-1 支持组合、升级窗口、摘流策略与 Run 收敛边界(AR-05/W05)
CI/CD 与容器发布平台、Runtime、Renderer 镜像与发布策略
备份与恢复数据库、资源、构建产物和密钥备份
升级与回滚镜像升级、数据库迁移和回滚注意事项
版本兼容矩阵N/N-1 支持组合、升级窗口、摘流策略与 Run 收敛边界(AR-05/W05)
部署排障健康检查、Runtime、AI 设置和数据库迁移问题
执行隔离与权限矩阵构建领取器 / 编译子进程边界、单任务 token 与 M02 验收要点(AR-01/W01)
Lite 规模与隔离决策推荐规模(5–10 人 / 并发 3,D2 前非 SLA)、Lite 故障域、G1 不拆容器与 G5 Renderer 隔离决策

常规部署涉及三个业务镜像:

  • llmxpm/web-presentation:latest:平台镜像,包含 Backend、Editor 静态资源和 Gateway Nginx。
  • llmxpm/web-runtime-vue:latest:Runtime 镜像,负责预览、诊断入口和构建。
  • llmxpm/web-presentation-renderer:latest:Renderer 镜像,负责真实 Chromium 截图与页面诊断。该镜像尚未发布、当前不可拉取(待下一次 Release 首次推送);引用它的生产模板在发布预演通过前不能视为可直接部署。

SQLite 轻量版使用 llmxpm/web-presentation:sqlite-lite:单容器内运行 Backend、Editor、Runtime、Gateway 与内置 Chromium Renderer,不依赖独立 Renderer 容器。轻量平台镜像由根仓 Release workflow 从 deploy/docker/Dockerfile.lite 构建并推送。

部署文件 ​

文件作用
deploy/compose/compose.sqlite-lite.ymlSQLite + memory runtime 轻量版;单镜像单容器内置全部平台服务(含 Chromium 渲染)。零必填变量、零密钥文件
deploy/compose/compose.prod.ymlruntime-all 兼容/Lite 生产版,拆分 backend-migrate、backend、runtime、renderer 与 gateway,通过 env_file: .env 读取生产环境变量;适合小团队或尚未分角色的环境
deploy/compose/compose.runtime-roles.yml官方生产主路径:分角色单机版,runtime-preview / runtime-build / runtime-check 各一实例,Backend 读取 deploy/.env,Runtime 角色只读取 deploy/runtime.env,Gateway 只代理预览
deploy/.env.example供 production env 版与分角色单机版的 Backend 复制为 deploy/.env 使用
deploy/runtime.env.example供分角色单机版的 Runtime 复制为 deploy/runtime.env 使用,不包含平台密钥
deploy/docker/nginx/web-presentation.conf平台镜像内置 Gateway 配置,托管 Editor 并代理 Backend 与 Runtime

简化版中,platform 容器同时运行 Backend 与 Gateway。runtime 默认只在 compose 内网访问。platform 容器内通过 extra_hosts 把 backend:8000 指向本机 Backend,compose 网络中通过别名把 backend:8000 暴露给 Runtime 回源。

SQLite 轻量版中,已发布的 platform-lite 单容器同时运行 Backend、Runtime、Gateway,并用镜像内置浏览器执行截图(不必再起独立 renderer,也不必准备 deploy/secrets/ 渲染凭证)。deploy/compose/compose.sqlite-lite.yml 另定义的 renderer 服务属开发中形态,其镜像尚未发布。平台容器入口脚本会把 backend 和 runtime 解析到本机,复用同一份 Gateway 配置。

production env 版中,访问入口是单独的 gateway 容器;backend 和 runtime 默认只在 compose 内网访问。正式生产推荐使用分角色单机版 compose.runtime-roles.yml:把 runtime 拆为 runtime-preview / runtime-build / runtime-check,Gateway 只代理预览角色,Runtime 不注入平台密钥。compose.prod.yml 保留为 runtime-all 兼容路径,详见 Compose 部署说明。

前置条件 ​

  • 已安装 Docker Engine 与 Docker Compose v2。
  • SQLite 轻量版不需要 PostgreSQL 与 Redis;platform-lite 只支持单实例。已发布镜像自带浏览器完成截图,不需要独立 Renderer 容器或 secret 文件。运行态使用 memory:// 适配器(见 运行态存储适配器)。
  • 外部依赖简化版和 production env 版需要已准备可访问的 PostgreSQL 与 Redis。
  • 内置依赖简化版会随应用启动 PostgreSQL 与 Redis,适合单机试部署或小规模自托管。
  • 部署机器可以拉取所需业务镜像;轻量版只需 llmxpm/web-presentation:sqlite-lite(自带浏览器)。web-presentation-renderer 与自构建 web-runtime-vue 尚未发布,生产多容器模板待其首次推送后才可直接拉取部署。
  • 从源码构建镜像时直接使用仓库原生目录;直接使用 Docker Hub 发布镜像不需要仓库源码。
  • 如需要 HTTPS,建议在外层 Nginx、Traefik 或云负载均衡终止 TLS,再转发到 compose 暴露的 HTTP 端口。

配置方式 ​

SQLite 轻量单容器版和两个简化版不读取 .env。直接打开对应 compose 文件,按文件顶部注释修改连接串或访问地址、默认管理员密码和 AI_SECRET_ENCRYPTION_KEY。

production env 版需要先复制环境变量模板:

bash
cp deploy/.env.example deploy/.env

Windows PowerShell 可使用:

powershell
Copy-Item .\deploy\.env.example .\deploy\.env

production env 版至少修改:

变量要求
BACKEND_PUBLIC_BASE_URLBackend 对外公开地址,用于生成资源、截图、构建产物和预览入口链接
RUNTIME_PUBLIC_BASE_URLRuntime 对外公开地址;同域 Gateway 模式通常为平台入口追加 /runtime
RUNTIME_SERVER_BASE_PATHRuntime Vite 资源挂载路径,应与 RUNTIME_PUBLIC_BASE_URL 的 URL path 一致;例如 /runtime/ 或 /
CORS_ORIGINSJSON 数组字符串,应包含浏览器访问平台的公网地址
DATABASE_URL已有 PostgreSQL 连接串,数据库和用户需要提前创建
REDIS_URL已有 Redis 连接串
DEFAULT_ADMIN_PASSWORD首次启动创建默认管理员时使用
AI_SECRET_ENCRYPTION_KEYFernet 密钥,用于加密用户模型凭证;必须重新生成并长期保存

可用 Python 标准库生成 AI_SECRET_ENCRYPTION_KEY:

powershell
python -c "import base64, os; print(base64.urlsafe_b64encode(os.urandom(32)).decode())"

AI_SECRET_ENCRYPTION_KEY 不是 API Key,也不是普通密码。它必须是 32 字节随机值的 URL-safe base64 编码,通常长度为 44 个字符并以 = 结尾。部署后不要随意更换;更换该值会导致已保存的用户模型凭证无法解密。

AI 会话、run、事件、消息、工具调用和 HITL 状态写入 Backend 主库中的 ai_agent_* 表,随常规数据库备份和 Alembic 迁移一起管理。

SQLite 轻量单容器版和简化版默认使用 http://127.0.0.1:8080 和 SESSION_SECURE=false 的应用默认值。正式 HTTPS 部署时,应把 BACKEND_PUBLIC_BASE_URL、RUNTIME_PUBLIC_BASE_URL、CORS_ORIGINS 改为真实域名,并在 Backend 环境变量中补充 SESSION_SECURE=true;production env 版已在 .env.example 中默认启用。

公网 URL 配置 ​

常规部署推荐同域 Gateway 模式:外层反向代理只把整个域名转发到简化版的 platform:80,或 production env 版的 gateway:80,再由平台内置 Nginx 处理 /api、/preview、/public 和 /runtime/。

text
BACKEND_PUBLIC_BASE_URL=https://presentation.example.com
RUNTIME_PUBLIC_BASE_URL=https://presentation.example.com/runtime
RUNTIME_SERVER_BASE_PATH=/runtime/
CORS_ORIGINS=["https://presentation.example.com"]

这种模式下,外部反向代理不需要单独处理 /runtime,平台 Gateway 会把 /runtime/ 转发到 runtime:7373 并保留 /runtime/ 前缀,Runtime Vite 会按 RUNTIME_SERVER_BASE_PATH=/runtime/ 解析资源路径。

如果要把 Backend 或 Runtime 单独暴露成独立域名,可以改成:

text
BACKEND_PUBLIC_BASE_URL=https://api.presentation.example.com
RUNTIME_PUBLIC_BASE_URL=https://runtime.presentation.example.com
RUNTIME_SERVER_BASE_PATH=/
CORS_ORIGINS=["https://presentation.example.com"]

拆分域名时,外部反向代理需要分别处理:

  • presentation.example.com 转发到简化版 platform:80,或 production env 版 gateway:80,用于 Editor 静态资源。
  • api.presentation.example.com 转发到 backend:8000,用于 /api、/preview、/public、/build-artifacts;旧 /media 入口已关闭,不应配置数据目录静态映射。
  • runtime.presentation.example.com 转发到 runtime:7373,路径不要再加 /runtime 前缀。

RUNTIME_BASE_URL=http://runtime:7373 是 Backend 容器访问 Runtime 的内网地址,不是浏览器访问地址;即使单独配置 RUNTIME_PUBLIC_BASE_URL,通常也不需要改它。

RUNTIME_SERVER_BASE_PATH 本质上就是 RUNTIME_PUBLIC_BASE_URL 的 URL path 部分,并规范化为以 / 开头和结尾。比如 https://presentation.example.com/runtime 对应 /runtime/,https://runtime.example.com 对应 /,https://runtime.example.com/app-runtime 对应 /app-runtime/。

启动部署 ​

推荐进入 deploy/ 目录执行。

SQLite 轻量单容器版:

bash
cd deploy
docker compose -f compose/compose.sqlite-lite.yml config
docker compose -f compose/compose.sqlite-lite.yml pull
docker compose -f compose/compose.sqlite-lite.yml up -d

production env 版(小团队生产):

bash
cd deploy
docker compose -f compose/compose.prod.yml config
docker compose -f compose/compose.prod.yml pull
docker compose -f compose/compose.prod.yml up -d

分角色单机版(官方生产主路径):

bash
cd deploy
docker compose -f compose/compose.runtime-roles.yml config
docker compose -f compose/compose.runtime-roles.yml pull
docker compose -f compose/compose.runtime-roles.yml up -d

各编排实际拉取的业务镜像为:

text
llmxpm/web-presentation:sqlite-lite
llmxpm/web-presentation:latest
llmxpm/web-runtime-vue:latest
llmxpm/web-presentation-renderer:latest

Redis 只保存短生命周期预览 artifact、构建状态和锁;主数据仍由 PostgreSQL 或 SQLite 管理。

latest 与 sqlite-lite 只适合跟随稳定 Release 自动升级。需要精确回滚时,已部署多容器拓扑的环境应把平台、Runtime、Renderer 的 image 一起固定到同一发布版本(Renderer 以已实际发布的标签为准;当前 renderer 镜像尚未发布)。已发布的轻量版只需固定 sqlite-lite-<release_tag>,无需对齐 Renderer 标签。

SQLite 轻量单容器版由 platform-lite 容器入口脚本在启动时执行一次 alembic upgrade head,随后同时启动 Backend、Runtime、Renderer 与 Nginx。生产模板由 backend-migrate 容器在 backend 启动前执行迁移。

验证服务 ​

查看容器状态:

bash
cd deploy
docker compose -f compose/compose.prod.yml ps

SQLite 轻量单容器版把 -f 改为 compose/compose.sqlite-lite.yml;分角色生产版把 -f 改为 compose/compose.runtime-roles.yml。

检查入口健康状态:

bash
curl -fsS http://127.0.0.1:8080/healthz

production env 版默认暴露宿主机 80 端口:

bash
curl -fsS http://127.0.0.1:80/healthz

Windows PowerShell 中如果 curl 被映射为 Invoke-WebRequest,可改用 curl.exe。

如需看日志:

bash
cd deploy
docker compose -f compose/compose.prod.yml logs -f backend runtime renderer gateway

分角色生产版使用:

bash
docker compose -f compose/compose.runtime-roles.yml logs -f

SQLite 轻量单容器版使用:

bash
docker compose -f compose/compose.sqlite-lite.yml logs -f platform-lite

应用侧业务日志默认使用 JSON Lines 输出到容器标准输出,便于 docker compose logs 之后接入 Loki、ELK 或云日志采集。部署模板默认关闭 Gateway、Backend 和 Runtime 的访问日志,以降低高频预览、静态资源与 Runtime 代理请求带来的 IO 成本;错误日志、业务日志和浏览器错误上报仍保留。gateway 仍负责生成或透传 X-Request-ID,backend 会在响应头返回同一个请求 ID。

常用日志环境变量:

text
LOG_LEVEL=INFO
LOG_FORMAT=json
ACCESS_LOG_ENABLED=false
CLIENT_ERROR_LOG_ENABLED=true
CLIENT_ERROR_LOG_MAX_BYTES=16384
RUNTIME_LOG_LEVEL=info
RUNTIME_LOG_FORMAT=json
RUNTIME_ACCESS_LOG_ENABLED=false

PostgreSQL 与 Redis 保持官方镜像原生日志格式;它们作为依赖服务查看,不纳入应用 JSON 日志契约。需要临时排查访问链路时,可把 ACCESS_LOG_ENABLED 或 RUNTIME_ACCESS_LOG_ENABLED 改为 true 后重建对应容器;Gateway 访问日志默认在镜像 Nginx 配置中关闭。

浏览器访问 compose 中配置的 BACKEND_PUBLIC_BASE_URL 对应平台入口后,使用默认管理员账号登录。

访问链路 ​

mermaid
flowchart LR
    Browser["浏览器"] --> Gateway["platform 或 gateway / Nginx"]
    Gateway --> Editor["Editor 静态资源"]
    Gateway --> Backend["Backend / backend:8000"]
    Gateway --> Runtime["runtime:7373"]
    Backend --> Database["PostgreSQL 或 SQLite"]
    Backend --> RuntimeState["Redis 或 memory runtime"]
    Backend --> Runtime
    Backend --> Renderer["renderer:7400"]
    Renderer --> Runtime
    Runtime --> Backend

关键路径:

  • / 由 Gateway 返回 Editor 静态资源。
  • /api、/public、/build-artifacts、/preview 代理到 Backend。
  • /media 及其子路径返回 404;Backend 不再挂载对象存储根目录。
  • /runtime/ 代理到 Runtime,并保留 /runtime/ 前缀。
  • Backend 通过 RUNTIME_BASE_URL 访问 Runtime。
  • Backend 通过受信控制 API 调度 Renderer;Renderer 在 Chromium 中加载 Runtime 页面并回传截图或诊断结果。
  • Runtime 通过 RUNTIME_BACKEND_API_BASE_URL 回源 Backend。
  • SQLite 轻量单容器版中,两个地址默认都是 127.0.0.1。
  • 简化版中,backend:8000 在 platform 容器内指向本机 Backend,对 runtime 则是 platform 的网络别名;production env 版中,它是独立 backend 容器。

数据与备份 ​

SQLite 轻量单容器版只管理 lite-data volume,用于 SQLite 数据库、本地资源、截图、构建产物和 Runtime RSA 私钥。备份时应完整备份该 volume;不要只复制数据库文件而遗漏同目录资源。

生产部署模板只管理 backend-data volume,用于本地资源、截图和构建产物。

PostgreSQL 与 Redis 使用已有基础设施时,应使用对应基础设施的备份机制。升级前至少备份 PostgreSQL 和 backend-data。

本地资源模式下,ASSET_STORAGE_DRIVER=local 会把文件写入 backend-data。如果需要对象存储,设置 ASSET_STORAGE_DRIVER=s3 并补齐 S3_ENDPOINT_URL、S3_ACCESS_KEY、S3_SECRET_KEY、S3_BUCKET 等变量。

不要在生产环境执行带 -v 的 docker compose down -v,否则会删除 compose 管理的数据卷。

升级与回滚 ​

升级流程:

  1. 备份数据库和本地文件 volume;SQLite 轻量单容器版备份 lite-data。
  2. 在 deploy/ 目录执行对应 compose 文件的 docker compose pull 拉取最新稳定镜像。
  3. 在 deploy/ 目录执行对应 compose 文件的 docker compose up -d。
  4. SQLite 轻量版观察 platform-lite 日志;生产版观察 backend-migrate、backend、runtime、renderer 和 gateway 日志。

预览多副本部署时采用滚动发布,顺序固定为「新副本就绪 → 流量切换 → 旧副本排空 → 下线」:先启动新版预览副本并核对 /__runtime_healthz 的 runtime_kit_version / build_id,再把新副本加入 Gateway 预览池、把旧副本标记 down 或移出列表并 reload,等旧副本在途连接结束后停容器。详见 Compose 部署说明「预览多副本与滚动发布」。

回滚时需要把对应 compose 文件中的 image 改为上一个可用版本标签,再执行对应 compose 文件的 docker compose pull && docker compose up -d。数据库迁移如已执行不可逆变更,需要按对应版本的迁移说明处理。

如果回滚或重新部署后出现 Can't locate revision identified by '<revision>',说明数据库中的 alembic_version 指向了当前镜像不包含的迁移脚本。这通常不是数据库连接问题,而是平台镜像版本早于数据库 schema 版本。应改回包含该 revision 的平台镜像,或重新拉取/发布正确镜像后再启动;不要直接执行 alembic stamp 覆盖版本号,除非已经人工确认数据库结构与目标 revision 完全一致。

常见问题 ​

入口健康但页面接口失败 ​

检查 BACKEND_PUBLIC_BASE_URL、RUNTIME_PUBLIC_BASE_URL、CORS_ORIGINS 和外层反向代理是否一致。跨域配置应使用 JSON 数组字符串,例如:

text
CORS_ORIGINS=["https://presentation.example.com"]

数据库迁移失败 ​

SQLite 轻量单容器版先看 platform-lite 日志;production env 版看 backend-migrate 日志。使用外部数据库时,先检查 DATABASE_URL 是否能从容器网络访问,确认数据库、用户和权限已提前创建。backend-migrate 和 lite 入口脚本都只负责执行应用 schema 迁移,不创建外部 PostgreSQL 实例。

如果日志包含 Can't locate revision identified by '20260529_0101' 这类信息,按下面顺序排查:

bash
# 查看当前平台镜像内是否包含该迁移文件。
docker compose -f compose/compose.prod.yml run --rm --entrypoint sh backend \
  -lc "ls -1 /app/backend/migrations/versions && alembic heads"

# 重新拉取并重建使用正确平台镜像的容器。
docker compose -f compose/compose.prod.yml pull backend runtime
docker compose -f compose/compose.prod.yml up -d --force-recreate backend runtime

production env 版把命令中的 compose 文件改为 compose/compose.prod.yml,并把 platform 替换为 backend-migrate 或 backend。如果镜像内 alembic heads 早于数据库里的 version_num,说明正在使用旧平台镜像,需要切到包含该迁移脚本的发布标签或等待 latest 更新完成。

Runtime 预览不可用 ​

SQLite 轻量单容器版先检查 platform-lite 内部 127.0.0.1:7373/__runtime_healthz;其它模板先确认 Runtime 容器健康,再检查 RUNTIME_PREVIEW_JWKS_URL、RUNTIME_BACKEND_API_BASE_URL 和 RUNTIME_PUBLIC_BASE_URL。平台部署模式下 RUNTIME_STANDALONE_PREVIEW_ENABLED 应保持 false。

如果浏览器控制台出现 Failed to load module script 且模块响应类型为 text/html,通常是同域 /runtime 部署没有让 Runtime Vite 资源带上 /runtime/ 前缀。检查 RUNTIME_SERVER_BASE_PATH=/runtime/ 是否已传给 Runtime 容器,并确认外层反向代理把整站流量转发到平台 Gateway,而不是只把 /runtime 单独转发出去。

AI 设置保存后无法解密 ​

AI_SECRET_ENCRYPTION_KEY 是加密用户模型凭证的长期 Fernet 密钥。它必须是 32 字节随机值的 URL-safe base64 编码。更换该值会导致已有密文无法解密,生产环境必须妥善备份。

CLI OpenAPI 契约入口 ​

CLI endpoint 使用平台 Gateway 根地址,不包含 /api/v1。外层代理必须保留 /openapi.json,内置 Gateway 将该精确路径代理到 Backend;该入口不需要 PAT,不开放额外文档 UI。业务 API 健康不代表契约入口正常:错误配置可能让该路径返回 HTTP 200 的 Editor HTML。

发布后执行只读检查(必须使用实际外部 Gateway 地址):

powershell
uv run --project backend python scripts/contracts/check-gateway-openapi.py https://presentation.example.com

检查 JSON Content-Type、OpenAPI 根结构及主题、样式、路由操作。真实 Nginx 隔离回归入口为 uv run --project backend python scripts/contracts/test-gateway-openapi.py,需要 Docker;覆盖契约成功、上游错误透传、现有 API 和 Editor SPA。

先发布平台镜像并更新 Gateway,外部契约检查通过后再升级 CLI/Skill。CLI 契约帮助失败立即退出 1 且无部分帮助;Doctor 的 OpenAPI 检查与健康、认证分开。六页演示验收仅在测试工作空间执行,记录页面 Job、路由顺序和挂载后的截图。代码测试与生产验收分开记录,生产仍返回 HTML 时不能宣布修复完成。