Files
Cloud-Tour-to-Libo/docs/DEPLOYMENT.md
T

480 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 部署指南
本文档说明如何用 Docker 启动、重置、配置和排查旅行知识图谱管理系统。
## 环境要求
- Docker Desktop 或 Docker Engine
- Docker Compose v2
- 至少 4 GB 可用内存
- 开发环境至少 10 GB、生产环境至少 50 GB 可用磁盘空间,并单独规划异地备份容量
## 本地开发启动
```bash
docker compose up -d --build
```
启动后访问:
```text
http://localhost:8102/admin
```
以下默认账号只允许本地开发使用:
```text
admin@example.com / change-me
```
生产环境必须先完成后文中的密钥、域名、HTTPS 和防火墙配置,再使用服务器覆盖文件启动:
```bash
docker compose -f docker-compose.yml -f docker-compose.server.yml up -d --build
```
## 首次启动会发生什么
1. 构建 API 镜像,并打包 React 管理后台。
2. 创建 PostgreSQL 数据卷。
3. PostgreSQL 初始化脚本恢复 `snapshots/postgres/kg_admin_new2.dump`。
4. `falkordb-seed` 把 `snapshots/falkordb/dump.rdb` 写入 FalkorDB 数据卷。
5. 初始化独立 MySQL 数据中心与账号签发用户。
6. 启动只允许数据库端口转发的 DBeaver SSH 网关。
7. FastAPI 服务等待 PostgreSQL、MySQL、FalkorDB 与 SSH 网关健康后启动。
## 常用命令
查看服务状态:
```bash
docker compose ps
```
查看 API 日志:
```bash
docker compose logs -f api
```
停止服务:
```bash
docker compose down
```
仅在本地开发环境停止并删除数据卷,下一次启动将重新恢复快照:
```bash
docker compose down -v
docker compose up -d --build
```
生产服务器禁止执行 `docker compose down -v`。生产 MySQL 使用独立磁盘绑定目录,
虽然该命令不会删除这个目录,但会删除 PostgreSQL、FalkorDB 等其他 Docker 数据卷。
## 无 sudo 服务器的 rootless Docker 启动
如果服务器账号不能使用 `sudo`,并且 `loginctl show-user <用户> -p Linger` 显示 `Linger=no`,用户级 `docker.service` 可能会在 SSH 退出后停止。此时可以使用项目脚本把 rootless Docker 的 socket 固定到用户目录:
```bash
cd /home/dockerop/travel-knowledge-graph
./scripts/server_rootless_docker.sh start
./scripts/server_rootless_docker.sh up
./scripts/server_rootless_docker.sh ps
./scripts/server_rootless_docker.sh health
```
本项目在 `8.163.40.99` 的当前部署使用:
```bash
DOCKER_HOST=unix:///home/dockerop/.docker/run/docker.sock
```
如果服务器重启,重新执行:
```bash
cd /home/dockerop/travel-knowledge-graph
./scripts/server_rootless_docker.sh up
```
更推荐的长期方式是让服务器管理员执行:
```bash
sudo loginctl enable-linger dockerop
```
这样 rootless Docker 可以由 systemd 用户服务长期托管。
## 生产安全入口
服务器覆盖配置默认令 API 只监听 `127.0.0.1:8102`。公网只能通过 Nginx 的
HTTPS 443 端口访问,不再直接开放 8102。可复制
`deploy/nginx/travel-kg.conf.example`,替换域名和证书路径后启用。
安全组入站规则建议仅保留:
- `443/tcp`:所有合法业务来源;
- `22/tcp`:仅运维固定 IP 或 VPN 网段;
- `2222/tcp`:仅 DBeaver 数据管理员固定 IP 或 VPN 网段;
- `80/tcp`:仅用于跳转 HTTPS 或证书签发。
严禁向公网开放 `8102`、`3307`、`5433`、`6380` 和 `3002`。
## 生产 MySQL 持久化存储(首次部署必做)
生产环境采用两层存储,职责不能混用:
1. MySQL 运行目录使用服务器独立的本地块存储(例如云硬盘/SSD 挂载到
`/mnt/sdr`),不放在代码仓库、容器可写层、临时目录、NFS 或对象存储中;
2. 加密逻辑备份写到另一个目录,并继续自动同步到异地对象存储或另一台服务器。
服务器运维人员先在云平台挂载并格式化数据盘、配置 `/etc/fstab`,确认服务器重启后
仍能自动挂载。项目脚本不会格式化磁盘,也不会自动搬迁旧数据。设置 `.env`:
```dotenv
MYSQL_STORAGE_MOUNT=/mnt/sdr
MYSQL_STORAGE_ID=prod-mysql-storage-2026-001
MYSQL_DATA_DIR=/mnt/sdr/nianxx/mysql
MYSQL_CONTAINER_UID=999
MYSQL_CONTAINER_GID=999
DATA_BACKUP_ROOT=/mnt/backup/nianxx/mysql
DATA_BACKUP_RETENTION_DAYS=30
DATA_BACKUP_PASSPHRASE_FILE=/etc/travel-kg/secrets/mysql-backup-passphrase
DATA_BACKUP_ENABLED=false
```
脚本读取当前进程环境,因此把 `.env` 中以下非敏感路径变量一并导出后再准备目录
(Linux 服务器):
```bash
export MYSQL_STORAGE_MOUNT=/mnt/sdr
export MYSQL_STORAGE_ID=prod-mysql-storage-2026-001
export MYSQL_DATA_DIR=/mnt/sdr/nianxx/mysql
export DATA_BACKUP_ROOT=/mnt/backup/nianxx/mysql
export MYSQL_CONTAINER_UID=999
export MYSQL_CONTAINER_GID=999
sudo -E ./scripts/prepare_mysql_production_storage.sh
```
该脚本会确认 `/mnt/sdr` 确实是独立、可写、本地持久化挂载,写入磁盘身份标识,并
设置 MySQL 容器 UID/GID 权限。如果挂载丢失、磁盘身份不一致、目录出现半初始化
数据,`mysql-storage-guard` 会阻止 MySQL 启动,避免系统在错误的空目录中悄悄创建
一个新数据库。存储标识位于挂载根目录,MySQL 数据目录在首次初始化前保持为空。
先只启动 MySQL:
```bash
docker compose -f docker-compose.yml -f docker-compose.server.yml up -d mysql
docker compose -f docker-compose.yml -f docker-compose.server.yml ps mysql mysql-storage-guard
```
然后安装每日加密备份。备份服务默认使用执行 `sudo` 的服务器账号;该账号必须有
Docker 权限,并独占读取口令文件:
```bash
sudo install -d -m 0750 -o root -g "$(id -gn)" /etc/travel-kg/secrets
sudo touch /etc/travel-kg/secrets/mysql-backup-passphrase
sudo chown "$USER:$(id -gn)" /etc/travel-kg/secrets/mysql-backup-passphrase
sudo chmod 600 /etc/travel-kg/secrets/mysql-backup-passphrase
openssl rand -base64 48 > /etc/travel-kg/secrets/mysql-backup-passphrase
export DATA_BACKUP_PASSPHRASE_FILE=/etc/travel-kg/secrets/mysql-backup-passphrase
export DATA_BACKUP_RETENTION_DAYS=30
sudo -E ./scripts/install_mysql_backup_systemd.sh
```
安装程序会立即执行并验证第一份备份,成功后启用每天的 systemd timer。确认备份已
同步到异地存储后,把 `.env` 中 `DATA_BACKUP_ENABLED=true`,再启动完整生产服务:
```bash
docker compose -f docker-compose.yml -f docker-compose.server.yml up -d --build
```
生产覆盖配置把 MySQL 主机端口硬编码绑定到 `127.0.0.1`;即使误设
`MYSQL_HOST_BIND=0.0.0.0` 也不会将 MySQL 暴露到公网。
### 从原 Docker named volume 迁移
已有数据不能通过重新初始化获得。先在旧 MySQL 仍运行时完成加密备份,再停写并冷
拷贝;以下示例中的卷名要先通过 `docker volume ls` 和 `docker volume inspect`
核实:
```bash
export DATA_BACKUP_ROOT=/mnt/backup/nianxx/mysql
export DATA_BACKUP_PASSPHRASE_FILE=/etc/travel-kg/secrets/mysql-backup-passphrase
export DATA_BACKUP_RETENTION_DAYS=30
./scripts/backup_mysql_encrypted.sh
docker compose stop api dbeaver-gateway mysql
sudo -E ./scripts/prepare_mysql_production_storage.sh
docker run --rm \
-v travel-knowledge-graph_mysql-data:/from:ro \
-v /mnt/sdr/nianxx/mysql:/to \
alpine:3.22 sh -c 'cd /from && tar cf - . | tar xpf - -C /to'
docker compose -f docker-compose.yml -f docker-compose.server.yml up -d mysql
docker compose -f docker-compose.yml -f docker-compose.server.yml exec -T mysql sh -c \
'MYSQL_PWD="$MYSQL_ROOT_PASSWORD" mysqlcheck -uroot --all-databases'
```
完成表数量、记录数量和业务抽样核对前,不要删除原 Docker volume。若源目录不是完整
MySQL 数据目录,准备脚本和启动守卫都会拒绝继续。
## 端口配置
可以在启动时覆盖端口:
```bash
API_PORT=18102 \
POSTGRES_PORT=15433 \
FALKORDB_PORT=16380 \
FALKORDB_BROWSER_PORT=13002 \
docker compose up -d --build
```
默认端口:
| 变量 | 默认值 | 说明 |
| --- | --- | --- |
| `API_HOST_BIND` | `0.0.0.0` | 本地 Compose 的 API/后台监听地址 |
| `API_BIND_HOST` | 服务器覆盖默认 `127.0.0.1` | 生产 Uvicorn 监听地址,只允许 Nginx 转发 |
| `API_PORT` | `8102` | FastAPI 与管理后台 |
| `POSTGRES_HOST_BIND` | `127.0.0.1` | PostgreSQL 只绑定服务器本机 |
| `POSTGRES_PORT` | `5433` | PostgreSQL 映射端口 |
| `MYSQL_HOST_BIND` | `127.0.0.1` | MySQL 只绑定服务器本机,DBeaver 经 SSH 隧道接入 |
| `MYSQL_PORT` | `3307` | MySQL 服务器本机映射端口,不向公网开放 |
| `DATA_MYSQL_SSH_BIND` | `127.0.0.1` | DBeaver 专用 SSH 网关监听地址;远程部署必须显式绑定可达网卡并配防火墙白名单 |
| `DATA_MYSQL_SSH_PORT` | `2222` | DBeaver 专用 SSH 网关端口,不提供服务器终端 |
| `FALKORDB_HOST_BIND` | `127.0.0.1` | FalkorDB Redis 协议只绑定服务器本机 |
| `FALKORDB_PORT` | `6380` | FalkorDB Redis 协议端口 |
| `FALKORDB_BROWSER_HOST_BIND` | `127.0.0.1` | FalkorDB Browser 只绑定服务器本机 |
| `FALKORDB_BROWSER_PORT` | `3002` | FalkorDB Browser |
## 环境变量
基础 Docker Compose 提供开发默认值;服务器覆盖配置会对关键变量使用必填校验,
任何占位密码、弱签名密钥、公开 MySQL 绑定、通配 CORS 或不可信 Host 都会令
应用拒绝启动。
服务器首次部署建议复制模板后修改:
```bash
cp .env.example .env
```
至少修改:
```env
AUTH_SECRET=换成32位以上随机字符串
AUTH_DEFAULT_PASSWORD=换成12位以上后台管理员密码
INTERFACE_API_SECRET=与AUTH_SECRET不同的32位以上随机字符串
MYSQL_ACCESS_BROKER_PASSWORD=至少24位独立随机字符串
INGEST_API_KEYS=至少24位的外部系统接口Key
CORS_ALLOWED_ORIGINS=https://你的后台域名
TRUSTED_HOSTS=你的后台域名
DATA_MYSQL_SSH_HOST=你的服务器域名或可达IP
LLM_API_BASE=你的OpenAI兼容模型地址
LLM_API_KEY=你的模型Key
```
新系统不再提供后台 Agent 设置页面。LLM 与外部接口 Key 统一通过 `.env`、Compose 环境变量或部署平台的密钥配置维护。
| 变量 | 说明 |
| --- | --- |
| `POSTGRES_USER`、`POSTGRES_PASSWORD`、`POSTGRES_DB` | PostgreSQL 容器账号、密码、库名 |
| `DATABASE_URL` | 后端连接 PostgreSQL 的 URL |
| `DB_SCHEMA` | 默认 `kg_admin_new2` |
| `DB_MIGRATIONS_ENABLED` | 快照部署默认 `false` |
| `DATA_MYSQL_SSH_HOST`、`DATA_MYSQL_SSH_PORT` | DBeaver SSH 标签页使用的服务器地址和 SSH 端口 |
| `DATA_MYSQL_SSH_BIND`、`DATA_MYSQL_SSH_USERNAME` | 专用 SSH 网关监听地址与固定隧道用户;默认用户为 `dbeaver` |
| `MYSQL_ACCESS_BROKER_USER`、`MYSQL_ACCESS_BROKER_PASSWORD` | 接口中心签发/撤销个人 MySQL 账号使用的独立服务凭证,不得与 `data_center` 或 `root` 共用 |
| `DATA_MYSQL_ADMIN_HOST`、`DATA_MYSQL_ADMIN_PORT` | DBeaver Main 标签页使用的隧道远端 MySQL 地址,默认 `127.0.0.1:3307` |
| `DATA_MYSQL_AUDIT_ENABLED` | 只有数据库审计真实启用后才设置为 `true` |
| `DATA_SQL_CONSOLE_WRITE_ENABLED` | 默认 `false`,生产 SQL 控制台保持只读 |
| `MYSQL_STORAGE_MOUNT`、`MYSQL_STORAGE_ID` | 独立数据盘挂载点及其唯一身份标识,生产必填 |
| `MYSQL_DATA_DIR` | 独立数据盘内的 MySQL 运行目录,不能位于代码仓库内 |
| `DATA_BACKUP_ROOT` | 代码仓库和 MySQL 运行目录之外的加密备份目录 |
| `DATA_BACKUP_ENABLED` | 首次备份与自动任务验证成功后才设置为 `true`,生产为必填真值 |
| `DATA_BACKUP_RETENTION_DAYS` | 本机加密备份保留天数,允许 7–3650 天 |
| `DATA_BACKUP_PASSPHRASE_FILE` | 仓库外的备份加密口令文件,权限必须为 600 |
| `FALKORDB_HOST` | Docker 内默认 `falkordb` |
| `FALKORDB_GRAPH` | 默认业务图 `guiyang_new2` |
| `AUTH_SECRET` | JWT 签名密钥,生产必须替换 |
| `INTERFACE_API_SECRET` | 接口密钥散列专用密钥,不得与 JWT 密钥复用 |
| `AUTH_DEFAULT_USERNAME` | 默认管理员用户名 |
| `AUTH_DEFAULT_PASSWORD` | 默认管理员密码 |
| `LLM_API_BASE` | OpenAI 兼容模型服务地址,可选 |
| `LLM_API_KEY` | LLM 密钥,可选 |
| `LLM_EXTRACTION_ENABLED` | 是否启用 LLM 抽取 |
| `AMAP_WEB_KEY`、`AMAP_JS_KEY` | 高德地图密钥,可选 |
| `GAODE_CRAWLER_PATH` | 外部高德采集脚本路径,可选 |
| `TRAVEL_AGENCY_SOURCE_ROOT` | 旅行社原始资料目录,仅运行采集/构图脚本时需要 |
| `TRAVEL_DELIVERY_ROOT` | POI 交付 CSV 目录,仅运行采集/增强脚本时需要 |
| `TRAVEL_KG_EXPORT_ROOT` | 采集/构图脚本导出目录 |
## DBeaver 远程管理接入
本地模拟服务器环境:
```env
DATA_MYSQL_SSH_HOST=localhost
DATA_MYSQL_SSH_BIND=127.0.0.1
DATA_MYSQL_SSH_PORT=2222
```
```bash
docker compose up -d --build mysql dbeaver-gateway api
```
打开 `/admin/system/interfaces`,页面直接提供 DBeaver 管理接入,由页面生成管理员
个人私钥、MySQL 用户名和一次性密码。DBeaver 的 SSH 标签页连接
`localhost:2222`、用户 `dbeaver`;Main 标签页固定使用 `127.0.0.1:3307`。
服务器部署时把 `DATA_MYSQL_SSH_HOST` 改为服务器域名。如果管理员电脑需要通过
网络连接,把 `DATA_MYSQL_SSH_BIND` 设置为服务器内网 IP;只有确实需要公网接入时
才使用 `0.0.0.0`,并在云安全组和主机防火墙中将 `2222/tcp` 限定到管理员固定 IP
或 VPN 网段。MySQL 的 `3307/tcp` 始终保持回环绑定。
升级已有 MySQL 数据卷后,初始化脚本不会自动重跑,需要执行一次:
```bash
./scripts/enable_managed_dbeaver_access.sh
docker compose up -d --build dbeaver-gateway api
```
专用 SSH 用户不能打开终端或 SFTP,只能转发到 MySQL;每位管理员的公钥与 MySQL
账号均可在接口中心一键撤销。完整私钥和 MySQL 密码只展示一次;DBeaver 首次
连接时必须核对页面显示的服务器 SSH 主机指纹。
## 百姓惠智能客服接口
给外部系统对接时,只开放这个接口即可:
```text
POST https://你的域名/v1/openapi/knowledge-qa/query
```
`/v1/admin/travel/customer-service-query` 会继续保留为兼容旧调用,新接入的第三方系统建议统一使用 `/v1/openapi/knowledge-qa/query`。
生产服务器不应放行 TCP `8102`。服务器本机可用
`curl http://127.0.0.1:8102/v1/admin/health` 检查应用,外部统一访问
`https://你的域名/`。不要把 `3307`、`5433`、`6380`、`3002` 暴露到公网。
生产环境必须通过环境变量 `INGEST_API_KEYS` 配置至少一个接口 Key;未配置时,对外问答接口会返回 `503`,避免无鉴权开放。
接口 Key 是给第三方系统调用本接口用的访问凭证;问答环节 LLM 的 API Key 是本系统调用大模型用的凭证,两者不要混用。
请求示例:
```bash
curl https://你的域名/v1/openapi/knowledge-qa/query \
-H 'Content-Type: application/json' \
-H 'X-KG-API-Key: 你的INGEST_API_KEYS之一' \
-d '{
"request_id": "crm-msg-20260610-0001",
"question": "黄小西三日游多少钱?",
"graph_name": "baixinghui_travel_agency",
"session_id": "demo-001",
"channel": "online_service",
"use_llm": true,
"llm_fusion": true
}'
```
## MySQL 加密备份
口令文件、外部备份目录和自动任务应按“生产 MySQL 持久化存储”章节创建。手动执行
与校验示例:
```bash
export DATA_BACKUP_ROOT=/mnt/backup/nianxx/mysql
export DATA_BACKUP_PASSPHRASE_FILE=/etc/travel-kg/secrets/mysql-backup-passphrase
export MYSQL_DATA_DIR=/mnt/sdr/nianxx/mysql
export MYSQL_STORAGE_ID=prod-mysql-你的唯一标识
export DATA_BACKUP_RETENTION_DAYS=30
./scripts/backup_mysql_encrypted.sh
./scripts/verify_mysql_backup.sh /mnt/backup/nianxx/mysql/mysql-all-时间.sql.gz.enc
```
脚本不再提供仓库内默认备份目录:目录缺失、位于仓库内、与运行数据重叠、口令权限
过宽或保留周期不合规都会直接失败。备份采用 `mysqldump --single-transaction`、
Binlog 坐标、gzip 和 AES-256/PBKDF2,生成 SHA-256 与清单,并在清理过期备份前
自动解密验包。`.enc`、`.sha256`、`.json` 三个文件必须同步到独立账号的异地对象
存储。验包不等于恢复演练;至少每月在隔离 MySQL 实例完整恢复一次。
默认问答链路需要配置问答环节 LLM;可在后台 `外部图谱问答 API -> 问答环节 LLM 模型` 单独配置,也可以继承全局 LLM。
主要返回字段:
| 字段 | 说明 |
| --- | --- |
| `request_id` | 外部请求 ID,原样回传或由服务端生成 |
| `trace_id` | 服务端链路追踪 ID |
| `answer` | 给外部系统展示的完整回答 |
| `customer_reply` | 可以直接发给客户的话术 |
| `follow_up_questions` | 建议追问 |
| `risk_notes` | 需要二次核实或不可直接承诺的事项 |
| `knowledge.plans` | 图谱命中的线路/方案 |
| `knowledge.evidence` | 图谱证据 |
## 健康检查
API:
```bash
curl http://localhost:8102/v1/admin/health
```
PostgreSQL:
```bash
docker compose exec postgres pg_isready -U admin -d kg_admin
```
FalkorDB:
```bash
docker compose exec falkordb redis-cli -p 6379 PING
```
## 常见问题
### 管理后台 404
确认镜像已重新构建:
```bash
docker compose up -d --build api
```
前端静态资源由 FastAPI 挂载在 `/admin`,直接访问根路径不会进入后台。
### 数据没有恢复
PostgreSQL 初始化脚本只在数据卷首次创建时运行。如果已经创建过数据卷,需要先删除卷:
```bash
docker compose down -v
docker compose up -d --build
```
### 端口被占用
使用端口变量覆盖默认端口,例如:
```bash
API_PORT=18102 docker compose up -d
```
### 登录失败
初始化脚本会把 `admin@example.com` 的演示密码设置为 `change-me`。如果仍失败,先确认 PostgreSQL 已重新恢复快照,再查看 API 日志。
## 生产加固建议
- 修改 `AUTH_SECRET`、数据库密码和默认管理员密码。
- 不要把真实 `.env`、LLM key、高德 key 提交到仓库。
- 用反向代理提供 HTTPS。
- 给 PostgreSQL 和 FalkorDB 配置持久化备份。
- MySQL、PostgreSQL 和 FalkorDB 全部只绑定服务器本机;DBeaver 经 SSH 私钥隧道接入,公网只暴露 HTTPS 和受控 SSH。
- 开启日志采集和容器监控。