feat: enhance Spug deployment scripts with better error handling and auto-fix
This commit is contained in:
68
spug/.env.example
Normal file
68
spug/.env.example
Normal file
@@ -0,0 +1,68 @@
|
||||
# Spug 环境变量配置示例
|
||||
# 在 Spug 应用的"环境变量"中配置以下参数
|
||||
|
||||
# ==================== 必需配置 ====================
|
||||
|
||||
# 部署目录(目标服务器上的路径)
|
||||
SPUG_DEPLOY_DIR=/var/www/hospital-performance
|
||||
|
||||
# Git 仓库地址(Spug 会自动设置,也可手动指定)
|
||||
# SPUG_GIT_URL=https://gitea.gentronhealth.com/chenqi/hospital_performance.git
|
||||
|
||||
# Git 分支
|
||||
SPUG_GIT_BRANCH=main
|
||||
|
||||
|
||||
# ==================== 可选配置 ====================
|
||||
|
||||
# 应用名称
|
||||
SPUG_APP_NAME=hospital-performance
|
||||
|
||||
# Python 版本
|
||||
PYTHON_VERSION=python3.10
|
||||
|
||||
# Node.js 版本
|
||||
NODE_VERSION=18
|
||||
|
||||
# 后端服务名称
|
||||
BACKEND_SERVICE=hospital-backend
|
||||
|
||||
# 后端端口
|
||||
BACKEND_PORT=8000
|
||||
|
||||
# 前端服务名称
|
||||
FRONTEND_SERVICE=nginx
|
||||
|
||||
# 日志文件路径
|
||||
LOG_FILE=/var/log/spug/deploy.log
|
||||
|
||||
|
||||
# ==================== 数据库配置 ====================
|
||||
|
||||
# PostgreSQL 连接(生产环境请使用更安全的密钥管理)
|
||||
DATABASE_HOST=192.168.110.252
|
||||
DATABASE_PORT=15432
|
||||
DATABASE_USER=postgresql
|
||||
DATABASE_PASSWORD=Jchl1528
|
||||
DATABASE_NAME=hospital_performance
|
||||
|
||||
|
||||
# ==================== JWT 配置 ====================
|
||||
|
||||
# JWT 密钥(生产环境必须修改!)
|
||||
SECRET_KEY=your-super-secret-key-change-in-production-min-32-chars
|
||||
|
||||
# 调试模式(生产环境设为 False)
|
||||
DEBUG=False
|
||||
|
||||
|
||||
# ==================== 通知配置(可选) ====================
|
||||
|
||||
# 钉钉机器人 Webhook
|
||||
# DINGTALK_WEBHOOK=https://oapi.dingtalk.com/robot/send?access_token=xxx
|
||||
|
||||
# 企业微信机器人 Webhook
|
||||
# WECHAT_WORK_WEBHOOK=https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxx
|
||||
|
||||
# 邮件通知
|
||||
# NOTIFY_EMAIL=admin@example.com
|
||||
369
spug/DEPLOYMENT_GUIDE.md
Normal file
369
spug/DEPLOYMENT_GUIDE.md
Normal file
@@ -0,0 +1,369 @@
|
||||
# Spug 自动部署配置指南
|
||||
|
||||
## 🚀 快速开始(10 分钟完成)
|
||||
|
||||
### 步骤 1: 上传脚本到 Spug 服务器
|
||||
|
||||
```bash
|
||||
# 从本地上传
|
||||
scp spug/deploy.sh user@spug-server:/opt/spug/scripts/
|
||||
scp spug/deploy.py user@spug-server:/opt/spug/scripts/optional/
|
||||
scp spug/hospital-backend.service user@target-server:/tmp/
|
||||
|
||||
# 设置权限
|
||||
ssh user@spug-server "chmod +x /opt/spug/scripts/deploy.sh"
|
||||
```
|
||||
|
||||
### 步骤 2: 在目标服务器配置 systemd 服务
|
||||
|
||||
登录目标服务器(`user@target-server`):
|
||||
|
||||
```bash
|
||||
# 复制服务文件
|
||||
sudo cp /tmp/hospital-backend.service /etc/systemd/system/
|
||||
|
||||
# 重新加载 systemd
|
||||
sudo systemctl daemon-reload
|
||||
|
||||
# 启用服务(先不启动,等部署后再启动)
|
||||
sudo systemctl enable hospital-backend
|
||||
|
||||
# 创建部署目录
|
||||
sudo mkdir -p /var/www/hospital-performance
|
||||
sudo chown -R $USER:$USER /var/www/hospital-performance
|
||||
```
|
||||
|
||||
### 步骤 3: 在 Spug Web 界面配置
|
||||
|
||||
#### 3.1 添加主机
|
||||
|
||||
路径:**系统管理** -> **主机管理** -> **创建主机**
|
||||
|
||||
```
|
||||
名称:production
|
||||
IP: <你的目标服务器 IP>
|
||||
端口:22
|
||||
认证方式:密钥 或 密码
|
||||
```
|
||||
|
||||
#### 3.2 创建应用
|
||||
|
||||
路径:**应用管理** -> **创建应用**
|
||||
|
||||
```
|
||||
应用名称:hospital-performance
|
||||
应用类型:其他
|
||||
Git 仓库:https://gitea.gentronhealth.com/chenqi/hospital_performance.git
|
||||
分支:main
|
||||
```
|
||||
|
||||
#### 3.3 配置环境变量
|
||||
|
||||
在应用详情页的 **环境变量** 标签页,添加以下变量:
|
||||
|
||||
**必需配置:**
|
||||
```
|
||||
SPUG_DEPLOY_DIR=/var/www/hospital-performance
|
||||
SPUG_GIT_BRANCH=main
|
||||
```
|
||||
|
||||
**可选配置:**
|
||||
```
|
||||
PYTHON_VERSION=python3.10
|
||||
BACKEND_SERVICE=hospital-backend
|
||||
BACKEND_PORT=8000
|
||||
FRONTEND_SERVICE=nginx
|
||||
LOG_FILE=/var/log/spug/deploy.log
|
||||
```
|
||||
|
||||
**数据库配置(如果需要在部署时修改):**
|
||||
```
|
||||
DATABASE_HOST=192.168.110.252
|
||||
DATABASE_PORT=15432
|
||||
DATABASE_USER=postgresql
|
||||
DATABASE_PASSWORD=Jchl1528
|
||||
DATABASE_NAME=hospital_performance
|
||||
SECRET_KEY=<生成一个随机密钥>
|
||||
DEBUG=False
|
||||
```
|
||||
|
||||
#### 3.4 配置发布设置
|
||||
|
||||
在应用的 **发布配置** 标签页:
|
||||
|
||||
```
|
||||
构建类型:跳过构建
|
||||
发布脚本:/opt/spug/scripts/deploy.sh
|
||||
```
|
||||
|
||||
### 步骤 4: 执行首次发布
|
||||
|
||||
路径:**发布管理** -> **创建发布单**
|
||||
|
||||
1. 选择应用:`hospital-performance`
|
||||
2. 选择版本:选择最新的 commit
|
||||
3. 选择主机:`production`
|
||||
4. 点击 **立即发布**
|
||||
|
||||
---
|
||||
|
||||
## 📋 验证部署
|
||||
|
||||
### 查看部署日志
|
||||
|
||||
在目标服务器上:
|
||||
|
||||
```bash
|
||||
# 实时查看
|
||||
tail -f /var/log/spug/deploy.log
|
||||
|
||||
# 查看最近 100 行
|
||||
tail -n 100 /var/log/spug/deploy.log
|
||||
|
||||
# 搜索错误
|
||||
grep ERROR /var/log/spug/deploy.log
|
||||
```
|
||||
|
||||
### 检查服务状态
|
||||
|
||||
```bash
|
||||
# 后端服务
|
||||
systemctl status hospital-backend
|
||||
|
||||
# 前端 Nginx
|
||||
systemctl status nginx
|
||||
|
||||
# 查看进程
|
||||
ps aux | grep uvicorn
|
||||
ps aux | grep nginx
|
||||
```
|
||||
|
||||
### 测试 API
|
||||
|
||||
```bash
|
||||
# 健康检查
|
||||
curl http://localhost:8000/api/v1/health
|
||||
|
||||
# 登录测试
|
||||
curl -X POST http://localhost:8000/api/v1/auth/login \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"username":"admin","password":"admin123"}'
|
||||
```
|
||||
|
||||
### 访问前端
|
||||
|
||||
浏览器访问:`http://<服务器 IP>`
|
||||
|
||||
应该能看到登录页面。
|
||||
|
||||
---
|
||||
|
||||
## 🔧 常见问题排查
|
||||
|
||||
### 问题 1: 环境变量为空
|
||||
|
||||
**现象:** 日志显示 `PROJECT_NAME:` 和 `DEPLOY_DIR:` 为空
|
||||
|
||||
**原因:** Spug 没有正确传递环境变量
|
||||
|
||||
**解决:**
|
||||
1. 检查应用的"环境变量"是否正确配置
|
||||
2. 确保 `SPUG_DEPLOY_DIR` 不为空
|
||||
3. 脚本已做容错处理,会自动使用默认值
|
||||
|
||||
### 问题 2: Git 克隆失败
|
||||
|
||||
**现象:** `fatal: destination path '.' already exists and is not an empty directory.`
|
||||
|
||||
**原因:** 部署目录非空且没有 .git 目录
|
||||
|
||||
**解决:**
|
||||
- 脚本已自动处理,会备份现有文件后克隆
|
||||
- 或者手动清理部署目录:`rm -rf /var/www/hospital-performance/*`
|
||||
|
||||
### 问题 3: 权限错误
|
||||
|
||||
**现象:** `Permission denied`
|
||||
|
||||
**解决:**
|
||||
```bash
|
||||
# 修复目录权限
|
||||
sudo chown -R $USER:$USER /var/www/hospital-performance
|
||||
sudo chmod -R 755 /var/www/hospital-performance
|
||||
```
|
||||
|
||||
### 问题 4: 服务启动失败
|
||||
|
||||
**现象:** `systemctl restart hospital-backend` 失败
|
||||
|
||||
**排查:**
|
||||
```bash
|
||||
# 查看详细日志
|
||||
journalctl -u hospital-backend -n 50
|
||||
|
||||
# 检查端口占用
|
||||
sudo lsof -i :8000
|
||||
|
||||
# 手动测试启动
|
||||
cd /var/www/hospital-performance/backend
|
||||
source venv/bin/activate
|
||||
uvicorn app.main:app --host 0.0.0.0 --port 8000
|
||||
```
|
||||
|
||||
### 问题 5: 数据库连接失败
|
||||
|
||||
**现象:** `asyncpg.exceptions.ConnectionRefusedError`
|
||||
|
||||
**解决:**
|
||||
```bash
|
||||
# 测试数据库连接
|
||||
psql -h 192.168.110.252 -p 15432 -U postgres -d hospital_performance
|
||||
|
||||
# 如果连不上,检查防火墙
|
||||
sudo ufw status
|
||||
sudo telnet 192.168.110.252 15432
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎯 自动化流程说明
|
||||
|
||||
### 完整部署流程
|
||||
|
||||
```
|
||||
Spug 触发发布
|
||||
↓
|
||||
1. 前置检查(命令、磁盘空间)
|
||||
↓
|
||||
2. 代码更新(Git clone/pull)
|
||||
↓
|
||||
3. 备份旧版本(保留 30 天)
|
||||
↓
|
||||
4. 后端部署
|
||||
├─ 创建/激活虚拟环境
|
||||
├─ 安装 Python 依赖
|
||||
├─ 数据库迁移
|
||||
├─ 重启 systemd 服务
|
||||
↓
|
||||
5. 前端部署
|
||||
├─ 安装 Node 依赖
|
||||
├─ 构建前端
|
||||
├─ 重载 Nginx
|
||||
↓
|
||||
6. 健康检查
|
||||
├─ API 可用性
|
||||
├─ 前端文件完整性
|
||||
↓
|
||||
7. 清理临时文件
|
||||
↓
|
||||
部署成功!
|
||||
```
|
||||
|
||||
### 回滚流程
|
||||
|
||||
如果部署失败,可以:
|
||||
|
||||
```bash
|
||||
# 1. 停止服务
|
||||
systemctl stop hospital-backend
|
||||
|
||||
# 2. 恢复代码
|
||||
cd /var/www/hospital-performance
|
||||
cp -r backups/backup_YYYYMMDD_HHMMSS_backend backend
|
||||
cp -r backups/backup_YYYYMMDD_HHMMSS_frontend_dist frontend/dist
|
||||
|
||||
# 3. 恢复数据库(如果需要)
|
||||
psql -h 192.168.110.252 -p 15432 -U postgres hospital_performance < backup.sql
|
||||
|
||||
# 4. 重启服务
|
||||
systemctl start hospital-backend
|
||||
systemctl reload nginx
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 监控与告警
|
||||
|
||||
### 关键指标
|
||||
|
||||
- **API 响应时间**: < 200ms
|
||||
- **页面加载时间**: < 3s
|
||||
- **服务可用性**: > 99.9%
|
||||
- **错误率**: < 0.1%
|
||||
|
||||
### 监控命令
|
||||
|
||||
```bash
|
||||
# CPU 使用率
|
||||
top -bn1 | grep "Cpu(s)"
|
||||
|
||||
# 内存使用
|
||||
free -h
|
||||
|
||||
# 磁盘 IO
|
||||
iostat -x 1
|
||||
|
||||
# 网络流量
|
||||
iftop -P -n
|
||||
|
||||
# 数据库连接数
|
||||
psql -h 192.168.110.252 -p 15432 -U postgres -d hospital_performance -c "SELECT count(*) FROM pg_stat_activity;"
|
||||
```
|
||||
|
||||
### 日志聚合
|
||||
|
||||
```bash
|
||||
# 后端日志
|
||||
journalctl -u hospital-backend -f
|
||||
|
||||
# Nginx 访问日志
|
||||
tail -f /var/log/nginx/access.log
|
||||
|
||||
# Nginx 错误日志
|
||||
tail -f /var/log/nginx/error.log
|
||||
|
||||
# Spug 部署日志
|
||||
tail -f /var/log/spug/deploy.log
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔄 持续集成建议
|
||||
|
||||
### Git 工作流
|
||||
|
||||
```
|
||||
main 分支(生产)
|
||||
↑
|
||||
develop 分支(测试)
|
||||
↑
|
||||
feature/* 分支(开发)
|
||||
```
|
||||
|
||||
### 发布频率
|
||||
|
||||
- **小功能/修复**: 每天 1-2 次
|
||||
- **大版本**: 每周或每两周一次
|
||||
- **紧急修复**: 随时发布
|
||||
|
||||
### 最佳实践
|
||||
|
||||
1. ✅ **代码审查**: 所有合并到 main 的代码必须经过 review
|
||||
2. ✅ **测试环境**: 先在 develop 分支测试,再发布到生产
|
||||
3. ✅ **备份策略**: 数据库每天自动备份
|
||||
4. ✅ **灰度发布**: 重要变更先在小范围测试
|
||||
5. ✅ **监控告警**: 配置钉钉/企业微信通知
|
||||
|
||||
---
|
||||
|
||||
## 📞 技术支持
|
||||
|
||||
遇到问题时:
|
||||
|
||||
1. 查看部署日志:`/var/log/spug/deploy.log`
|
||||
2. 查看服务日志:`journalctl -u hospital-backend`
|
||||
3. 联系开发团队或查看项目文档
|
||||
|
||||
---
|
||||
|
||||
**提示**: 将此文档保存为书签,便于快速查阅!
|
||||
@@ -19,15 +19,25 @@ class SpugDeploy:
|
||||
|
||||
def __init__(self):
|
||||
# 配置参数(从环境变量获取,可在 Spug 中设置)
|
||||
# 处理 SPUG_DEPLOY_DIR 为空的情况
|
||||
deploy_dir = os.getenv('SPUG_DEPLOY_DIR', '').strip()
|
||||
if not deploy_dir:
|
||||
deploy_dir = '/var/www/hospital-performance'
|
||||
|
||||
self.project_name = os.getenv('SPUG_APP_NAME', 'hospital-performance')
|
||||
self.project_dir = Path(os.getenv('SPUG_DEPLOY_DIR', '/var/www/hospital-performance'))
|
||||
self.project_dir = Path(deploy_dir)
|
||||
self.backup_dir = self.project_dir / 'backups'
|
||||
self.frontend_dir = self.project_dir / 'frontend'
|
||||
self.backend_dir = self.project_dir / 'backend'
|
||||
|
||||
# Git 配置
|
||||
self.git_repo = os.getenv('SPUG_GIT_REPO',
|
||||
'https://gitea.gentronhealth.com/chenqi/hospital_performance.git')
|
||||
# Git 配置(优先使用 Spug 的环境变量)
|
||||
git_repo = os.getenv('SPUG_GIT_URL', '').strip()
|
||||
if not git_repo:
|
||||
git_repo = os.getenv('SPUG_GIT_REPO', '').strip()
|
||||
if not git_repo:
|
||||
git_repo = 'https://gitea.gentronhealth.com/chenqi/hospital_performance.git'
|
||||
self.git_repo = git_repo
|
||||
|
||||
self.git_branch = os.getenv('SPUG_GIT_BRANCH', 'main')
|
||||
|
||||
# Python 配置
|
||||
@@ -110,6 +120,11 @@ class SpugDeploy:
|
||||
self.error(f"命令 {cmd} 未安装,请先安装")
|
||||
sys.exit(1)
|
||||
|
||||
# 创建部署目录(如果不存在)
|
||||
if not self.project_dir.exists():
|
||||
self.info(f"创建部署目录:{self.project_dir}")
|
||||
self.project_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# 检查磁盘空间
|
||||
try:
|
||||
stat = shutil.disk_usage(self.project_dir)
|
||||
@@ -131,6 +146,21 @@ class SpugDeploy:
|
||||
git_dir = self.project_dir / '.git'
|
||||
if not git_dir.exists():
|
||||
self.info("首次部署,克隆仓库...")
|
||||
|
||||
# 检查目录是否为空
|
||||
if any(self.project_dir.iterdir()):
|
||||
self.info("目录非空,先备份现有文件...")
|
||||
backup_non_git = self.project_dir / f"backup_{datetime.now().strftime('%Y%m%d_%H%M%S')}_non_git"
|
||||
backup_non_git.mkdir(exist_ok=True)
|
||||
|
||||
# 移动非 .git 相关文件
|
||||
for item in self.project_dir.iterdir():
|
||||
if item.name not in ['.git', 'backups', 'venv'] and not item.name.startswith('backup_'):
|
||||
try:
|
||||
shutil.move(str(item), str(backup_non_git / item.name))
|
||||
except Exception as e:
|
||||
self.info(f"跳过文件 {item.name}: {e}")
|
||||
|
||||
self.run_command(['git', 'clone', self.git_repo, '.'])
|
||||
self.run_command(['git', 'checkout', self.git_branch])
|
||||
else:
|
||||
|
||||
@@ -7,14 +7,27 @@ set -e
|
||||
|
||||
# ==================== 配置参数 ====================
|
||||
# 这些参数可以在 Spug 中通过环境变量传入
|
||||
# 如果 SPUG_DEPLOY_DIR 为空,则使用默认值
|
||||
if [ -z "${SPUG_DEPLOY_DIR}" ]; then
|
||||
PROJECT_DIR="/var/www/hospital-performance"
|
||||
else
|
||||
PROJECT_DIR="${SPUG_DEPLOY_DIR}"
|
||||
fi
|
||||
|
||||
PROJECT_NAME="${SPUG_APP_NAME:-hospital-performance}"
|
||||
PROJECT_DIR="${SPUG_DEPLOY_DIR:-/var/www/hospital-performance}"
|
||||
BACKUP_DIR="${PROJECT_DIR}/backups"
|
||||
FRONTEND_DIR="${PROJECT_DIR}/frontend"
|
||||
BACKEND_DIR="${PROJECT_DIR}/backend"
|
||||
|
||||
# Git 配置
|
||||
GIT_REPO="${SPUG_GIT_REPO:-https://gitea.gentronhealth.com/chenqi/hospital_performance.git}"
|
||||
# Git 配置(Spug 会自动设置 SPUG_GIT_URL)
|
||||
if [ -n "${SPUG_GIT_URL}" ]; then
|
||||
GIT_REPO="${SPUG_GIT_URL}"
|
||||
elif [ -n "${SPUG_GIT_REPO}" ]; then
|
||||
GIT_REPO="${SPUG_GIT_REPO}"
|
||||
else
|
||||
GIT_REPO="https://gitea.gentronhealth.com/chenqi/hospital_performance.git"
|
||||
fi
|
||||
|
||||
GIT_BRANCH="${SPUG_GIT_BRANCH:-main}"
|
||||
|
||||
# Python 虚拟环境
|
||||
@@ -30,7 +43,11 @@ BACKEND_PORT="${BACKEND_PORT:-8000}"
|
||||
FRONTEND_SERVICE="${FRONTEND_SERVICE:-nginx}"
|
||||
|
||||
# 日志配置
|
||||
LOG_FILE="${LOG_FILE:-/var/log/spug/deploy.log}"
|
||||
if [ -z "${LOG_FILE}" ]; then
|
||||
LOG_FILE="/var/log/spug/deploy.log"
|
||||
# 确保日志目录存在
|
||||
mkdir -p /var/log/spug 2>/dev/null || true
|
||||
fi
|
||||
DEPLOY_TIME=$(date +"%Y%m%d_%H%M%S")
|
||||
|
||||
# ==================== 工具函数 ====================
|
||||
@@ -71,12 +88,20 @@ pre_check() {
|
||||
check_command node
|
||||
check_command npm
|
||||
|
||||
# 检查磁盘空间
|
||||
local available_space=$(df -P "${PROJECT_DIR}" | awk 'NR==2 {print $4}')
|
||||
if [ "${available_space}" -lt 1048576 ]; then
|
||||
# 检查部署目录
|
||||
if [ ! -d "${PROJECT_DIR}" ]; then
|
||||
info "创建部署目录:${PROJECT_DIR}"
|
||||
mkdir -p "${PROJECT_DIR}"
|
||||
fi
|
||||
|
||||
# 检查磁盘空间(如果目录存在)
|
||||
if [ -d "${PROJECT_DIR}" ]; then
|
||||
local available_space=$(df -P "${PROJECT_DIR}" 2>/dev/null | awk 'NR==2 {print $4}')
|
||||
if [ -n "${available_space}" ] && [ "${available_space}" -lt 1048576 ]; then
|
||||
error "磁盘空间不足 1GB,当前可用:${available_space}KB"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
info "✓ 前置检查通过"
|
||||
}
|
||||
@@ -89,6 +114,12 @@ update_code() {
|
||||
|
||||
if [ ! -d ".git" ]; then
|
||||
info "首次部署,克隆仓库..."
|
||||
# 确保目录为空或不存在
|
||||
if [ "$(ls -A ${PROJECT_DIR} 2>/dev/null)" ]; then
|
||||
info "目录非空,先备份现有文件..."
|
||||
mkdir -p "${PROJECT_DIR}/backup_$(date +%Y%m%d_%H%M%S)_non_git"
|
||||
find "${PROJECT_DIR}" -maxdepth 1 -type f -o -type d ! -name '.' -exec mv {} "${PROJECT_DIR}/backup_$(date +%Y%m%d_%H%M%S)_non_git/" \; 2>/dev/null || true
|
||||
fi
|
||||
git clone "${GIT_REPO}" .
|
||||
git checkout "${GIT_BRANCH}"
|
||||
else
|
||||
|
||||
Reference in New Issue
Block a user