jenkins使用pipeline实现滚动发布

jenkins使用pipeline实现滚动发布

需求:
1、服务有多个,发布的时候,不能影响用户正常使用

脚本如下:

pipeline 复制代码
pipeline {
    agent any
    
    // 定义全局变量,请根据你的实际环境修改
    environment {
        NACOS_SERVER = "http://nacos.com.cn"
        NACOS_USER = "nacos"
        NACOS_PASSWORD = "nacos"
        SERVICE_NAME = "bdo-pm-project"
        NACOS_NAMESPACE_ID = "a7a24094-1599-4126-a72c-161d5b05c955"
        
        // 服务部署信息(IP、端口、部署路径)
        SERVER_PORT = "11002"
        SERVER_1_IP = "10.17.10.67"
        SERVER_2_IP = "10.17.10.66"
        SERVER_PATH = "/home/bdo.dev"
        SERVER_USER = "username"
        
        // 本地项目路径和Git仓库
        GIT_REPO_URL = "https://github.com/**.git"
        JAVA_HOME = tool name: 'jdk8', type: 'hudson.model.JDK'
        WECOM_WEBHOOK_URL = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key="
    }

    stages {
        // 阶段1:编译项目 (对应你的步骤2)
        stage('编译项目') {
            steps {
                echo "开始拉取代码并编译..."
                // 拉取代码
                git branch: 'main',
                    credentialsId: 'fang_gitlab',
                    url: 'https://git**.git'
                    
                echo "代码拉取完成,开始编译。。。"
                // 执行Maven编译
                sh '''
                    export PATH=$JAVA_HOME/bin:$PATH
                    mvn clean package -Dmaven.test.skip=true
                '''
                echo "编译完成"
            }
        }

        // 阶段2:滚动发布 - 服务1 (对应你的步骤1, 3)
        stage('滚动发布 - 服务1') {
            steps {
                script {
                    // 1. 调用Nacos接口,将服务1下线
                    deregisterFromNacos(SERVER_1_IP, SERVER_PORT)
                    
                    // 2. 替换jar包并重启服务1
                    deployAndRestartService(SERVER_USER, SERVER_1_IP)
                    
                    // 3. 等待服务1重新注册到Nacos并健康检查通过
                    waitForServiceUp(SERVER_1_IP, SERVER_PORT)
                }
            }
        }

        // 阶段3:滚动发布 - 服务2 (对应你的步骤4, 5)
        stage('滚动发布 - 服务2') {
            steps {
                script {
                    // 1. 调用Nacos接口,将服务2下线
                    deregisterFromNacos(SERVER_2_IP, SERVER_PORT)
                    
                    // 2. 替换jar包并重启服务2
                    deployAndRestartService(SERVER_USER, SERVER_2_IP)
                    
                    // 3. 等待服务2重新注册到Nacos并健康检查通过
                    waitForServiceUp(SERVER_2_IP, SERVER_PORT)
                }
            }
        }
    }
    
    post {
        success {
            echo "🎉 滚动发布全部完成!"
        }
        failure {
            echo "❌ 发布过程中出现异常,请检查日志!"
        }
        always {
            script {
                sendWeComNotification("1")
            }
        }
    }
}

// ================= 自定义函数区域 =================

// 函数:从 Nacos 注销指定实例
def deregisterFromNacos(String ip, String port) {
    echo "正在将实例 ${ip}:${port} 从 Nacos 下线..."
    // 获取 Nacos accessToken
    def authResponse = sh(script: "curl -X POST '${NACOS_SERVER}/nacos/v1/auth/users/login?username=${NACOS_USER}&password=${NACOS_PASSWORD}'", returnStdout: true).trim()
    // 简单的JSON解析获取token(实际生产环境建议使用 readJSON)
    def token = authResponse.substring(authResponse.indexOf('accessToken\":\"') + 14)
    token = token.substring(0, token.indexOf('\"'))
    
    // 调用注销接口
    sh """
        curl -X PUT '${NACOS_SERVER}/nacos/v1/ns/instance' \
        -d 'serviceName=${SERVICE_NAME}' \
        -d 'clusterName=DEFAULT' \
        -d 'groupName=DEFAULT_GROUP' \
        -d 'ip=${ip}' \
        -d 'port=${port}' \
        -d 'ephemeral=true' \
        -d 'weight=1' \
        -d 'enabled=false' \
        -d 'metadata={"region":"SH","env":"uat","preserved.register.source":"SPRING_CLOUD","version":"1.0"}' \
        -d 'namespaceId=${NACOS_NAMESPACE_ID}' \
        -d 'accessToken=${token}'
    """
    echo "实例 ${ip}:${port} 已请求下线,等待 Nacos 缓存刷新..."
    // 强制等待一段时间,确保调用方(如网关或其他微服务)的 Nacos 客户端缓存失效
    sleep time: 20, unit: 'SECONDS'
}

// 函数:远程替换 Jar 包并重启服务
def deployAndRestartService(String user, String serverIp) {
    echo "开始部署到服务器 ${serverIp}..."
    // 1. 传输 jar 包到目标服务器
    sh "scp bdo-pm-project-provider/target/bdo-pm-project-provider.jar ${user}@${serverIp}:${SERVER_PATH}/temp/"

    // 2. 远程执行重启命令 (这里使用简单的 kill 后启动,建议配合 start.sh 脚本)
    sh """
        ssh ${user}@${serverIp} "sh ${SERVER_PATH}/apps/restart.sh bdo-pm-project-provider.jar ${SERVER_PATH}"
    """
    echo "服务器 ${serverIp} 重启命令已发送。"
}

// 函数:等待服务启动并健康检查通过
def waitForServiceUp(String ip, String port) {
    echo "等待服务 ${ip}:${port} 启动并健康检查..."
    echo "等待30秒后再检测服务"
    sleep time: 30, unit: 'SECONDS'
    def maxRetries = 10
    def retryInterval = 5 // 秒
    def success = false
    
    for (int i = 0; i < maxRetries; i++) {
        // 这里假设你的微服务有 /actuator/health 健康检查接口
        def existCode = sh(script: "curl -s --connect-timeout 5 -o /dev/null -w '%{http_code}' http://${ip}:${port}/actuator/health", returnStatus: true)
        if (existCode == 0) {
            echo "✅ 服务 ${ip}:${port} 启动成功且健康检查通过!"
            success = true
            break
        }
        echo "服务尚未就绪,${retryInterval}秒后重试... (${i+1}/${maxRetries})"
        sleep time: retryInterval, unit: 'SECONDS'
    }
    
    if (!success) {
        error "❌ 服务 ${ip}:${port} 启动超时,发布失败!"
    }
}


// ================= 封装的企业微信通知方法 =================
def sendWeComNotification(String onlyFail) {
    // 获取当前构建的真实结果(SUCCESS 或 FAILURE)
    def buildStatus = currentBuild.currentResult
    // 根据构建状态,动态设置消息的 Emoji 图标、文字颜色和标题
    def statusEmoji = (buildStatus == 'SUCCESS') ? '🚀' : '❌'
    def statusText = (buildStatus == 'SUCCESS') ? '发布成功' : '发布失败'
    
    if (buildStatus == 'SUCCESS') {
        if (onlyFail == '1') {
            return
        }
    }
    
    def color = (buildStatus == 'SUCCESS') ? 'info' : 'warning' // 企业微信支持的颜色:info(绿色), warning(橙色)
    
    // 构造 Markdown 格式的消息内容
    // 注意:Groovy 三引号 """ """ 中的变量会自动替换,> 符号在企业微信 Markdown 中需要转义为 &gt;
    def content = """
### ${statusEmoji} ${SERVICE_NAME} ${statusText}
> **项目名称**:${env.JOB_NAME}
> **构建编号**:#${env.BUILD_NUMBER}
> **发布状态**:<font color='${color}'>${statusText}</font>
> **发布时间**:${new Date().format('yyyy-MM-dd HH:mm:ss')}
> **构建详情**:[点击查看完整日志](${currentBuild.absoluteUrl})
""".stripIndent().trim() // stripIndent() 去除每行前多余的缩进空格,trim() 去除首尾空行<websource>source_group_web_1</websource>

    // 构造企业微信机器人要求的 JSON 请求体
    def requestBody = """
{
    "msgtype": "markdown",
    "markdown": {
        "content": "${content}"
    }
}
"""
    // 发送 HTTP POST 请求到企业微信
    try {
        sh '''
            curl -X POST \
            -H "Content-Type: application/json" \
            -d \'''' + requestBody + '''\' \
            ''' + WECOM_WEBHOOK_URL + '''
        '''
        echo "✅ 企业微信通知发送成功!"
    } catch (Exception e) {
        echo "⚠️ 企业微信通知发送失败,请检查 Webhook 地址或网络连通性<websource>source_group_web_2</websource>。错误信息: ${e.getMessage()}"
    }
}
相关推荐
闲猫1 小时前
SSH 黑屏配置试用环境机器,受限bash和自定义shell
运维·ssh·bash
行者-全栈开发1 小时前
SpringBoot CI/CD 流水线实战|Jenkins+GitLab CI,从手动到自动化交付
ci/cd·jenkins·springboot·devops·自动化部署·gitlab ci
北执南念1 小时前
Docker实用篇2
运维·docker·容器
古道青阳1 小时前
构建工业级短视频生成流水线:Playwright + FFmpeg 自动化指南
运维·自动化·音视频
Ameilide1 小时前
Linux 应用软件编程 文件编程(IO)
linux·运维·服务器
枕星而眠2 小时前
Linux IO多路复用:select、poll、epoll 核心原理与进阶实战
linux·运维·服务器·c++·后端
Rain5092 小时前
GitLab-Runner + AI 代码审查服务 + 远程大模型 全套部署运维实战
linux·运维·人工智能·python·ci/cd·gitlab·ai编程
只看不学2 小时前
jenkins+Kubernetes实现流水线CI/CD 接口自动化测试
运维·ci/cd·jenkins
Black蜡笔小新2 小时前
零代码自动化企业私有化AI训练推理一体工作站DLTM训推一体化助力企业自主掌控AI能力
运维·人工智能·自动化