- 新增 deploy/docker/:Docker 本机模拟部署,含 Dockerfile、docker-compose、deploy.sh 一键脚本 - 新增 deploy/remote/:远程服务器部署,含 SSH 自动上传、重启、回滚脚本 - 新增 deploy/README.md:完整使用手册,含现状分析、落地调整工作清单、命令速查 - 新增 build.sh/start.sh:本地构建和启动脚本(含飞书通知) - 新增前端 .env.docker 环境配置,API 指向测试服务器 - 前端 package.json 新增 build-docker 命令 - 更新 .gitignore:排除 IDE 配置、SQL 数据、Docker 敏感文件 - 前端 UI 样式优化(多个页面组件) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
727 lines
23 KiB
Markdown
727 lines
23 KiB
Markdown
# SmartClean 自动化打包部署方案(无 Docker 版)
|
||
|
||
## 一、目标
|
||
|
||
基于现有 build.sh 构建能力,实现 **一键构建 → 远程备份 → 上传部署 → 服务重启 → 健康检查 → 失败回滚 → 飞书通知** 全流程自动化。不依赖 Docker,直接操作远程服务器。
|
||
|
||
## 二、前提条件
|
||
|
||
| 条件 | 说明 |
|
||
|------|------|
|
||
| SSH 免密登录 | 本机 → 182 服务器配好 SSH Key,免输密码 |
|
||
| 服务器目录约定 | 统一部署路径,脚本按约定操作 |
|
||
| 现有构建脚本 | 复用已有的 build.sh |
|
||
|
||
### SSH 免密配置(一次性)
|
||
|
||
```bash
|
||
# 生成密钥(如已有可跳过)
|
||
ssh-keygen -t ed25519
|
||
|
||
# 将公钥复制到服务器
|
||
ssh-copy-id root@192.168.1.182
|
||
|
||
# 验证
|
||
ssh root@192.168.1.182 "echo ok"
|
||
```
|
||
|
||
## 三、整体架构
|
||
|
||
```
|
||
本机 (Mac) 远程服务器 (192.168.1.182)
|
||
┌──────────────────────┐ ┌──────────────────────────────┐
|
||
│ │ │ │
|
||
│ 源码 → build.sh 构建 │ │ /opt/smartclean/ │
|
||
│ │ SCP 上传 │ ├── web/ │
|
||
│ 产物: │ ──────────────→ │ │ └── ROOT.war │
|
||
│ - ROOT.war │ │ ├── task/ │
|
||
│ - task.jar │ SSH 远程执行 │ │ └── task.jar │
|
||
│ - dist/ │ ──────────────→ │ ├── front/ │
|
||
│ │ │ │ └── dist/ │
|
||
│ deploy.sh 控制全流程 │ │ ├── backups/ │
|
||
│ │ │ │ ├── 20260415-153000/ │
|
||
│ │ │ │ └── 20260415-140000/ │
|
||
│ │ │ └── scripts/ │
|
||
│ │ │ ├── restart-web.sh │
|
||
│ │ │ ├── restart-task.sh │
|
||
│ │ │ └── restart-front.sh │
|
||
└──────────────────────┘ └──────────────────────────────┘
|
||
```
|
||
|
||
## 四、服务器目录规划
|
||
|
||
```
|
||
/opt/smartclean/ # 部署根目录
|
||
├── web/
|
||
│ ├── tomcat/ # Tomcat 安装目录
|
||
│ │ └── webapps/
|
||
│ │ └── ROOT.war # Web 服务 WAR 包
|
||
│ └── logs/
|
||
├── task/
|
||
│ ├── task.jar # Task 服务 JAR 包
|
||
│ ├── task.pid # 进程 PID 文件
|
||
│ └── logs/
|
||
├── front/
|
||
│ └── dist/ # 前端静态文件(Nginx 指向此目录)
|
||
├── backups/ # 版本备份(自动保留最近 5 个)
|
||
│ ├── 20260415-153000/
|
||
│ │ ├── ROOT.war
|
||
│ │ ├── task.jar
|
||
│ │ └── dist/
|
||
│ └── 20260415-140000/
|
||
│ └── ...
|
||
└── scripts/ # 服务器端管理脚本
|
||
├── restart-web.sh
|
||
├── restart-task.sh
|
||
└── restart-front.sh
|
||
```
|
||
|
||
## 五、新增文件结构
|
||
|
||
```
|
||
smartclean/
|
||
├── build.sh # 已有,构建脚本
|
||
└── deploy/
|
||
├── deploy.sh # 一键部署主脚本(本机执行)
|
||
├── rollback.sh # 一键回滚脚本(本机执行)
|
||
├── config.sh # 部署配置(服务器地址、路径等)
|
||
├── .env # 敏感信息(密码等,不入 git)
|
||
└── remote/ # 服务器端脚本(首次部署时自动上传)
|
||
├── setup.sh # 服务器初始化(创建目录结构,一次性)
|
||
├── restart-web.sh # 重启 Web 服务(Tomcat)
|
||
├── restart-task.sh # 重启 Task 服务(JAR)
|
||
└── restart-front.sh # 重新加载前端(Nginx reload)
|
||
```
|
||
|
||
## 六、配置文件
|
||
|
||
### 6.1 部署配置
|
||
|
||
```bash
|
||
# deploy/config.sh
|
||
|
||
# ===== 服务器配置 =====
|
||
DEPLOY_HOST="192.168.1.182"
|
||
DEPLOY_USER="root"
|
||
DEPLOY_BASE="/opt/smartclean"
|
||
|
||
# ===== 服务器目录 =====
|
||
REMOTE_WEB_DIR="$DEPLOY_BASE/web"
|
||
REMOTE_TASK_DIR="$DEPLOY_BASE/task"
|
||
REMOTE_FRONT_DIR="$DEPLOY_BASE/front"
|
||
REMOTE_BACKUP_DIR="$DEPLOY_BASE/backups"
|
||
REMOTE_SCRIPTS_DIR="$DEPLOY_BASE/scripts"
|
||
|
||
# ===== Tomcat 配置 =====
|
||
TOMCAT_HOME="/opt/smartclean/web/tomcat"
|
||
TOMCAT_WEBAPPS="$TOMCAT_HOME/webapps"
|
||
|
||
# ===== Nginx 配置 =====
|
||
NGINX_HTML="/opt/smartclean/front/dist"
|
||
|
||
# ===== Task 服务配置 =====
|
||
TASK_JAR_NAME="xiaoqu-intellectual-task-0.0.1-SNAPSHOT.jar"
|
||
TASK_PROFILE="prod"
|
||
TASK_JVM_OPTS="-Xms256m -Xmx512m"
|
||
|
||
# ===== 本地构建产物路径 =====
|
||
LOCAL_WAR="backend/xiaoqu-intellectual-web/target/ROOT.war"
|
||
LOCAL_TASK_JAR="backend/xiaoqu-intellectual-task/target/$TASK_JAR_NAME"
|
||
LOCAL_FRONT_DIST="frontend/witcleansystem/dist"
|
||
|
||
# ===== 备份保留数量 =====
|
||
MAX_BACKUPS=5
|
||
|
||
# ===== 健康检查 =====
|
||
HEALTHCHECK_URL="http://$DEPLOY_HOST:8095/dropDown/districtTree"
|
||
HEALTHCHECK_RETRIES=20
|
||
HEALTHCHECK_INTERVAL=5
|
||
|
||
# ===== 飞书通知 =====
|
||
FEISHU_WEBHOOK="https://open.feishu.cn/open-apis/bot/v2/hook/5703e8cc-6998-46a6-af9d-8c5102cc8c1e"
|
||
```
|
||
|
||
## 七、服务器端脚本
|
||
|
||
### 7.1 服务器初始化脚本(一次性执行)
|
||
|
||
```bash
|
||
# deploy/remote/setup.sh
|
||
#!/bin/bash
|
||
# 在服务器上创建标准目录结构,仅需执行一次
|
||
|
||
DEPLOY_BASE="/opt/smartclean"
|
||
|
||
mkdir -p "$DEPLOY_BASE"/{web/logs,task/logs,front,backups,scripts}
|
||
|
||
echo "目录结构创建完成:"
|
||
find "$DEPLOY_BASE" -maxdepth 2 -type d
|
||
```
|
||
|
||
### 7.2 重启 Web 服务
|
||
|
||
```bash
|
||
# deploy/remote/restart-web.sh
|
||
#!/bin/bash
|
||
# 重启 Tomcat(部署 ROOT.war)
|
||
|
||
TOMCAT_HOME="/opt/smartclean/web/tomcat"
|
||
|
||
echo "[INFO] 停止 Tomcat..."
|
||
"$TOMCAT_HOME/bin/shutdown.sh" 2>/dev/null
|
||
sleep 3
|
||
|
||
# 确保进程已停
|
||
TOMCAT_PID=$(ps -ef | grep "catalina" | grep -v grep | awk '{print $2}')
|
||
if [ -n "$TOMCAT_PID" ]; then
|
||
echo "[WARN] Tomcat 未正常关闭,强制终止 PID=$TOMCAT_PID"
|
||
kill -9 $TOMCAT_PID
|
||
sleep 1
|
||
fi
|
||
|
||
# 清理旧的解压目录,保留 WAR
|
||
rm -rf "$TOMCAT_HOME/webapps/ROOT"
|
||
rm -rf "$TOMCAT_HOME/work/Catalina"
|
||
|
||
echo "[INFO] 启动 Tomcat..."
|
||
"$TOMCAT_HOME/bin/startup.sh"
|
||
|
||
echo "[INFO] Tomcat 已启动"
|
||
```
|
||
|
||
### 7.3 重启 Task 服务
|
||
|
||
```bash
|
||
# deploy/remote/restart-task.sh
|
||
#!/bin/bash
|
||
# 重启 Task 服务(Spring Boot JAR)
|
||
|
||
TASK_DIR="/opt/smartclean/task"
|
||
JAR_FILE="$TASK_DIR/task.jar"
|
||
PID_FILE="$TASK_DIR/task.pid"
|
||
LOG_FILE="$TASK_DIR/logs/task.log"
|
||
PROFILE="${1:-prod}"
|
||
JVM_OPTS="${2:--Xms256m -Xmx512m}"
|
||
|
||
# 停止旧进程
|
||
if [ -f "$PID_FILE" ]; then
|
||
OLD_PID=$(cat "$PID_FILE")
|
||
if kill -0 "$OLD_PID" 2>/dev/null; then
|
||
echo "[INFO] 停止旧进程 PID=$OLD_PID"
|
||
kill "$OLD_PID"
|
||
sleep 3
|
||
# 强制终止
|
||
if kill -0 "$OLD_PID" 2>/dev/null; then
|
||
kill -9 "$OLD_PID"
|
||
fi
|
||
fi
|
||
rm -f "$PID_FILE"
|
||
fi
|
||
|
||
# 启动新进程
|
||
echo "[INFO] 启动 Task 服务 (profile=$PROFILE)..."
|
||
nohup java $JVM_OPTS \
|
||
-jar "$JAR_FILE" \
|
||
--spring.profiles.active=$PROFILE \
|
||
> "$LOG_FILE" 2>&1 &
|
||
|
||
echo $! > "$PID_FILE"
|
||
echo "[INFO] Task 服务已启动, PID=$(cat "$PID_FILE")"
|
||
```
|
||
|
||
### 7.4 重新加载前端
|
||
|
||
```bash
|
||
# deploy/remote/restart-front.sh
|
||
#!/bin/bash
|
||
# 重新加载 Nginx(前端静态文件已更新)
|
||
|
||
echo "[INFO] 测试 Nginx 配置..."
|
||
nginx -t 2>&1
|
||
if [ $? -ne 0 ]; then
|
||
echo "[ERROR] Nginx 配置有误"
|
||
exit 1
|
||
fi
|
||
|
||
echo "[INFO] 重新加载 Nginx..."
|
||
nginx -s reload
|
||
|
||
echo "[INFO] Nginx 已重新加载"
|
||
```
|
||
|
||
## 八、一键部署主脚本
|
||
|
||
```bash
|
||
# deploy/deploy.sh
|
||
#!/bin/bash
|
||
#
|
||
# SmartClean 一键部署脚本(无 Docker 版)
|
||
#
|
||
# 用法:
|
||
# ./deploy.sh # 构建并部署所有服务
|
||
# ./deploy.sh web # 仅构建部署 Web 服务
|
||
# ./deploy.sh task # 仅构建部署 Task 服务
|
||
# ./deploy.sh front # 仅构建部署前端
|
||
# ./deploy.sh front-test # 构建测试环境前端并部署
|
||
# ./deploy.sh backend # 构建部署后端(web + task)
|
||
# ./deploy.sh rollback # 回滚到上一版本
|
||
# ./deploy.sh setup # 首次初始化服务器目录
|
||
|
||
set -e
|
||
|
||
DEPLOY_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||
ROOT_DIR="$(dirname "$DEPLOY_DIR")"
|
||
source "$DEPLOY_DIR/config.sh"
|
||
|
||
# ===== 版本号 =====
|
||
GIT_HASH=$(cd "$ROOT_DIR" && git rev-parse --short HEAD)
|
||
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
|
||
VERSION="${TIMESTAMP}-${GIT_HASH}"
|
||
BRANCH=$(cd "$ROOT_DIR" && git rev-parse --abbrev-ref HEAD)
|
||
COMMIT=$(cd "$ROOT_DIR" && git log -1 --format='%h %s')
|
||
|
||
RED='\033[0;31m'
|
||
GREEN='\033[0;32m'
|
||
YELLOW='\033[1;33m'
|
||
NC='\033[0m'
|
||
|
||
log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
|
||
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
||
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
|
||
|
||
SSH_CMD="ssh $DEPLOY_USER@$DEPLOY_HOST"
|
||
SCP_CMD="scp"
|
||
|
||
# ===== 飞书通知 =====
|
||
notify_feishu() {
|
||
local title="$1" content="$2" color="$3"
|
||
curl -s -X POST "$FEISHU_WEBHOOK" \
|
||
-H "Content-Type: application/json" \
|
||
-d "{
|
||
\"msg_type\": \"interactive\",
|
||
\"card\": {
|
||
\"header\": {\"title\":{\"tag\":\"plain_text\",\"content\":\"$title\"},\"template\":\"$color\"},
|
||
\"elements\": [{\"tag\":\"markdown\",\"content\":\"$content\"}]
|
||
}
|
||
}" > /dev/null 2>&1
|
||
}
|
||
|
||
# ===== 检查 SSH 连接 =====
|
||
check_ssh() {
|
||
log_info "检查 SSH 连接..."
|
||
if ! $SSH_CMD "echo ok" > /dev/null 2>&1; then
|
||
log_error "无法连接到 $DEPLOY_USER@$DEPLOY_HOST"
|
||
log_error "请先配置 SSH 免密登录: ssh-copy-id $DEPLOY_USER@$DEPLOY_HOST"
|
||
exit 1
|
||
fi
|
||
log_info "SSH 连接正常"
|
||
}
|
||
|
||
# ===== 首次初始化服务器 =====
|
||
setup_server() {
|
||
log_info "初始化服务器目录结构..."
|
||
$SCP_CMD "$DEPLOY_DIR/remote/setup.sh" "$DEPLOY_USER@$DEPLOY_HOST:/tmp/setup.sh"
|
||
$SSH_CMD "bash /tmp/setup.sh"
|
||
|
||
log_info "上传服务器端管理脚本..."
|
||
$SCP_CMD "$DEPLOY_DIR/remote/restart-web.sh" "$DEPLOY_USER@$DEPLOY_HOST:$REMOTE_SCRIPTS_DIR/"
|
||
$SCP_CMD "$DEPLOY_DIR/remote/restart-task.sh" "$DEPLOY_USER@$DEPLOY_HOST:$REMOTE_SCRIPTS_DIR/"
|
||
$SCP_CMD "$DEPLOY_DIR/remote/restart-front.sh" "$DEPLOY_USER@$DEPLOY_HOST:$REMOTE_SCRIPTS_DIR/"
|
||
$SSH_CMD "chmod +x $REMOTE_SCRIPTS_DIR/*.sh"
|
||
|
||
log_info "服务器初始化完成"
|
||
}
|
||
|
||
# ===== 本地构建 =====
|
||
build_local() {
|
||
local target="$1"
|
||
log_info "开始本地构建 (目标: $target)..."
|
||
|
||
cd "$ROOT_DIR"
|
||
bash build.sh "$target"
|
||
|
||
# 验证产物存在
|
||
case "$target" in
|
||
web|backend|all)
|
||
if [ ! -f "$ROOT_DIR/$LOCAL_WAR" ]; then
|
||
log_error "构建产物不存在: $LOCAL_WAR"
|
||
exit 1
|
||
fi
|
||
;;
|
||
esac
|
||
|
||
case "$target" in
|
||
task|backend|all)
|
||
if [ ! -f "$ROOT_DIR/$LOCAL_TASK_JAR" ]; then
|
||
log_error "构建产物不存在: $LOCAL_TASK_JAR"
|
||
exit 1
|
||
fi
|
||
;;
|
||
esac
|
||
|
||
case "$target" in
|
||
front|front-test|all)
|
||
if [ ! -d "$ROOT_DIR/$LOCAL_FRONT_DIST" ]; then
|
||
log_error "构建产物不存在: $LOCAL_FRONT_DIST"
|
||
exit 1
|
||
fi
|
||
;;
|
||
esac
|
||
|
||
log_info "本地构建完成"
|
||
}
|
||
|
||
# ===== 远程备份 =====
|
||
backup_remote() {
|
||
local target="$1"
|
||
log_info "备份服务器当前版本 ($VERSION)..."
|
||
|
||
$SSH_CMD bash <<EOF
|
||
BACKUP="$REMOTE_BACKUP_DIR/$VERSION"
|
||
mkdir -p "\$BACKUP"
|
||
|
||
# 按目标备份
|
||
case "$target" in
|
||
web|backend|all)
|
||
cp "$TOMCAT_WEBAPPS/ROOT.war" "\$BACKUP/" 2>/dev/null && echo "已备份 ROOT.war" || echo "ROOT.war 不存在,跳过"
|
||
;;
|
||
esac
|
||
|
||
case "$target" in
|
||
task|backend|all)
|
||
cp "$REMOTE_TASK_DIR/task.jar" "\$BACKUP/" 2>/dev/null && echo "已备份 task.jar" || echo "task.jar 不存在,跳过"
|
||
;;
|
||
esac
|
||
|
||
case "$target" in
|
||
front|front-test|all)
|
||
if [ -d "$REMOTE_FRONT_DIR/dist" ]; then
|
||
cp -r "$REMOTE_FRONT_DIR/dist" "\$BACKUP/"
|
||
echo "已备份 front/dist"
|
||
fi
|
||
;;
|
||
esac
|
||
|
||
# 清理过期备份,保留最近 $MAX_BACKUPS 个
|
||
cd "$REMOTE_BACKUP_DIR"
|
||
ls -dt */ 2>/dev/null | tail -n +\$(($MAX_BACKUPS + 1)) | xargs rm -rf 2>/dev/null
|
||
echo "当前备份列表:"
|
||
ls -dt */ 2>/dev/null | head -5
|
||
EOF
|
||
|
||
log_info "远程备份完成"
|
||
}
|
||
|
||
# ===== 上传产物 =====
|
||
upload_artifacts() {
|
||
local target="$1"
|
||
|
||
case "$target" in
|
||
web|backend|all)
|
||
log_info "上传 ROOT.war..."
|
||
$SCP_CMD "$ROOT_DIR/$LOCAL_WAR" "$DEPLOY_USER@$DEPLOY_HOST:$TOMCAT_WEBAPPS/ROOT.war"
|
||
;;
|
||
esac
|
||
|
||
case "$target" in
|
||
task|backend|all)
|
||
log_info "上传 task.jar..."
|
||
$SCP_CMD "$ROOT_DIR/$LOCAL_TASK_JAR" "$DEPLOY_USER@$DEPLOY_HOST:$REMOTE_TASK_DIR/task.jar"
|
||
;;
|
||
esac
|
||
|
||
case "$target" in
|
||
front|front-test|all)
|
||
log_info "上传前端文件..."
|
||
# 先清空远程 dist,再上传
|
||
$SSH_CMD "rm -rf $REMOTE_FRONT_DIR/dist"
|
||
$SCP_CMD -r "$ROOT_DIR/$LOCAL_FRONT_DIST" "$DEPLOY_USER@$DEPLOY_HOST:$REMOTE_FRONT_DIR/dist"
|
||
;;
|
||
esac
|
||
|
||
log_info "产物上传完成"
|
||
}
|
||
|
||
# ===== 远程重启服务 =====
|
||
restart_services() {
|
||
local target="$1"
|
||
|
||
case "$target" in
|
||
web|backend|all)
|
||
log_info "重启 Web 服务..."
|
||
$SSH_CMD "bash $REMOTE_SCRIPTS_DIR/restart-web.sh"
|
||
;;
|
||
esac
|
||
|
||
case "$target" in
|
||
task|backend|all)
|
||
log_info "重启 Task 服务..."
|
||
$SSH_CMD "bash $REMOTE_SCRIPTS_DIR/restart-task.sh $TASK_PROFILE '$TASK_JVM_OPTS'"
|
||
;;
|
||
esac
|
||
|
||
case "$target" in
|
||
front|front-test|all)
|
||
log_info "重新加载前端..."
|
||
$SSH_CMD "bash $REMOTE_SCRIPTS_DIR/restart-front.sh"
|
||
;;
|
||
esac
|
||
}
|
||
|
||
# ===== 健康检查 =====
|
||
healthcheck() {
|
||
local target="$1"
|
||
|
||
# Web 服务检查
|
||
if [ "$target" = "all" ] || [ "$target" = "web" ] || [ "$target" = "backend" ]; then
|
||
log_info "健康检查 Web 服务 ($HEALTHCHECK_URL)..."
|
||
for i in $(seq 1 $HEALTHCHECK_RETRIES); do
|
||
if curl -sf "$HEALTHCHECK_URL" > /dev/null 2>&1; then
|
||
log_info "✅ Web 服务健康 (第${i}次检查)"
|
||
return 0
|
||
fi
|
||
echo -n "."
|
||
sleep $HEALTHCHECK_INTERVAL
|
||
done
|
||
log_error "❌ Web 服务健康检查失败 (${HEALTHCHECK_RETRIES}次重试后)"
|
||
return 1
|
||
fi
|
||
|
||
# 前端检查
|
||
if [ "$target" = "front" ] || [ "$target" = "front-test" ]; then
|
||
log_info "健康检查前端..."
|
||
if curl -sf "http://$DEPLOY_HOST/" > /dev/null 2>&1; then
|
||
log_info "✅ 前端服务健康"
|
||
return 0
|
||
else
|
||
log_error "❌ 前端服务健康检查失败"
|
||
return 1
|
||
fi
|
||
fi
|
||
|
||
return 0
|
||
}
|
||
|
||
# ===== 回滚 =====
|
||
rollback() {
|
||
log_info "查找最近的备份..."
|
||
|
||
# 获取最近的备份目录名
|
||
LATEST_BACKUP=$($SSH_CMD "ls -dt $REMOTE_BACKUP_DIR/*/ 2>/dev/null | head -1 | xargs basename 2>/dev/null")
|
||
|
||
if [ -z "$LATEST_BACKUP" ]; then
|
||
log_error "没有可回滚的备份"
|
||
exit 1
|
||
fi
|
||
|
||
log_warn "回滚到版本: $LATEST_BACKUP"
|
||
|
||
$SSH_CMD bash <<EOF
|
||
BACKUP="$REMOTE_BACKUP_DIR/$LATEST_BACKUP"
|
||
|
||
# 还原 WAR
|
||
if [ -f "\$BACKUP/ROOT.war" ]; then
|
||
cp "\$BACKUP/ROOT.war" "$TOMCAT_WEBAPPS/ROOT.war"
|
||
echo "已还原 ROOT.war"
|
||
fi
|
||
|
||
# 还原 Task JAR
|
||
if [ -f "\$BACKUP/task.jar" ]; then
|
||
cp "\$BACKUP/task.jar" "$REMOTE_TASK_DIR/task.jar"
|
||
echo "已还原 task.jar"
|
||
fi
|
||
|
||
# 还原前端
|
||
if [ -d "\$BACKUP/dist" ]; then
|
||
rm -rf "$REMOTE_FRONT_DIR/dist"
|
||
cp -r "\$BACKUP/dist" "$REMOTE_FRONT_DIR/dist"
|
||
echo "已还原 front/dist"
|
||
fi
|
||
EOF
|
||
|
||
# 重启所有服务
|
||
restart_services "all"
|
||
|
||
log_info "回滚完成"
|
||
|
||
notify_feishu "⚠️ SmartClean 已回滚" \
|
||
"**回滚版本:** $LATEST_BACKUP\\n**服务器:** $DEPLOY_HOST\\n**操作人:** $(whoami)@$(hostname)" \
|
||
"yellow"
|
||
}
|
||
|
||
# ===== 主流程 =====
|
||
TARGET="${1:-all}"
|
||
DEPLOY_START=$(date +%s)
|
||
|
||
# 特殊命令
|
||
case "$TARGET" in
|
||
setup)
|
||
check_ssh
|
||
setup_server
|
||
exit 0
|
||
;;
|
||
rollback)
|
||
check_ssh
|
||
rollback
|
||
exit 0
|
||
;;
|
||
esac
|
||
|
||
log_info "======================================"
|
||
log_info " SmartClean 自动化部署"
|
||
log_info " 版本: $VERSION"
|
||
log_info " 分支: $BRANCH"
|
||
log_info " 目标: $TARGET"
|
||
log_info " 服务器: $DEPLOY_USER@$DEPLOY_HOST"
|
||
log_info "======================================"
|
||
|
||
# 1. 检查连接
|
||
check_ssh
|
||
|
||
# 2. 本地构建
|
||
build_local "$TARGET"
|
||
|
||
# 3. 远程备份当前版本
|
||
backup_remote "$TARGET"
|
||
|
||
# 4. 上传构建产物
|
||
upload_artifacts "$TARGET"
|
||
|
||
# 5. 远程重启服务
|
||
restart_services "$TARGET"
|
||
|
||
# 6. 健康检查
|
||
if healthcheck "$TARGET"; then
|
||
ELAPSED=$(( $(date +%s) - DEPLOY_START ))
|
||
log_info "======================================"
|
||
log_info " ✅ 部署成功!"
|
||
log_info " 版本: $VERSION"
|
||
log_info " 耗时: ${ELAPSED}s"
|
||
log_info " 前端: http://$DEPLOY_HOST"
|
||
log_info " Web: http://$DEPLOY_HOST:8095"
|
||
log_info " Task: http://$DEPLOY_HOST:8097"
|
||
log_info "======================================"
|
||
|
||
notify_feishu "✅ SmartClean 部署成功" \
|
||
"**版本:** $VERSION\\n**分支:** $BRANCH\\n**提交:** $COMMIT\\n**目标:** $TARGET\\n**服务器:** $DEPLOY_HOST\\n**耗时:** ${ELAPSED}s\\n**操作人:** $(whoami)@$(hostname)" \
|
||
"green"
|
||
else
|
||
log_error "健康检查失败,自动回滚..."
|
||
rollback
|
||
|
||
ELAPSED=$(( $(date +%s) - DEPLOY_START ))
|
||
notify_feishu "❌ SmartClean 部署失败(已回滚)" \
|
||
"**版本:** $VERSION\\n**分支:** $BRANCH\\n**提交:** $COMMIT\\n**服务器:** $DEPLOY_HOST\\n**耗时:** ${ELAPSED}s\\n**状态:** 健康检查失败,已自动回滚" \
|
||
"red"
|
||
|
||
exit 1
|
||
fi
|
||
```
|
||
|
||
## 九、回滚脚本
|
||
|
||
```bash
|
||
# deploy/rollback.sh
|
||
#!/bin/bash
|
||
# 快捷回滚入口
|
||
DEPLOY_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||
exec "$DEPLOY_DIR/deploy.sh" rollback
|
||
```
|
||
|
||
## 十、使用方式
|
||
|
||
### 首次使用(一次性设置)
|
||
|
||
```bash
|
||
# 1. 配置 SSH 免密登录
|
||
ssh-copy-id root@192.168.1.182
|
||
|
||
# 2. 初始化服务器目录结构 + 上传管理脚本
|
||
./deploy/deploy.sh setup
|
||
|
||
# 3. 根据实际情况修改 deploy/config.sh 中的路径配置
|
||
# 特别是 TOMCAT_HOME 要对应服务器上实际的 Tomcat 安装路径
|
||
```
|
||
|
||
### 日常部署
|
||
|
||
```bash
|
||
# 全量部署(构建全部 + 部署全部)
|
||
./deploy/deploy.sh
|
||
|
||
# 只改了后端 Web 代码
|
||
./deploy/deploy.sh web
|
||
|
||
# 只改了 Task 服务代码
|
||
./deploy/deploy.sh task
|
||
|
||
# 后端都改了
|
||
./deploy/deploy.sh backend
|
||
|
||
# 只改了前端(生产包)
|
||
./deploy/deploy.sh front
|
||
|
||
# 前端测试环境包
|
||
./deploy/deploy.sh front-test
|
||
|
||
# 出问题了,一键回滚
|
||
./deploy/deploy.sh rollback
|
||
```
|
||
|
||
### 查看服务器上的备份
|
||
|
||
```bash
|
||
ssh root@192.168.1.182 "ls -lt /opt/smartclean/backups/"
|
||
```
|
||
|
||
## 十一、部署流程图(以 `./deploy.sh web` 为例)
|
||
|
||
```
|
||
本机 192.168.1.182
|
||
──── ─────────────
|
||
|
||
1. mvn clean package
|
||
→ ROOT.war 生成
|
||
2. mkdir backups/20260415-153000/
|
||
cp ROOT.war → backups/
|
||
|
||
3. scp ROOT.war ───────────────→ 4. ROOT.war → tomcat/webapps/
|
||
|
||
5. shutdown.sh → rm ROOT/ → startup.sh
|
||
|
||
6. curl http://182:8095/health
|
||
├── ✅ 成功 → 飞书通知"部署成功"
|
||
└── ❌ 失败 → 触发回滚
|
||
7. (回滚) cp backups/最新/ROOT.war → webapps/
|
||
→ restart tomcat
|
||
飞书通知"已回滚" ←──────
|
||
```
|
||
|
||
## 十二、与 Docker 版方案对比
|
||
|
||
| 维度 | 本方案(无 Docker) | Docker 版 |
|
||
|------|---------------------|-----------|
|
||
| **依赖** | 仅需 SSH + SCP | 需要 Docker |
|
||
| **环境一致性** | 依赖服务器已有环境(JDK、Tomcat、Nginx) | 镜像自包含,环境完全一致 |
|
||
| **部署速度** | 快(只传产物,几十 MB) | 慢(构建镜像,几百 MB) |
|
||
| **适合场景** | 服务器少(1-3 台),已有运行环境 | 多环境、多服务器、需要环境隔离 |
|
||
| **回滚** | 文件级回滚(还原备份文件) | 镜像级回滚(切换镜像 tag) |
|
||
| **学习成本** | 低(Shell + SSH) | 中(需了解 Docker) |
|
||
| **改造成本** | 低(复用现有 build.sh + 服务器环境) | 中(需编写 Dockerfile、调试容器配置) |
|
||
|
||
## 十三、后续演进
|
||
|
||
| 阶段 | 动作 |
|
||
|------|------|
|
||
| **当前** | 本方案:本机执行 deploy.sh,SSH 部署到 182 |
|
||
| **阶段 2** | 多环境:config.sh 支持 `--env test/prod` 参数,切换不同服务器 |
|
||
| **阶段 3** | CI/CD:将 deploy.sh 逻辑搬到 Gitea Actions,push 自动触发 |
|
||
| **阶段 4** | 按需容器化:逐步将服务迁入 Docker,deploy.sh 改为推送镜像 |
|
||
|
||
## 十四、安全注意事项
|
||
|
||
1. **deploy/.env 不入 git**:在 `.gitignore` 中添加 `deploy/.env`
|
||
2. **SSH Key 管理**:建议为部署专用创建独立 Key,限制只能执行特定命令
|
||
3. **备份清理**:默认保留最近 5 个备份,可在 config.sh 中调整 `MAX_BACKUPS`
|
||
4. **操作审计**:每次部署的版本号、操作人、时间都会通过飞书通知记录
|