Files
thebet365/docs/Docker部署指南.md
Mars b929fd01c0 docs: 完善镜像发版流程文档并清理过时文档
补充本地打包到线上更新的分步说明;部署脚本对示例密钥改为警告而非阻断。
2026-06-18 14:35:05 +08:00

510 lines
16 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.
# TheBet365 Docker 全栈部署指南
本文档说明如何使用 Docker **一键部署** API、玩家前台、管理后台、PostgreSQL 与 Redis。
> 本地开发仍可使用根目录 `docker-compose.yml` 仅启动数据库,应用在本机 `pnpm dev` 运行。
---
## 一、架构
```text
浏览器
└─ 宝塔/Nginx HTTPS 反代
├─ 127.0.0.1:8082 player (Nginx) ── /api、/uploads ──► api (NestJS :3000)
└─ 127.0.0.1:8081 admin (Nginx) ── /api、/uploads ──► api (NestJS :3000)
api ──► postgres:5432
└──► redis:6379
```
| 容器 | 默认端口 | 说明 |
|------|----------|------|
| `thebet365-player` | 127.0.0.1:8082 | 玩家 H5 前台 |
| `thebet365-admin` | 127.0.0.1:8081 | 管理后台(平台 + 代理) |
| `thebet365-api` | 不对外暴露 | NestJS API / Swagger / 健康检查 |
| `thebet365-postgres` | 不对外暴露 | PostgreSQL 16 |
| `thebet365-redis` | 不对外暴露 | Redis 7 |
---
## 二、服务器要求
| 项目 | 要求 |
|------|------|
| 系统 | Linux推荐 Ubuntu 22.04+ |
| Docker | 24+ |
| Docker Compose | v2`docker compose` |
| 内存 | 建议 ≥ 2 GB首次构建需更多 |
| 磁盘 | 建议 ≥ 10 GB |
宝塔面板:左侧 **Docker** → 确认 Docker 服务已安装并运行。
---
## 三、首次部署(命令行)
### 1. 上传代码
将项目放到服务器,例如 `/www/wwwroot/thebet365`(宝塔 **文件** 上传或 `git clone`)。
### 2. 配置环境变量
```bash
cd /www/wwwroot/thebet365
cp .env.docker.example .env.docker
```
**生产环境务必修改:**
- `POSTGRES_PASSWORD` — 数据库密码
- `JWT_SECRET` — 足够长的随机字符串
- `IMAGE_TAG=latest` — 首次部署可保留;后续用 `--tag` 发布时脚本会写回真实版本
- `BIND_ADDR=127.0.0.1` — 默认只允许宝塔本机反代访问 player/admin 端口
- `RUN_MIGRATIONS_ON_START=false` — 迁移由部署脚本执行API 容器启动时不重复跑迁移
- `BACKUP_RETENTION_DAYS=` — 留空表示不自动清理备份;填数字时脚本会清理更旧备份
- `SEED_DATABASE=false` — 保持默认即可;首次部署脚本会在没有 `admin` 时一次性执行生产 seed
### 3. 首次部署
```bash
chmod +x scripts/*.sh
./scripts/deploy-first.sh
```
脚本会执行:
- 启动 PostgreSQL / Redis
- 构建 API、玩家端、管理端镜像
- 执行 `prisma migrate deploy`
- 启动全栈容器
- 如果数据库中没有 `admin`,执行一次生产 seed
如果服务器已经提前 `docker load` 了镜像,不想在服务器构建:
```bash
./scripts/deploy-first.sh --no-build
```
如果上传的是版本化镜像包,可让脚本自动加载镜像并记录发布 tag
```bash
./scripts/deploy-first.sh --images thebet365-images-v1.2.3.tar --tag v1.2.3
```
镜像包文件名符合 `thebet365-images-<tag>.tar` 时,脚本也会自动推断 tag显式传 `--tag` 更清晰。
如需全量初始化生产数据(会清空业务表,仅限全新库或明确重置):
```bash
./scripts/deploy-first.sh --init-db
```
### 4. 查看状态
```bash
docker compose -f docker-compose.prod.yml --env-file .env.docker ps
docker compose -f docker-compose.prod.yml --env-file .env.docker logs -f api
```
启动成功后访问:
| 服务 | 地址 |
|------|------|
| 玩家前台 | 宝塔绑定的玩家域名,或服务器本机 `http://127.0.0.1:8082` |
| 管理后台 | 宝塔绑定的管理域名,或服务器本机 `http://127.0.0.1:8081` |
API 只在 Docker 网络内暴露,前端容器通过 `/api` 代理到 API。player/admin 默认只绑定 `127.0.0.1`,外层域名和 HTTPS 由宝塔网站反代处理。临时需要公网 IP 直连时,才在 `.env.docker` 中设置 `BIND_ADDR=0.0.0.0`
---
## 四、宝塔面板操作
### 方式 A容器编排推荐
1. **文件** — 上传/克隆项目到 `/www/wwwroot/thebet365`
2. **终端** — 执行 `cp .env.docker.example .env.docker` 并编辑密钥
3. **Docker****容器编排****添加**
- 编排文件:选择 `docker-compose.prod.yml`
- 环境文件:选择 `.env.docker`(若面板支持)
4. **构建并启动**
若面板不支持指定 env 文件,在 **终端** 中执行第三节第 3 步脚本即可。
### 方式 B绑定域名Nginx 反代)
在宝塔 **网站** 中为玩家站、管理站分别建站,**不**使用 PHP只做反向代理
**玩家站**(根目录可留空或任意):
```nginx
location / {
proxy_pass http://127.0.0.1:8082;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
```
**管理站** — 将 `8082` 改为 `8081`
API 端口 3000 不在生产 compose 中公网映射Swagger 如需临时查看,建议只在内网或通过 VPN 暴露。
---
## 五、默认账号
生产 seed 仅创建平台管理员与基础赛事/内容数据,不创建代理/玩家演示账号;详见 [默认数据说明.md](./默认数据说明.md)。
| 角色 | 用户名 | 密码 | 入口 |
|------|--------|------|------|
| 平台管理员 | admin | Admin@123 | 管理后台 :8081 |
---
## 六、常用命令
```bash
# 停止
docker compose -f docker-compose.prod.yml --env-file .env.docker down
# 停止并删除数据卷(清空数据库,慎用)
docker compose -f docker-compose.prod.yml --env-file .env.docker down -v
# 仅重建 API
docker compose -f docker-compose.prod.yml --env-file .env.docker up -d --build api
# 手动备份数据库
./scripts/backup-db.sh
# 完整生产备份:数据库 + uploads
./scripts/backup-prod.sh --prefix pre-release
# 全量初始化(生产上线:仅 admin + WC2026 赛事,会先备份到 ./backups/
CONFIRM=YES ./scripts/prod-init-db.sh
# Windows PowerShell:
# $env:CONFIRM = "YES"; .\scripts\prod-init-db.ps1
# 查看 API 日志
docker compose -f docker-compose.prod.yml --env-file .env.docker logs -f api
# 回滚应用镜像到指定 tag不自动回滚数据库
./scripts/rollback.sh --to v1.2.2
```
根目录 `package.json` 快捷脚本(需已存在 `.env.docker`
```bash
pnpm docker:up
pnpm docker:down
pnpm docker:logs
pnpm docker:ps
```
---
## 七、数据持久化
| 卷名 | 内容 |
|------|------|
| `postgres_data` | 数据库 |
| `redis_data` | Redis |
| `uploads_data` | Banner、充值截图、支付二维码等用户上传文件 |
备份示例:
```bash
# 仅数据库:生成 backups/thebet365-db-*.sql.gz 和 .sha256
./scripts/backup-db.sh
# 数据库 + uploads生成 .sql.gz、.tar.gz 和对应 .sha256
./scripts/backup-prod.sh --prefix manual
```
`BACKUP_RETENTION_DAYS` 留空时不自动删除历史备份;设置为数字时,部署和备份脚本会清理更早的备份文件。
---
## 八、推荐发版流程:本地打包 → 上传 → 线上更新
**日常更新推荐走这条链路**,不在服务器上编译,省 CPU/内存,发版包可重复部署。
```text
本地 Windows/Mac 宝塔/SCP 上传 服务器终端
───────────────── ─────────────── ─────────────────
checkout 目标分支 → 只传 tar + manifest → deploy-update.sh
build 三端镜像 到 /www/wwwroot/thebet365 自动备份+迁移+重启
导出 .tar + .manifest 不要覆盖 .env.docker
```
镜像构建细节见:[docker/镜像构建与导出.md](./docker/镜像构建与导出.md)。
---
### 步骤 1本地打包镜像
#### 1.1 前置条件
- 已安装 **Docker Desktop**Windows/Mac或 Linux Docker
- 在项目根目录,且已 `git checkout` 到要发布的分支(如 `main``theme-4`
#### 1.2 确认环境文件
本地需有 `.env.docker`(可从 `.env.docker.example` 复制)。构建 **admin** 镜像时会读取其中的 `VITE_PLAYER_URL`(玩家站公网地址,用于邀请链接)。若玩家域名有变,**改 `.env.docker` 后需重新打包 admin**。
#### 1.3 执行构建脚本
**Windows推荐 CMD**
```bat
cd C:\path\to\thebet365
docs\docker\build-and-export-images.bat --tag latest
```
带版本号(便于回滚追溯):
```bat
docs\docker\build-and-export-images.bat --tag v20260618
```
**Linux / macOS / Git Bash**
```bash
cd /path/to/thebet365
chmod +x docs/docker/build-and-export-images.sh
./docs/docker/build-and-export-images.sh --tag latest
```
首次或发版建议不加 `--use-cache`(默认全量构建)。仅重新导出已有镜像时:
```bat
docs\docker\build-and-export-images.bat --export-only --tag latest
```
#### 1.4 构建产物(在项目根目录)
| 文件 | 说明 |
|------|------|
| `thebet365-images-<tag>.tar` | 含 `api` / `player` / `admin` 三个镜像,约 200300 MB |
| `thebet365-images-<tag>.manifest.txt` | 记录 tag、构建时间、`git_commit`、镜像 ID、tar SHA-256 |
示例:`thebet365-images-latest.tar``thebet365-images-latest.manifest.txt`
> 这两个文件已在 `.gitignore` 中,**不要提交到 Git**。
---
### 步骤 2上传到服务器
#### 2.1 上传目标目录
服务器项目目录,例如:
```text
/www/wwwroot/thebet365
```
#### 2.2 本次更新需要上传的文件
| 文件 | 是否必传 |
|------|----------|
| `thebet365-images-<tag>.tar` | **必传** |
| `thebet365-images-<tag>.manifest.txt` | 建议传(便于核对版本) |
可用 **宝塔 → 文件 → 上传**,或 SCP
```bash
scp thebet365-images-latest.tar thebet365-images-latest.manifest.txt \
root@你的服务器IP:/www/wwwroot/thebet365/
```
#### 2.3 不要覆盖的文件
| 文件/目录 | 说明 |
|-----------|------|
| **`.env.docker`** | 生产密钥、端口、域名配置;**保留服务器上原有文件** |
| `postgres_data` 等 Docker 卷 | 数据库与上传文件,不在文件管理里替换 |
服务器目录里应**早已存在**(首次部署时上传过):`docker-compose.prod.yml``scripts/``docker/` 等。仅换镜像时**不必**重传整份代码。
若部署脚本有更新(如 `scripts/deploy-lib.sh`),可单独上传覆盖 `scripts/` 目录。
---
### 步骤 3服务器执行更新脚本
SSH 或 **宝塔 → 终端**,进入项目目录:
```bash
cd /www/wwwroot/thebet365
chmod +x scripts/*.sh
./scripts/deploy-update.sh --images thebet365-images-latest.tar --tag latest
```
`--tag` 必须与打包时一致(上例为 `latest`;若本地用了 `--tag v20260618`,这里也要写 `v20260618`)。
#### 脚本会自动完成(按顺序)
1. 检查 `.env.docker`(缺少密钥会报错;示例密码仅**警告**,不阻断)
2. 启动并等待 PostgreSQL / Redis 就绪
3. **更新前备份**`./backups/thebet365-db-pre-update-<时间>.sql.gz``thebet365-uploads-pre-update-<时间>.tar.gz`
4. `docker load -i` 导入镜像包
5. 用新 API 镜像执行 `prisma migrate deploy`(数据库结构变更)
6. 重启/替换 `api``player``admin` 容器
7. 等待健康检查通过,执行 `prisma migrate status`
8. 将本次发布写入 `.deploy/current-release.env`(含备份路径)
#### `.env.docker` 会被改什么?
- **不会**整文件覆盖,**不会**改 `JWT_SECRET``POSTGRES_PASSWORD`
- 部署结束时可能只更新一行:`IMAGE_TAG=<本次 tag>`
#### 首次用镜像包部署(新机器)
若服务器从未部署过,用首次脚本:
```bash
./scripts/deploy-first.sh --images thebet365-images-latest.tar --tag latest
```
---
### 步骤 4验证是否成功
```bash
# 容器状态api / player / admin 应为 healthy
docker compose -f docker-compose.prod.yml --env-file .env.docker ps
# 迁移是否全部应用
docker compose -f docker-compose.prod.yml --env-file .env.docker exec api \
sh -c 'cd /app/apps/api && npx prisma migrate status'
# 本次发布记录与备份路径
cat .deploy/current-release.env
# 今天是否生成了新备份
ls -lh backups/ | tail -5
# API 日志(无报错即可)
docker compose -f docker-compose.prod.yml --env-file .env.docker logs --tail=50 api
```
浏览器:玩家站、管理站各访问一次;必要时强刷或清 CDN 缓存。
---
### 步骤 5部署后管理端配置镜像不会自动开启
若本次更新含站内邮箱、员工菜单等新功能,需在**管理后台**手动配置:
1. **员工管理** → 给员工勾选可见菜单(`visibleMenus`
2. **内容管理** → 开启 Inbox站内邮箱及充值/Banner/公告通知开关
3. 验证:充值审核后玩家收到站内信、侧栏充值待审角标、在线人数等
---
### 备份说明
| 项目 | 说明 |
|------|------|
| **何时备份** | 执行 `deploy-update.sh` 时,在**加载新镜像、跑迁移之前** |
| **备份内容** | 更新前一刻的 PostgreSQL 全库 + `uploads` 用户上传文件 |
| **存放位置** | `/www/wwwroot/thebet365/backups/` |
| **文件命名** | `thebet365-db-pre-update-YYYYMMDD-HHMMSS.sql.gz``thebet365-uploads-pre-update-....tar.gz` |
| **记录位置** | `.deploy/current-release.env` 中的 `db_backup=``uploads_backup=` |
手动备份(不更新时也可执行):
```bash
./scripts/backup-db.sh
./scripts/backup-prod.sh --prefix manual
```
**恢复数据库**(仅出问题时):先 `stop api`,再将 `.sql.gz` 导入 postgres最后 `start api`。项目无一键恢复脚本,需手工操作;详见下方回滚说明。
---
### 其他更新方式(备选)
| 方式 | 适用场景 | 命令 |
|------|----------|------|
| **A服务器拉代码构建** | 服务器性能足够、不用传 tar | `./scripts/deploy-update.sh --pull` |
| **B上传 zip 后服务器构建** | 无 Git、在服务器编译 | 替换代码后 `./scripts/deploy-update.sh` |
方式 B 上传代码时**保留** `.env.docker`;若用 zip 覆盖,注意清理旧的中文目录 `packages/shared/public/球员`(见第九节故障排查)。
---
### 回滚应用镜像
```bash
cd /www/wwwroot/thebet365
./scripts/rollback.sh --to <旧tag>
```
`rollback.sh` **只切换** `api` / `player` / `admin` 镜像 tag**不自动恢复数据库**。若新版本已执行不可逆迁移,需先从 `backups/` 中选取 `pre-update``.sql.gz` 手工恢复 PostgreSQL再回滚镜像。
除非已确认另有备份,否则不要使用 `--no-backup` 跳过自动备份。
---
## 九、故障排查
### 1. API 一直重启
```bash
docker logs thebet365-api
```
常见原因:数据库未就绪(稍等重试)、`DATABASE_URL` 密码与 `POSTGRES_PASSWORD` 不一致、`/api/health/ready` 检查 DB/Redis 失败。
### 2. 前端 502 / 接口失败
确认 `thebet365-api` 为 healthy且 player/admin 容器能解析主机名 `api`(同一 compose 网络)。
```bash
docker compose -f docker-compose.prod.yml --env-file .env.docker ps
docker compose -f docker-compose.prod.yml --env-file .env.docker logs --tail=120 api
```
### 3. player/admin 端口无法从公网 IP 直连
生产默认只绑定 `127.0.0.1`,需要通过宝塔网站反代访问。临时调试公网 IP 直连时,在 `.env.docker` 中设置:
```bash
BIND_ADDR=0.0.0.0
```
然后重新执行部署脚本。
### 4. 构建慢或内存不足
首次 `docker compose build` 会安装 pnpm 依赖并编译三端,建议服务器 ≥ 2 GB 内存;可在低峰期构建。
### 5. 端口被占用
修改 `.env.docker` 中的 `PLAYER_PORT` / `ADMIN_PORT` 后重新部署。API 不对公网映射端口。
### 6. player/admin 构建报错 `ENOENT ... packages/shared/public/球员`
旧版中文目录 `球员` 在 Linux 上编码异常。确认已使用含 `packages/shared/public/players/` 的新代码包,并:
```bash
find packages/shared/public -mindepth 1 -maxdepth 1 -type d \
! -name flags ! -name players -exec rm -rf {} +
docker compose -f docker-compose.prod.yml --env-file .env.docker build --no-cache player admin
```
---
## 十、与本地开发的区别
| 场景 | 命令 / 文档 |
|------|------|
| 本地开发(仅 DB 用 Docker | `docker compose up -d` + `pnpm dev` |
| 生产首次部署 | `./scripts/deploy-first.sh` |
| 生产后续更新(推荐) | 本地打包 → 上传 tar → `./scripts/deploy-update.sh --images ... --tag ...`(见第八节) |
相关文档:[项目启动指南.md](./项目启动指南.md)