Files
smartClean/docs/自动化部署方案-无Docker版.md
xqzp2026 8373460096 feat: 添加自动化部署方案(Docker + 远程服务器两套方案)
- 新增 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>
2026-04-15 18:41:15 +09:30

727 lines
23 KiB
Markdown
Raw Permalink 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.
# 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.shSSH 部署到 182 |
| **阶段 2** | 多环境config.sh 支持 `--env test/prod` 参数,切换不同服务器 |
| **阶段 3** | CI/CD将 deploy.sh 逻辑搬到 Gitea Actionspush 自动触发 |
| **阶段 4** | 按需容器化:逐步将服务迁入 Dockerdeploy.sh 改为推送镜像 |
## 十四、安全注意事项
1. **deploy/.env 不入 git**:在 `.gitignore` 中添加 `deploy/.env`
2. **SSH Key 管理**:建议为部署专用创建独立 Key限制只能执行特定命令
3. **备份清理**:默认保留最近 5 个备份,可在 config.sh 中调整 `MAX_BACKUPS`
4. **操作审计**:每次部署的版本号、操作人、时间都会通过飞书通知记录