第12章 部署与生产化:从本地到上线
H2 与 MySQL 的取舍、Docker 编排、上线前检查清单
12.1 本章导读
前面 11 章把系统里外讲透了,本章解决最后一公里:怎么把它稳定地跑在服务器上。包括数据源的取舍、Docker 编排细节、以及一份可以直接照着打勾的上线检查清单。
12.2 数据源:H2 还是 MySQL
| 维度 | H2(standalone profile) | MySQL(默认 profile) |
|---|---|---|
| 部署 | 零依赖,文件库(./data/*.mv.db) | 需独立 MySQL 实例 |
| 兼容性 | MODE=MySQL 兼容模式,共用同一 schema.sql | 原生 |
| 适用 | 本地开发、演示、单机轻量使用 | 生产、联调、多实例共享(未来) |
| 风险 | 文件锁、并发能力有限 | 需运维(备份、连接池、监控) |
schema.sql 共 12 张表,sql.init.mode=always 幂等重跑。一个迁移陷阱:schema.sql 用 INSERT ... WHERE NOT EXISTS 只插不更——已存在的配置行(如模型设置)不会被覆盖,下线旧模型需手动 UPDATE。
本项目曾发生过一次真实误判:在容器沙箱里用 lsof/ps 看不到跨用户的 MySQL 进程,误判"MySQL 没跑"而切了 H2——结果应用连 H2、截图看的是 MySQL,两边数据对不上。判断 MySQL 是否在跑,用 nc -z 127.0.0.1 3306 或 netstat -an | grep LISTEN,别依赖进程列表。
12.3 Docker 部署
项目根目录的 Dockerfile 是多阶段构建:
# 阶段1: maven:3.9-eclipse-temurin-17-alpine 构建 # - 先 COPY 各模块 pom.xml 跑 dependency:go-offline(利用层缓存) # - 再 COPY 全部源码 mvn package -DskipTests # 阶段2: eclipse-temurin:17-jre-alpine 运行 # - 只 COPY fat jar,EXPOSE 8090 # - ENTRYPOINT 默认 standalone profile
docker-compose.yml 的关键挂载与环境变量:
services:
app:
build: .
ports: ["8090:8090"]
environment:
- DEEPSEEK_API_KEY=${DEEPSEEK_API_KEY:-sk-local}
- SPRING_PROFILES_ACTIVE=standalone
volumes:
- ./data:/app/data # H2 数据持久化
- ./plugins:/app/plugins # 插件目录
- ./workspaces:/app/workspaces # Agent 工作区
restart: unless-stopped去掉 SPRING_PROFILES_ACTIVE=standalone,改用 SPRING_DATASOURCE_URL/USERNAME/PASSWORD 环境变量指向外部 MySQL 即可。注意容器内访问宿主机 MySQL 要用 host.docker.internal(Mac/Windows)或宿主机内网 IP(Linux)。
12.4 上线检查清单
- ☐ 认证:配置
harness.auth.api-keys(为空则全放行!);服务不直接暴露公网,前置网关 - ☐ 密钥:模型 API Key 走环境变量/密钥管理系统,绝不硬编码进 yml 提交仓库
- ☐ 审批:确认
required-tools覆盖 shell_execute/fs_write/plugin.run/subprocess.spawn;评估是否启用运行期审批 gate - ☐ 沙箱:评估
harness.sandbox.default-mode;用容器/低权限 OS 用户隔离 - ☐ 数据库:外部 MySQL + 连接池配置 + 备份 + 事件表归档策略(事件表会持续增长)
- ☐ 可观测:接日志采集、Metrics(/actuator 默认公开,注意加保护)、Tracing、审计
- ☐ 资源:关注无界线程池(ReactLoopAgent.kick 用 newCachedThreadPool),必要时改有界
- ☐ 横向扩展:当前是单实例设计——Agent 内存状态、任务锁、会话亲和性都需处理后才能多实例
12.5 配置项速查表
| 配置项 | 默认 | 生产建议 |
|---|---|---|
server.port | 8090 | 按需 |
harness.auth.api-keys | 空(全放行) | 必须配置 |
harness.llm.deepseek.base-url | 127.0.0.1:8777/v1 | 指向生产网关 |
harness.llm.deepseek.api-key | ${DEEPSEEK_API_KEY:} | 环境变量注入 |
harness.approval.required-tools | shell/fs_write/plugin.run/subprocess | 按需收紧 |
harness.sandbox.default-mode | WORKSPACE_WRITE | 生产考虑 READ_ONLY 起步 |
harness.agent.temperature | 空(不发送) | 网关只接受 temperature=1 时保持为空 |
harness.extensions.skills.roots | ./skills | 新增目录前确认来源可信 |
harness.extensions.mcp.servers | 内置 Baidu SSE 示例 | 校验传输、URL、命令与密钥 |
harness.extensions.plugins.preset | 内置 Node 与 Java 示例 | 生产只保留必要插件 |
12.6 小结与下一章预告
本章要点:H2 用于开发演示、MySQL 用于生产;Harness 运行配置统一在 harness.yml;Docker 多阶段构建 + 三个关键挂载卷;上线检查清单覆盖认证/密钥/审批/沙箱/数据库/可观测/资源/扩展八大项;schema.sql 只插不更是迁移陷阱。
下一章:系统的已知边界是什么?想继续深入该往哪个方向走?第13章给出完整的边界清单与进阶路线。