#!/bin/bash # 情感博物馆 - 远程部署脚本 # 作者: emotion-museum # 日期: 2025-07-18 # 用途: 将构建好的jar包部署到远程服务器 set -e # 配置变量 REMOTE_HOST="'root@47.111.10.27'" REMOTE_BUILD_DIR="/data/builds" REMOTE_DOCKER_COMPOSE_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' # 日志函数 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-explore:19005" "emotion-reward:19006" "emotion-websocket:19007" "emotion-auth:19008" "emotion-stats:19009" ) # 部署状态跟踪 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 'root@47.111.10.27' "echo 'Connection successful'" > /dev/null 2>&1; then log_success "远程服务器连接正常" else log_error "无法连接到远程服务器 'root@47.111.10.27'" exit 1 fi } # 检查本地jar包 check_local_jars() { log_info "检查本地jar包..." local missing_jars=0 for service_info in "${SERVICES[@]}"; do service_name=$(echo $service_info | cut -d':' -f1) jar_file="${service_name}/target/${service_name}-1.0.0.jar" if [ -f "$jar_file" ]; then jar_size=$(du -h "$jar_file" | cut -f1) log_info "✅ $service_name: $jar_size" else log_error "❌ $service_name: jar包不存在" missing_jars=$((missing_jars + 1)) fi done if [ $missing_jars -gt 0 ]; then log_error "发现 $missing_jars 个缺失的jar包,请先执行构建" exit 1 fi log_success "所有jar包检查通过" } # 传输所有jar包到远程服务器 transfer_all_jars() { log_info "开始传输所有jar包到远程服务器..." # 创建远程目录 ssh 'root@47.111.10.27' "mkdir -p $REMOTE_BUILD_DIR" local transfer_success=0 local transfer_failed=0 for service_info in "${SERVICES[@]}"; do service_name=$(echo $service_info | cut -d':' -f1) jar_file="${service_name}/target/${service_name}-1.0.0.jar" if [ ! -f "$jar_file" ]; then log_warning "跳过不存在的jar包: $service_name" continue fi log_info "传输jar包: $service_name" # 删除远程旧jar包 ssh 'root@47.111.10.27' "rm -f $REMOTE_BUILD_DIR/${service_name}-*.jar" # 上传新jar包 if scp "$jar_file" 'root@47.111.10.27':$REMOTE_BUILD_DIR/${service_name}-1.0.0.jar; then # 验证远程jar包 remote_size=$(ssh 'root@47.111.10.27' "du -h $REMOTE_BUILD_DIR/${service_name}-1.0.0.jar | cut -f1") log_success "✅ $service_name 传输成功 (远程大小: $remote_size)" transfer_success=$((transfer_success + 1)) else log_error "❌ $service_name 传输失败" transfer_failed=$((transfer_failed + 1)) fi done log_info "传输统计: 成功 $transfer_success, 失败 $transfer_failed" if [ $transfer_failed -eq 0 ]; then log_success "所有jar包传输成功" return 0 else log_error "部分jar包传输失败" return 1 fi } # 创建远程目录 create_remote_directories() { log_info "创建远程目录结构..." ssh 'root@47.111.10.27' " 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 "远程目录创建完成" } # 创建Docker网络 create_docker_network() { log_info "创建Docker网络..." ssh 'root@47.111.10.27' " docker network create emotion-network 2>/dev/null || true " log_success "Docker网络创建完成" } # 部署单个服务到远程 deploy_service_to_remote() { local service_name=$1 local service_port=$2 local start_time=$(date +%s) log_info "部署服务到远程: $service_name" DEPLOYMENT_STATUS[$service_name]="DEPLOYING" # 验证远程jar包存在 if ! ssh 'root@47.111.10.27' "test -f $REMOTE_BUILD_DIR/${service_name}-1.0.0.jar"; then local error_msg="远程jar包不存在" log_error "$error_msg" DEPLOYMENT_STATUS[$service_name]="FAILED" DEPLOYMENT_ERRORS[$service_name]="$error_msg" return 1 fi # 创建Dockerfile create_dockerfile $service_name $service_port # 停止并删除旧容器 log_info "停止旧容器: $service_name" ssh 'root@47.111.10.27' " 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 'root@47.111.10.27' " cd $REMOTE_DOCKER_COMPOSE_DIR docker build -t ${PROJECT_NAME}/${service_name}:latest -f Dockerfile.${service_name} . " # 启动新容器 log_info "启动新容器: $service_name" ssh 'root@47.111.10.27' " 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 MYSQL_PASSWORD='EmotionMuseum2025*#' \\ -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 'root@47.111.10.27' "docker ps | grep ${service_name}" > /dev/null 2>&1; then log_success "服务 $service_name 启动成功" # 显示容器日志 log_info "显示服务日志 最后10行: $service_name" ssh 'root@47.111.10.27' "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 启动失败" local error_logs=$(ssh 'root@47.111.10.27' "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 'root@47.111.10.27' "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/* 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" } # 主函数 main() { local start_time=$(date +%s) log_info "🚀 开始远程部署任务..." log_info "目标服务器: $REMOTE_HOST" log_info "部署环境: $PROFILE" log_info "服务总数: $TOTAL_SERVICES" # 检查远程连接 check_remote_connection # 检查本地jar包 check_local_jars # 创建远程目录和网络 create_remote_directories create_docker_network # 传输所有jar包 if ! transfer_all_jars; then log_error "jar包传输失败,部署终止" 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_to_remote $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 # 计算总耗时 local end_time=$(date +%s) local total_time=$((end_time - start_time)) log_info "部署统计: 成功 $SUCCESSFUL_DEPLOYMENTS, 失败 $FAILED_DEPLOYMENTS" # 根据部署结果设置退出码 if [ $FAILED_DEPLOYMENTS -eq 0 ]; then log_success "🎉 远程部署任务完成!总耗时: ${total_time}s" exit 0 else log_warning "⚠️ 部分服务部署失败,总耗时: ${total_time}s" exit 1 fi } # 执行主函数 main "$@"