#!/bin/bash # 情绪博物馆阿里云服务器部署脚本 - 适配现有Docker环境 # 服务器已有MySQL/Redis/Nacos容器运行 set -e # 颜色定义 RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # 服务器配置 SERVER_IP="47.111.10.27" SERVER_USER="root" # 现有Docker容器配置 MYSQL_ROOT_PASSWORD="123456" MYSQL_CONTAINER="emotion-mysql-prod" REDIS_CONTAINER="emotion-redis-prod" NACOS_CONTAINER="emotion-nacos-prod" # 部署目录配置 BUILDS_DIR="/data/builds" WEB_DIR="/data/www/emotion-museum" CONFIG_FILE="/data/deployment_config.md" # 日志函数 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 "${BLUE}[STEP]${NC} $1" } # 远程执行命令 remote_exec() { local command="$1" ssh -o StrictHostKeyChecking=no "${SERVER_USER}@${SERVER_IP}" "$command" } # 复制文件到服务器 remote_copy() { local local_path="$1" local remote_path="$2" scp -o StrictHostKeyChecking=no -r "$local_path" "${SERVER_USER}@${SERVER_IP}:$remote_path" } # 检查服务器连接 check_server_connection() { log_step "检查服务器连接..." if ! ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 "${SERVER_USER}@${SERVER_IP}" "echo 'Connected'" &>/dev/null; then log_error "无法连接到服务器 ${SERVER_IP}" exit 1 fi log_info "服务器连接正常" } # 检查现有Docker服务 check_existing_services() { log_step "检查现有Docker服务..." # 检查Docker容器状态 if remote_exec "docker ps | grep -q ${MYSQL_CONTAINER}"; then log_info "✅ MySQL容器运行正常" else log_error "❌ MySQL容器未运行" exit 1 fi if remote_exec "docker ps | grep -q ${REDIS_CONTAINER}"; then log_info "✅ Redis容器运行正常" else log_error "❌ Redis容器未运行" exit 1 fi if remote_exec "docker ps | grep -q ${NACOS_CONTAINER}"; then log_info "✅ Nacos容器运行正常" else log_error "❌ Nacos容器未运行" exit 1 fi # 测试MySQL连接 if remote_exec "docker exec ${MYSQL_CONTAINER} mysql -u root -p'${MYSQL_ROOT_PASSWORD}' -e 'SELECT 1;' &>/dev/null"; then log_info "✅ MySQL连接正常" else log_error "❌ MySQL连接失败" exit 1 fi log_info "现有服务检查完成" } # 创建目录结构 setup_directories() { log_step "创建部署目录..." remote_exec "mkdir -p ${BUILDS_DIR}" remote_exec "mkdir -p ${WEB_DIR}" remote_exec "mkdir -p /data/logs/{app,nginx}" log_info "目录创建完成" } # 上传构建产物 upload_artifacts() { log_step "上传构建产物..." # 检查构建产物 if [ ! -d "build-output" ]; then log_error "构建产物不存在,请先运行: ./deploy-aliyun.sh build" exit 1 fi # 上传JAR文件 if [ -d "build-output/jars" ]; then for jar in build-output/jars/*.jar; do if [ -f "$jar" ]; then remote_copy "$jar" "${BUILDS_DIR}/" log_info "上传: $(basename $jar)" fi done fi # 上传前端文件 if [ -d "build-output/web" ]; then remote_exec "rm -rf ${WEB_DIR}/*" remote_copy "build-output/web/*" "${WEB_DIR}/" log_info "前端文件上传完成" fi # 上传数据库脚本 if [ -f "backend/mysql_emotion_museum_final.sql" ]; then remote_copy "backend/mysql_emotion_museum_final.sql" "/tmp/" log_info "数据库脚本上传完成" fi log_info "构建产物上传完成" } # 导入数据库 import_database() { log_step "导入数据库..." remote_exec " if [ -f /tmp/mysql_emotion_museum_final.sql ]; then docker exec -i ${MYSQL_CONTAINER} mysql -u root -p'${MYSQL_ROOT_PASSWORD}' emotion_museum < /tmp/mysql_emotion_museum_final.sql echo '数据库导入完成' else echo '数据库脚本不存在,跳过导入' fi " log_info "数据库处理完成" } # 创建应用配置文件 create_app_configs() { log_step "创建应用配置..." # 创建应用properties文件 cat > /tmp/application-prod.yml << 'EOF' server: port: 9000 spring: application: name: emotion-gateway cloud: nacos: discovery: server-addr: host.docker.internal:8848 config: server-addr: host.docker.internal:8848 file-extension: yml datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://host.docker.internal:3306/emotion_museum?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true username: emotion password: emotion123 redis: host: host.docker.internal port: 6379 timeout: 2000 jedis: pool: max-active: 8 max-wait: -1 max-idle: 8 min-idle: 0 logging: level: com.emotionmuseum: DEBUG file: name: /app/logs/emotion-gateway.log EOF # 上传配置文件 remote_copy "/tmp/application-prod.yml" "${BUILDS_DIR}/" rm /tmp/application-prod.yml log_info "应用配置创建完成" } # 创建Docker Compose配置 create_docker_compose() { log_step "创建应用Docker Compose配置..." cat > /tmp/docker-compose.yml << 'EOF' version: '3.8' services: # 网关服务 emotion-gateway: image: openjdk:17-jre-slim container_name: emotion-gateway restart: always ports: - "9000:9000" environment: SPRING_PROFILES_ACTIVE: prod TZ: Asia/Shanghai JAVA_OPTS: '-Xmx512m -Xms256m' volumes: - /data/builds/emotion-gateway-1.0.0.jar:/app/app.jar:ro - /data/builds/application-prod.yml:/app/application-prod.yml:ro - /data/logs/app:/app/logs working_dir: /app command: ["java", "-jar", "app.jar"] extra_hosts: - "host.docker.internal:172.17.0.1" networks: - emotion-network # AI服务 emotion-ai: image: openjdk:17-jre-slim container_name: emotion-ai restart: always ports: - "9002:9002" environment: SPRING_PROFILES_ACTIVE: prod TZ: Asia/Shanghai JAVA_OPTS: '-Xmx512m -Xms256m' volumes: - /data/builds/emotion-ai-1.0.0.jar:/app/app.jar:ro - /data/logs/app:/app/logs working_dir: /app command: ["java", "-jar", "app.jar"] extra_hosts: - "host.docker.internal:172.17.0.1" networks: - emotion-network # 用户服务 emotion-user: image: openjdk:17-jre-slim container_name: emotion-user restart: always ports: - "9001:9001" environment: SPRING_PROFILES_ACTIVE: prod TZ: Asia/Shanghai JAVA_OPTS: '-Xmx512m -Xms256m' volumes: - /data/builds/emotion-user-1.0.0.jar:/app/app.jar:ro - /data/logs/app:/app/logs working_dir: /app command: ["java", "-jar", "app.jar"] extra_hosts: - "host.docker.internal:172.17.0.1" networks: - emotion-network networks: emotion-network: driver: bridge EOF # 上传Docker Compose文件 remote_copy "/tmp/docker-compose.yml" "${BUILDS_DIR}/docker-compose.yml" rm /tmp/docker-compose.yml log_info "Docker Compose配置创建完成" } # 配置Nginx setup_nginx() { log_step "配置Nginx..." # 检查Nginx是否已安装 if ! remote_exec "command -v nginx &> /dev/null"; then remote_exec "yum install -y nginx" fi # 创建Nginx配置 cat > /tmp/nginx.conf << EOF user nginx; worker_processes auto; error_log /var/log/nginx/error.log; pid /run/nginx.pid; events { worker_connections 1024; } http { log_format main '\$remote_addr - \$remote_user [\$time_local] "\$request" ' '\$status \$body_bytes_sent "\$http_referer" ' '"\$http_user_agent" "\$http_x_forwarded_for"'; access_log /var/log/nginx/access.log main; sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 65; types_hash_max_size 2048; include /etc/nginx/mime.types; default_type application/octet-stream; # 前端应用 server { listen 80; server_name ${SERVER_IP} _; root ${WEB_DIR}; index index.html; # 前端路由 location / { try_files \$uri \$uri/ /index.html; } # API代理到网关 location /api/ { proxy_pass http://127.0.0.1:9000/; proxy_set_header Host \$host; proxy_set_header X-Real-IP \$remote_addr; proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto \$scheme; proxy_connect_timeout 60s; proxy_read_timeout 60s; proxy_send_timeout 60s; } # 直接访问网关 location /gateway/ { proxy_pass http://127.0.0.1:9000/; proxy_set_header Host \$host; proxy_set_header X-Real-IP \$remote_addr; proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto \$scheme; } # 静态资源缓存 location ~* \\.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { expires 1y; add_header Cache-Control "public, no-transform"; } # 健康检查 location /health { access_log off; return 200 "healthy\\n"; add_header Content-Type text/plain; } } } EOF # 上传并应用Nginx配置 remote_copy "/tmp/nginx.conf" "/etc/nginx/nginx.conf" rm /tmp/nginx.conf # 启动Nginx remote_exec " nginx -t && systemctl start nginx && systemctl enable nginx echo 'Nginx配置完成' " log_info "Nginx配置完成" } # 启动应用服务 start_application_services() { log_step "启动应用服务..." remote_exec " cd ${BUILDS_DIR} # 停止可能存在的同名容器 docker stop emotion-gateway emotion-ai emotion-user 2>/dev/null || true docker rm emotion-gateway emotion-ai emotion-user 2>/dev/null || true # 启动应用服务 docker-compose up -d echo '应用服务启动完成' " log_info "应用服务启动完成" } # 等待服务启动 wait_for_services() { log_step "等待服务启动..." sleep 20 # 检查服务状态 log_info "检查应用容器状态..." remote_exec "docker ps | grep -E 'emotion-(gateway|ai|user)'" log_info "等待服务完全启动..." sleep 30 } # 健康检查 health_check() { log_step "执行健康检查..." log_info "检查基础服务..." remote_exec "docker ps | grep -E '(mysql|redis|nacos)'" log_info "检查应用服务..." remote_exec "docker ps | grep -E 'emotion-(gateway|ai|user)'" log_info "检查端口监听..." remote_exec "netstat -tlnp | grep -E ':(80|3306|6379|8848|9000|9001|9002)'" # HTTP健康检查 log_info "HTTP接口测试..." # 测试前端 if remote_exec "curl -s -o /dev/null -w '%{http_code}' http://localhost/ | grep -q 200"; then log_info "✅ 前端应用正常" else log_warn "❌ 前端应用异常" fi # 测试网关健康检查 sleep 10 if remote_exec "curl -s http://localhost:9000/actuator/health 2>/dev/null | grep -q UP"; then log_info "✅ 网关服务正常" else log_warn "❌ 网关服务异常,检查日志:" remote_exec "docker logs emotion-gateway | tail -20" fi # 测试Nacos if remote_exec "curl -s -o /dev/null -w '%{http_code}' http://localhost:8848/nacos | grep -q 200"; then log_info "✅ Nacos控制台正常" else log_warn "❌ Nacos控制台异常" fi log_info "健康检查完成" } # 显示服务日志 show_logs() { log_step "显示服务日志..." log_info "网关服务日志:" remote_exec "docker logs emotion-gateway | tail -10" log_info "AI服务日志:" remote_exec "docker logs emotion-ai | tail -10" log_info "用户服务日志:" remote_exec "docker logs emotion-user | tail -10" } # 创建部署记录 create_deployment_record() { log_step "创建部署记录..." cat > /tmp/deployment_config.md << EOF # 情绪博物馆部署配置记录 ## 服务器信息 - 服务器IP: ${SERVER_IP} - 部署时间: $(date '+%Y-%m-%d %H:%M:%S') ## Docker服务配置 ### 基础服务 (现有容器) - MySQL: ${MYSQL_CONTAINER} (端口: 3306, 密码: ${MYSQL_ROOT_PASSWORD}) - Redis: ${REDIS_CONTAINER} (端口: 6379) - Nacos: ${NACOS_CONTAINER} (端口: 8848) ### 应用服务 (新部署) - 网关服务: emotion-gateway (端口: 9000) - AI服务: emotion-ai (端口: 9002) - 用户服务: emotion-user (端口: 9001) - Nginx: 系统服务 (端口: 80) ## 访问地址 - 前端应用: http://${SERVER_IP}/ - API网关: http://${SERVER_IP}:9000/ - Nacos控制台: http://${SERVER_IP}:8848/nacos (nacos/nacos) ## 目录结构 - 应用JAR包: ${BUILDS_DIR}/*.jar - 前端文件: ${WEB_DIR}/ - Docker配置: ${BUILDS_DIR}/docker-compose.yml - 应用日志: /data/logs/app/ - Nginx日志: /var/log/nginx/ ## 管理命令 \`\`\`bash # 查看所有容器状态 docker ps # 查看应用日志 docker logs -f emotion-gateway docker logs -f emotion-ai docker logs -f emotion-user # 重启应用服务 cd ${BUILDS_DIR} docker-compose restart # 更新应用 docker-compose down # 替换JAR文件 docker-compose up -d # 重启Nginx systemctl restart nginx # 重启基础服务 docker restart ${MYSQL_CONTAINER} ${REDIS_CONTAINER} ${NACOS_CONTAINER} \`\`\` ## 故障排除 \`\`\`bash # 检查服务状态 docker ps systemctl status nginx # 检查端口占用 netstat -tlnp | grep -E ':(80|3306|6379|8848|9000|9001|9002)' # 查看详细日志 docker logs emotion-gateway docker logs emotion-ai docker logs emotion-user # 重新部署应用 cd ${BUILDS_DIR} docker-compose down docker-compose up -d \`\`\` EOF # 上传部署记录 remote_copy "/tmp/deployment_config.md" "${CONFIG_FILE}" rm /tmp/deployment_config.md log_info "部署记录已保存到: ${CONFIG_FILE}" } # 显示部署结果 show_deployment_result() { echo "" echo "🎉 情绪博物馆部署完成!" echo "" echo "📱 访问地址:" echo " 前端应用: http://${SERVER_IP}/" echo " API网关: http://${SERVER_IP}:9000/" echo " Nacos: http://${SERVER_IP}:8848/nacos" echo "" echo "📁 重要文件:" echo " 部署记录: ${CONFIG_FILE}" echo " 应用目录: ${BUILDS_DIR}/" echo " 前端目录: ${WEB_DIR}/" echo "" echo "🔧 管理命令:" echo " ssh ${SERVER_USER}@${SERVER_IP}" echo " docker ps" echo " docker logs -f emotion-gateway" echo "" echo "⚠️ 注意事项:" echo " 1. 基础服务(MySQL/Redis/Nacos)使用现有Docker容器" echo " 2. 应用服务已部署为新的Docker容器" echo " 3. 请检查防火墙设置,确保端口80可访问" echo "" } # 主部署流程 main() { echo "🚀 开始部署情绪博物馆应用服务..." echo "🔍 检测到服务器已有MySQL/Redis/Nacos容器,将使用现有基础设施" echo "" check_server_connection check_existing_services setup_directories upload_artifacts import_database create_app_configs create_docker_compose setup_nginx start_application_services wait_for_services health_check show_logs create_deployment_record show_deployment_result } # 命令行参数处理 case "${1:-}" in "check") check_server_connection check_existing_services ;; "deploy-app") log_info "仅部署应用服务..." check_server_connection check_existing_services upload_artifacts create_app_configs create_docker_compose start_application_services wait_for_services health_check ;; "health") check_server_connection health_check ;; "logs") check_server_connection show_logs ;; *) main ;; esac