#!/bin/bash # ============================================ # Soul创业实验项目 - NAS部署脚本 # 用途: 自动部署Next.js应用到NAS-2 # ============================================ set -e # 配置 NAS_USER="fnvtk" NAS_IP="192.168.2.201" NAS_PASSWORD="Zhiqun1984" SUDO_PASSWORD="Zhiqun1984" DOCKER_CMD="/volume1/@appstore/ContainerManager/usr/bin/docker" DOCKER_COMPOSE_CMD="/volume1/@appstore/ContainerManager/usr/bin/docker-compose" PROJECT_NAME="soul-book" PROJECT_DIR="/volume1/docker/${PROJECT_NAME}" LOCAL_PROJECT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" LOG_FILE="/tmp/soul_deploy_$(date +%Y%m%d_%H%M%S).log" # 颜色输出 RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' echo "==========================================" | tee -a $LOG_FILE echo "Soul创业实验项目 - NAS部署脚本" | tee -a $LOG_FILE echo "开始时间: $(date)" | tee -a $LOG_FILE echo "==========================================" | tee -a $LOG_FILE echo "" print_step() { echo -e "${GREEN}[步骤 $1]${NC} $2" | tee -a $LOG_FILE } print_error() { echo -e "${RED}[错误]${NC} $1" | tee -a $LOG_FILE } print_warn() { echo -e "${YELLOW}[警告]${NC} $1" | tee -a $LOG_FILE } # 检查 expect if ! command -v expect &> /dev/null; then print_error "expect 未安装,请先安装: brew install expect" exit 1 fi # ============================================ # 步骤 1: 检查网络连通性 # ============================================ print_step "1" "检查网络连通性..." if ping -c 1 $NAS_IP > /dev/null 2>&1; then echo "✅ NAS-2 在线" | tee -a $LOG_FILE else print_error "无法连接到 NAS-2 ($NAS_IP)" exit 1 fi # ============================================ # 步骤 2: 创建项目目录 # ============================================ print_step "2" "创建项目目录..." expect << EOF | tee -a $LOG_FILE set timeout 30 spawn ssh -t -o KexAlgorithms=+diffie-hellman-group1-sha1 -o Ciphers=+aes128-cbc,3des-cbc,aes192-cbc,aes256-cbc $NAS_USER@$NAS_IP "sudo mkdir -p ${PROJECT_DIR} && sudo chown -R ${NAS_USER}:users ${PROJECT_DIR} && echo '目录创建完成'" expect { "password:" { send "$NAS_PASSWORD\r" exp_continue } "Password:" { send "$SUDO_PASSWORD\r" exp_continue } "目录创建完成" { puts "✅ 目录创建成功" } timeout { puts "❌ 超时" exit 1 } } expect eof EOF # ============================================ # 步骤 3: 检查nas-network是否存在 # ============================================ print_step "3" "检查Docker网络..." expect << EOF | tee -a $LOG_FILE set timeout 30 spawn ssh -t -o KexAlgorithms=+diffie-hellman-group1-sha1 -o Ciphers=+aes128-cbc,3des-cbc,aes192-cbc,aes256-cbc $NAS_USER@$NAS_IP "sudo $DOCKER_CMD network inspect nas-network > /dev/null 2>&1 || sudo $DOCKER_CMD network create nas-network && echo '网络检查完成'" expect { "password:" { send "$NAS_PASSWORD\r" exp_continue } "Password:" { send "$SUDO_PASSWORD\r" exp_continue } "网络检查完成" { puts "✅ 网络检查成功" } timeout { puts "⚠️ 超时,但继续执行" } } expect eof EOF # ============================================ # 步骤 4: 传输项目文件(排除node_modules和.git) # ============================================ print_step "4" "传输项目文件到NAS(这可能需要几分钟)..." # 创建临时排除文件 EXCLUDE_FILE=$(mktemp) cat > $EXCLUDE_FILE << 'EXCLUDES' node_modules/ .git/ .next/ .DS_Store *.log .env.local .env*.local EXCLUDES expect << EOF | tee -a $LOG_FILE set timeout 600 spawn rsync -avz --progress --exclude-from=$EXCLUDE_FILE -e "ssh -o KexAlgorithms=+diffie-hellman-group1-sha1 -o Ciphers=+aes128-cbc,3des-cbc,aes192-cbc,aes256-cbc" "$LOCAL_PROJECT_DIR/" $NAS_USER@$NAS_IP:${PROJECT_DIR}/ expect { "password:" { send "$NAS_PASSWORD\r" exp_continue } timeout { puts "⚠️ 传输可能超时,但继续执行" } } expect eof EOF rm -f $EXCLUDE_FILE # ============================================ # 步骤 5: 在NAS上安装依赖和构建 # ============================================ print_step "5" "在NAS上安装依赖(使用pnpm)..." expect << EOF | tee -a $LOG_FILE set timeout 300 spawn ssh -t -o KexAlgorithms=+diffie-hellman-group1-sha1 -o Ciphers=+aes128-cbc,3des-cbc,aes192-cbc,aes256-cbc $NAS_USER@$NAS_IP "cd ${PROJECT_DIR} && if ! command -v pnpm &> /dev/null; then npm install -g pnpm; fi && pnpm install --frozen-lockfile && echo '依赖安装完成'" expect { "password:" { send "$NAS_PASSWORD\r" exp_continue } "Password:" { send "$SUDO_PASSWORD\r" exp_continue } "依赖安装完成" { puts "✅ 依赖安装成功" } timeout { puts "⚠️ 安装可能超时,但继续执行" } } expect eof EOF # ============================================ # 步骤 6: 停止并删除旧容器(如果存在) # ============================================ print_step "6" "停止并删除旧容器(如果存在)..." expect << EOF | tee -a $LOG_FILE set timeout 60 spawn ssh -t -o KexAlgorithms=+diffie-hellman-group1-sha1 -o Ciphers=+aes128-cbc,3des-cbc,aes192-cbc,aes256-cbc $NAS_USER@$NAS_IP "cd ${PROJECT_DIR} && sudo $DOCKER_COMPOSE_CMD down 2>/dev/null || true && sudo $DOCKER_CMD rm -f ${PROJECT_NAME} 2>/dev/null || true && echo '旧容器清理完成'" expect { "password:" { send "$NAS_PASSWORD\r" exp_continue } "Password:" { send "$SUDO_PASSWORD\r" exp_continue } "旧容器清理完成" { puts "✅ 旧容器清理成功" } timeout { puts "⚠️ 清理可能超时,但继续执行" } } expect eof EOF # ============================================ # 步骤 7: 构建Docker镜像 # ============================================ print_step "7" "构建Docker镜像(这可能需要5-10分钟)..." expect << EOF | tee -a $LOG_FILE set timeout 1200 spawn ssh -t -o KexAlgorithms=+diffie-hellman-group1-sha1 -o Ciphers=+aes128-cbc,3des-cbc,aes192-cbc,aes256-cbc $NAS_USER@$NAS_IP "cd ${PROJECT_DIR} && sudo $DOCKER_COMPOSE_CMD build --no-cache && echo '镜像构建完成'" expect { "password:" { send "$NAS_PASSWORD\r" exp_continue } "Password:" { send "$SUDO_PASSWORD\r" exp_continue } "镜像构建完成" { puts "✅ 镜像构建成功" } timeout { puts "⚠️ 构建可能超时,但继续执行" } } expect eof EOF # ============================================ # 步骤 8: 启动容器 # ============================================ print_step "8" "启动容器..." expect << EOF | tee -a $LOG_FILE set timeout 120 spawn ssh -t -o KexAlgorithms=+diffie-hellman-group1-sha1 -o Ciphers=+aes128-cbc,3des-cbc,aes192-cbc,aes256-cbc $NAS_USER@$NAS_IP "cd ${PROJECT_DIR} && sudo $DOCKER_COMPOSE_CMD up -d && echo '容器启动完成'" expect { "password:" { send "$NAS_PASSWORD\r" exp_continue } "Password:" { send "$SUDO_PASSWORD\r" exp_continue } "容器启动完成" { puts "✅ 容器启动成功" } timeout { puts "⚠️ 启动可能超时" } } expect eof EOF # 等待服务启动 echo "等待服务启动(30秒)..." | tee -a $LOG_FILE sleep 30 # ============================================ # 步骤 9: 检查容器状态 # ============================================ print_step "9" "检查容器状态..." expect << EOF | tee -a $LOG_FILE set timeout 30 spawn ssh -t -o KexAlgorithms=+diffie-hellman-group1-sha1 -o Ciphers=+aes128-cbc,3des-cbc,aes192-cbc,aes256-cbc $NAS_USER@$NAS_IP "sudo $DOCKER_CMD ps --filter name=${PROJECT_NAME} --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}'" expect { "password:" { send "$NAS_PASSWORD\r" exp_continue } "Password:" { send "$SUDO_PASSWORD\r" exp_continue } } expect eof EOF # ============================================ # 步骤 10: 检查服务健康状态 # ============================================ print_step "10" "检查服务健康状态..." expect << EOF | tee -a $LOG_FILE set timeout 30 spawn ssh -o KexAlgorithms=+diffie-hellman-group1-sha1 -o Ciphers=+aes128-cbc,3des-cbc,aes192-cbc,aes256-cbc $NAS_USER@$NAS_IP "curl -s -o /dev/null -w '%{http_code}' http://localhost:3000 || echo '服务未响应'" expect { "password:" { send "$NAS_PASSWORD\r" exp_continue } "200" { puts "✅ 服务运行正常" } "服务未响应" { puts "⚠️ 服务可能还在启动中" } } expect eof EOF # ============================================ # 完成 # ============================================ echo "" | tee -a $LOG_FILE echo "==========================================" | tee -a $LOG_FILE echo "✅ 部署脚本执行完成!" | tee -a $LOG_FILE echo "完成时间: $(date)" | tee -a $LOG_FILE echo "日志文件: $LOG_FILE" | tee -a $LOG_FILE echo "==========================================" | tee -a $LOG_FILE echo "" echo "访问地址:" echo " http://${NAS_IP}:3000" echo "" echo "下一步操作:" echo "1. 查看容器日志: ssh $NAS_USER@$NAS_IP 'sudo $DOCKER_CMD logs -f ${PROJECT_NAME}'" echo "2. 查看容器状态: ssh $NAS_USER@$NAS_IP 'sudo $DOCKER_CMD ps --filter name=${PROJECT_NAME}'" echo "3. 重启容器: ssh $NAS_USER@$NAS_IP 'cd ${PROJECT_DIR} && sudo $DOCKER_COMPOSE_CMD restart'" echo "4. 停止容器: ssh $NAS_USER@$NAS_IP 'cd ${PROJECT_DIR} && sudo $DOCKER_COMPOSE_CMD down'"