7.5 KiB
KOC LOOP 私有化部署指南
本文适用于 codex/self-hosted-mysql 分支。目标架构是运维提出的:Nginx 代理 + KOC 服务 + MySQL 数据库,并让后台、外部 KOC 领取页和 Agent MCP 都能通过一个公网域名访问。
1. 部署形态
| 组件 | 容器 | 作用 | 持久化 |
|---|---|---|---|
| Nginx | nginx |
公网入口、反向代理、托管 KOC 领取页 | 配置随镜像 |
| KOC 服务 | app |
Next.js 后台、API、MCP、每天 09:00 自动采集 | 上传目录挂载卷 |
| MySQL 8 | mysql |
任务、笔记、领取、回填、账号、采集和用户数据 | MySQL 数据卷 |
访问路径:
https://你的域名/:运营后台;https://你的域名/koc/:外部 KOC 领取和回填;https://你的域名/api/mcp:Agent MCP;https://你的域名/api/health:服务健康检查。
第一版按单实例 KOC 服务设计。MySQL 和上传目录均持久化。以后需要横向扩容时,可以把上传卷换成共享 NAS;对象存储接口已与业务代码分离。
2. 服务器要求
- Linux 服务器一台,建议至少 4 核、8 GB 内存、100 GB 数据盘;
- Docker Engine 24+;
- Docker Compose v2;
- 可解析到服务器或负载均衡的公网域名;
- HTTPS 证书由公司网关、负载均衡或 Nginx 统一终止;
- 服务器可以访问飞书 OpenAPI 和正式数据采集 MCP。
服务器只需对公网开放 80/443。MySQL 不映射公网端口。
3. 准备代码与环境变量
git clone ssh://git@gta.gbotai.cn:42001/wufengping/koc-loop.git
cd koc-loop
git checkout codex/self-hosted-mysql
cp .env.self-hosted.example .env.self-hosted
编辑 .env.self-hosted。必须替换所有 replace-with-* 占位值:
| 变量 | 用途 |
|---|---|
MYSQL_ROOT_PASSWORD |
MySQL root 密码,仅数据库容器使用 |
MYSQL_PASSWORD |
KOC 服务的数据库密码 |
APP_ORIGIN |
后台公网地址,如 https://koc.example.com |
KOC_PORTAL_URL |
领取页完整地址,如 https://koc.example.com/koc/ |
SUPER_ADMIN_USERNAME |
首次启动创建唯一超级管理员 |
SUPER_ADMIN_PASSWORD |
首次启动的超级管理员初始密码,至少 8 位 |
ADMIN_INTERNAL_TOKEN |
内部管理调用密钥 |
KOC_MCP_API_KEY |
Agent 调用 KOC LOOP MCP 的独立 Bearer 密钥 |
FEISHU_APP_ID / FEISHU_APP_SECRET |
读取飞书内容表和配图 |
AI_TOOL_CENTER_MCP_URL / AI_TOOL_CENTER_MCP_KEY |
小红书公开数据采集服务 |
ENABLE_SCHEDULER |
是否启用每天 09:00 自动采集,生产保持 true |
密钥必须由密码管理器生成,禁止提交到 Git、聊天、部署日志或 URL。三个业务密钥 ADMIN_INTERNAL_TOKEN、KOC_MCP_API_KEY、AI_TOOL_CENTER_MCP_KEY 不得复用。
4. 首次启动
docker compose --env-file .env.self-hosted \
-f docker-compose.self-hosted.yml up -d --build
应用容器启动时会先执行同一套数据库迁移脚本,成功后才启动 KOC 服务。查看状态:
docker compose --env-file .env.self-hosted \
-f docker-compose.self-hosted.yml ps
curl -fsS http://127.0.0.1:${HTTP_PORT:-80}/api/health
健康检查应返回 status: ok 和 database: true。
首次打开后台登录页时,系统会根据环境变量创建唯一超级管理员。创建成功后,可从运行环境移除 SUPER_ADMIN_PASSWORD 的明文值并重启应用;后续账号与密码统一在“用户管理”中维护。
5. Nginx 与 HTTPS
仓库内 deploy/nginx/koc-loop.conf 默认监听容器 80 端口,适合前置公司网关或负载均衡终止 HTTPS。
如果证书直接挂在本机 Nginx:
- 把证书和私钥以只读卷挂载进
nginx容器; - 增加 443
listen ... ssl配置; - 80 端口只做 301 跳转;
- 保留
/koc/静态规则、/api/mcp长连接规则和/反向代理规则。
MCP 路由已经关闭代理缓冲并将超时时间延长到 1 小时,避免 Agent 的长调用被 Nginx 提前截断。
6. 迁移原 Sites 数据
迁移分为数据库与图片两部分。先在原生产站点保持只读窗口,完成导出后再切换域名,避免新旧系统同时写入。
6.1 D1 数据导入 MySQL
把 D1 各表导出成一个 JSON 文件,结构如下:
{
"tables": {
"partners": [{ "id": "...", "name": "..." }],
"tasks": [{ "id": "...", "name": "..." }],
"contents": [],
"accounts": [],
"claims": [],
"delegation_bundles": [],
"distributions": [],
"collection_runs": [],
"users": [],
"auth_sessions": [],
"mcp_export_tokens": []
}
}
先确保 MySQL 迁移已完成,再在应用环境中运行:
npm run db:import-json -- /backup/koc-d1-export.json
导入脚本按业务依赖顺序写入,并使用主键/唯一键安全更新已有记录。正式迁移前先在测试库演练并核对任务数、笔记数、领取数、发布数和账号数。
6.2 R2 图片导入
把 R2 按原对象 key 导出到一个目录,目录层级必须保留,例如:
content-assets/...
publish-evidence/...
creator-center/...
运行:
UPLOAD_DIR=/data/koc/uploads \
npm run storage:import -- /backup/koc-r2-export
脚本会复制文件,并为缺少元数据的图片生成 Content-Type 元数据。容器部署时也可以在宿主机临时挂载 upload_data 卷后执行。
7. 上线验收
必须逐项验证:
- 超级管理员可以登录,普通用户看不到 KOC 资源模块;
- 用真实飞书表格创建一个 1 篇测试任务;
- 返回的领取链接以
/koc/?task=开头; - 手机公网打开领取页,能查看正文与配图;
- 回填短链/长链、上传发布截图、刷新后记录仍在;
- 上传创作者截图并填写曝光量、阅读量;
- 后台立即采集一篇笔记成功;
- 保存次日采集计划,确认数据库产生
collection_runs; - 导出的 Excel 内能直接看到原图和截图;
- Agent 用
KOC_MCP_API_KEY调用/api/mcp能发现全部工具; - 重启全部容器后数据与图片不丢失。
8. 备份与恢复
每天至少备份:
- MySQL:
mysqldump --single-transaction; mysql_data卷;upload_data卷;- 当前 Git 提交号和脱敏后的环境变量清单。
备份必须复制到另一台机器或对象存储,不能只保存在部署服务器。恢复演练至少每季度一次。
9. 升级与回滚
升级前先备份数据库和上传卷:
git pull
docker compose --env-file .env.self-hosted \
-f docker-compose.self-hosted.yml up -d --build
数据库迁移只允许向前追加新的 mysql/*.sql 文件,禁止修改已经在生产执行过的迁移。应用回滚到旧镜像前,要确认旧代码兼容当前数据库结构;涉及不可逆结构变化时,必须同时准备数据库恢复方案。
10. 运维排查
| 现象 | 处理 |
|---|---|
/api/health 返回 503 |
查看 MySQL 容器健康状态与应用数据库变量 |
| 登录页可开但登录失败 | 确认迁移完成、超级管理员变量仅用于初始化 |
| 配图或截图 404 | 检查 upload_data 卷和 UPLOAD_DIR=/data/koc/uploads |
| 09:00 未自动采集 | 检查 ENABLE_SCHEDULER=true、服务器日志和采集 MCP 网络 |
| MCP 401 | 检查请求头是否为 Authorization: Bearer <KOC_MCP_API_KEY> |
| 飞书读取失败 | 检查应用权限、文档授权和服务器到飞书 OpenAPI 的网络 |
生产日志不得打印数据库密码、飞书 Secret、MCP key 或完整带 key 的采集服务 URL。