Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/deployment-source.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

生产服务器使用单一工作区 `/opt/unispeaking`。服务器定时检查主仓库 `main`,发现新提交后同步源码,在服务器本地构建三个应用镜像,并使用 Docker Compose 启动应用服务。GitHub Actions 只负责 CI,不访问生产服务器,也不推送或拉取应用镜像。

日常维护、故障排查和回滚速查见 [`source-deployment-maintenance.md`](source-deployment-maintenance.md)。

本流程不会迁移或重建数据库。PostgreSQL 使用现有外部 Volume `deploy_postgres_data`,MinIO、证书、监控 Agent 等宿主机资源保持原路径。严禁执行 `docker compose down -v`、`docker volume prune` 或删除 `deploy_postgres_data`。

## 文件职责
Expand Down
159 changes: 159 additions & 0 deletions docs/source-deployment-maintenance.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
# 源码部署维护手册

本文档面向维护 UniSpeaking 生产服务器的人员。当前生产部署不使用 GHCR/ACR 应用镜像,服务器固定使用 `/opt/unispeaking`,定时同步主仓库 `main`,在服务器本地构建应用镜像并启动 Compose 服务。

完整首次切换流程见 [`deployment-source.md`](deployment-source.md)。本文档说明日常维护时可修改的内容、必须保留的资源、故障处理和回滚方式。

## 部署链路

```text
main
-> systemd timer
-> /opt/unispeaking
-> git fetch/reset/clean
-> docker compose build backend frontend admin
-> docker compose up -d --no-build
-> readiness、首页、Admin 检查
```

GitHub Actions 只执行 CI,不访问生产服务器。生产服务器不拉取 GHCR/ACR 应用镜像。

## 文件职责

| 文件 | 作用 |
| --- | --- |
| `deploy/docker-compose.prod.yml` | 生产服务、构建上下文、挂载和网络 |
| `deploy/scripts/sync-build-deploy.sh` | 同步、清理、构建、启动和健康检查 |
| `deploy/systemd/unispeaking-source-deploy.service` | 单次部署任务 |
| `deploy/systemd/unispeaking-source-deploy.timer` | 定时调度,当前为开机 5 分钟后检查、之后每 10 分钟检查 |
| `deploy/env/.env.example` | 非敏感配置示例 |
| `deploy/env/.env` | 服务器私有生产配置,不提交 Git |
| `deploy/nginx/nginx.prod.conf` | 生产反向代理配置 |

所有脚本和 systemd 文件只在仓库中修改,再通过 PR 合并。不要只在服务器直接修改脚本,否则下次同步会被覆盖。

## 服务器保留资源

同步脚本执行 `git reset --hard` 和 `git clean` 时会保留:

- `/opt/unispeaking/deploy/env/`,包括生产 `.env` 和环境备份;
- `/opt/unispeaking/backups/`,包括数据库和代码备份;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] Ensure deployment preserves the documented backups

文档把 /opt/unispeaking/backups/ 列为同步时必须保留的资源,但 sync-build-deploy.sh 实际执行的 git clean -fd -e deploy/env/ -e runtime-logs/ -e .source-deploy-state 没有排除 backups/;因此下一次源码部署会删除其中的未跟踪数据库/代码备份。更具体地说,前面的配置变更示例还把 .env 备份写入这个目录,随后该备份会在部署时丢失。请让脚本和文档使用同一保留规则(或改用脚本已经保留的目录),并补上目录创建/验证步骤。

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] Ensure deployment preserves the documented backups

文档把 /opt/unispeaking/backups/ 列为同步时必须保留的资源,但 sync-build-deploy.sh 实际执行的 git clean -fd -e deploy/env/ -e runtime-logs/ -e .source-deploy-state 没有排除 backups/;因此下一次源码部署会删除其中的未跟踪数据库/代码备份。更具体地说,前面的配置变更示例还把 .env 备份写入这个目录,随后该备份会在部署时丢失。请让脚本和文档使用同一保留规则(或改用脚本已经保留的目录),并补上目录创建/验证步骤。

- `/opt/unispeaking/runtime-logs/`;
- `/opt/unispeaking/.source-deploy-state`;
- Docker 外部 Volume `deploy_postgres_data`;
- 外部网络 `monitoring_default`;
- `/etc/unispeaking/certs/`;
- `/opt/monitoring/opentelemetry-javaagent.jar`。

新增服务器持久化目录时,必须先修改脚本保留规则和本文档,并通过 PR 评审。

## 自动部署行为

脚本比较远程 `main` SHA 和 `.env` SHA。两者都未变化时退出,不重建容器。任一项变化时构建 `backend`、`frontend`、`admin`,然后启动 `postgres`、`backend`、`frontend`、`admin`、`nginx`。

Docker 会复用未变化的缓存层。PostgreSQL、Nginx 和 TURN 不在应用构建列表中。脚本不会执行 `docker compose down`,也不会删除 Volume。

构建失败时不会启动新版本;健康检查失败时保留现场,先查看日志再处理。

## 配置变更

数据库密码、JWT、七牛云、阿里云、科大讯飞、邮件等运行时密钥只修改服务器文件:

```text
/opt/unispeaking/deploy/env/.env
```

修改前备份并保持权限:

```bash
cp -a /opt/unispeaking/deploy/env/.env \
/opt/unispeaking/backups/env-before-change-$(date -u +%Y%m%dT%H%M%SZ)
chmod 600 /opt/unispeaking/deploy/env/.env
vi /opt/unispeaking/deploy/env/.env
systemctl start --no-block unispeaking-source-deploy.service
```

`VITE_*` 值会编译进前端静态文件,修改后必须等待前端重新构建。任何密钥不得进入 Git、`VITE_*`、Docker build args 或 Actions 日志。

## 定时器管理

```bash
systemctl status unispeaking-source-deploy.timer --no-pager
systemctl list-timers --all --no-pager | grep unispeaking
```

手动触发一次部署:

```bash
systemctl start --no-block unispeaking-source-deploy.service
```

暂停和恢复自动部署:

```bash
systemctl stop unispeaking-source-deploy.timer
systemctl enable --now unispeaking-source-deploy.timer
```

## GitHub 网络故障

单次 `fetch` 失败不会修改工作区、容器或数据库。可以配置 root 的 Git 连接参数后重试:

```bash
git config --global http.version HTTP/1.1
git config --global http.lowSpeedLimit 1000
git config --global http.lowSpeedTime 120
git fetch --prune origin main
```

确认 fetch 成功后再启动部署服务;fetch 失败时不要手工执行 `reset` 或 `clean`。

## 故障定位

```bash
journalctl -u unispeaking-source-deploy.service -n 200 --no-pager
systemctl status unispeaking-source-deploy.service --no-pager -l
cd /opt/unispeaking
docker compose --env-file deploy/env/.env --project-name deploy --file deploy/docker-compose.prod.yml ps
docker compose --env-file deploy/env/.env --project-name deploy --file deploy/docker-compose.prod.yml logs --tail=200 backend nginx
```

常见错误处理:

- `dubious ownership`:执行 `git config --global --add safe.directory /opt/unispeaking`;
- `fetch` TLS 超时:按上面的 HTTP/1.1 配置重试;
- Volume 不存在:立即停止,禁止让 Compose 创建新 Volume;
- Compose 校验失败:检查 `.env` 和最近的 Compose 改动,不要重启;
- readiness 失败:检查 backend 日志和 Flyway 状态,不要删除数据库容器。

## 数据库与回滚

正式部署前生成并验证 PostgreSQL Custom Format 备份,并记录 SHA256。部署后检查 Flyway 全部为 `success = t`,确认 `deploy_postgres_data` 仍存在。

禁止使用 `docker compose down -v`、`docker volume prune`、`docker volume rm deploy_postgres_data`,也不要手工删除 `/var/lib/docker/volumes/` 内容。

回滚只回滚源码和应用容器,不回滚数据库。先暂停 timer,再选择已验证的提交:

```bash
systemctl stop unispeaking-source-deploy.timer
cd /opt/unispeaking
git fetch --prune origin main
git log --oneline -20
git reset --hard <GOOD_SHA>
git clean -fd -e deploy/env/ -e backups/ -e runtime-logs/ -e .source-deploy-state
systemctl start --no-block unispeaking-source-deploy.service

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] Do not restart the auto-sync service for rollback

这条回滚流程在 git reset --hard <GOOD_SHA> 后启动 unispeaking-source-deploy.service,但该 service 会运行 sync-build-deploy.sh,先 git fetch --prune origin main,再把工作区重置到最新的 origin/main。所以只要 <GOOD_SHA> 不是当前 main,服务会立即覆盖回滚结果并部署坏版本,维护人员无法按此步骤恢复。请改为在固定提交上显式构建/启动 Compose,或为部署脚本增加不会重新同步的回滚路径,并在验收后再恢复 timer。

```

确认业务恢复后:

```bash
systemctl enable --now unispeaking-source-deploy.timer
```

已执行的 Flyway migration 不通过镜像回滚撤销,必须使用兼容代码或新的前向修复 migration。

## 维护原则

1. 生产服务器只保留一个源码工作区:`/opt/unispeaking`。
2. `.env`、备份、证书、监控 Agent 和 Docker Volume 属于服务器状态,不提交 Git。
3. 先备份,再同步;先手动验收,再依赖定时器。
Loading