jenkins pipeline slave部署nacos负载均衡平滑升级

1、背景

公司有一个项目使用nacos作为服务注册和配置中心。老板希望项目更新升级过程尽量不影响用户使用,特申请一台服务器作负载均衡服务器。

服务器部署内容
172.16.0.14nginx、nacos、mysql、项目服务
172.16.0.13jenkins、项目服务

一共就两台服务器资源有限且项目的服务较多,故没有搭建harbor做镜像仓库。172.16.0.14作为jenkins slave执行代码获取、项目构建、部署。

2、jenkins配置

jenkin安装配置不作介绍,由于在pipeline执行过程环境变量是统一的,所以两台服务器上maven、jdk版本必须一致。
下面重点介绍slave配置:

2.1 slave新增节点

点击 manage jenkins(管理jenkins)–>manage nodes and clouds(节点管理)–>新增节点
在这里插入图片描述
输入节点名称,选中"Permanent Agent",点确定
在这里插入图片描述

2.2 配置slave节点

在这里插入图片描述
在这里插入图片描述

2.3服务器配置SSH免密登录

在172.16.0.13(master)执行命来 ssh-keygen,一路点enter生成SSH密钥。在用户的家目录下生成隐藏文件夹.ssh及密钥文件
[ .ssh]# ls
authorized_keys id_rsa id_rsa.pub known_hosts
公钥拷贝到authorized_keys文件
]# cat id_rsa.pub >> authorized_keys
]# chmod 600 authorized_keys
将shh密钥发给slave主机,方便从主机上免密登录
]# $ ssh-copy-id root@172.16.0.13

2.4 jenkins配置SSH凭据

点击manage jenkins(管理jenkins)–>manage credentials(凭据管理)–>添加凭据
在这里插入图片描述

类型选择:username with password在这里插入图片描述

2.5启动slave节点

slave没有启动前此处有启动按钮,配置正确点启动即可启动slave节点
在这里插入图片描述
注:需要安装SSH插件,开始没有安装SSH插件。slave可以启动,能运行系统命令。但是执行git拉取代码报错,后来安装插件后搞定。具体哪些插件是必须的不记得了,当时找到疑似的都安装了
在这里插入图片描述

3、nacos服务

网关actuator,可以获取各种状态的参数
http://ip:port/actuator,此处ip:port是网关的地址

nacos服务下线,此处172.16.0.13:39001是具体某一个服务的地址,status=DOWN表示将该服务下线
curl -X ‘POST’ ‘http://172.16.0.13:39001/actuator/service-registry?status=DOWN’ -H ‘Content-Type: application/vnd.spring-boot.actuator.v2+json;charset=UTF-8’
转:微服务架构:Nacos本地缓存 PK 微服务优雅下线

获取服务状态,此处ip:port是nacos的地址
http://ip:port/nacos/v1/ns/instance/list?serviceName=wekb-print
nacos接口文档: https://nacos.io/zh-cn/docs/what-is-nacos.html

更新服务流程如下:
1、将服务A的第一个服务A1下线,更新;
2、将服务A的第二个服务A2下线,更新;
其中需要注意的是服务注册到nacos后需要时间一点时间才是健康状态。如果服务更新速度太快A1还没有恢复正常,此时A2下线会造成服务A无法对外提供服务。每个服务更新都需要计算好时间,可能更新A2时需要先sleep一小会。

4、项目pipeline

服务如果较多,需要使用ansible部署项目可以参考:jenkins pipeline harbor docker ansible-playbook部署maven项目实战

pipeline {
    agent {
        label "master" ##此处需要写节点agent,具体stage执行时用不同agent
    }
    tools { ##定义maven jdk环境变量
        maven 'maven'
        jdk   'JDK_1.8'
    }
    environment {
        DOCKER_TAG=createVersion()
    }
    stages {
        stage('master git pull'){
        	## 先在master节点执行任务,每个stage都指定agent
            agent {
                label "master"
            }
            steps {
                sh 'pwd'
                git branch: "${params.branch}" ,credentialsId: 'wekb', url: '你的项目git地址'
            }
        }
        stage('master mvn install') {
            agent {
                label "master"
            }
            steps {
                script {
                    try {
                        sh 'pwd'
                        ## 微服务较多,有些微服务在子目录中,maven构建时需要指定父目录
                        if (params.DOCKER_NAME in ['blade-system', 'blade-user'] ){
                            sh "mvn clean install -pl blade-service/${params.DOCKER_NAME} -Dmaven.repo.local=/root/.m2/repository -Dmaven.test.skip=true -am"
                        }
                        else if (params.DOCKER_NAME in ['blade-resource', 'blade-xxljob-admin', 'blade-xxljob'] ){
                            sh "mvn clean install -pl blade-ops/${params.DOCKER_NAME} -Dmaven.repo.local=/root/.m2/repository -Dmaven.test.skip=true -am"
                        }
                        else {
                            sh "mvn clean install -pl ${params.DOCKER_NAME} -Dmaven.repo.local=/root/.m2/repository -Dmaven.test.skip=true -am"
                        }
                        currentBuild.result="SUCCESS"
                    } catch (e) {
                        currentBuild.result="FAILURE"
                        throw e
                    } finally {
                    }
                }
            }
        }
        stage('master image build & run container') {
            agent {
                label "master"
            }
            steps {
                script {
                    try {
                    	## 有些服务需要到子目录build镜像
                        if (params.DOCKER_NAME in ['blade-system', 'blade-user'] ){
                            sh "cd ./blade-service/${params.DOCKER_NAME} && docker build -t ${params.DOCKER_NAME}:${DOCKER_TAG} ."
                        }
                        else if (params.DOCKER_NAME in ['blade-resource', 'blade-xxljob-admin', 'blade-xxljob'] ){
                            sh "cd ./blade-ops/${params.DOCKER_NAME} && docker build -t ${params.DOCKER_NAME}:${DOCKER_TAG} ."
                        }
                        else {
                            sh "cd ./${params.DOCKER_NAME} && docker build -t ${params.DOCKER_NAME}:${DOCKER_TAG} ."
                        }
                        currentBuild.result="SUCCESS"
                        
                        if (params.DOCKER_NAME=='blade-auth'){
                         ## 更新服务前先将服务下线
                            sh " curl -X 'POST' 'http://172.16.0.13:8100/actuator/service-registry?status=DOWN' -H 'Content-Type: application/vnd.spring-boot.actuator.v2+json;charset=UTF-8' || true"
                            ## 再删除服务的容器
                            sh "docker rm -f ${params.DOCKER_NAME}||true"
                            ## 最后启动服务的容器
                            sh """docker run -p 8100:8100 -p 9106:9106   \
                            --name=${params.DOCKER_NAME}  \
                            -v /data/config/${params.DOCKER_NAME}:/data/config/${params.DOCKER_NAME}  \
                            -v /data/logs/${params.DOCKER_NAME}:/data/logs/${params.DOCKER_NAME} \
                            -v /etc/localtime:/etc/localtime:ro \
                            --network=host \
                            -e TZ=Asia/Shanghai \
                            -d ${params.DOCKER_NAME}:${DOCKER_TAG} \
                            --spring.profiles.active=test \
                            --logging.config=/data/config/${params.DOCKER_NAME}/logback-prod.xml
                            """
                        }
                    }
                    
                    catch (e) {
                        currentBuild.result="FAILURE"
                        throw e
                    } 
                    finally {
                        sh "docker images | grep ${params.DOCKER_NAME} | sed -n \'2, \$p\' | awk \'{print \$3}\' | xargs docker rmi ||true" 
                    }
                }
            }
        }
        stage('slave git pull'){
        	## 再在slave节点执行任务,每个stage都指定agent
            agent {
                label "qwin226"
            }
            steps {
                sh 'pwd'
                git branch: "${params.branch}" ,credentialsId: 'wekb', url: '你的项目git地址'
            }
        }
        stage('slave mvn install') {
            agent {
                label "qwin226"
            }
            steps {
                script {
                    try {
                        sh 'pwd'
                        if (params.DOCKER_NAME in ['blade-system', 'blade-user'] ){
                            sh "mvn clean install -pl blade-service/${params.DOCKER_NAME} -Dmaven.repo.local=/root/.m2/repository -Dmaven.test.skip=true -am"
                        }
                        else if (params.DOCKER_NAME in ['blade-resource', 'blade-xxljob-admin', 'blade-xxljob'] ){
                            sh "mvn clean install -pl blade-ops/${params.DOCKER_NAME} -Dmaven.repo.local=/root/.m2/repository -Dmaven.test.skip=true -am"
                        }
                        else {
                            sh "mvn clean install -pl ${params.DOCKER_NAME} -Dmaven.repo.local=/root/.m2/repository -Dmaven.test.skip=true -am"
                        }
                        currentBuild.result="SUCCESS"
                    } catch (e) {
                        currentBuild.result="FAILURE"
                        throw e
                    } finally {
                    }
                }
            }
        }
        stage('slave image build & run container') {
            agent {
                label "qwin226"
            }
            steps {
                script {
                    try {
                        if (params.DOCKER_NAME in ['blade-system', 'blade-user'] ){
                            sh "cd ./blade-service/${params.DOCKER_NAME} && docker build -t ${params.DOCKER_NAME}:${DOCKER_TAG} ."
                        }
                        else if (params.DOCKER_NAME in ['blade-resource', 'blade-xxljob-admin', 'blade-xxljob'] ){
                            sh "cd ./blade-ops/${params.DOCKER_NAME} && docker build -t ${params.DOCKER_NAME}:${DOCKER_TAG} ."
                        }
                        else {
                            sh "cd ./${params.DOCKER_NAME} && docker build -t ${params.DOCKER_NAME}:${DOCKER_TAG} ."
                        }
                        currentBuild.result="SUCCESS"
                        
                        if (params.DOCKER_NAME=='blade-auth'){
                        	## 该服务更新速度快,需要先sleep10秒等master节点上的服务恢复健康再更新
                            sh "sleep 10s"
                            sh " curl -X 'POST' 'http://172.16.0.14:8100/actuator/service-registry?status=DOWN' -H 'Content-Type: application/vnd.spring-boot.actuator.v2+json;charset=UTF-8' || true"
                            sh "docker rm -f ${params.DOCKER_NAME}||true"
                            sh """docker run -p 8100:8100 -p 9106:9106   \
                            --name=${params.DOCKER_NAME}  \
                            -v /data/config/${params.DOCKER_NAME}:/data/config/${params.DOCKER_NAME}  \
                            -v /data/logs/${params.DOCKER_NAME}:/data/logs/${params.DOCKER_NAME} \
                            -v /etc/localtime:/etc/localtime:ro \
                            --network=host \
                            -e TZ=Asia/Shanghai \
                            -d ${params.DOCKER_NAME}:${DOCKER_TAG} \
                            --spring.profiles.active=test \
                            --logging.config=/data/config/${params.DOCKER_NAME}/logback-prod.xml
                            """
                        }
                    }
                    
                    catch (e) {
                        currentBuild.result="FAILURE"
                        throw e
                    } 
                    finally {
                        sh "docker images | grep ${params.DOCKER_NAME} | sed -n \'2, \$p\' | awk \'{print \$3}\' | xargs docker rmi ||true" 
                    }
                }
            }
        }
    }
}
def createVersion() {
    // 定义一个版本号作为当次构建的版本
    return new Date().format('yyyyMMddHHmmss') + "-${env.BUILD_ID}"
}

5、遗留问题

a、定时任务类服务未确认是否可以启两个,定时任务类服务先只启一个。
b、gateway是通过nginx转发,更新gateway服务如果想不影响用户使用,还需要修改nginx配置。gateway很少更新,到时需要手动修改nginx配置并reload。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值