docs: 整理全量文档

- README: 补充 reset.sh 快速入口和脚本对比表
- runbook: 新增「容器重置」章节和「故障排查」(502/crash-loop/WebSocket/镜像未生效)
- configuration: 修正端口表(im=11228/push=11229/update=11230/license=11231),
  补充 SPRING_DATASOURCE_PASSWORD 设计说明,移除已废弃的 environment 密码块说明
- acceptance-checklist: 修正端口号,补充 WebSocket 多层透传说明,移除不存在的 11227 行
- deployment-defaults: 同步端口表,补充 reset.sh 和 WebSocket 注意事项

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
这个提交包含在:
徐勤民 2026-05-21 11:48:45 +08:00
父节点 e61a1dce08
当前提交 dde3f9ece7
共有 5 个文件被更改,包括 213 次插入71 次删除

查看文件

@ -2,32 +2,54 @@
## 快速开始
**首次部署:**
### 首次部署
```bash
curl -fsSL https://xuqinmin.com/xuqmGroup/XuqmGroup-PrivateDeploy/raw/branch/main/install.sh \
-o install.sh && bash install.sh
```
脚本自动完成:依赖检测 → 配置生成 → 镜像拉取 → 容器启动 → 租户初始化(新建或迁移)→ 全量验证。
交互式向导依次完成:依赖检测 → 配置生成 → 镜像拉取 → 容器启动 → 租户初始化 → 全量验证。
**升级已有部署:**
### 升级已有部署
```bash
curl -fsSL https://xuqinmin.com/xuqmGroup/XuqmGroup-PrivateDeploy/raw/branch/main/upgrade.sh \
-o upgrade.sh && bash upgrade.sh
```
升级脚本自动完成:下载最新脚本 → 保留全部数据和配置 → 修复配置问题 → 可选拉取新镜像 → 重启容器 → 全量验证。
保留全部数据和配置,自动修复已知配置问题,可选拉取新镜像,完成后运行全量验证。
部署完成后根据输出的端口表配置宿主机 nginx,详见 [docs/runbook.md](docs/runbook.md)。
### 容器异常重置
```bash
bash scripts/reset.sh
```
适用于nginx 502、服务 crash-loop、密码注入错误、升级后镜像未生效。保留数据和配置,彻底重建所有容器。
---
## 脚本说明
| 脚本 | 适用场景 |
|------|---------|
| `install.sh` | 全新服务器首次部署;或彻底清除数据重装 |
| `upgrade.sh` | 保留数据,更新部署脚本和镜像(推荐日常升级方式) |
| `scripts/reset.sh` | 保留数据,容器状态异常时快速重建恢复 |
| `scripts/update.sh` | 在安装目录内执行的局部升级(由 upgrade.sh 调用) |
| `scripts/verify.sh` | 随时重新运行全量验证 |
| `scripts/backup.sh` | 备份数据 |
| `scripts/restore.sh` | 恢复备份 |
---
## 部署架构
```
上层 nginx任意层级
上层 nginx任意层级,HTTPS / 域名
└── 本机 IP:80 内置 nginx 容器(统一入口,无需宿主机 nginx
└── 本机 IP:80 内置 nginx 容器(统一入口)
├── tenant-service /api/ /actuator/
├── file-service /file/
@ -38,7 +60,9 @@ curl -fsSL https://xuqinmin.com/xuqmGroup/XuqmGroup-PrivateDeploy/raw/branch/mai
└── push-service (厂商回调,按需)
```
内置 nginx 容器直接绑定宿主机 `0.0.0.0:80`,**宿主机无需任何 nginx 配置**,上层 nginx 直接 proxy_pass 到本机 IP 即可。各业务容器11224–11231绑定 `127.0.0.1`,仅用于本地调试。
内置 nginx 容器直接绑定宿主机 `0.0.0.0:80`,**宿主机无需配置 nginx**,上层 nginx 直接 proxy_pass 到本机 IP 即可。各业务容器11224–11231绑定 `127.0.0.1`,仅用于本地调试。
---
## 租户初始化方式
@ -47,6 +71,8 @@ curl -fsSL https://xuqinmin.com/xuqmGroup/XuqmGroup-PrivateDeploy/raw/branch/mai
- **新建租户**:填写邮箱、用户名、密码,首次启动自动创建
- **迁移租户**:在公有化平台安全中心生成迁移密钥(`pmk_` 开头),粘贴后自动完成导入
---
## 服务说明
| Profile | 服务 | 说明 |
@ -59,21 +85,24 @@ curl -fsSL https://xuqinmin.com/xuqmGroup/XuqmGroup-PrivateDeploy/raw/branch/mai
| update | update-service | 版本管理 + RN 热更新 |
| license | license-service | License 校验 |
## 后期启用/禁用服务
```bash
./scripts/enable-service.sh im
./scripts/disable-service.sh im
# 后期启用 / 禁用可选服务
bash scripts/enable-service.sh im
bash scripts/disable-service.sh im
```
---
## 注意事项
- `application.yml` 中数据库 URL 硬编码了生产地址,`docker-compose.yml` 的 `environment:` 节负责覆盖,**不能删除**
- 上层 nginx `proxy_pass` 写本机 IP端口 80,须透传 `Upgrade` / `Connection`WebSocket 必需),每层都要加,详见 [docs/runbook.md](docs/runbook.md)
- 上层 nginx 每一层都必须透传 `Upgrade` / `Connection` 头,否则 IM WebSocket 会断开,详见 [docs/runbook.md](docs/runbook.md)
- 宿主机本身无需配置 nginx
---
## 文档
- [运行手册](docs/runbook.md) — 完整部署流程、nginx 配置、常用运维命令
- [运行手册](docs/runbook.md) — 完整部署流程、nginx 配置、故障排查、常用运维命令
- [配置说明](docs/configuration.md) — 各配置文件字段说明
- [验收清单](docs/acceptance-checklist.md) — 交付验收检查项
- [部署信息记录模板](docs/deployment-defaults.md) — 填写后交付客户存档

查看文件

@ -11,7 +11,6 @@
- [ ] tenant-service 健康:`curl http://127.0.0.1:11224/actuator/health` 返回 200
- [ ] 控制台前端可访问:`curl http://127.0.0.1:11226` 返回 HTML
- [ ] 运营后台可访问:`curl http://127.0.0.1:11227` 返回 HTML
- [ ] 文件服务可访问:`curl http://127.0.0.1:11225/file/health` 返回正常
- [ ] 私有化模式确认:`/api/private/deployment/status` 返回 `"mode":"PRIVATE"`
- [ ] 注册入口关闭:`"tenantRegisterEnabled":false`
@ -19,8 +18,8 @@
## 可选服务
- [ ] im-service 健康:`curl http://127.0.0.1:11228/actuator/health` 返回 200
- [ ] update-service 健康:`curl http://127.0.0.1:11229/actuator/health` 返回 200
- [ ] license-service 健康:`curl http://127.0.0.1:11230/actuator/health` 返回 200
- [ ] update-service 健康:`curl http://127.0.0.1:11230/actuator/health` 返回 200
- [ ] license-service 健康:`curl http://127.0.0.1:11231/actuator/health` 返回 200
- [ ] push-service 已启动(厂商凭据按需填写)
## 中间件
@ -38,9 +37,9 @@
- [ ] 宿主机 nginx 配置后,通过外部地址可访问控制台
- [ ] `/api/im/` 路由到 im-service11228
- [ ] `/ws/im` WebSocket 连接正常(需 `Upgrade` 头)
- [ ] `/api/v1/updates/` 路由到 update-service11229,不被 `/api/` 拦截
- [ ] `/api/license/` 路由到 license-service11230,不被 `/api/` 拦截
- [ ] `/ws/im` WebSocket 连接正常(需每层 nginx 透传 `Upgrade` / `Connection` 头)
- [ ] `/api/v1/updates/` 路由到 update-service11230,不被通用 `/api/` 拦截
- [ ] `/api/license/` 路由到 license-service11231,不被通用 `/api/` 拦截
- [ ] `/file/` 支持大文件上传500MB
## 公有化隔离

查看文件

@ -14,16 +14,25 @@
| `MYSQL_MODE` | `managed`(容器托管)或 `external`(客户自备) |
| `REDIS_MODE` | `managed``external` |
| `CONSOLE_DOMAIN` | 对外访问地址,用于服务间跳转和 SDK 配置 |
| `NGINX_BIND` | 内置 nginx 绑定端口。`80` = 直接监听宿主机 80;`127.0.0.1:11223` = 有宿主机 nginx 场景 |
---
## `config/secrets.env`
敏感配置,权限 600,不提交 Git。
敏感配置,权限 600,不提交 Git。`install.sh` 自动生成。
| 字段 | 说明 |
|------|------|
| `MYSQL_ROOT_PASSWORD` | MySQL root 密码 |
| `MYSQL_PASSWORD` | 业务账号密码 |
| `REDIS_PASSWORD` | Redis 密码 |
| `SPRING_DATASOURCE_PASSWORD` | Spring 读取的数据库密码(与 `MYSQL_PASSWORD` 值相同) |
| `SPRING_DATA_REDIS_PASSWORD` | Spring 读取的 Redis 密码(与 `REDIS_PASSWORD` 值相同) |
> **设计说明**Spring 通过 `env_file` 直接读取 `SPRING_DATASOURCE_PASSWORD`,而非通过 `docker-compose.yml``environment:` 节替换 `${MYSQL_PASSWORD}`。这是因为 compose 变量替换在 shell 中找不到该变量时会替换为空字符串,导致"Access denied (using password: NO)"。旧版部署如缺少这两个字段,`reset.sh` 和 `update.sh` 会自动补齐。
---
## `config/xuqm.env`
@ -38,32 +47,19 @@ TENANT_BOOTSTRAP_ENABLED=true
SDK 对外地址也在此文件配置:
```env
CONSOLE_DOMAIN=http://your-domain-or-ip
SDK_FILE_SERVICE_URL=http://your-domain-or-ip
SDK_IM_API_URL=http://your-domain-or-ip
SDK_IM_WS_URL=ws://your-domain-or-ip/ws/im
CONSOLE_DOMAIN=https://your-domain
SDK_FILE_SERVICE_URL=https://your-domain
SDK_IM_API_URL=https://your-domain
SDK_IM_WS_URL=wss://your-domain/ws/im
```
---
## `config/tenant/bootstrap.env`
初始租户配置,权限 600。迁移模式下由迁移流程自动覆盖。
## Spring Boot 数据库 URL 覆盖
`application.yml` 中数据库 URL 硬编码了生产地址。私有化部署**必须**通过 `docker-compose.yml``environment:` 节覆盖:
```yaml
environment:
SPRING_DATASOURCE_URL: "jdbc:mysql://${MYSQL_HOST}:${MYSQL_PORT:-3306}/${MYSQL_DATABASE:-xuqm_private}?..."
SPRING_DATASOURCE_USERNAME: "${MYSQL_USERNAME:-xuqm}"
SPRING_DATASOURCE_PASSWORD: "${MYSQL_PASSWORD}"
SPRING_DATA_REDIS_HOST: "${REDIS_HOST}"
SPRING_DATA_REDIS_PORT: "${REDIS_PORT:-6379}"
SPRING_DATA_REDIS_PASSWORD: "${REDIS_PASSWORD}"
SPRING_DATA_REDIS_DATABASE: "${REDIS_DATABASE:-0}"
```
`docker-compose.yml` 已包含此配置,不需要手动修改。
---
## 服务端口
@ -74,12 +70,30 @@ environment:
| 11224 | tenant-service | 9001 | `/api/` `/actuator/` |
| 11225 | file-service | 8086 | `/file/` |
| 11226 | tenant-web | 80 | `/`(兜底路由) |
| 11227 | im-service | 8082 | `/api/im/` `/ws/im` |
| 11229 | update-service | 8084 | `/api/v1/updates/` `/api/v1/rn/` |
| 11230 | license-service | 8085 | `/api/license/` |
| 11231 | push-service | 8083 | 厂商回调(按需) |
| 11228 | im-service | 8082 | `/api/im/` `/ws/im` |
| 11229 | push-service | 8083 | 厂商回调(按需) |
| 11230 | update-service | 8084 | `/api/v1/updates/` `/api/v1/rn/` |
| 11231 | license-service | 8085 | `/api/license/` |
内置 nginx 绑定宿主机 `0.0.0.0:80`,上层直接 proxy_pass 到本机 IP 即可。11224–11231 绑定 `127.0.0.1`,仅用于直接调试。
11224–11231 全部绑定 `127.0.0.1`,仅用于直接调试。正常流量走内置 nginx80 / 11223
---
## Spring Boot 数据库 URL 覆盖
`application.yml` 中数据库 URL 硬编码了生产地址。私有化部署通过 `docker-compose.yml``environment:` 节覆盖连接信息URL、用户名、Host、端口,密码通过 `config/secrets.env``env_file` 直接注入,两者不重叠:
```yaml
environment:
SPRING_DATASOURCE_URL: "jdbc:mysql://${MYSQL_HOST}:${MYSQL_PORT:-3306}/..."
SPRING_DATASOURCE_USERNAME: "${MYSQL_USERNAME:-xuqm}"
SPRING_DATA_REDIS_HOST: "${REDIS_HOST}"
SPRING_DATA_REDIS_PORT: "${REDIS_PORT:-6379}"
SPRING_DATA_REDIS_DATABASE: "${REDIS_DATABASE:-0}"
# 密码不在此处设置,由 secrets.env env_file 注入,见上方说明
```
---
## 容器内部通信
@ -94,11 +108,15 @@ PUSH_SERVICE_URL: "http://push-service:8083"
SDK_TENANT_SERVICE_URL: "http://tenant-service:9001"
```
`docker-compose.yml` 已包含上述配置,不需要手动修改。
---
## 内置 nginx
nginx 容器属于 `base` 必启服务,直接绑定宿主机 `0.0.0.0:80`,统一处理所有内部路由(按 Docker 服务名转发,无需关心各服务端口)。**宿主机无需额外 nginx 配置**,上层 nginx 直接 proxy_pass 指向本机 IP:80 即可,详见 [runbook.md](runbook.md#nginx-配置)。
nginx 容器属于 `base` 必启服务,直接绑定宿主机 `0.0.0.0:80`,统一处理所有内部路由。配置文件为 `config/nginx/conf.d/xuqm.conf`,使用 `resolver 127.0.0.11 valid=10s` + 变量化 `proxy_pass`,**容器重建后 nginx 无需 reload**,自动通过 Docker DNS 获取新 IP。
详见 [runbook.md](runbook.md#nginx-配置)。
---
## `config/vendors/`
@ -107,10 +125,14 @@ nginx 容器属于 `base` 必启服务,直接绑定宿主机 `0.0.0.0:80`,
| `push.env` | 华为/小米/OPPO/vivo/荣耀/APNs/FCM 推送凭据,默认全部 `false` |
| `store-submit.env` | 应用市场自动发布凭据,默认全部 `false` |
---
## `config/mail/smtp.env`
邮件服务配置,不填则邮件功能不可用,不影响其他服务启动。
---
## `config/sdk/xuqm-private-sdk.json`
SDK 初始化配置,由 `deploy.sh` 自动生成,客户端应用使用此文件初始化 SDK,不再指向公有化地址。
SDK 初始化配置,由 `deploy.sh` 自动生成,客户端应用使用此文件初始化 SDK,不再指向公有化地址。如地址错误可执行 `bash scripts/update.sh` 自动修正。

查看文件

@ -43,10 +43,10 @@
| 11224 | tenant-service | 9001 |
| 11225 | file-service | 8086 |
| 11226 | tenant-web | 80 |
| 11227 | im-service | 8082 |
| 11229 | update-service | 8084 |
| 11230 | license-service | 8085 |
| 11231 | push-service | 8083 |
| 11228 | im-service | 8082 |
| 11229 | push-service | 8083 |
| 11230 | update-service | 8084 |
| 11231 | license-service | 8085 |
宿主机 nginx 配置参考见 [docs/runbook.md](runbook.md)。
@ -79,6 +79,9 @@ bash scripts/verify.sh
# 停止所有服务(保留数据)
docker compose -f docker-compose.yml -f docker-compose.infra.yml down
# 容器异常时重置(保留数据)
bash scripts/reset.sh
```
---
@ -88,3 +91,4 @@ docker compose -f docker-compose.yml -f docker-compose.infra.yml down
1. **注册功能已禁用**`TENANT_REGISTER_ENABLED=false`,只能用初始化的账号登录。
2. **数据持久化**MySQL 和 Redis 数据存储在 `data/` 目录,容器重启不丢失。
3. **推送服务**push-service 已启动,各厂商推送默认关闭,需填写 `config/vendors/push.env` 并重启。
4. **WebSocket**:每一层 nginx 代理都必须透传 `Upgrade` / `Connection` 头,否则 IM 连接失败。

查看文件

@ -11,7 +11,7 @@ curl -fsSL https://xuqinmin.com/xuqmGroup/XuqmGroup-PrivateDeploy/raw/branch/mai
1. 选择租户初始化方式(新建 / 迁移)
2. 填写租户信息或迁移密钥
3. 填写外部访问地址(宿主机 nginx 对外的地址,用于 SDK 配置)
3. 填写外部访问地址(上层 nginx 对外的域名/IP,用于 SDK 配置)
4. 自动完成容器启动、数据库初始化、全量验证
完成后按输出的端口表配置宿主机 nginx。
@ -56,9 +56,9 @@ location / {
}
```
> **两种场景通用**每一层 nginx 代理都必须加 `proxy_http_version 1.1``Upgrade`/`Connection` 头,缺少任意一层 IM WebSocket 就会断开。
> **每一层 nginx 代理都必须加** `proxy_http_version 1.1``Upgrade`/`Connection` 头,缺少任意一层 IM WebSocket 就会断开。
内置 nginx 路由配置在 `config/nginx/conf.d/xuqm.conf`,使用 Docker 服务名路由到各容器,无需关心具体端口
内置 nginx 路由配置在 `config/nginx/conf.d/xuqm.conf`,使用 Docker 服务名动态路由,容器重建后无需重载 nginx
---
@ -68,31 +68,44 @@ location / {
|-----------|------|
| **80** | 内置 nginx 入口(无宿主机 nginx 场景,`NGINX_BIND=80` |
| **11223** | 内置 nginx 入口(有宿主机 nginx 场景,`NGINX_BIND=127.0.0.1:11223` |
| 11224–11231 | 各业务容器(绑定 127.0.0.1,调试用) |
| 11224–11231 | 各业务容器(绑定 127.0.0.1,调试用) |
各业务容器端口仅用于直接调试,正常流量全部走内置 nginx 入口。
---
## 租户迁移
## 日常运维
迁移通过一键部署向导完成,选择「迁移租户」后:
### 升级
1. 前往公有化平台控制台 → 安全中心 → 私有化部署迁移 → 生成迁移密钥
2. 将 `pmk_` 开头的密钥粘贴进向导
3. 脚本自动导出租户数据、导入私有库、写入 license 记录、重启服务
```bash
curl -fsSL https://xuqinmin.com/xuqmGroup/XuqmGroup-PrivateDeploy/raw/branch/main/upgrade.sh \
-o upgrade.sh && bash upgrade.sh
```
迁移为单租户操作,会清空现有 bootstrap 数据后写入迁移数据
保留全部数据和配置,自动修复已知配置问题,可选拉取新镜像,完成后运行全量验证
---
### 容器重置(保留数据)
## 常用运维命令
容器状态异常时502、crash-loop、密码错误执行
```bash
bash scripts/reset.sh
```
彻底重建所有容器并拉取最新镜像,不删除数据卷,不修改配置文件。如只想重建容器而不拉取镜像:
```bash
bash scripts/reset.sh --no-pull
```
### 常用命令
```bash
# 查看所有容器状态
docker compose -f docker-compose.yml -f docker-compose.infra.yml ps
# 查看服务日志
# 查看服务日志(替换 tenant-service 为目标服务名)
docker compose -f docker-compose.yml -f docker-compose.infra.yml logs --tail 100 tenant-service
# 重新运行全量验证
@ -116,6 +129,18 @@ bash scripts/restore.sh <backup-file>
---
## 租户迁移
迁移通过一键部署向导完成,选择「迁移租户」后:
1. 前往公有化平台控制台 → 安全中心 → 私有化部署迁移 → 生成迁移密钥
2. 将 `pmk_` 开头的密钥粘贴进向导
3. 脚本自动导出租户数据、导入私有库、写入 license 记录、重启服务
迁移为单租户操作,会清空现有 bootstrap 数据后写入迁移数据。
---
## 数据目录
| 路径 | 说明 |
@ -129,12 +154,75 @@ bash scripts/restore.sh <backup-file>
---
## 重新部署(幂等)
## 故障排查
脚本幂等,可重复执行:
### nginx 返回 502
**现象**:所有接口返回 502,容器状态正常。
**原因**nginx 缓存了旧容器 IP,容器重建后 IP 变更未同步。
**处理**
```bash
bash install.sh
bash scripts/reset.sh --no-pull
```
已运行的容器不会被重建,数据目录不受影响。
内置 nginx 配置已使用 Docker DNS 动态解析(`resolver 127.0.0.11 valid=10s`),重置后新容器 IP 会自动生效,无需手动 reload。
---
### 服务 crash-loop / 启动失败
**现象**`docker compose ps` 显示服务 `Restarting`,日志反复出现启动异常。
**处理**:先查日志确认原因:
```bash
docker compose -f docker-compose.yml -f docker-compose.infra.yml logs --tail 50 <服务名>
```
常见原因及处理:
| 日志关键字 | 原因 | 处理 |
|-----------|------|------|
| `Access denied ... (using password: NO)` | secrets.env 缺少 SPRING_DATASOURCE_PASSWORD | 执行 `bash scripts/reset.sh`,脚本会自动补齐 |
| `Communications link failure` | MySQL 尚未就绪 | 等待约 30s 后自动恢复,或执行 reset |
| `NOAUTH Authentication required` | secrets.env 缺少 SPRING_DATA_REDIS_PASSWORD | 执行 `bash scripts/reset.sh` |
| `host not found in upstream` | nginx 启动时 DNS 解析失败 | 确认其他容器正常启动后执行 reset |
---
### IM WebSocket 连接失败426 / 502
**现象**:浏览器控制台 WebSocket 握手返回 426 或 502。
**排查顺序**
1. 确认每一层 nginx 都有 `proxy_http_version 1.1` + `Upgrade`/`Connection` 头
2. 确认 im-service 容器正常运行:`docker compose ps im-service`
3. 确认镜像版本包含私有化 CORS 修复(`DEPLOYMENT_MODE=PRIVATE` 放开所有 Origin
```bash
# 检查 im-service 是否健康
curl -s http://127.0.0.1:11228/actuator/health
```
---
### 升级后镜像未生效
**现象**:更新了镜像但服务行为未变化。
**原因**`docker compose restart` 不会应用新镜像,需 `up -d` 触发重建。
**处理**
```bash
bash scripts/reset.sh # 拉取最新镜像并重建所有容器
```
---
### SDK 地址仍指向公有化平台
**现象**`config/sdk/xuqm-private-sdk.json` 或 `config/xuqm.env` 中仍有 `xuqinmin.com` 地址。
**处理**
```bash
bash scripts/update.sh # 自动检测并修正 SDK URL
```