Files
Hulumath-Web/docs/DEPLOYMENT.md
T
Jacky 7c9e0303b9
CI / test (pull_request) Successful in 3m27s
ci: add rollback-aware production smoke checks
2026-08-09 00:10:35 +08:00

152 lines
5.0 KiB
Markdown

# Gitea 生产部署
Ubuntu 宝塔面板全新服务器请优先阅读 [Ubuntu + 宝塔面板从零部署](BAOTA_UBUNTU_FROM_ZERO.md)。本文只描述自动发布机制。
PR 质量门禁由 `.gitea/workflows/ci.yml` 执行:
1. Ruff 静态检查。
2. 迁移文件、Django 和 ASGI 导入检查。
3. SQLite 单元测试,覆盖率不得低于 75%。
4. MySQL 8.0.35 全量迁移和测试。
同一 PR 推送新提交时,旧 CI 会自动取消。单次 CI 最多运行 20 分钟。
生产发布由 `.gitea/workflows/deploy.yml` 执行。目标为 `main` 的 PR 被真正合并后:
1. Runner 检出 `main` 并在临时 MySQL 8.0.35 上执行发布迁移检查。
2. 检查通过后 SSH 到生产服务器。
3. 服务器备份 MySQL,安装依赖,执行迁移并收集静态资源。
4. systemd 重启 Django ASGI 服务。
5. 验证应用 HTTP、Redis Channel Layer 和直连 WebSocket。
6. 通过 Nginx 验证健康接口、首页、后台、视频目录和 WebSocket。
7. Runner 从外部容器网络再次验证 Nginx 健康接口。
8. 失败时回退应用代码;数据库备份保留,不自动执行破坏性反向迁移。
关闭但未合并的 PR 不会部署。同一时间只允许一个生产部署任务执行。
## main 分支保护
仓库必须保护 `main` 分支:
- 禁止直接推送,所有改动必须通过 PR。
- 合并前必须通过状态检查 `CI / test`
- 禁止在检查未完成时合并。
- 管理员也应遵守分支保护。
完整测试只在 PR 阶段执行。合并后发布流程不重复运行全量测试,因此分支保护是生产发布的必要条件。
## Gitea Secrets
在仓库 `Settings > Actions > Secrets` 配置:
| Secret | 内容 |
| --- | --- |
| `DEPLOY_HOST` | 生产服务器 IP 或域名 |
| `DEPLOY_USER` | SSH 用户,要求 root 或具备免密 sudo |
| `DEPLOY_SSH_KEY` | 对应用户的 SSH 私钥全文 |
## 首次切换
自动部署前,服务器必须满足:
- Gitea 仓库为 `http://117.72.28.96:8765/Jacky/Hulumath-Web.git`
- 项目目录为 `/www/wwwroot/Hulumath-Web`
- Python 位于 `/www/server/pyporject_evn/versions/3.12.13/bin/python3` 或同目录 `python`
- 已安装 MySQL 8 客户端命令 `mysqldump`
- MySQL 8.0.35 数据库和用户已经创建。
- Redis 正在运行。
- 项目根目录存在 `.env.production`
- Nginx 已反向代理 `127.0.0.1:8000`
创建生产环境文件:
```bash
cd /www/wwwroot/Hulumath-Web
cp .env.production.example .env.production
chmod 600 .env.production
```
替换其中的域名、密钥、MySQL 密码和 Redis 地址。该文件只保留在服务器,不提交 Git。
首次从 Flask 切换时,需要先在宝塔或原进程管理器中停止旧 Flask Gunicorn,释放 `127.0.0.1:8000`。部署脚本不会强杀未知进程。
代码合并到 `main` 后,可以等待 workflow 自动执行,也可以首次手动执行:
```bash
cd /www/wwwroot/Hulumath-Web
git remote set-url origin http://117.72.28.96:8765/Jacky/Hulumath-Web.git
git fetch origin main
PREVIOUS_REVISION="$(git rev-parse HEAD)"
git reset --hard origin/main
PROJECT_DIR="$PWD" \
PREVIOUS_REVISION="$PREVIOUS_REVISION" \
bash scripts/deploy_production.sh
```
首次空数据库还需导入官方内容:
```bash
cd /www/wwwroot/Hulumath-Web
set -a
source .env.production
set +a
.venv-production/bin/python backend/manage.py seed_initial_content
.venv-production/bin/python backend/manage.py seed_contests
```
种子命令不放在每次自动部署中,避免覆盖运营后台后续修改的内容。
## Nginx
现有 Flask 反向代理可改为:
```nginx
location / {
proxy_pass http://127.0.0.1:8000;
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;
}
location /ws/ {
proxy_pass http://127.0.0.1:8000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
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;
proxy_read_timeout 120s;
}
```
应用静态资源由 WhiteNoise 提供,无需额外暴露项目目录。
## 运维命令
```bash
systemctl status hulumath-web
journalctl -u hulumath-web -n 200 --no-pager
journalctl -u hulumath-web -f
systemctl restart hulumath-web
```
健康检查:
```bash
curl -H 'Host: 你的域名' -H 'X-Forwarded-Proto: https' \
http://127.0.0.1:8000/health/
```
MySQL 压缩 SQL 备份默认保存在 `/www/backup/hulumath/`,保留 14 天。
## 数据迁移边界
旧 Flask 的 `data/miniapp_v3.db` 不会再进入自动部署,也不会覆盖 MySQL。若旧生产 SQLite 中存在必须保留的账号或运行数据,需要在首次公开切换前单独执行一次数据迁移和核对。
数据库迁移应采用“先扩展、后清理”策略。不要在同一次发布中删除旧字段并立即依赖新字段,以保证应用代码可回退。