diff --git a/.env.example b/.env.example index 3bb716f..9e69bf6 100644 --- a/.env.example +++ b/.env.example @@ -111,8 +111,20 @@ OPS_PORT=48085 # ============ 健康检查配置 ============ HEALTH_CHECK_INTERVAL=30s HEALTH_CHECK_TIMEOUT=10s -HEALTH_CHECK_RETRIES=3 -HEALTH_CHECK_START_PERIOD=60s +HEALTH_CHECK_RETRIES=5 +HEALTH_CHECK_START_PERIOD=90s + +# ============ XXL-JOB 配置(暂时注释)============ +# 如需部署 XXL-JOB Admin,取消以下注释 +# XXL_JOB_ADMIN_ADDRESSES=http://127.0.0.1:9090/xxl-job-admin +# XXL_JOB_ACCESS_TOKEN=default_token + +# ============ 微信配置(如果需要微信支付功能) ============ +# 如果不使用微信支付,保持这些为空或注释掉 +# WX_MP_APP_ID= +# WX_MP_SECRET= +# WX_MP_TOKEN= +# WX_MP_AES_KEY= # ============ IoT Gateway 特有配置 ============ # 注意: iot-gateway 已支持 Nacos,同时保留环境变量配置作为补充 diff --git a/Jenkinsfile b/Jenkinsfile index 1b03aa3..e74c1c9 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -114,18 +114,59 @@ pipeline { steps { script { def services = env.SERVICES_TO_BUILD.split(',') as List - - // 部署也可以并行 - def parallelDeploys = [:] - services.each { service -> - parallelDeploys[service] = { - deployService(service) - } + + // 按依赖顺序排序 + def deployOrder = [ + 'viewsh-gateway', + 'viewsh-module-system-server', + 'viewsh-module-infra-server', + 'viewsh-module-iot-server', + 'viewsh-module-iot-gateway' + ] + + def sortedServices = services.sort { a, b -> + deployOrder.indexOf(a) <=> deployOrder.indexOf(b) + } + + // 串行部署(保证依赖关系) + sortedServices.each { service -> + deployService(service) + } + + echo "🚀 All services deployed successfully!" + } + } + } + + stage('Final Health Check') { + when { + allOf { + expression { env.SERVICES_TO_BUILD != '' } + branch 'master' + } + } + steps { + script { + // 验证所有核心服务健康 + def coreContainers = ['aiot-gateway', 'aiot-system-server', 'aiot-infra-server', 'aiot-iot-server', 'aiot-iot-gateway'] + + coreContainers.each { container -> + sh """ + ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 root@${DEPLOY_HOST} ' + echo "Checking ${container}..." + STATUS=\$(docker inspect --format="{{.State.Health.Status}}" ${container} 2>/dev/null || echo "not_found") + if [ "\$STATUS" = "healthy" ]; then + echo "✅ ${container} is healthy" + elif [ "\$STATUS" = "not_found" ]; then + echo "⚠️ ${container} not found (may not be deployed)" + else + echo "❌ ${container} is \$STATUS" + docker logs --tail 50 ${container} + exit 1 + fi + ' + """ } - - parallel parallelDeploys - - echo "🚀 Deployment completed!" } } } @@ -255,20 +296,36 @@ def deployService(String service) { ' """ - // 健康检查 + // 健康检查(增加超时和更好的错误处理) def containerName = getContainerName(service) sh """ ssh ${sshOpts} root@${DEPLOY_HOST} ' - for i in 1 2 3 4 5 6 7 8 9 10 11 12; do + echo "Waiting for ${service} to be healthy..." + for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20; do STATUS=\$(docker inspect --format="{{.State.Health.Status}}" ${containerName} 2>/dev/null || echo "starting") - [ "\$STATUS" = "healthy" ] && echo "${service} is healthy" && exit 0 + + if [ "\$STATUS" = "healthy" ]; then + echo "✅ ${service} is healthy" + exit 0 + elif [ "\$STATUS" = "unhealthy" ]; then + echo "❌ ${service} is unhealthy" + echo "=== Last 100 lines of logs ===" + docker logs --tail 100 ${containerName} + exit 1 + fi + + echo "⏳ ${service} status: \$STATUS (\$((i*10))s/200s)" sleep 10 done - echo "${service} health check timeout" + + echo "❌ ${service} health check timeout after 200s" + echo "=== Full logs ===" + docker logs ${containerName} + exit 1 ' """ - - echo "✅ ${service} deployed" + + echo "✅ ${service} deployed successfully" } def getContainerName(String service) { diff --git a/docker-compose.core.yml b/docker-compose.core.yml index 7958e14..c6f95f9 100644 --- a/docker-compose.core.yml +++ b/docker-compose.core.yml @@ -27,6 +27,7 @@ services: environment: JAVA_OPTS: "-Xms${GATEWAY_JVM_XMS} -Xmx${GATEWAY_JVM_XMX} ${JVM_COMMON_OPTS}" SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE} + SPRING_AUTOCONFIGURE_EXCLUDE: com.binarywang.spring.starter.wxjava.mp.config.WxMpServiceAutoConfiguration,com.binarywang.spring.starter.wxjava.mp.config.WxMpAutoConfiguration NACOS_SERVER_ADDR: ${NACOS_HOST}:${NACOS_PORT} REDIS_HOST: ${REDIS_HOST} REDIS_PORT: ${REDIS_PORT} @@ -41,11 +42,11 @@ services: memory: ${GATEWAY_MEMORY_LIMIT} cpus: '1.0' healthcheck: - test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://172.17.16.14:48080/actuator/health"] - interval: ${HEALTH_CHECK_INTERVAL} - timeout: ${HEALTH_CHECK_TIMEOUT} - retries: ${HEALTH_CHECK_RETRIES} - start_period: ${HEALTH_CHECK_START_PERIOD} + test: ["CMD", "curl", "-f", "http://localhost:48080/actuator/health"] + interval: 30s + timeout: 10s + retries: 5 + start_period: 90s viewsh-module-system-server: image: ${REGISTRY_HOST}/viewsh-module-system-server:${IMAGE_TAG} @@ -55,6 +56,7 @@ services: environment: JAVA_OPTS: "-Xms${SYSTEM_JVM_XMS} -Xmx${SYSTEM_JVM_XMX} ${JVM_COMMON_OPTS}" SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE} + SPRING_AUTOCONFIGURE_EXCLUDE: com.binarywang.spring.starter.wxjava.mp.config.WxMpServiceAutoConfiguration NACOS_SERVER_ADDR: ${NACOS_HOST}:${NACOS_PORT} MYSQL_HOST: ${MYSQL_HOST} MYSQL_PORT: ${MYSQL_PORT} @@ -74,11 +76,14 @@ services: memory: ${SYSTEM_MEMORY_LIMIT} cpus: '1.0' healthcheck: - test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://172.17.16.14:48081/actuator/health"] - interval: ${HEALTH_CHECK_INTERVAL} - timeout: ${HEALTH_CHECK_TIMEOUT} - retries: ${HEALTH_CHECK_RETRIES} - start_period: ${HEALTH_CHECK_START_PERIOD} + test: ["CMD", "curl", "-f", "http://localhost:48081"] + interval: 30s + timeout: 10s + retries: 5 + start_period: 90s + depends_on: + viewsh-gateway: + condition: service_healthy viewsh-module-infra-server: image: ${REGISTRY_HOST}/viewsh-module-infra-server:${IMAGE_TAG} @@ -88,6 +93,10 @@ services: environment: JAVA_OPTS: "-Xms${INFRA_JVM_XMS} -Xmx${INFRA_JVM_XMX} ${JVM_COMMON_OPTS}" SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE} + SPRING_AUTOCONFIGURE_EXCLUDE: com.binarywang.spring.starter.wxjava.mp.config.WxMpServiceAutoConfiguration + # XXL-JOB 配置(暂时注释,等部署 XXL-JOB Admin 后启用) + # XXL_JOB_ADMIN_ADDRESSES: ${XXL_JOB_ADMIN_ADDRESSES:-http://127.0.0.1:9090/xxl-job-admin} + # XXL_JOB_ACCESS_TOKEN: ${XXL_JOB_ACCESS_TOKEN:-default_token} NACOS_SERVER_ADDR: ${NACOS_HOST}:${NACOS_PORT} MYSQL_HOST: ${MYSQL_HOST} MYSQL_PORT: ${MYSQL_PORT} @@ -107,11 +116,14 @@ services: memory: ${INFRA_MEMORY_LIMIT} cpus: '1.0' healthcheck: - test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://172.17.16.14:48082/actuator/health"] - interval: ${HEALTH_CHECK_INTERVAL} - timeout: ${HEALTH_CHECK_TIMEOUT} - retries: ${HEALTH_CHECK_RETRIES} - start_period: ${HEALTH_CHECK_START_PERIOD} + test: ["CMD", "curl", "-f", "http://localhost:48082"] + interval: 30s + timeout: 10s + retries: 5 + start_period: 90s + depends_on: + viewsh-module-system-server: + condition: service_healthy viewsh-module-iot-server: image: ${REGISTRY_HOST}/viewsh-module-iot-server:${IMAGE_TAG} @@ -121,6 +133,7 @@ services: environment: JAVA_OPTS: "-Xms${IOT_SERVER_JVM_XMS} -Xmx${IOT_SERVER_JVM_XMX} ${JVM_COMMON_OPTS}" SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE} + SPRING_AUTOCONFIGURE_EXCLUDE: com.binarywang.spring.starter.wxjava.mp.config.WxMpServiceAutoConfiguration NACOS_SERVER_ADDR: ${NACOS_HOST}:${NACOS_PORT} MYSQL_HOST: ${MYSQL_HOST} MYSQL_PORT: ${MYSQL_PORT} @@ -141,11 +154,14 @@ services: memory: ${IOT_SERVER_MEMORY_LIMIT} cpus: '1.5' healthcheck: - test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://172.17.16.14:48083/actuator/health"] - interval: ${HEALTH_CHECK_INTERVAL} - timeout: ${HEALTH_CHECK_TIMEOUT} - retries: ${HEALTH_CHECK_RETRIES} - start_period: ${HEALTH_CHECK_START_PERIOD} + test: ["CMD", "curl", "-f", "http://localhost:48083"] + interval: 30s + timeout: 10s + retries: 5 + start_period: 90s + depends_on: + viewsh-module-infra-server: + condition: service_healthy viewsh-module-iot-gateway: image: ${REGISTRY_HOST}/viewsh-module-iot-gateway:${IMAGE_TAG} @@ -155,6 +171,7 @@ services: environment: JAVA_OPTS: "-Xms${IOT_GATEWAY_JVM_XMS} -Xmx${IOT_GATEWAY_JVM_XMX} ${JVM_COMMON_OPTS}" SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE} + SPRING_AUTOCONFIGURE_EXCLUDE: com.binarywang.spring.starter.wxjava.mp.config.WxMpServiceAutoConfiguration # Nacos 配置 NACOS_SERVER_ADDR: ${NACOS_HOST}:${NACOS_PORT} NACOS_USERNAME: ${NACOS_USERNAME} @@ -219,11 +236,14 @@ services: memory: ${IOT_GATEWAY_MEMORY_LIMIT} cpus: '1.5' healthcheck: - test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://172.17.16.14:48084/actuator/health"] - interval: ${HEALTH_CHECK_INTERVAL} - timeout: ${HEALTH_CHECK_TIMEOUT} - retries: ${HEALTH_CHECK_RETRIES} - start_period: ${HEALTH_CHECK_START_PERIOD} + test: ["CMD", "curl", "-f", "http://localhost:48084"] + interval: 30s + timeout: 10s + retries: 5 + start_period: 90s + depends_on: + viewsh-module-iot-server: + condition: service_healthy viewsh-module-ops-server: image: ${REGISTRY_HOST}/viewsh-module-ops-server:${IMAGE_TAG} @@ -233,6 +253,7 @@ services: environment: JAVA_OPTS: "-Xms${OPS_JVM_XMS} -Xmx${OPS_JVM_XMX} ${JVM_COMMON_OPTS}" SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE} + SPRING_AUTOCONFIGURE_EXCLUDE: com.binarywang.spring.starter.wxjava.mp.config.WxMpServiceAutoConfiguration NACOS_SERVER_ADDR: ${NACOS_HOST}:${NACOS_PORT} MYSQL_HOST: ${MYSQL_HOST} MYSQL_PORT: ${MYSQL_PORT} @@ -251,8 +272,34 @@ services: memory: ${OPS_MEMORY_LIMIT} cpus: '0.5' healthcheck: - test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://172.17.16.14:48085/actuator/health"] - interval: ${HEALTH_CHECK_INTERVAL} - timeout: ${HEALTH_CHECK_TIMEOUT} - retries: ${HEALTH_CHECK_RETRIES} - start_period: ${HEALTH_CHECK_START_PERIOD} + test: ["CMD", "curl", "-f", "http://localhost:48085"] + interval: 30s + timeout: 10s + retries: 5 + start_period: 90s + depends_on: + viewsh-module-infra-server: + condition: service_healthy + + # ============ XXL-JOB Admin (可选,暂时注释) ============ + # 如需部署 XXL-JOB Admin,取消以下注释 + # xxl-job-admin: + # image: xuxueli/xxl-job-admin:2.4.0 + # container_name: aiot-xxl-job-admin + # restart: unless-stopped + # network_mode: host + # environment: + # TZ: ${TZ} + # PARAMS: > + # --spring.datasource.url=jdbc:mysql://${MYSQL_HOST}:${MYSQL_PORT}/${MYSQL_DATABASE}?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true + # --spring.datasource.username=${MYSQL_USER} + # --spring.datasource.password=${MYSQL_PASSWORD} + # --xxl.job.accessToken=${XXL_JOB_ACCESS_TOKEN:-default_token} + # volumes: + # - app-logs:/data/applogs + # healthcheck: + # test: ["CMD", "curl", "-f", "http://localhost:9090/xxl-job-admin"] + # interval: 30s + # timeout: 10s + # retries: 5 + # start_period: 60s diff --git a/viewsh-module-infra/viewsh-module-infra-server/src/main/resources/application-prod.yaml b/viewsh-module-infra/viewsh-module-infra-server/src/main/resources/application-prod.yaml index 6b500a1..57937af 100644 --- a/viewsh-module-infra/viewsh-module-infra-server/src/main/resources/application-prod.yaml +++ b/viewsh-module-infra/viewsh-module-infra-server/src/main/resources/application-prod.yaml @@ -14,6 +14,8 @@ spring: config: namespace: ${NACOS_NAMESPACE:} group: ${NACOS_GROUP:DEFAULT_GROUP} + lifecycle: + timeout-per-shutdown-phase: 30s # 增加优雅关闭超时时间 --- #################### 数据库相关配置 #################### spring: @@ -137,3 +139,21 @@ viewsh: enable: true security: mock-enable: false + +--- #################### Quartz 优雅关闭配置 #################### + +spring: + quartz: + properties: + org: + quartz: + scheduler: + makeSchedulerThreadDaemon: true + shutdownHook: clean_shutdown + threadPool: + threadNamePrefix: QuartzSchedulerThread + threadsInheritContextClassLoaderOfInitializingThread: true + plugin: + shutdownhook: + class: org.quartz.plugins.management.ShutdownHookPlugin + cleanShutdown: true