同步架构文档与下一步优化计划

This commit is contained in:
geeker
2026-09-23 08:43:29 +08:00
parent 8bb8c2d7b8
commit 4c813d6575
7 changed files with 1887 additions and 1 deletions
+472
View File
@@ -0,0 +1,472 @@
# 学生管理系统 — 运维部署文档
## 1. 环境说明
| 环境 | 用途 | 访问地址 | 数据库 |
|------|------|----------|--------|
| 本地开发 | 功能开发、调试 | http://localhost:8000 | SQLite(文件) |
| 测试环境 | 集成测试、QA 验证 | http://sms-test.internal:8000 | MySQL 5.7+ |
| 生产环境 | 线上服务 | https://sms.example.com | MySQL 8.0 主从集群 |
---
## 2. 本地开发部署
### 2.1 前置依赖
```bash
# Python 3.13+
# uv 包管理器
pip install uv
```
### 2.2 一键启动
```bash
# 进入项目目录
cd student_manage_system
# 同步依赖(首次运行)
uv sync
# 启动开发服务器(热重载)
uv run uvicorn main:app --reload --host 0.0.0.0 --port 8000
```
### 2.3 验证
```bash
# 访问 API 文档
curl http://localhost:8000/docs
# 健康检查
curl http://localhost:8000/api/teachers/
```
### 2.4 初始化数据
应用启动时 `lifespan` 自动调用 `seed.init_db()` 建表,`seed.seed_data()` 写入测试数据。
如需重新初始化:
```bash
# 删除本地数据库文件后重启即可
rm -f sms.db
uv run uvicorn main:app --reload
```
---
## 3. Docker 部署
### 3.1 Dockerfile
```dockerfile
# 多阶段构建,减小镜像体积
FROM python:3.13-slim AS base
WORKDIR /app
# 安装 uv
RUN pip install --no-cache-dir uv
COPY pyproject.toml uv.lock ./
RUN uv sync --frozen --no-dev
COPY . .
# 使用 gunicorn + uvicorn worker 生产部署
RUN uv pip install "gunicorn[asyncio]>=23.0.0"
EXPOSE 8000
# 生产启动命令:4 个 worker 进程
CMD ["gunicorn", "main:app", \
"--workers", "4", \
"--worker-class", "uvicorn.workers.UvicornWorker", \
"--bind", "0.0.0.0:8000", \
"--timeout", "120"]
```
### 3.2 docker-compose.yml
```yaml
version: "3.9"
services:
app:
build: .
ports:
- "8000:8000"
environment:
- DATABASE_URL=mysql+pymysql://sms_user:SMS_pass@db:3306/sms
depends_on:
db:
condition: service_healthy
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/api/teachers/"]
interval: 30s
timeout: 10s
retries: 3
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: root_pass
MYSQL_DATABASE: sms
MYSQL_USER: sms_user
MYSQL_PASSWORD: SMS_pass
volumes:
- sms_mysql_data:/var/lib/mysql
- ./init.sql:/docker-entrypoint-initdb.d/init.sql # 可选:初始化 SQL
ports:
- "3306:3306"
restart: unless-stopped
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "sms_user", "-pSMS_pass"]
interval: 10s
timeout: 5s
retries: 5
volumes:
sms_mysql_data:
```
### 3.3 启动命令
```bash
# 构建并启动
docker-compose up -d --build
# 查看日志
docker-compose logs -f app
# 停止
docker-compose down
# 清理数据卷(慎重)
docker-compose down -v
```
---
## 4. Nginx 反向代理配置
```nginx
server {
listen 80;
server_name sms.example.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name sms.example.com;
# SSL 证书(Let's Encrypt 示例)
ssl_certificate /etc/nginx/ssl/sms.example.com/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/sms.example.com/privkey.pem;
# 安全头
add_header X-Frame-Options DENY always;
add_header X-Content-Type-Options nosniff always;
add_header Strict-Transport-Security "max-age=31536000" always;
# 静态文件
location /static/ {
proxy_pass http://127.0.0.1:8000;
expires 7d;
add_header Cache-Control "public, immutable";
}
# API 请求
location /api/ {
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_connect_timeout 30s;
proxy_read_timeout 60s;
}
# API 文档(仅内网访问)
location /docs {
allow 10.0.0.0/8;
allow 172.16.0.0/12;
allow 192.168.0.0/16;
deny all;
proxy_pass http://127.0.0.1:8000;
}
location / {
proxy_pass http://127.0.0.1:8000;
}
}
```
---
## 5. 生产环境部署
### 5.1 部署架构
```
┌─────────────────┐
│ Cloudflare / │
│ CDN + WAF │
└────────┬────────┘
│
┌────────▼────────┐
│ Nginx │ ← 反向代理 + HTTPS
│ (port 443) │
└────────┬────────┘
│
┌──────────────┼──────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ FastAPI │ │ FastAPI │ │ FastAPI │ ← 多实例(水平扩展)
│ :8000 │ │ :8001 │ │ :8002 │
│ (Worker1)│ │ (Worker2)│ │ (Worker3)│
└────┬─────┘ └────┬─────┘ └────┬─────┘
│ │ │
└──────────────┼──────────────┘
▼
┌────────────────┐
│ MySQL 主从 │ ← 一主两从,读写分离
│ 主库:3306 │
│ 从库:3307/3308│
└────────────────┘
│
┌────────────────┐
│ Redis │ ← 缓存层(V2.0 规划)
│ :6379 │
└────────────────┘
```
### 5.2 systemd 服务配置(Linux 直接部署)
```ini
# /etc/systemd/system/sms.service
[Unit]
Description=Student Manage System API
After=network.target mysql.service
[Service]
Type=notify
User=sms
Group=sms
WorkingDirectory=/opt/sms
ExecStart=/opt/sms/.venv/bin/gunicorn main:app \
--workers 4 \
--worker-class uvicorn.workers.UvicornWorker \
--bind 127.0.0.1:8000 \
--timeout 120 \
--access-logfile - \
--error-logfile -
Restart=always
RestartSec=10
# 环境变量
Environment="DATABASE_URL=mysql+pymysql://sms_user:SMS_pass@localhost:3306/sms"
Environment="LOG_LEVEL=info"
# 安全加固
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/opt/sms/logs
[Install]
WantedBy=multi-user.target
```
```bash
# 启用服务
sudo systemctl daemon-reload
sudo systemctl enable sms
sudo systemctl start sms
sudo systemctl status sms
# 查看日志
sudo journalctl -u sms -f
```
---
## 6. 监控与告警
### 6.1 健康检查端点(待 V2.0 实现)
```python
@app.get("/health")
async def health_check(db: Session = Depends(get_db)):
"""综合健康检查"""
try:
db.execute(text("SELECT 1"))
db_status = "ok"
except Exception as e:
db_status = f"error: {e}"
return {
"status": "healthy" if db_status == "ok" else "degraded",
"database": db_status,
"timestamp": datetime.utcnow().isoformat(),
}
```
### 6.2 关键监控指标
| 指标 | 工具 | 阈值建议 | 告警方式 |
|------|------|----------|----------|
| API 响应时间 P99 | Prometheus + Grafana | > 500ms | 邮件/钉钉 |
| 错误率(5xx) | Prometheus | > 1% | 即时告警 |
| 数据库连接数 | MySQL SHOW STATUS | > 80% max_connections | 邮件 |
| 磁盘使用率 | node_exporter | > 85% | 即时告警 |
| 服务可用性 | Uptime Robot / Prometheus | < 99.9% | 即时告警 |
### 6.3 Prometheus 配置示例
```yaml
# prometheus.yml
scrape_configs:
- job_name: 'sms-api'
metrics_path: '/metrics' # FastAPI 内置指标(需安装 prometheus-fastapi-instrumentor)
static_configs:
- targets: ['sms-app:8000']
- job_name: 'mysql'
static_configs:
- targets: ['exporter-mysql:9104']
```
---
## 7. 日志管理
### 7.1 日志级别
| 环境 | 级别 | 说明 |
|------|------|------|
| 开发 | DEBUG | 详细调试信息 |
| 测试 | INFO | 常规业务日志 |
| 生产 | WARNING | 仅警告及以上 |
### 7.2 日志收集方案
```
应用日志 → /var/log/sms/
├── access.log # HTTP 请求日志(Nginx upstream_log)
├── error.log # 错误日志
└── app.log # 业务日志(loguru)
日志收集:
方案A(轻量):logrotate 轮转 + 手动归档
方案B(推荐):Filebeat → Logstash → Elasticsearch(ELK)
方案C(云原生):Fluent Bit → Loki
```
### 7.3 logrotate 配置
```conf
# /etc/logrotate.d/sms
/opt/sms/logs/*.log {
weekly
rotate 12
compress
missingok
notifempty
copytruncate
}
```
---
## 8. 备份与恢复
### 8.1 数据库备份策略
```bash
#!/bin/bash
# /opt/sms/scripts/backup.sh
BACKUP_DIR="/opt/sms/backups"
DATE=$(date +%Y%m%d_%H%M%S)
DB_NAME="sms"
DB_USER="sms_user"
DB_PASS="SMS_pass"
mkdir -p "$BACKUP_DIR"
# 全量备份
mysqldump -u"$DB_USER" -p"$DB_PASS" \
--single-transaction \
--routines \
--triggers \
"$DB_NAME" > "$BACKUP_DIR/sms_$DATE.sql"
# 压缩
gzip "$BACKUP_DIR/sms_$DATE.sql"
# 保留最近 30 天
find "$BACKUP_DIR" -name "sms_*.sql.gz" -mtime +30 -delete
echo "Backup completed: sms_$DATE.sql.gz"
```
### 8.2 定时备份(crontab)
```cron
# 每天凌晨 2 点备份
0 2 * * * /opt/sms/scripts/backup.sh >> /var/log/sms/backup.log 2>&1
```
### 8.3 恢复流程
```bash
# 查看备份文件
ls -lh /opt/sms/backups/
# 恢复(先停止服务写入)
gunzip < /opt/sms/backups/sms_20250101_020000.sql.gz | mysql -u sms_user -p sms
```
---
## 9. 故障排查手册
### 9.1 常见问题
| 现象 | 可能原因 | 排查步骤 |
|------|----------|----------|
| 服务无法启动 | 端口被占用 | `lsof -i :8000` 查看并 kill |
| 数据库连接失败 | URL 错误/网络不通 | 检查 DATABASE_URL,`telnet db_host 3306` |
| API 返回 500 | SQL 错误/模型字段缺失 | 查看 `journalctl -u sms -n 100` |
| 响应慢 | 缺少索引/慢查询 | `SHOW PROCESSLIST`,分析 EXPLAIN |
| 磁盘满 | 日志未轮转/备份堆积 | `du -sh /var/log/sms/`,清理旧日志 |
### 9.2 快速重启命令
```bash
sudo systemctl restart sms
sudo systemctl status sms
sudo journalctl -u sms --since "10 minutes ago" -n 50
```
---
## 10. 版本发布流程
```
1. 代码审查(PR merge to main)
2. 自动化测试(CI pipeline)
3. 构建镜像(docker build + push)
4. 部署测试环境(docker-compose up -d)
5. 集成测试通过
6. 生产灰度发布(先上 1 个实例)
7. 观察 30 分钟无异常
8. 全量发布(剩余实例滚动更新)
9. 发布报告归档
```