Files
Hulumath-Web/docs/DEPLOYMENT.md
T
Jacky 52816ce442
CI / test (pull_request) Successful in 3m24s
PR合并自动部署 / release-check (pull_request) Successful in 12s
PR合并自动部署 / deploy (pull_request) Successful in 12s
ci: remove duplicate release dependency installs
2026-08-09 03:58:04 +08:00

5.2 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,检查必要发布文件、Python 语法和部署脚本语法。
  2. 轻量检查通过后 SSH 到生产服务器;该阶段不重复安装 Python/MySQL 依赖。
  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
  • 禁止在检查未完成时合并。
  • 管理员也应遵守分支保护。

完整测试、MySQL 迁移验证和生产静态资源检查只在 PR 阶段执行。合并后的 release-check 不创建虚拟环境或临时 MySQL,避免在一次变更中重复下载依赖; 因此分支保护和 CI / test 是生产发布的必要条件。

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

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