同步架构文档与下一步优化计划
This commit is contained in:
@@ -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. 发布报告归档
|
||||
```
|
||||
Reference in New Issue
Block a user