Files
smartClean/deploy/docker/deploy.sh
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

356 lines
10 KiB
Bash
Executable File
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.
#!/bin/bash
#
# SmartClean 一键部署脚本Docker 版 — 本机模拟)
#
# 用法:
# ./deploy.sh # 构建并部署所有服务
# ./deploy.sh web # 仅重建部署 Web 服务
# ./deploy.sh task # 仅重建部署 Task 服务
# ./deploy.sh front # 仅重建部署前端
# ./deploy.sh rollback # 回滚到上一版本
# ./deploy.sh status # 查看容器状态
# ./deploy.sh logs [服务名] # 查看日志
# ./deploy.sh stop # 停止所有容器
# ./deploy.sh clean # 停止并清理(含数据卷)
set -e
DEPLOY_DIR="$(cd "$(dirname "$0")" && pwd)"
ROOT_DIR="$(dirname "$(dirname "$DEPLOY_DIR")")"
source "$DEPLOY_DIR/.env"
# ===== 版本号 =====
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')
BACKUP_FILE="$DEPLOY_DIR/.last-version"
CURRENT_FILE="$DEPLOY_DIR/.current-version"
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
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"; }
log_step() { echo -e "${CYAN}[STEP]${NC} $1"; }
DC="docker compose -f $DEPLOY_DIR/docker-compose.yml"
# ===== 飞书通知 =====
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
}
# ===== 检查 Docker =====
check_docker() {
if ! command -v docker &> /dev/null; then
log_error "Docker 未安装,请先安装 Docker Desktop"
exit 1
fi
if ! docker info > /dev/null 2>&1; then
log_error "Docker 未启动,请先启动 Docker Desktop"
exit 1
fi
}
# ===== 备份当前版本号 =====
backup_version() {
if [ -f "$CURRENT_FILE" ]; then
cp "$CURRENT_FILE" "$BACKUP_FILE"
log_info "已备份当前版本: $(cat "$BACKUP_FILE")"
fi
}
# ===== 本地构建产物 =====
build_local() {
local target="${1:-all}"
local start_time=$(date +%s)
cd "$ROOT_DIR"
case "$target" in
web)
log_step "本地构建 Web 服务..."
bash build.sh web
;;
task)
log_step "本地构建 Task 服务..."
bash build.sh task
;;
front)
log_step "本地构建前端 (docker 模式)..."
cd "$ROOT_DIR/frontend/witcleansystem"
npm run build-docker
;;
all)
log_step "本地构建全部..."
bash build.sh backend
cd "$ROOT_DIR/frontend/witcleansystem"
npm run build-docker
;;
esac
local elapsed=$(( $(date +%s) - start_time ))
log_info "本地构建完成 (${elapsed}s)"
}
# ===== 打包 Docker 镜像 =====
build_images() {
local target="${1:-all}"
local start_time=$(date +%s)
cd "$DEPLOY_DIR"
case "$target" in
web)
log_step "打包 Web 镜像 ($VERSION)..."
$DC build --no-cache web
docker tag smartclean-web:latest smartclean-web:$VERSION 2>/dev/null || true
;;
task)
log_step "打包 Task 镜像 ($VERSION)..."
$DC build --no-cache task
docker tag smartclean-task:latest smartclean-task:$VERSION 2>/dev/null || true
;;
front)
log_step "打包前端镜像 ($VERSION)..."
$DC build --no-cache frontend
docker tag smartclean-front:latest smartclean-front:$VERSION 2>/dev/null || true
;;
all)
log_step "打包全部镜像 ($VERSION)..."
$DC build --no-cache web frontend
docker tag smartclean-web:latest smartclean-web:$VERSION 2>/dev/null || true
docker tag smartclean-front:latest smartclean-front:$VERSION 2>/dev/null || true
;;
esac
local elapsed=$(( $(date +%s) - start_time ))
log_info "镜像打包完成 (${elapsed}s)"
}
# ===== 部署服务 =====
deploy_services() {
local target="${1:-all}"
cd "$DEPLOY_DIR"
log_step "部署应用服务..."
case "$target" in
web)
$DC down 2>/dev/null || true
$DC up -d web frontend
;;
task)
$DC up -d --no-deps task
;;
front)
$DC down 2>/dev/null || true
$DC up -d web frontend
;;
all)
$DC down 2>/dev/null || true
$DC up -d web frontend
;;
esac
log_info "容器已启动"
}
# ===== 健康检查 =====
healthcheck() {
local target="${1:-all}"
local max_retries=30
local interval=3
log_step "健康检查..."
# Web 服务
if [ "$target" = "all" ] || [ "$target" = "web" ]; then
log_info " 检查 Web 服务 (http://localhost:$WEB_PORT)..."
for i in $(seq 1 $max_retries); do
if curl -sf -X POST "http://localhost:$WEB_PORT/dropDown/districtTree" > /dev/null 2>&1; then
log_info " Web 服务健康 (第${i}次检查通过)"
break
fi
if [ $i -eq $max_retries ]; then
log_error " Web 服务健康检查失败"
log_error " 查看日志: $DC logs web"
return 1
fi
printf "."
sleep $interval
done
fi
# 前端
if [ "$target" = "all" ] || [ "$target" = "front" ]; then
log_info " 检查前端服务 (http://localhost:$FRONT_PORT)..."
for i in $(seq 1 15); do
if curl -sf "http://localhost:$FRONT_PORT/" > /dev/null 2>&1; then
log_info " 前端服务健康 (第${i}次检查通过)"
break
fi
if [ $i -eq 15 ]; then
log_error " 前端服务健康检查失败"
return 1
fi
sleep 2
done
fi
log_info "健康检查通过"
return 0
}
# ===== 回滚 =====
rollback() {
if [ ! -f "$BACKUP_FILE" ]; then
log_error "没有可回滚的版本"
exit 1
fi
local old_version=$(cat "$BACKUP_FILE")
log_warn "回滚到版本: $old_version"
local has_images=true
docker image inspect smartclean-web:$old_version > /dev/null 2>&1 || has_images=false
docker image inspect smartclean-front:$old_version > /dev/null 2>&1 || has_images=false
if [ "$has_images" = false ]; then
log_error "旧版本镜像不存在 ($old_version),无法回滚"
exit 1
fi
export VERSION="$old_version"
cd "$DEPLOY_DIR"
$DC down 2>/dev/null || true
$DC up -d web frontend
echo "$old_version" > "$CURRENT_FILE"
log_info "回滚完成"
notify_feishu "⚠️ SmartClean 已回滚 (Docker)" \
"**回滚版本:** $old_version\\n**触发原因:** 健康检查失败" \
"yellow"
}
# ===== 清理旧镜像 =====
cleanup_images() {
log_info "清理旧镜像(保留最近 5 个版本)..."
for name in smartclean-web smartclean-task smartclean-front; do
docker images "$name" --format "{{.Tag}}" | grep -v "latest" | sort -r | tail -n +6 | while read tag; do
docker rmi "$name:$tag" 2>/dev/null && echo " 删除 $name:$tag"
done
done
}
# ===== 主流程 =====
TARGET="${1:-all}"
DEPLOY_START=$(date +%s)
case "$TARGET" in
status)
$DC ps
exit 0
;;
logs)
shift
$DC logs -f $@
exit 0
;;
stop)
log_info "停止所有容器..."
$DC down
log_info "已停止"
exit 0
;;
clean)
log_warn "停止所有容器并清理数据卷..."
$DC down -v --rmi all
log_info "已清理"
exit 0
;;
rollback)
check_docker
rollback
exit 0
;;
all|web|task|front)
;;
*)
echo "用法: $0 {all|web|task|front|rollback|status|logs|stop|clean}"
exit 1
;;
esac
echo ""
log_info "======================================"
log_info " SmartClean 自动化部署 (Docker)"
log_info " 版本: $VERSION"
log_info " 分支: $BRANCH"
log_info " 提交: $COMMIT"
log_info " 目标: $TARGET"
log_info "======================================"
echo ""
# 1. 检查 Docker
check_docker
# 2. 备份当前版本号
backup_version
# 3. 本地构建产物Maven + npm
build_local "$TARGET"
# 4. 打包 Docker 镜像COPY 本地产物)
build_images "$TARGET"
# 5. 部署容器
deploy_services "$TARGET"
# 6. 健康检查
if healthcheck "$TARGET"; then
echo "$VERSION" > "$CURRENT_FILE"
cleanup_images
ELAPSED=$(( $(date +%s) - DEPLOY_START ))
echo ""
log_info "======================================"
log_info " ✅ 部署成功!"
log_info " 版本: $VERSION"
log_info " 耗时: ${ELAPSED}s"
log_info " 前端: http://localhost:$FRONT_PORT"
log_info " Web: http://localhost:$WEB_PORT"
log_info "======================================"
notify_feishu "✅ SmartClean 部署成功 (Docker)" \
"**版本:** $VERSION\\n**分支:** $BRANCH\\n**提交:** $COMMIT\\n**目标:** $TARGET\\n**耗时:** ${ELAPSED}s\\n**前端:** http://localhost:$FRONT_PORT\\n**Web:** http://localhost:$WEB_PORT" \
"green"
else
log_error "健康检查失败,自动回滚..."
rollback
ELAPSED=$(( $(date +%s) - DEPLOY_START ))
notify_feishu "❌ SmartClean 部署失败 (Docker已回滚)" \
"**版本:** $VERSION\\n**分支:** $BRANCH\\n**提交:** $COMMIT\\n**耗时:** ${ELAPSED}s\\n**状态:** 健康检查失败,已自动回滚" \
"red"
exit 1
fi