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

本文涉及的产品
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
注册配置 MSE Nacos/ZooKeeper,118元/月
云原生网关 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


相关文章
|
16天前
|
Java 对象存储 开发者
解析Spring Cloud与Netflix OSS:微服务架构中的左右手如何协同作战
Spring Cloud与Netflix OSS不仅是现代微服务架构中不可或缺的一部分,它们还通过不断的技术创新和社区贡献推动了整个行业的发展。无论是对于初创企业还是大型组织来说,掌握并合理运用这两套工具,都能极大地提升软件系统的灵活性、可扩展性以及整体性能。随着云计算和容器化技术的进一步普及,Spring Cloud与Netflix OSS将继续引领微服务技术的发展潮流。
31 0
|
2天前
|
开发者 Docker 微服务
利用Docker Compose优化微服务架构
在微服务架构中,Docker Compose提供了一种简便有效的方法来定义和运行多容器Docker应用程序,通过YAML文件配置服务、网络和卷,实现一键创建和启动。这不仅确保了开发、测试和生产环境的一致性,还简化了团队协作和维护工作,大幅提升了开发效率。本文将详细介绍Doker Compose的核心优势、基本使用方法及高级功能,帮助你更好地管理和优化微服务架构。
|
3天前
|
监控 Java 对象存储
监控与追踪:如何利用Spring Cloud Sleuth和Netflix OSS工具进行微服务调试
监控与追踪:如何利用Spring Cloud Sleuth和Netflix OSS工具进行微服务调试
11 1
|
14天前
|
负载均衡 Java 网络架构
实现微服务网关:Zuul与Spring Cloud Gateway的比较分析
实现微服务网关:Zuul与Spring Cloud Gateway的比较分析
27 5
|
13天前
|
前端开发 API 微服务
SpringCloud微服务之间使用Feign调用不通情况举例
SpringCloud微服务之间使用Feign调用不通情况举例
80 2
|
16天前
|
Java API 对象存储
微服务魔法启动!Spring Cloud与Netflix OSS联手,零基础也能创造服务奇迹!
这段内容介绍了如何使用Spring Cloud和Netflix OSS构建微服务架构。首先,基于Spring Boot创建项目并添加Spring Cloud依赖项。接着配置Eureka服务器实现服务发现,然后创建REST控制器作为API入口。为提高服务稳定性,利用Hystrix实现断路器模式。最后,在启动类中启用Eureka客户端功能。此外,还可集成其他Netflix OSS组件以增强系统功能。通过这些步骤,开发者可以更高效地构建稳定且可扩展的微服务系统。
34 1
|
1天前
|
jenkins Shell 持续交付
Jenkins持续集成GitLab项目 GitLab提交分支后触发Jenkis任务 持续集成 CI/CD 超级详细 超多图(一)
Jenkins持续集成GitLab项目 GitLab提交分支后触发Jenkis任务 持续集成 CI/CD 超级详细 超多图(一)
11 0
|
1天前
|
jenkins Shell 持续交付
Jenkins持续集成GitLab项目 GitLab提交分支后触发Jenkis任务 持续集成 CI/CD 超级详细 超多图(二)
Jenkins持续集成GitLab项目 GitLab提交分支后触发Jenkis任务 持续集成 CI/CD 超级详细 超多图(二)
10 0
|
27天前
|
监控 Java Nacos
SpringCloud基础5——微服务保护、Sentinel
sentinel、雪崩问题、流量控制、隔离和降级、授权规则、规则持久化
SpringCloud基础5——微服务保护、Sentinel
|
16天前
|
Java 对象存储 开发者
微服务世界的双雄争霸:Spring Cloud与Netflix OSS——谁将引领下一次企业级应用变革的风暴?
Spring Cloud与Netflix OSS是微服务架构的核心组件集,分别以其与Spring Boot的紧密集成及为大规模分布式系统设计的特性,在Java开发社区中广受青睐。前者通过Eureka提供服务发现机制,简化服务注册与定位;后者借助Hystrix增强系统弹性和可靠性,避免雪崩效应。此外,二者还包含负载均衡(Ribbon)、声明式HTTP客户端(Feign)及API网关(Zuul)等功能,共同构建强大微服务体系,助力开发者聚焦业务逻辑,提升系统灵活性与性能。
34 0