This commit is contained in:
toom1996
2026-10-19 00:33:25 +08:00
parent 3860e731db
commit 5db90af73a
17 changed files with 853 additions and 5244 deletions

175
DEPLOY.md Normal file
View File

@ -0,0 +1,175 @@
# 部署手册(Docker · 蓝绿零停机)
## 0. 一图看懂
```
浏览器 ──▶ proxy(nginx:80,常驻,发布时从不重建)
├─ /api ──▶ backend_a 或 backend_b
├─ / ──▶ web_a 或 web_b
├─ 18090(仅容器网络)SSR 取数 → 当前活跃后端
├─ 127.0.0.1:18091 前端构建期取 SSG
└─ 127.0.0.1:18092 管理后台(SSH 隧道)
```
发布时只重建**空闲槽**,等它健康后改写切换文件并 `nginx -s reload`,在途请求由旧槽跑完。
镜像以 `RELEASE`(`时间戳-后端提交号`)打标签,**不可变**,历史版本不会被覆盖。
部署入口在本仓库(后端仓),脚本会自动把前端仓也拉到位。
---
## 1. 服务器准备(只做一次)
```bash
# Ubuntu 22.04 / 24.04,2 核 4G 起
curl -fsSL https://get.docker.com | sh
apt install -y docker-compose-plugin git
mkdir -p /opt/fashion && cd /opt/fashion
git clone <后端仓库> backend
git clone <前端仓库> test # 必须与 backend 同级
cd backend
cp .env.example .env && vi .env
chmod +x deploy.sh init-db.sh
```
私有仓库需要配好拉取凭据:SSH deploy key,或 HTTPS token + `git config credential.helper store`。
---
## 2. 首次部署(只做一次)
```bash
./init-db.sh /path/to/your.sql # 建库(传入与当前代码匹配的 SQL)
./deploy.sh all # 启动全部服务
curl http://127.0.0.1/api/health # {"status":"ok"}
```
`.env` 必填 6 项:
| 变量 | 说明 |
| --- | --- |
| `DB_PASSWORD` | 数据库口令,别沿用开发库的 |
| `HASHID_SECRET` | 公开 ID 混淆盐,**上线后不要改**(改了所有旧链接失效) |
| `JWT_SECRET` | 登录令牌密钥 |
| `INGEST_SECRET` | 爬虫上报密钥,须与爬虫端一致 |
| `S4_AK` / `S4_SK` | 对象存储密钥,缺了图片全 404 |
---
## 3. 日常发布
```bash
./deploy.sh # 拉 main + 只更新后端(默认)
./deploy.sh all # 拉 main + 前后端都更新
./deploy.sh web # 只更新前端
./deploy.sh all --no-pull # 不拉代码,用服务器现有代码构建
GIT_BRANCH=dev ./deploy.sh all # 临时指定分支(优先于 .env)
```
**拉代码**(`.env` 的 `GIT_BRANCH`,默认 `main`):
- 后端仓 `.` 和前端仓 `$WEB_DIR`(默认 `../test`)都会 `fetch` → 必要时 `checkout` → `pull --ff-only`
- **不能快进就报错退出**,不会硬覆盖(有本地改动时会提示 `reset --hard` 命令)
- 每次打印实际提交:`后端:main @ a1b2c3d fix: xxx`
**发布过程**:
| 步 | 动作 | 用户感受 |
| --- | --- | --- |
| 1 | 生成 `RELEASE`,拉代码 | 无感 |
| 2 | `docker compose build` 构建 `fashion-*:$RELEASE` | 无感(旧槽在跑) |
| 3 | 重建空闲槽 | 无感 |
| 4 | 轮询 health,未通过则**直接退出** | 旧槽继续服务 |
| 5 | 改切换文件 → `nginx -t` → `nginx -s reload` | 在途请求不丢 |
| 6 | 写 `.deploy/releases.log` | — |
---
## 4. 回滚
```bash
./deploy.sh --rollback
# 回滚 backend:b → a(该槽镜像 fashion-backend:20261018-2210-9f3e1a2)
```
不动代码、不重建容器,只改 nginx 指向 + reload,秒级生效。
前提是该槽容器还在(正常交替发布时,非活跃槽就是上一版)。
---
## 5. 版本与镜像管理
```bash
cat .deploy/releases.log # 发布历史
docker inspect -f '{{.Config.Image}}' $(docker compose ps -q backend_a) # 线上版本
./deploy.sh --prune # 清理旧镜像
KEEP_IMAGES=3 ./deploy.sh --prune # 只留 3 个
```
镜像标签形如 `fashion-backend:20261019-153012-a1b2c3d`(时间 + 提交号),不会被自动清理。
`latest` 只是手动执行 compose 命令的别名,`deploy.sh` 从不依赖它。
---
## 6. 必须记住的规则
1. **默认发布 `main`,日常开发在 `dev`** —— 直接 `./deploy.sh all` 会部署 main 上的旧代码。要么先 merge,要么 `GIT_BRANCH=dev ./deploy.sh all`。
2. **改表要向后兼容** —— 切换的几十秒里新旧代码同时在跑。加字段/加表可以;改类型、删字段要拆两次发布。
3. **`HASHID_SECRET` 上线后别动**。
4. **镜像会占磁盘**(前端约 250MB/版),每 5 版跑一次 `./deploy.sh --prune`。
---
## 7. 管理后台
```bash
ssh -L 18092:127.0.0.1:18092 user@server
# 本机浏览器打开 http://localhost:18092/admin
```
---
## 8. 域名 / HTTPS
前端只监听 80,二选一:
- **宿主机 nginx / 宝塔**:`.env` 里 `HTTP_PORT=8080`,外层反代到 `127.0.0.1:8080`,证书在外层做
- **Caddy(自动证书)**:同 compose 加 caddy 容器,`reverse_proxy 127.0.0.1:80`
---
## 9. 备份
```bash
docker compose exec -T db pg_dump -U fashion fashion | gzip > backups/db-$(date +%F).sql.gz
```
每天 4 点自动备份并保留 7 天:
```
0 4 * * * cd /opt/fashion/backend && docker compose exec -T db pg_dump -U fashion fashion | gzip > backups/db-$(date +\%F).sql.gz && find backups -name 'db-*.sql.gz' -mtime +7 -delete
```
---
## 10. 排障
```bash
docker compose ps # 各容器状态与健康
docker compose logs -f proxy # 切换失败先看这里
docker compose logs -f backend_a
docker compose logs --tail 100 web_b
```
| 现象 | 原因 |
| --- | --- |
| 全站接口 404 | `nginx/default.conf` 的 `proxy_pass` 带了 URI/斜杠(会剥掉 `/api` 前缀) |
| 详情页 500 | 前端 `BASE_API_SSR` 必须是 `http://proxy:18090`(容器里的 localhost 是它自己) |
| 图片全 404 | `S4_AK/S4_SK` 没填或填错 |
| 前端构建失败 | 构建期要连 18091 取 SSG:确认 `proxy` 在跑、`backend_a` healthy |
| 发布后仍是旧版本 | 分支不对(默认 `main`,日常在 `dev`);或 `docker compose logs proxy` 里 reload 报错 |
| 回滚提示"槽不可用" | 该槽被更早的发布重建过,回滚只能退一格 |
| 登录后跳回登录页 | `JWT_SECRET` 改过,旧 token 失效 |
| 磁盘涨得快 | `./deploy.sh --prune` |