Files
FastapiAdmin/DEPLOYMENT.md
T
zhangtao ce9401196a chore(deploy): 成品部署链路、依赖分组与运维基建
部署方式:服务器不再接收源码、不执行构建,改为本地构建成品后交付
- deploy-artifacts.sh:构建→备份→校验→原子替换,支持回滚、镜像/备份轮转、
  mysql 与 postgres 双变体镜像;后端以 docker save|ssh|docker load 交付
- 依赖分组:requirements/{base,storage}.txt + db/{mysql,postgres,sqlite}.txt,
  镜像按所选数据库只装对应驱动(MySQL 变体 565MB → 521MB)

编排与运行时加固
- compose:MySQL/Redis/backend 端口仅绑 127.0.0.1;密钥(SECRET_KEY、
  DATA_ENCRYPTION_KEY、DATA_ENCRYPTION_OLD_KEYS)、PROD_CORS_ORIGINS、
  FORWARDED_ALLOW_IPS、DEMO_ENABLE 一律由 docker/.env 注入
- Dockerfile:对齐依赖分组、以非 root(uid=1000) 运行;.dockerignore 排除全部真实 .env.*
- nginx:静态资源长缓存、隐藏文件拒绝、接口文档仅内网可访问、放行 ACME http-01 校验路径

工程基建
- 新增 CI:backend(uv sync + ruff + pytest)与 frontend(web type-check+vitest、app type-check)
- 新增 DEPLOYMENT.md 运维手册(部署/回滚/密钥轮换/每日巡检/证书/排查)
2026-10-04 04:26:03 +08:00

120 lines
5.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# FastapiAdmin 部署运维手册
> 适用环境:阿里云单机 Docker 部署(`service.fastapiadmin.com`)
> 最近更新:2026-10-04(本轮密钥轮换与加固之后)
---
## 1. 架构与目录
| 组件 | 说明 | 对外端口 |
| --- | --- | --- |
| nginx | TLS 终止、静态站点、反向代理 `/api/v1` | 80 / 443 |
| backend | FastAPI(uvicorn,1 worker,非 root 运行 uid=1000) | 仅 127.0.0.1:8001 |
| mysql | MySQL 8.0,数据卷 `mysql_data` | 仅 127.0.0.1:3306 |
| redis | Redis 7(会话/缓存/限流) | 仅 127.0.0.1:6379 |
服务器布局(`/home/FastapiAdmin`):
```
docker/ # 唯一部署目录,不含业务源码
├── docker-compose.yaml # 服务编排(端口仅 127.0.0.1,密钥由 .env 注入)
├── docker-compose.override.yaml # nginx 版本固定 + 缓存配置挂载 + ACME 目录
├── .env # 真实凭据(600,不入库)
├── .env.example # 占位模板(入库)
├── backend/Dockerfile # 源码构建路径(正式部署不用它)
├── nginx/
│ ├── nginx.conf # 站点配置(含文档封堵、隐藏文件拒绝、ACME 放行)
│ ├── cache-headers.inc # 静态资源长缓存片段
│ ├── acme/ # ACME http-01 校验目录
│ ├── ssl/ # 证书与私钥(600,不入库)
│ └── {web,app,docs}/dist # 三端静态成品(由本地构建后上传)
└── mysql/ redis/ # 初始化 SQL 与 redis.conf
backups/ # 部署前自动备份(每类保留 5 份)
```
仓库侧:`deploy-artifacts.sh`(本机执行的成品部署脚本)、`backend/requirements/`(依赖分组)、`.github/workflows/`(CI)。
---
## 2. 日常部署
在本机仓库根目录执行(需要本机可免密 SSH 到服务器、且本机 Docker 可用——`colima start`):
```bash
./deploy-artifacts.sh web # 构建并部署 Web 管理后台
./deploy-artifacts.sh app # 构建并部署移动端 H5
./deploy-artifacts.sh backend # 本地构建 MySQL 版镜像 → 传输 → 只重建 backend
./deploy-artifacts.sh all # 三样全发
./deploy-artifacts.sh verify # 公网四端验证
./deploy-artifacts.sh backups # 列出服务器备份
./deploy-artifacts.sh prune # 轮转清理旧备份与旧镜像
./deploy-artifacts.sh rollback web|app|backend
```
要点:
- **服务器不接收源码、不执行构建**:后端以 `docker save → ssh → docker load` 交付镜像,前端以 tar 管道交付成品。
- 每次部署前自动备份到 `backups/<标签>-<时间戳>`,备份失败即中止,不会带着假备份上线。
- 只重建变化的容器;MySQL/Redis/Nginx 不受影响。
- PostgreSQL 版本镜像:`./deploy-artifacts.sh backend-pg`(**只构建与自检,不切换线上**)。切换需人工把 `BACKEND_IMAGE_TAG` 指到该镜像并确保 compose 的数据库配置已改。
---
## 3. 密钥与口令
| 项 | 位置 | 说明 |
| --- | --- | --- |
| `SECRET_KEY` | 服务器 `docker/.env` | JWT 签名密钥,64 位 hex,随机生成 |
| `DATA_ENCRYPTION_KEY` | 服务器 `docker/.env` | 落库敏感数据加密主密钥 |
| `DATA_ENCRYPTION_OLD_KEYS` | 服务器 `docker/.env` | 轮换前的旧密钥,仅用于解密历史密文 |
| 种子账号口令 | 服务器 `/root/.fa-seed-passwords`(600) | `super`/`admin`/`user`,登录后请自行修改并删除该文件 |
**轮换密钥的正确步骤**(不可跳步,否则历史密文无法解密):
1. 把当前 `DATA_ENCRYPTION_KEY`(未设置时取当前 `SECRET_KEY`)追加到 `DATA_ENCRYPTION_OLD_KEYS`;
2. 生成新值:`openssl rand -hex 32`,写入 `SECRET_KEY` / `DATA_ENCRYPTION_KEY`;
3. `docker compose up -d --force-recreate backend`;
4. 验证历史密文仍可解密(例如存储源口令),再删除 `OLD_KEYS` 中不再需要的旧值。
> 注意:更换 `SECRET_KEY` 会使已签发的令牌全部失效,用户需重新登录。
---
## 4. 运行状态与巡检
- **每日巡检**:`systemctl list-timers fa-health-check.timer`(每天 08:30,带 5 分钟随机延迟)
- 巡检内容:4 个容器健康、`/api/v1/monitor/health/check` 与 `/web/`、`/app/` 可用性、证书剩余天数、磁盘、swap、备份与镜像堆积
- 结果:`/var/log/fa-health.log`;有告警时写入 syslog(`journalctl -t fa-health`)且服务退出码非 0
- 手动执行:`systemctl start fa-health-check.service && tail -n 30 /var/log/fa-health.log`
常用排查命令:
```bash
cd /home/FastapiAdmin/docker
docker compose ps
docker logs --tail 100 backend
docker inspect backend --format '{{.State.Health.Status}}'
ss -tlnp | grep docker-proxy # 确认对外只有 80/443
```
---
## 5. 证书
- 当前证书:`docker/nginx/ssl/server.pem`(`CN=service.fastapiadmin.com`),由巡检脚本监控到期天数
- 续期(重新签发后):替换 `server.pem` 与 `server.key` → `docker exec nginx nginx -s reload`
- 已放行 ACME http-01 校验路径(`/.well-known/acme-challenge/` → `docker/nginx/acme`),如需切换到 acme.sh 自动续期,把校验文件写入该目录即可,无需再改 nginx 配置
---
## 6. 已知遗留问题
安全与质量债的完整清单见 `AUDIT-REPORT.md` 与各分报告(`backend/audit-*.md`、`frontend/audit-*.md`、`docker/audit-deploy.md`)。当前仍待处理的高优先级项:
1. 会话失效机制:停用/删除/改密后旧会话仍有效(最长 7 天)——需应用侧改造;
2. 数据权限注入不完整:`CRUDBase` 的删除/清空/改状态路径未注入数据权限;
3. Web i18n 与通知状态枚举:`85/101` 个页面未接入 i18n;通知状态枚举与后端语义冲突;
4. 测试覆盖:后端仅 2 个用例,夹具掩盖缺陷(见 `backend/audit-testing.md`);
5. 多租户需求与实现错位(见 `backend/audit-requirements.md`)。