Files
happy-life-star/backend/deploy-all.sh
T
peanut b150cede84 feat: 添加完整的容器化部署脚本系统
 新增功能:
- 全量部署脚本 (backend/deploy-all.sh) - 支持一键部署所有微服务
- 单服务部署脚本 - 每个微服务独立部署脚本
- 前端部署脚本 (web-flowith/deploy.sh) - Vue应用自动构建部署
- Jenkins CI/CD 支持 - 完整的Pipeline配置

�� 部署特性:
- 容错机制: 单个服务失败不影响其他服务部署
- 详细报告: 完整的部署状态统计和错误日志
- 容器化: 使用Docker进行服务部署
- 健康检查: 自动验证服务启动状态
- 版本备份: 自动备份旧版本支持快速回滚

🛠️ 技术改进:
- emotion-auth服务启动问题修复
- 跨域配置优化
- 数据库连接配置统一
- OAuth服务实现完善
- WebSocket依赖更新

📚 文档:
- Jenkins部署说明文档
- 部署脚本使用指南
- 故障排查手册

🌐 部署环境:
- 目标服务器: 47.111.10.27
- 容器化部署到 /data/builds
- 前端部署到 /data/www/emotion-museum
- 支持test/prod环境配置
2025-07-18 11:41:11 +08:00

445 lines
13 KiB
Bash
Executable File

#!/bin/bash
# 情感博物馆 - 全服务容器化部署脚本
# 作者: emotion-museum
# 日期: 2025-07-18
# 支持Jenkins CI/CD部署
# 不要在遇到错误时立即退出,让所有模块都尝试部署
set +e
# 配置变量 - 支持Jenkins环境变量覆盖
REMOTE_HOST="${DEPLOY_HOST:-root@47.111.10.27}"
REMOTE_BUILD_DIR="${REMOTE_BUILD_DIR:-/data/builds}"
REMOTE_DOCKER_COMPOSE_DIR="${REMOTE_DOCKER_DIR:-/data/docker}"
PROFILE="${DEPLOY_ENV:-test}"
PROJECT_NAME="${PROJECT_NAME:-emotion-museum}"
# Jenkins构建信息
BUILD_NUMBER="${BUILD_NUMBER:-manual}"
JOB_NAME="${JOB_NAME:-local-deploy}"
BUILD_URL="${BUILD_URL:-}"
# 颜色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# 日志函数
log_info() {
echo -e "${BLUE}[INFO]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
}
log_success() {
echo -e "${GREEN}[SUCCESS]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
}
log_warning() {
echo -e "${YELLOW}[WARNING]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
}
# 服务列表
SERVICES=(
"emotion-gateway:19000"
"emotion-user:19001"
"emotion-ai:19002"
"emotion-record:19003"
"emotion-growth:19004"
"emotion-websocket:19007"
"emotion-auth:19008"
)
# 部署状态跟踪
declare -A DEPLOYMENT_STATUS
declare -A DEPLOYMENT_ERRORS
declare -A DEPLOYMENT_TIMES
TOTAL_SERVICES=${#SERVICES[@]}
SUCCESSFUL_DEPLOYMENTS=0
FAILED_DEPLOYMENTS=0
# 检查远程服务器连接
check_remote_connection() {
log_info "检查远程服务器连接..."
if ssh -o ConnectTimeout=10 $REMOTE_HOST "echo 'Connection successful'" > /dev/null 2>&1; then
log_success "远程服务器连接正常"
else
log_error "无法连接到远程服务器 $REMOTE_HOST"
exit 1
fi
}
# 创建远程目录
create_remote_directories() {
log_info "创建远程目录结构..."
ssh $REMOTE_HOST "
mkdir -p $REMOTE_BUILD_DIR
mkdir -p $REMOTE_DOCKER_COMPOSE_DIR
mkdir -p /data/logs/emotion-museum
mkdir -p /data/config/emotion-museum
"
log_success "远程目录创建完成"
}
# 构建所有服务
build_all_services() {
log_info "开始构建所有微服务..."
# 先构建父项目
log_info "构建父项目..."
mvn clean install -DskipTests -q
for service_info in "${SERVICES[@]}"; do
service_name=$(echo $service_info | cut -d':' -f1)
log_info "构建服务: $service_name"
cd $service_name
if mvn clean package -DskipTests -Ptest -q; then
log_success "服务 $service_name 构建成功"
else
log_error "服务 $service_name 构建失败"
cd ..
exit 1
fi
cd ..
done
log_success "所有服务构建完成"
}
# 部署单个服务
deploy_service() {
local service_name=$1
local service_port=$2
local start_time=$(date +%s)
log_info "开始部署服务: $service_name"
DEPLOYMENT_STATUS[$service_name]="DEPLOYING"
# 检查jar包是否存在
local jar_file="${service_name}/target/${service_name}-1.0.0.jar"
if [ ! -f "$jar_file" ]; then
local error_msg="JAR包不存在: $jar_file"
log_error "$error_msg"
DEPLOYMENT_STATUS[$service_name]="FAILED"
DEPLOYMENT_ERRORS[$service_name]="$error_msg"
return 1
fi
# 删除远程旧jar包
log_info "删除远程旧jar包: $service_name"
ssh $REMOTE_HOST "rm -f $REMOTE_BUILD_DIR/${service_name}-*.jar"
# 上传新jar包
log_info "上传jar包: $service_name"
if scp "$jar_file" "$REMOTE_HOST:$REMOTE_BUILD_DIR/${service_name}-1.0.0.jar"; then
log_success "jar包上传成功: $service_name"
else
log_error "jar包上传失败: $service_name"
return 1
fi
# 创建Dockerfile
create_dockerfile $service_name $service_port
# 停止并删除旧容器
log_info "停止旧容器: $service_name"
ssh $REMOTE_HOST "
docker stop ${service_name} 2>/dev/null || true
docker rm ${service_name} 2>/dev/null || true
docker rmi ${PROJECT_NAME}/${service_name}:latest 2>/dev/null || true
"
# 构建Docker镜像
log_info "构建Docker镜像: $service_name"
ssh $REMOTE_HOST "
cd $REMOTE_DOCKER_COMPOSE_DIR
docker build -t ${PROJECT_NAME}/${service_name}:latest -f Dockerfile.${service_name} .
"
# 启动新容器
log_info "启动新容器: $service_name"
ssh $REMOTE_HOST "
docker run -d \\
--name ${service_name} \\
--network emotion-network \\
-p ${service_port}:${service_port} \\
-v /data/logs/emotion-museum:/app/logs \\
-e SPRING_PROFILES_ACTIVE=${PROFILE} \\
-e MYSQL_HOST=47.111.10.27 \\
-e MYSQL_PORT=3306 \\
-e MYSQL_DATABASE=emotion_museum \\
-e MYSQL_USERNAME=root \\
-e REDIS_HOST=47.111.10.27 \\
-e REDIS_PORT=6379 \\
-e REDIS_PASSWORD= \\
-e REDIS_DATABASE=0 \\
-e NACOS_SERVER_ADDR=47.111.10.27:8848 \\
-e NACOS_USERNAME=nacos \\
-e NACOS_PASSWORD=Peanut2817*# \\
--restart unless-stopped \\
${PROJECT_NAME}/${service_name}:latest
"
# 等待服务启动
log_info "等待服务启动: $service_name"
sleep 10
# 检查容器状态
if ssh $REMOTE_HOST "docker ps | grep ${service_name}" > /dev/null 2>&1; then
log_success "服务 $service_name 启动成功"
# 显示容器日志
log_info "显示服务日志 (最后10行): $service_name"
ssh $REMOTE_HOST "docker logs --tail 10 ${service_name}" 2>/dev/null || true
# 记录成功状态
local end_time=$(date +%s)
local duration=$((end_time - start_time))
DEPLOYMENT_STATUS[$service_name]="SUCCESS"
DEPLOYMENT_TIMES[$service_name]="${duration}s"
return 0
else
local error_msg="服务启动失败"
log_error "服务 $service_name 启动失败"
log_error "错误日志:"
local error_logs=$(ssh $REMOTE_HOST "docker logs ${service_name}" 2>&1 || echo "无法获取日志")
echo "$error_logs"
# 记录失败状态
local end_time=$(date +%s)
local duration=$((end_time - start_time))
DEPLOYMENT_STATUS[$service_name]="FAILED"
DEPLOYMENT_ERRORS[$service_name]="$error_msg: $error_logs"
DEPLOYMENT_TIMES[$service_name]="${duration}s"
return 1
fi
}
# 创建Dockerfile
create_dockerfile() {
local service_name=$1
local service_port=$2
log_info "创建Dockerfile: $service_name"
ssh $REMOTE_HOST "cat > $REMOTE_DOCKER_COMPOSE_DIR/Dockerfile.${service_name} << 'EOF'
FROM openjdk:17-jre-slim
# 设置工作目录
WORKDIR /app
# 安装必要的工具
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
# 复制jar包
COPY $REMOTE_BUILD_DIR/${service_name}-1.0.0.jar app.jar
# 创建日志目录
RUN mkdir -p /app/logs
# 设置时区
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/\$TZ /etc/localtime && echo \$TZ > /etc/timezone
# 暴露端口
EXPOSE ${service_port}
# 健康检查
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \\
CMD curl -f http://localhost:${service_port}/actuator/health || exit 1
# 启动应用
ENTRYPOINT [\"java\", \"-Djava.security.egd=file:/dev/./urandom\", \"-Xms512m\", \"-Xmx1024m\", \"-jar\", \"app.jar\"]
EOF"
}
# 创建Docker网络
create_docker_network() {
log_info "创建Docker网络..."
ssh $REMOTE_HOST "
docker network create emotion-network 2>/dev/null || true
"
log_success "Docker网络创建完成"
}
# 健康检查
health_check() {
log_info "执行服务健康检查..."
for service_info in "${SERVICES[@]}"; do
service_name=$(echo $service_info | cut -d':' -f1)
service_port=$(echo $service_info | cut -d':' -f2)
log_info "检查服务健康状态: $service_name"
# 等待服务完全启动
sleep 5
if ssh $REMOTE_HOST "curl -f -s http://localhost:${service_port}/actuator/health" > /dev/null 2>&1; then
log_success "服务 $service_name 健康检查通过"
else
log_warning "服务 $service_name 健康检查失败,可能仍在启动中"
fi
done
}
# 显示详细部署报告
show_deployment_report() {
local total_time=$1
echo ""
echo "========================================"
echo " 部署完成报告"
echo "========================================"
echo "项目名称: $PROJECT_NAME"
echo "部署环境: $PROFILE"
echo "目标服务器: $REMOTE_HOST"
echo "部署时间: $(date '+%Y-%m-%d %H:%M:%S')"
echo "总耗时: ${total_time}s"
if [ "$BUILD_NUMBER" != "manual" ]; then
echo "Jenkins构建: #$BUILD_NUMBER"
echo "Jenkins任务: $JOB_NAME"
[ -n "$BUILD_URL" ] && echo "构建链接: $BUILD_URL"
fi
echo "========================================"
echo ""
echo "📊 部署统计:"
echo " 总服务数: $TOTAL_SERVICES"
echo " 成功部署: $SUCCESSFUL_DEPLOYMENTS"
echo " 失败部署: $FAILED_DEPLOYMENTS"
echo " 成功率: $(( SUCCESSFUL_DEPLOYMENTS * 100 / TOTAL_SERVICES ))%"
echo ""
echo "📋 服务部署详情:"
printf "%-20s %-10s %-10s %s\n" "服务名称" "状态" "耗时" "备注"
echo "----------------------------------------"
for service_info in "${SERVICES[@]}"; do
service_name=$(echo $service_info | cut -d':' -f1)
service_port=$(echo $service_info | cut -d':' -f2)
status=${DEPLOYMENT_STATUS[$service_name]:-"UNKNOWN"}
time=${DEPLOYMENT_TIMES[$service_name]:-"N/A"}
case $status in
"SUCCESS")
printf "%-20s ${GREEN}%-10s${NC} %-10s %s\n" "$service_name" "✅ 成功" "$time" "http://47.111.10.27:$service_port"
;;
"FAILED")
printf "%-20s ${RED}%-10s${NC} %-10s %s\n" "$service_name" "❌ 失败" "$time" "查看错误日志"
;;
*)
printf "%-20s ${YELLOW}%-10s${NC} %-10s %s\n" "$service_name" "⚠️ 未知" "$time" "状态异常"
;;
esac
done
echo ""
# 显示失败服务的错误信息
if [ $FAILED_DEPLOYMENTS -gt 0 ]; then
echo "❌ 失败服务错误详情:"
echo "----------------------------------------"
for service_info in "${SERVICES[@]}"; do
service_name=$(echo $service_info | cut -d':' -f1)
if [ "${DEPLOYMENT_STATUS[$service_name]}" = "FAILED" ]; then
echo "🔸 $service_name:"
echo " ${DEPLOYMENT_ERRORS[$service_name]}" | head -3
echo ""
fi
done
fi
# 显示当前运行的容器状态
echo "🐳 当前容器运行状态:"
echo "----------------------------------------"
ssh $REMOTE_HOST "docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}' | grep emotion || echo '没有运行的emotion相关容器'"
echo ""
echo "========================================"
# 根据部署结果设置退出码
if [ $FAILED_DEPLOYMENTS -eq 0 ]; then
echo "🎉 所有服务部署成功!"
return 0
else
echo "⚠️ 部分服务部署失败,请检查错误日志"
return 1
fi
}
# 主函数
main() {
local start_time=$(date +%s)
log_info "🚀 开始全服务容器化部署..."
log_info "目标服务器: $REMOTE_HOST"
log_info "部署环境: $PROFILE"
log_info "服务总数: $TOTAL_SERVICES"
# 检查连接
if ! check_remote_connection; then
log_error "远程服务器连接失败,部署终止"
exit 1
fi
# 创建目录
create_remote_directories
# 创建Docker网络
create_docker_network
# 构建服务
if ! build_all_services; then
log_error "服务构建失败,部署终止"
exit 1
fi
# 部署所有服务 - 单个失败不影响其他服务
log_info "开始逐个部署服务..."
for service_info in "${SERVICES[@]}"; do
service_name=$(echo $service_info | cut -d':' -f1)
service_port=$(echo $service_info | cut -d':' -f2)
echo ""
log_info "[$((SUCCESSFUL_DEPLOYMENTS + FAILED_DEPLOYMENTS + 1))/$TOTAL_SERVICES] 部署服务: $service_name"
if deploy_service $service_name $service_port; then
SUCCESSFUL_DEPLOYMENTS=$((SUCCESSFUL_DEPLOYMENTS + 1))
log_success "✅ 服务 $service_name 部署成功"
else
FAILED_DEPLOYMENTS=$((FAILED_DEPLOYMENTS + 1))
log_error "❌ 服务 $service_name 部署失败,继续部署其他服务..."
fi
done
# 健康检查
log_info "执行服务健康检查..."
health_check
# 计算总耗时
local end_time=$(date +%s)
local total_time=$((end_time - start_time))
# 显示详细报告
show_deployment_report $total_time
# 根据部署结果设置退出码
if [ $FAILED_DEPLOYMENTS -eq 0 ]; then
log_success "🎉 全服务容器化部署完成!"
exit 0
else
log_warning "⚠️ 部分服务部署失败,请查看详细报告"
exit 1
fi
}
# 执行主函数
main "$@"