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

5.0 KiB

Gitea 生产部署

Ubuntu 宝塔面板全新服务器请优先阅读 Ubuntu + 宝塔面板从零部署。本文只描述自动发布机制。

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

创建生产环境文件:

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 自动执行,也可以首次手动执行:

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

首次空数据库还需导入官方内容:

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 反向代理可改为:

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 提供,无需额外暴露项目目录。

运维命令

systemctl status hulumath-web
journalctl -u hulumath-web -n 200 --no-pager
journalctl -u hulumath-web -f
systemctl restart hulumath-web

健康检查:

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 中存在必须保留的账号或运行数据,需要在首次公开切换前单独执行一次数据迁移和核对。

数据库迁移应采用“先扩展、后清理”策略。不要在同一次发布中删除旧字段并立即依赖新字段,以保证应用代码可回退。