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环境配置
This commit is contained in:
Executable
+217
@@ -0,0 +1,217 @@
|
||||
#!/bin/bash
|
||||
|
||||
# emotion-gateway 单独部署脚本
|
||||
# 作者: emotion-museum
|
||||
# 日期: 2025-07-18
|
||||
|
||||
set -e
|
||||
|
||||
# 配置变量
|
||||
SERVICE_NAME="emotion-gateway"
|
||||
SERVICE_PORT="19000"
|
||||
REMOTE_HOST="root@47.111.10.27"
|
||||
REMOTE_BUILD_DIR="/data/builds"
|
||||
REMOTE_DOCKER_COMPOSE_DIR="/data/docker"
|
||||
PROFILE="test"
|
||||
PROJECT_NAME="emotion-museum"
|
||||
|
||||
# 颜色输出
|
||||
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"
|
||||
}
|
||||
|
||||
# 检查远程服务器连接
|
||||
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
|
||||
}
|
||||
|
||||
# 构建服务
|
||||
build_service() {
|
||||
log_info "构建服务: $SERVICE_NAME"
|
||||
|
||||
# 构建父项目依赖
|
||||
cd ..
|
||||
mvn clean install -DskipTests -q
|
||||
cd $SERVICE_NAME
|
||||
|
||||
# 构建当前服务
|
||||
if mvn clean package -DskipTests -Ptest -q; then
|
||||
log_success "服务 $SERVICE_NAME 构建成功"
|
||||
else
|
||||
log_error "服务 $SERVICE_NAME 构建失败"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 创建Dockerfile
|
||||
create_dockerfile() {
|
||||
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/*
|
||||
|
||||
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"
|
||||
}
|
||||
|
||||
# 部署服务
|
||||
deploy_service() {
|
||||
log_info "开始部署服务: $SERVICE_NAME"
|
||||
|
||||
# 检查jar包
|
||||
local jar_file="target/${SERVICE_NAME}-1.0.0.jar"
|
||||
if [ ! -f "$jar_file" ]; then
|
||||
log_error "JAR包不存在: $jar_file"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 创建远程目录
|
||||
ssh $REMOTE_HOST "
|
||||
mkdir -p $REMOTE_BUILD_DIR
|
||||
mkdir -p $REMOTE_DOCKER_COMPOSE_DIR
|
||||
mkdir -p /data/logs/emotion-museum
|
||||
"
|
||||
|
||||
# 删除旧jar包
|
||||
log_info "删除远程旧jar包"
|
||||
ssh $REMOTE_HOST "rm -f $REMOTE_BUILD_DIR/${SERVICE_NAME}-*.jar"
|
||||
|
||||
# 上传新jar包
|
||||
log_info "上传jar包"
|
||||
if scp "$jar_file" "$REMOTE_HOST:$REMOTE_BUILD_DIR/${SERVICE_NAME}-1.0.0.jar"; then
|
||||
log_success "jar包上传成功"
|
||||
else
|
||||
log_error "jar包上传失败"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 创建Dockerfile
|
||||
create_dockerfile
|
||||
|
||||
# 停止旧容器
|
||||
log_info "停止旧容器"
|
||||
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网络
|
||||
ssh $REMOTE_HOST "docker network create emotion-network 2>/dev/null || true"
|
||||
|
||||
# 构建镜像
|
||||
log_info "构建Docker镜像"
|
||||
ssh $REMOTE_HOST "
|
||||
cd $REMOTE_DOCKER_COMPOSE_DIR
|
||||
docker build -t ${PROJECT_NAME}/${SERVICE_NAME}:latest -f Dockerfile.${SERVICE_NAME} .
|
||||
"
|
||||
|
||||
# 启动容器
|
||||
log_info "启动新容器"
|
||||
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 "等待服务启动..."
|
||||
sleep 15
|
||||
|
||||
# 检查状态
|
||||
if ssh $REMOTE_HOST "docker ps | grep ${SERVICE_NAME}" > /dev/null; then
|
||||
log_success "服务启动成功"
|
||||
|
||||
# 显示日志
|
||||
log_info "服务日志 (最后20行):"
|
||||
ssh $REMOTE_HOST "docker logs --tail 20 ${SERVICE_NAME}"
|
||||
|
||||
# 健康检查
|
||||
log_info "执行健康检查..."
|
||||
sleep 10
|
||||
if ssh $REMOTE_HOST "curl -f -s http://localhost:${SERVICE_PORT}/actuator/health" > /dev/null 2>&1; then
|
||||
log_success "健康检查通过"
|
||||
else
|
||||
log_warning "健康检查失败,服务可能仍在启动中"
|
||||
fi
|
||||
else
|
||||
log_error "服务启动失败"
|
||||
ssh $REMOTE_HOST "docker logs ${SERVICE_NAME}"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 主函数
|
||||
main() {
|
||||
log_info "开始部署 $SERVICE_NAME 服务"
|
||||
log_info "目标服务器: $REMOTE_HOST"
|
||||
log_info "服务端口: $SERVICE_PORT"
|
||||
log_info "部署环境: $PROFILE"
|
||||
|
||||
check_remote_connection
|
||||
build_service
|
||||
deploy_service
|
||||
|
||||
log_success "$SERVICE_NAME 服务部署完成!"
|
||||
log_info "访问地址: http://47.111.10.27:$SERVICE_PORT"
|
||||
}
|
||||
|
||||
# 执行主函数
|
||||
main "$@"
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
package com.emotionmuseum.gateway.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.cors.reactive.CorsWebFilter;
|
||||
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
|
||||
|
||||
/**
|
||||
* CORS配置
|
||||
*/
|
||||
@Configuration
|
||||
public class CorsConfig {
|
||||
|
||||
@Bean
|
||||
public CorsWebFilter corsWebFilter() {
|
||||
CorsConfiguration corsConfiguration = new CorsConfiguration();
|
||||
|
||||
// 允许所有来源
|
||||
corsConfiguration.addAllowedOriginPattern("*");
|
||||
|
||||
// 允许所有请求头
|
||||
corsConfiguration.addAllowedHeader("*");
|
||||
|
||||
// 允许所有请求方法
|
||||
corsConfiguration.addAllowedMethod("*");
|
||||
|
||||
// 不允许携带凭证(这样就可以使用 * 作为来源)
|
||||
corsConfiguration.setAllowCredentials(false);
|
||||
|
||||
// 预检请求的缓存时间
|
||||
corsConfiguration.setMaxAge(3600L);
|
||||
|
||||
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||
source.registerCorsConfiguration("/**", corsConfiguration);
|
||||
|
||||
return new CorsWebFilter(source);
|
||||
}
|
||||
}
|
||||
@@ -35,14 +35,7 @@ spring:
|
||||
locator:
|
||||
enabled: true
|
||||
lower-case-service-id: true
|
||||
# 全局跨域配置
|
||||
globalcors:
|
||||
cors-configurations:
|
||||
'[/**]':
|
||||
allowed-origins: "*"
|
||||
allowed-methods: "*"
|
||||
allowed-headers: "*"
|
||||
allow-credentials: true
|
||||
|
||||
routes:
|
||||
# 用户服务路由 (包含认证功能)
|
||||
- id: emotion-user-route
|
||||
@@ -74,7 +67,7 @@ spring:
|
||||
predicates:
|
||||
- Path=/ai/**
|
||||
filters:
|
||||
- StripPrefix=0
|
||||
- StripPrefix=1
|
||||
|
||||
# WebSocket聊天服务路由
|
||||
- id: emotion-websocket-route
|
||||
|
||||
@@ -35,14 +35,7 @@ spring:
|
||||
locator:
|
||||
enabled: true
|
||||
lower-case-service-id: true
|
||||
# 全局跨域配置
|
||||
globalcors:
|
||||
cors-configurations:
|
||||
'[/**]':
|
||||
allowed-origins: "*"
|
||||
allowed-methods: "*"
|
||||
allowed-headers: "*"
|
||||
allow-credentials: true
|
||||
|
||||
routes:
|
||||
# 用户服务路由 (包含认证功能)
|
||||
- id: emotion-user-route
|
||||
@@ -74,7 +67,7 @@ spring:
|
||||
predicates:
|
||||
- Path=/ai/**
|
||||
filters:
|
||||
- StripPrefix=0
|
||||
- StripPrefix=1
|
||||
|
||||
# WebSocket聊天服务路由
|
||||
- id: emotion-websocket-route
|
||||
|
||||
@@ -35,14 +35,7 @@ spring:
|
||||
locator:
|
||||
enabled: true
|
||||
lower-case-service-id: true
|
||||
# 全局跨域配置
|
||||
globalcors:
|
||||
cors-configurations:
|
||||
"[/**]":
|
||||
allowed-origins: "*"
|
||||
allowed-methods: "*"
|
||||
allowed-headers: "*"
|
||||
allow-credentials: true
|
||||
|
||||
routes:
|
||||
# 用户服务路由 (包含认证功能)
|
||||
- id: emotion-user-route
|
||||
@@ -74,7 +67,7 @@ spring:
|
||||
predicates:
|
||||
- Path=/ai/**
|
||||
filters:
|
||||
- StripPrefix=0
|
||||
- StripPrefix=1
|
||||
|
||||
# WebSocket聊天服务路由
|
||||
- id: emotion-websocket-route
|
||||
|
||||
@@ -62,69 +62,159 @@ spring:
|
||||
enabled: true
|
||||
lower-case-service-id: true
|
||||
routes:
|
||||
# 认证服务路由
|
||||
- id: emotion-auth
|
||||
# 认证服务路由 - API路径
|
||||
- id: emotion-auth-api
|
||||
uri: lb://emotion-auth
|
||||
predicates:
|
||||
- Path=/api/auth/**
|
||||
filters:
|
||||
- StripPrefix=2
|
||||
|
||||
# 用户服务路由
|
||||
- id: emotion-user
|
||||
# 认证服务路由 - 直接路径
|
||||
- id: emotion-auth-direct
|
||||
uri: lb://emotion-auth
|
||||
predicates:
|
||||
- Path=/auth/**
|
||||
filters:
|
||||
- StripPrefix=1
|
||||
|
||||
# 用户服务路由 - API路径
|
||||
- id: emotion-user-api
|
||||
uri: lb://emotion-user
|
||||
predicates:
|
||||
- Path=/api/user/**
|
||||
filters:
|
||||
- StripPrefix=2
|
||||
|
||||
# AI对话服务路由
|
||||
- id: emotion-ai
|
||||
# 用户服务路由 - 直接路径
|
||||
- id: emotion-user-direct
|
||||
uri: lb://emotion-user
|
||||
predicates:
|
||||
- Path=/user/**
|
||||
filters:
|
||||
- StripPrefix=1
|
||||
|
||||
# AI对话服务路由 - API路径
|
||||
- id: emotion-ai-api
|
||||
uri: lb://emotion-ai
|
||||
predicates:
|
||||
- Path=/api/ai/**
|
||||
filters:
|
||||
- StripPrefix=2
|
||||
|
||||
# 情绪记录服务路由
|
||||
- id: emotion-record
|
||||
|
||||
# AI对话服务路由 - 直接路径
|
||||
- id: emotion-ai-direct
|
||||
uri: lb://emotion-ai
|
||||
predicates:
|
||||
- Path=/ai/**
|
||||
filters:
|
||||
- StripPrefix=1
|
||||
|
||||
# WebSocket聊天服务路由 - API路径
|
||||
- id: emotion-websocket-api
|
||||
uri: lb://emotion-websocket
|
||||
predicates:
|
||||
- Path=/api/websocket/**
|
||||
filters:
|
||||
- StripPrefix=2
|
||||
|
||||
# WebSocket聊天服务路由 - 直接路径
|
||||
- id: emotion-websocket-direct
|
||||
uri: lb://emotion-websocket
|
||||
predicates:
|
||||
- Path=/websocket/**
|
||||
filters:
|
||||
- StripPrefix=1
|
||||
|
||||
# 情绪记录服务路由 - API路径
|
||||
- id: emotion-record-api
|
||||
uri: lb://emotion-record
|
||||
predicates:
|
||||
- Path=/api/record/**
|
||||
filters:
|
||||
- StripPrefix=2
|
||||
|
||||
# 成长课题服务路由
|
||||
- id: emotion-growth
|
||||
|
||||
# 情绪记录服务路由 - 直接路径
|
||||
- id: emotion-record-direct
|
||||
uri: lb://emotion-record
|
||||
predicates:
|
||||
- Path=/record/**
|
||||
filters:
|
||||
- StripPrefix=1
|
||||
|
||||
# 成长课题服务路由 - API路径
|
||||
- id: emotion-growth-api
|
||||
uri: lb://emotion-growth
|
||||
predicates:
|
||||
- Path=/api/growth/**
|
||||
filters:
|
||||
- StripPrefix=2
|
||||
|
||||
# 地图探索服务路由
|
||||
- id: emotion-explore
|
||||
|
||||
# 成长课题服务路由 - 直接路径
|
||||
- id: emotion-growth-direct
|
||||
uri: lb://emotion-growth
|
||||
predicates:
|
||||
- Path=/growth/**
|
||||
filters:
|
||||
- StripPrefix=1
|
||||
|
||||
# 地图探索服务路由 - API路径
|
||||
- id: emotion-explore-api
|
||||
uri: lb://emotion-explore
|
||||
predicates:
|
||||
- Path=/api/explore/**
|
||||
filters:
|
||||
- StripPrefix=2
|
||||
|
||||
# 成就奖励服务路由
|
||||
- id: emotion-reward
|
||||
|
||||
# 地图探索服务路由 - 直接路径
|
||||
- id: emotion-explore-direct
|
||||
uri: lb://emotion-explore
|
||||
predicates:
|
||||
- Path=/explore/**
|
||||
filters:
|
||||
- StripPrefix=1
|
||||
|
||||
# 成就奖励服务路由 - API路径
|
||||
- id: emotion-reward-api
|
||||
uri: lb://emotion-reward
|
||||
predicates:
|
||||
- Path=/api/reward/**
|
||||
filters:
|
||||
- StripPrefix=2
|
||||
|
||||
# 统计分析服务路由
|
||||
- id: emotion-stats
|
||||
|
||||
# 成就奖励服务路由 - 直接路径
|
||||
- id: emotion-reward-direct
|
||||
uri: lb://emotion-reward
|
||||
predicates:
|
||||
- Path=/reward/**
|
||||
filters:
|
||||
- StripPrefix=1
|
||||
|
||||
# 统计分析服务路由 - API路径
|
||||
- id: emotion-stats-api
|
||||
uri: lb://emotion-stats
|
||||
predicates:
|
||||
- Path=/api/stats/**
|
||||
filters:
|
||||
- StripPrefix=2
|
||||
|
||||
# 统计分析服务路由 - 直接路径
|
||||
- id: emotion-stats-direct
|
||||
uri: lb://emotion-stats
|
||||
predicates:
|
||||
- Path=/stats/**
|
||||
filters:
|
||||
- StripPrefix=1
|
||||
|
||||
# 验证码服务路由 (通过认证服务)
|
||||
- id: captcha-service
|
||||
uri: lb://emotion-auth
|
||||
predicates:
|
||||
- Path=/captcha/**
|
||||
filters:
|
||||
- StripPrefix=1
|
||||
|
||||
|
||||
|
||||
# 全局过滤器 (暂时禁用,需要实现对应的过滤器类)
|
||||
# default-filters:
|
||||
|
||||
Reference in New Issue
Block a user