303 lines
10 KiB
Markdown
303 lines
10 KiB
Markdown
# 小白复盘局域网 Docker 部署
|
||
|
||
本文以 Linux 服务器为目标,容器内外均使用 `8765` 端口,宿主机监听
|
||
`0.0.0.0:8765`。局域网用户通过 `http://服务器局域网IP:8765` 访问。
|
||
|
||
## 1. 部署结构
|
||
|
||
```text
|
||
局域网浏览器
|
||
|
|
||
v
|
||
服务器 0.0.0.0:8765
|
||
|
|
||
v
|
||
xiaobai-review 容器 :8765
|
||
|-- /app 只读应用代码
|
||
| `-- backend/features/heaven/assets/heaven_knowledge.json
|
||
| 镜像内 seed(不受 data 挂载遮盖)
|
||
`-- /app/data 宿主机 ./data 持久化挂载
|
||
|-- review.db
|
||
|-- iching_zh.json
|
||
`-- heaven_knowledge.json 优先读取;缺失时回退到上方 seed
|
||
```
|
||
|
||
账号、加密后的公共数据 Token、平台模型 API Key、生辰资料、行情快照和复盘数据均在
|
||
`data/review.db`。解密密钥来自 `.env` 中的 `APP_ENCRYPTION_KEY`。数据库与
|
||
密钥必须成对备份,任意一个丢失都无法恢复账号内的加密资料。
|
||
|
||
问天静态知识文件:
|
||
|
||
- `data/iching_zh.json`、`data/heaven_knowledge.json` 纳入 Git 与镜像构建;
|
||
`.dockerignore` 不排除这两个文件(只排除 `data/*.db`、`data/cache/` 等运行时产物)。
|
||
- Compose 把宿主机 `./data` 整目录挂到 `/app/data`,会遮盖镜像里同路径文件。
|
||
因此宿主机 `data/` 应保留上述两个 JSON;若只缺 `heaven_knowledge.json`,
|
||
服务会回退读取镜像内
|
||
`backend/features/heaven/assets/heaven_knowledge.json`,解势仍可用。
|
||
- 持久化位置:正式环境以宿主机项目目录下的 `./data/heaven_knowledge.json` 为准;
|
||
补文件后无需改代码,重启容器即可加载。
|
||
|
||
管理员私有的问师 Skill 保存在宿主机 `data/private-mentor-skills/`。该目录随 `data`
|
||
挂载进入容器,但被 Git 与 Docker 构建上下文排除,不会进入 Gitea 或镜像。私有 Skill
|
||
只对管理员账号返回和开放调用,也会随本指南的 `data` 备份一起保存。
|
||
|
||
首个注册账号自动成为管理员。管理员在“系统管理”中配置全站共享行情、后台刷新、平台会员模型及手动会员;普通用户的“账号设置”用于个人资料、会员状态、修改密码和切换账号。后台行情更新不会主动刷新任何浏览器页面。
|
||
|
||
## 2. 服务器要求
|
||
|
||
- 64 位 Linux 服务器;
|
||
- Docker Engine 24 或更新版本;
|
||
- Docker Compose v2,命令形式为 `docker compose`;
|
||
- 服务器可以访问 Tushare、已配置的 LLM 和实时聚合数据源;
|
||
- 局域网内没有其他服务占用 TCP `8765`。
|
||
|
||
验证 Docker:
|
||
|
||
```bash
|
||
docker --version
|
||
docker compose version
|
||
```
|
||
|
||
## 3. 迁移现有数据
|
||
|
||
迁移前先停止当前 Windows 服务,避免复制过程中 SQLite 继续写入。
|
||
然后在应用目录执行一次 WAL 检查点:
|
||
|
||
```powershell
|
||
python -c "import sqlite3; c=sqlite3.connect('data/review.db'); print(c.execute('PRAGMA wal_checkpoint(TRUNCATE)').fetchone()); c.close()"
|
||
```
|
||
|
||
结果第一项应为 `0`。必须迁移以下内容:
|
||
|
||
```text
|
||
data/
|
||
.env
|
||
Dockerfile
|
||
compose.yaml
|
||
其余程序文件
|
||
```
|
||
|
||
不要重新生成 `APP_ENCRYPTION_KEY`。部署已有数据库时,目标服务器 `.env` 中的
|
||
值必须与原服务器完全一致。
|
||
|
||
可以在项目目录生成迁移包:
|
||
|
||
```powershell
|
||
tar --exclude='__pycache__' --exclude='*.log' --exclude='data/cache' -czf ..\xiaobai-review.tar.gz .
|
||
scp ..\xiaobai-review.tar.gz 用户名@服务器IP:/tmp/
|
||
```
|
||
|
||
迁移包包含数据库和密钥,传输完成后应及时删除两端的压缩包。
|
||
|
||
## 4. 首次启动
|
||
|
||
在 Linux 服务器执行:
|
||
|
||
```bash
|
||
sudo mkdir -p /opt/xiaobai-review
|
||
sudo chown "$USER":"$USER" /opt/xiaobai-review
|
||
tar -xzf /tmp/xiaobai-review.tar.gz -C /opt/xiaobai-review
|
||
cd /opt/xiaobai-review
|
||
chmod 600 .env
|
||
sudo chown -R 10001:10001 data
|
||
docker compose config
|
||
docker compose build --pull
|
||
docker compose up -d
|
||
```
|
||
|
||
镜像使用 UID/GID `10001` 的非 root 用户运行,因此宿主机 `data` 目录必须允许
|
||
该用户写入。不要把整个应用目录设为可写。
|
||
|
||
检查运行状态:
|
||
|
||
```bash
|
||
docker compose ps
|
||
docker compose logs --tail=100 xiaobai-review
|
||
curl http://127.0.0.1:8765/api/health
|
||
docker inspect --format '{{.State.Health.Status}}' xiaobai-review
|
||
```
|
||
|
||
健康接口应返回类似内容:
|
||
|
||
```json
|
||
{"ok": true, "storage": "sqlite", "account_required": true}
|
||
```
|
||
|
||
随后在局域网电脑访问:
|
||
|
||
```text
|
||
http://服务器局域网IP:8765
|
||
```
|
||
|
||
## 5. 防火墙
|
||
|
||
Compose 已明确绑定 `0.0.0.0:8765`。服务器防火墙建议只允许实际局域网网段,
|
||
不要在路由器上把该端口映射到公网。
|
||
|
||
Ubuntu/UFW 示例,假设局域网为 `192.168.1.0/24`:
|
||
|
||
```bash
|
||
sudo ufw allow from 192.168.1.0/24 to any port 8765 proto tcp
|
||
sudo ufw status
|
||
```
|
||
|
||
如果服务器位于其他网段,应替换为实际 CIDR。访问失败时同时检查云服务器安全组、
|
||
虚拟化平台防火墙和宿主机防火墙。
|
||
|
||
## 6. 日常管理
|
||
|
||
查看日志:
|
||
|
||
```bash
|
||
cd /opt/xiaobai-review
|
||
docker compose logs -f --tail=100 xiaobai-review
|
||
```
|
||
|
||
重启:
|
||
|
||
```bash
|
||
docker compose restart xiaobai-review
|
||
```
|
||
|
||
停止:
|
||
|
||
```bash
|
||
docker compose down
|
||
```
|
||
|
||
### 镜像构建的唯一安全入口(2026-08 HEL-235 起)
|
||
|
||
生产机 `192.168.200.11` 上的 `/opt/1panel/docker/compose/xiaobaifupan` 只是历史文件树:
|
||
不是 Git 仓库、内容停在旧提交、与线上镜像不一致,且其 `compose.yaml` 会把构建结果打进
|
||
`xiaobai-review:latest`。**禁止在该目录(或任何服务器工作树)里 `docker build` /
|
||
`docker compose build`**,否则会把已上线功能悄悄打回旧版。
|
||
|
||
唯一安全构建方式是在有仓库检出、能免密 SSH 到部署机的机器上运行:
|
||
|
||
```bash
|
||
tools/build_image.sh <提交号> <镜像tag>
|
||
# 示例:tools/build_image.sh cefc86917d89 verify-hel235-cefc869
|
||
```
|
||
|
||
该脚本的行为约束:
|
||
|
||
- 先 `git fetch`,再把提交号解析为完整 SHA,解析失败立即中止,绝不使用本地脏状态或服务器旧目录;
|
||
- 镜像 tag 必须以 `-<提交短号7位>` 结尾(如 `hel234-cefc869`),禁止 `latest`、`rollback-*`;
|
||
- 通过 `git archive <提交> | ssh 部署机 docker build -` 流式构建,服务器上不存在构建用工作树;
|
||
- 构建后回读镜像 label 里的 `org.opencontainers.image.revision`,与预期提交不一致则删除镜像并中止;
|
||
- 每次构建在部署机 `~/xiaobai-build/BUILD_LOG.tsv` 留痕,可追溯每个镜像的来源提交。
|
||
|
||
构建只产出镜像,不启动、不替换任何容器;换版用新 tag 起新容器,回滚用既有镜像 tag 重跑。
|
||
|
||
### 使用 Gitea 更新程序(旧方式,生产机禁用)
|
||
|
||
代码仓库为:
|
||
|
||
```text
|
||
http://192.168.200.36:3200/leefer/xiaobaifupan.git
|
||
```
|
||
|
||
首次在服务器部署代码时,可以直接克隆到目标目录:
|
||
|
||
```bash
|
||
sudo mkdir -p /opt/xiaobai-review
|
||
sudo chown "$USER":"$USER" /opt/xiaobai-review
|
||
git clone http://192.168.200.36:3200/leefer/xiaobaifupan.git /opt/xiaobai-review
|
||
cd /opt/xiaobai-review
|
||
```
|
||
|
||
私有仓库会提示输入 Gitea 用户名和密码或访问令牌。不要把密码写入仓库 URL、
|
||
`compose.yaml` 或脚本。然后把原 `.env` 与 `data/` 放回该目录;这两项已被 Git
|
||
忽略,后续拉取代码不会覆盖数据库与密钥。
|
||
|
||
如需部署管理员私有问师,通过 NAS 文件管理器将本地
|
||
`data/private-mentor-skills/` 复制到服务器项目的同名 `data` 目录,并保持目录仅由
|
||
部署账号和容器运行用户读取。该内容不会通过 Gitea 同步。
|
||
|
||
每次更新前先创建 SQLite 一致性备份,再拉取并重建容器(注意:`docker compose up -d --build`
|
||
从服务器本地工作树构建,仅适用于来源可信的全新环境;生产机 `192.168.200.11` 禁用,
|
||
请用 `tools/build_image.sh` 构建后换容器):
|
||
|
||
```bash
|
||
cd /opt/xiaobai-review
|
||
docker compose exec -T xiaobai-review python -c "import sqlite3; s=sqlite3.connect('/app/data/review.db'); d=sqlite3.connect('/app/data/review-before-update.db'); s.backup(d); d.close(); s.close()"
|
||
git pull --ff-only origin main
|
||
docker compose up -d --build
|
||
docker compose ps
|
||
curl --fail http://127.0.0.1:8765/api/health
|
||
```
|
||
|
||
`docker compose up -d --build` 会原地替换应用容器,不删除宿主机的 `data` 目录。
|
||
数据库迁移会在新容器启动时自动执行。若 `git pull --ff-only` 提示本地代码有修改,
|
||
先用 `git status` 查明原因,不要用强制重置覆盖 `.env` 或 `data`。
|
||
|
||
### 不使用 Git 时更新(生产机禁用)
|
||
|
||
`docker compose build` 会从服务器本地目录构建,来源提交不可追溯。生产机
|
||
`192.168.200.11` 上禁止使用本节方式,一律改用上一节的 `tools/build_image.sh`。
|
||
|
||
重新上传代码后执行:
|
||
|
||
```bash
|
||
docker compose down
|
||
docker compose build --pull
|
||
docker compose up -d
|
||
```
|
||
|
||
`docker compose down` 不会删除宿主机的 `data` 目录。不要使用带有手工删除
|
||
`data` 目录的清理命令。
|
||
|
||
## 7. 备份与恢复
|
||
|
||
最稳妥的备份方式是短暂停服后同时备份数据库目录和密钥:
|
||
|
||
```bash
|
||
cd /opt/xiaobai-review
|
||
docker compose stop xiaobai-review
|
||
tar -czf "xiaobai-backup-$(date +%Y%m%d-%H%M%S).tar.gz" data .env
|
||
docker compose start xiaobai-review
|
||
```
|
||
|
||
恢复时先停止容器,再恢复 `data` 和与其配套的 `.env`,修复权限后启动:
|
||
|
||
```bash
|
||
docker compose down
|
||
sudo chown -R 10001:10001 data
|
||
chmod 600 .env
|
||
docker compose up -d
|
||
```
|
||
|
||
## 8. 常见问题
|
||
|
||
### 容器反复重启
|
||
|
||
```bash
|
||
docker compose logs --tail=200 xiaobai-review
|
||
```
|
||
|
||
优先检查 `.env` 是否存在、`APP_ENCRYPTION_KEY` 是否为空,以及 `data` 是否可写。
|
||
|
||
### 提示账号加密数据无法解密
|
||
|
||
目标服务器使用了错误的 `APP_ENCRYPTION_KEY`。停止容器并恢复与数据库配套的
|
||
原始 `.env`,不要通过重置密钥绕过该错误。
|
||
|
||
### SQLite 显示只读或无法打开
|
||
|
||
```bash
|
||
sudo chown -R 10001:10001 /opt/xiaobai-review/data
|
||
sudo chmod -R u+rwX /opt/xiaobai-review/data
|
||
docker compose restart xiaobai-review
|
||
```
|
||
|
||
### 本机健康检查正常但其他电脑无法访问
|
||
|
||
确认 `docker compose ps` 显示 `0.0.0.0:8765->8765/tcp`,然后检查服务器防火墙和
|
||
客户端到服务器的网络路由。
|
||
|
||
## 9. 安全边界
|
||
|
||
当前部署使用局域网 HTTP,账号密码和会话只适合可信内网使用。不要直接将
|
||
`8765` 暴露到互联网。以后需要公网访问时,应在容器前增加 Caddy 或 Nginx,
|
||
启用 HTTPS,并限制可信来源。
|