Jenkins+Docker+Harbor+SpringCloud微服务持续集成(下)

本文涉及的产品
注册配置 MSE Nacos/ZooKeeper,182元/月
任务调度 XXL-JOB 版免费试用,400 元额度,开发版规格
云原生网关 MSE Higress,422元/月
简介: Jenkins+Docker+Harbor+SpringCloud微服务持续集成(下)

正文


5.5 完成微服务构建镜像,上传私服


//gitlab的凭证
def git_auth = "14ae86e8-c3b4-4d7d-afe1-8c23d9fed317"
//gitlab的地址
def git_url = "git@192.168.5.4:root/tensquare_bak.git"
// 构建版本的名称
def tag = "latest"
//Harbor私服地址
def harbor_url = "192.168.5.5:8080"
//Harbor的项目名称
def harbor_project_name = "tensquare"
//Harbor的凭证
def harbor_auth = "cd0b948d-e82b-4c0c-8a7c-8c6b8fb5454b"
node {
    // 获取当前选择的项目名称
    def selectedProjects = "${project_name}".split(',')
    stage('拉取代码') {
        checkout([$class: 'GitSCM', branches: [[name: "*/${branch}"]], extensions: [], userRemoteConfigs: [[credentialsId: "${git_auth}", url: "${git_url}"]]])
    }
    stage('代码审查') {
        for(int i=0;i<selectedProjects.size();i++){
            //取出每个项目的名称和端口
            def currentProject = selectedProjects[i];
             //项目名称
            def currentProjectName = currentProject.split('@')[0]
             //项目启动端口
            def currentProjectPort = currentProject.split('@')[1]
                //定义当前Jenkins的SonarQubeScanner工具
                def scannerHome = tool 'sonarqube-scanner'
                //引用当前Jenkins SonarQube环境
                withSonarQubeEnv('sonarqube') {
                    sh """
                        cd ${currentProjectName}
                        ${scannerHome}/bin/sonar-scanner
                    """
                 }
         }
    }
    stage('编译,安装公共工程') {
        //编译,安装公共工程
        sh "mvn -f tensquare_common clean install"
    }
    stage('编译,打包微服务工程,上传镜像') {
         for(int i=0;i<selectedProjects.size();i++){
            //取出每个项目的名称和端口
            def currentProject = selectedProjects[i];
            //项目名称
            def currentProjectName = currentProject.split('@')[0]
             //项目启动端口
             def currentProjectPort = currentProject.split('@')[1]
                //编译,构建本地镜像
                sh "mvn -f ${currentProjectName} clean package dockerfile:build"
                //定义镜像名称
                def imageName = "${currentProjectName}:${tag}"
                //给镜像打标签
                sh "docker tag ${imageName} ${harbor_url}/${harbor_project_name}/${imageName}"
                //登录Harbor,并上传镜像
                withCredentials([usernamePassword(credentialsId: "${harbor_auth}",passwordVariable: 'password', usernameVariable: 'username')]) {
                    //登录
                    sh "docker login -u ${username} -p ${password} ${harbor_url}"
                    //上传镜像
                    sh "docker push ${harbor_url}/${harbor_project_name}/${imageName}"
                }
                //删除本地镜像
                sh "docker rmi -f ${imageName}"
                sh "docker rmi -f ${harbor_url}/${harbor_project_name}/${imageName}"
          }
    }
}


2)编译部署 for循环遍历分割详解


node {
    // 获取当前选择的项目名称
    def selectedProjects = "${project_name}".split(',') 
    stage('代码审查') {
        for(int i=0;i<selectedProjects.size();i++){
            //取出每个项目的名称和端口
            def currentProject = selectedProjects[i];
             //项目名称
            def currentProjectName = currentProject.split('@')[0]
             //项目启动端口
            def currentProjectPort = currentProject.split('@')[1]
                //定义当前Jenkins的SonarQubeScanner工具
                def scannerHome = tool 'sonarqube-scanner'
                //引用当前Jenkins SonarQube环境
                withSonarQubeEnv('sonarqube') {
                    sh """
                        cd ${currentProjectName}
                        ${scannerHome}/bin/sonar-scanner
                    """
          }
    }
}


首先在node中定义获取当前项目的变量 def selectedProjects = "${project_name}".split(',')


  • selectedProjects:这个是变量名称可以随意定义,
  • ${project_name}:这里指的Extended Choice Parameter参数化构建中定义的项目名称
  • .split(','):这是一个分割语法,意思为以","来进行分割,因为之前在Extended Choice Parameter参数化变量中是这样定义的,同理,如果这里更换为其他的分割服务符号,那么在.split(',')更换为对应的分割符号


tensquare_eureka_server@10086,tensquare_zuul@10020,tensquare_admin_service@9001,tensquare_gathering@9002


这时就可以获取到对应的微服务,比如这个样子


selectedProjects=tensquare_eureka_server@10086
selectedProjects=tensquare_zuul@10020
selectedProjects=tensquare_admin_service@9001
selectedProjects=tensquare_gathering@9002

目前已经成功获取到对应的微服务,但是还是无法直接使用,因为服务名称和端口号为同一个字符串并且使用"@"符号进行连接,那么这里可以使用之前的方法,对"@"符号进行分割再次分割,得到最后的服务名称和端口


for(int i=0;i<selectedProjects.size();i++){
  基础的for循环取出语句


//取出每个项目的名称和端口
def currentProject = selectedProjects[i];
  这里可以理解为,逐步取出selectedProjects变量中每个项目的名称及端口号


//项目名称
def currentProjectName = currentProject.split('@')[0]
  对currentProject变量以@符号进行分割,并获取第0个字符,可以理解为获取@符号左边的字符,可以得出服务名称


//项目启动端口
def currentProjectPort = currentProject.split('@')[1]
  对currentProject变量以@符号进行分割,并获取第1个字符,可以理解为获取@符号右边的字符,可以得出服务名称


经过两次分割(第一个以","分割从Extended Choice Parameter参数化构建中获取服务,第二次以"@"分割分割获取到的服务名称和端口)获得到


项目名称变量(currentProjectName)

项目端口号变量(currentProjectPort)


在进行代码质量扫描时更换相对于的变量,并且在编译,打包微服务工程,上传镜像时使用的for循环分割与此处逻辑一致


3)编译镜像测试


7777.png


5.6 完成微服务多服务器远程发布


1)环境配置


  • 安装docker
  • 拷贝公钥到远程服务器
  • 系统配置->添加远程服务器

66.png


2)添加参数


多选框:部署服务器


666.png

6666.png


master_server,slave_server_01


65.png

64.png


最终效果:


63.png

62.png


3)修改Jenkinsfile构建脚本


//gitlab的凭证
def git_auth = "14ae86e8-c3b4-4d7d-afe1-8c23d9fed317"
//gitlab的地址
def git_url = "git@192.168.5.4:root/tensquare_bak.git"
// 构建版本的名称
def tag = "latest"
//Harbor私服地址
def harbor_url = "192.168.5.5:8080"
//Harbor的项目名称
def harbor_project_name = "tensquare"
//Harbor的凭证
def harbor_auth = "cd0b948d-e82b-4c0c-8a7c-8c6b8fb5454b"
node {
    // 获取当前选择的项目名称
    def selectedProjects = "${project_name}".split(',')
    // 获取当前选择的服务器名称
    def selectedServers = "${publish_server}".split(',')
    stage('拉取代码') {
        checkout([$class: 'GitSCM', branches: [[name: "*/${branch}"]], extensions: [], userRemoteConfigs: [[credentialsId: "${git_auth}", url: "${git_url}"]]])
    }
  stage('代码审查') {
      for(int i=0;i<selectedProjects.size();i++){
            //取出每个项目的名称和端口
            def currentProject = selectedProjects[i];
             //项目名称
            def currentProjectName = currentProject.split('@')[0]
             //项目启动端口
            def currentProjectPort = currentProject.split('@')[1]
            //定义当前Jenkins的SonarQubeScanner工具
            def scannerHome = tool 'sonarqube-scanner'
            //引用当前Jenkins SonarQube环境
            withSonarQubeEnv('sonarqube') {
              sh """
                cd ${currentProjectName}
                ${scannerHome}/bin/sonar-scanner
              """
            }
    }
  }
  stage('编译,安装公共工程') {
    //编译,安装公共工程
    sh "mvn -f tensquare_common clean install"
  }
    stage('编译,打包微服务工程,上传镜像') {
         for(int i=0;i<selectedProjects.size();i++){
            //取出每个项目的名称和端口
            def currentProject = selectedProjects[i];
            //项目名称
            def currentProjectName = currentProject.split('@')[0]
             //项目启动端口
             def currentProjectPort = currentProject.split('@')[1]
                //编译,构建本地镜像
                sh "mvn -f ${currentProjectName} clean package dockerfile:build"
                //定义镜像名称
                def imageName = "${currentProjectName}:${tag}"
                //给镜像打标签
                sh "docker tag ${imageName} ${harbor_url}/${harbor_project_name}/${imageName}"
                //登录Harbor,并上传镜像
                withCredentials([usernamePassword(credentialsId: "${harbor_auth}",passwordVariable: 'password', usernameVariable: 'username')]) {
                    //登录
                    sh "docker login -u ${username} -p ${password} ${harbor_url}"
                    //上传镜像
                    sh "docker push ${harbor_url}/${harbor_project_name}/${imageName}"
                }
                //删除本地镜像
                sh "docker rmi -f ${imageName}"
                sh "docker rmi -f ${harbor_url}/${harbor_project_name}/${imageName}"
                  //远程部署服务器应用
                for(int j=0;j<selectedServers.size();j++){
                  //每个服务名称
                  def currentServer = selectedServers[j]
                  //添加微服务运行时的参数:spring.profiles.active
                  def activeProfile = "--spring.profiles.active="
                  if(currentServer=="master_server"){
                      activeProfile = activeProfile+"eureka-server1"
                  }else if(currentServer=="slave_server_01"){
                      activeProfile = activeProfile+"eureka-server2"
                  }
                  // 远程触发部署脚本
                  sshPublisher(publishers: [sshPublisherDesc(configName: "${currentServer}", transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: "/opt/jenkins_shell/deployCluster.sh $harbor_url $harbor_project_name $currentProjectName $tag $currentProjectPort $activeProfile", execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: '')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
                }
                echo "${currentProjectName}完成编译,构建镜像!"
         }
  }
}


4)远程部署 for循环遍历分割详解


for(int j=0;j<selectedServers.size();j++){
  //每个服务名称
  def currentServer = selectedServers[j]
  //添加微服务运行时的参数:spring.profiles.active
  def activeProfile = "--spring.profiles.active="
  if(currentServer=="master_server"){
      activeProfile = activeProfile+"eureka-server1"
  }else if(currentServer=="slave_server_01"){
      activeProfile = activeProfile+"eureka-server2"
  }
}


这里远程部署到对应的服务器和之前编译,打包微服务工程,上传镜像时使用的for循环分割逻辑基本一致,


首先在node中定义获取当前项目的变量 def selectedServers = "${publish_server}".split(',')


  • selectedServers:这个是变量名称可以随意定义,
  • ${publish_server}:这里指的Extended Choice Parameter参数化构建中定义的服务器名称
  • .split(','):这是一个分割语法,意思为以","来进行分割,因为之前在Extended Choice Parameter参数化变量中是这样定义的,同理,如果这里更换为其他的分割服务符号,那么在.split(',')更换为对应的分割符号


master_server,slave_server_01


这时就可以获取到对应的微服务,比如这个样子


selectedServers = master_server

selectedServers = slave_server_01


目前已经成功获取到对应服务器名称,但是还是无法直接使用,因为要对应之前在eureka网关中定义的服务器地址来进行远程部署


77777.png


for(int j=0;j<selectedServers.size();j++){
  基础的for循环取出语句,这里不能再次使用i,因为在之前已经使用过


//每个服务名称
def currentServer = selectedServers[j]
  这里可以理解为,逐步取出selectedServers变量中服务器名称


//添加微服务运行时的参数:spring.profiles.active
def activeProfile = "--spring.profiles.active="
  因为要读取服务中的配置文件,所以这里使用变量(activeProfile)定义了参数(--spring.profiles.active)


//if 判断语句
  if(currentServer == "master_server"){
      activeProfile = activeProfile+"eureka-server1"
  }else if(currentServer == "slave_server_01"){
      activeProfile = activeProfile+"eureka-server2"
  }


//if 判断语句
  if(currentServer == "master_server"){
      activeProfile = activeProfile+"eureka-server1"
  }else if(currentServer == "slave_server_01"){
      activeProfile = activeProfile+"eureka-server2"
  }


这里可以理解为
当currentServer等于master_server时,就读取配置文件中的eureka-server1
当currentServer等于slave_server_01时,就读取配置文件中的eureka-server2


5)编写deployCluster.sh部署脚本


#! /bin/sh
#接收外部参数
harbor_url=$1
harbor_project_name=$2
project_name=$3
tag=$4
port=$5
profile=$6
imageName=$harbor_url/$harbor_project_name/$project_name:$tag
echo "$imageName"
#查询容器是否存在,存在则删除
containerId=`docker ps -a | grep -w ${project_name}:${tag} | awk '{print $1}'`
if [ "$containerId" != "" ] ; then
        #停掉容器
        docker stop $containerId
        #删除容器
        docker rm $containerId
        echo "成功删除容器"
fi
#查询镜像是否存在,存在则删除
imageId=`docker images | grep -w $project_name | awk '{print $3}'`
if [ "$imageId" != "" ] ; then
        #删除镜像
        docker rmi -f $imageId
        echo "成功删除镜像"
fi
# 登录Harbor私服
docker login -u admin -p Harbor12345 $harbor_url
# 下载镜像
docker pull $imageName
# 启动容器
docker run -di -p $port:$port $imageName $profile
  echo "容器启动成功"


登录Harbor私服


docker login -u admin -p Harbor12345 $harbor_url


下载镜像


docker pull $imageName


启动容器


docker run -di -p p o r t : port:port:port $imageName $profile


echo “容器启动成功”


2)修改Dockerfile


FROM nginx
COPY ./dist /usr/share/nginx/html
COPY ./nginx.conf /etc/nginx/nginx.conf
EXPOSE 80


3)修改代码连接后端微服务服务


777.png

77.png


4)项目测试


由于是测试阶段,如果前端容器没有开通85端口,那么一定会造成Network Error网关错误,正常环境中建议部署一台专门的转发服务器,来达成轮询访问的效果


88888.png


造成这种原因是因为转发请求并没有到达容器内的Nginx 所有并不能完成转发,本次测试也是在启动容器前,修改了容器启动脚本,开通了85端口,所以可以正常访问


8888.png

888.png

88.png

999999.png


5.8 Jenkins的Master-Slave分布式构建


5.8.1 什么是Master-Slave分布式构建


8.png

Jenkins的Master-Slave分布式构建,就是通过将构建过程分配到从属Slave节点上,从而减轻Master节点的压力,而且可以同时构建多个,有点类似负载均衡的概念。


5.8.2 如何实现Master-Slave分布式构建


1)开启代理程序的TCP端口


Manage Jenkins -> Configure Global Security


6.png

7.png


2)新建节点


Manage Jenkins—Manage Nodes—新建节点


5.png

4.png

3.png

2.png

1.png


3)节点部署


889.png


下载jar包到slave节点服务器(slave节点服务器必须有java环境)

89.png

java -jar agent.jar -jnlpUrl http://192.168.5.3:808/computer/slave_01/jenkins-agent.jnlp -secret 2d2371ae0ed0e91ad74c67aabecb5af24d001ff7f5fb440cb5bc663b57a52898 -workDir "/root/jenkins"


4)完成部署


0099.png


5)拉取测试


009.png

09.png


相关文章
|
29天前
|
监控 安全 Java
Spring Cloud 微服务治理技术详解与实践指南
本文档全面介绍 Spring Cloud 微服务治理框架的核心组件、架构设计和实践应用。作为 Spring 生态系统中构建分布式系统的标准工具箱,Spring Cloud 提供了一套完整的微服务解决方案,涵盖服务发现、配置管理、负载均衡、熔断器等关键功能。本文将深入探讨其核心组件的工作原理、集成方式以及在实际项目中的最佳实践,帮助开发者构建高可用、可扩展的分布式系统。
98 1
|
1月前
|
jenkins Java 持续交付
使用 Jenkins 和 Spring Cloud 自动化微服务部署
随着单体应用逐渐被微服务架构取代,企业对快速发布、可扩展性和高可用性的需求日益增长。Jenkins 作为领先的持续集成与部署工具,结合 Spring Cloud 提供的云原生解决方案,能够有效简化微服务的开发、测试与部署流程。本文介绍了如何通过 Jenkins 实现微服务的自动化构建与部署,并结合 Spring Cloud 的配置管理、服务发现等功能,打造高效、稳定的微服务交付流程。
191 0
使用 Jenkins 和 Spring Cloud 自动化微服务部署
|
1月前
|
Kubernetes Java 微服务
Spring Cloud 微服务架构技术解析与实践指南
本文档全面介绍 Spring Cloud 微服务架构的核心组件、设计理念和实现方案。作为构建分布式系统的综合工具箱,Spring Cloud 为微服务架构提供了服务发现、配置管理、负载均衡、熔断器等关键功能的标准化实现。本文将深入探讨其核心组件的工作原理、集成方式以及在实际项目中的最佳实践,帮助开发者构建高可用、可扩展的分布式系统。
272 0
|
SpringCloudAlibaba API 开发者
新版-SpringCloud+SpringCloud Alibaba
新版-SpringCloud+SpringCloud Alibaba
|
7月前
|
负载均衡 Dubbo Java
Spring Cloud Alibaba与Spring Cloud区别和联系?
Spring Cloud Alibaba与Spring Cloud区别和联系?
|
资源调度 Java 调度
Spring Cloud Alibaba 集成分布式定时任务调度功能
定时任务在企业应用中至关重要,常用于异步数据处理、自动化运维等场景。在单体应用中,利用Java的`java.util.Timer`或Spring的`@Scheduled`即可轻松实现。然而,进入微服务架构后,任务可能因多节点并发执行而重复。Spring Cloud Alibaba为此发布了Scheduling模块,提供轻量级、高可用的分布式定时任务解决方案,支持防重复执行、分片运行等功能,并可通过`spring-cloud-starter-alibaba-schedulerx`快速集成。用户可选择基于阿里云SchedulerX托管服务或采用本地开源方案(如ShedLock)
312 1
|
8月前
|
人工智能 SpringCloudAlibaba 自然语言处理
SpringCloud Alibaba AI整合DeepSeek落地AI项目实战
在现代软件开发领域,微服务架构因其灵活性、可扩展性和模块化特性而受到广泛欢迎。微服务架构通过将大型应用程序拆分为多个小型、独立的服务,每个服务运行在其独立的进程中,服务与服务间通过轻量级通信机制(通常是HTTP API)进行通信。这种架构模式有助于提升系统的可维护性、可扩展性和开发效率。
2420 2
|
10月前
|
SpringCloudAlibaba 负载均衡 Dubbo
【SpringCloud Alibaba系列】Dubbo高级特性篇
本章我们介绍Dubbo的常用高级特性,包括序列化、地址缓存、超时与重试机制、多版本、负载均衡。集群容错、服务降级等。
1341 8
【SpringCloud Alibaba系列】Dubbo高级特性篇
|
10月前
|
Java Nacos Sentinel
Spring Cloud Alibaba:一站式微服务解决方案
Spring Cloud Alibaba(简称SCA) 是一个基于 Spring Cloud 构建的开源微服务框架,专为解决分布式系统中的服务治理、配置管理、服务发现、消息总线等问题而设计。
1992 13
Spring Cloud Alibaba:一站式微服务解决方案
|
10月前
|
存储 SpringCloudAlibaba Java
【SpringCloud Alibaba系列】一文全面解析Zookeeper安装、常用命令、JavaAPI操作、Watch事件监听、分布式锁、集群搭建、核心理论
一文全面解析Zookeeper安装、常用命令、JavaAPI操作、Watch事件监听、分布式锁、集群搭建、核心理论。
【SpringCloud Alibaba系列】一文全面解析Zookeeper安装、常用命令、JavaAPI操作、Watch事件监听、分布式锁、集群搭建、核心理论

热门文章

最新文章