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

简介: 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


相关文章
|
2月前
|
算法 Java 微服务
【SpringCloud(1)】初识微服务架构:创建一个简单的微服务;java与Spring与微服务;初入RestTemplate
微服务架构是What?? 微服务架构是一种架构模式,它提出将单一应用程序划分为一组小的服务,服务之间互相协调、互相配合,为用户提供最终价值。 每个服务允许在其独立的进程中,服务于服务间采用轻量级的通信机制互相协作(通常是Http协议的RESTful API或RPC协议)。 每个服务都围绕着具体业务进行构建,并且能够被独立的部署到生产环境、类生产环境等。另外应当尽量避免统一的、集中式的服务管理机制,对具体的一个服务而言,应根据上下文,选择合适的语言、工具对其进行构建
504 126
|
2月前
|
负载均衡 算法 Java
【SpringCloud(2)】微服务注册中心:Eureka、Zookeeper;CAP分析;服务注册与服务发现;单机/集群部署Eureka;连接注册中心
1. 什么是服务治理? SpringCloud封装了Netfix开发的Eureka模块来实现服务治理 在传统pc的远程调用框架中,管理每个服务与服务之间依赖关系比较复杂,管理比较复杂,所以需要使用服务治理,管理服务于服务之间依赖关系,可以实现服务调用、负载均衡、容错等,实现服务发现与注册
303 0
|
3月前
|
弹性计算 关系型数据库 微服务
基于 Docker 与 Kubernetes(K3s)的微服务:阿里云生产环境扩容实践
在微服务架构中,如何实现“稳定扩容”与“成本可控”是企业面临的核心挑战。本文结合 Python FastAPI 微服务实战,详解如何基于阿里云基础设施,利用 Docker 封装服务、K3s 实现容器编排,构建生产级微服务架构。内容涵盖容器构建、集群部署、自动扩缩容、可观测性等关键环节,适配阿里云资源特性与服务生态,助力企业打造低成本、高可靠、易扩展的微服务解决方案。
1738 10
|
4月前
|
监控 Java API
Spring Boot 3.2 结合 Spring Cloud 微服务架构实操指南 现代分布式应用系统构建实战教程
Spring Boot 3.2 + Spring Cloud 2023.0 微服务架构实践摘要 本文基于Spring Boot 3.2.5和Spring Cloud 2023.0.1最新稳定版本,演示现代微服务架构的构建过程。主要内容包括: 技术栈选择:采用Spring Cloud Netflix Eureka 4.1.0作为服务注册中心,Resilience4j 2.1.0替代Hystrix实现熔断机制,配合OpenFeign和Gateway等组件。 核心实操步骤: 搭建Eureka注册中心服务 构建商品
825 3
|
2月前
|
负载均衡 Java API
《深入理解Spring》Spring Cloud 构建分布式系统的微服务全家桶
Spring Cloud为微服务架构提供一站式解决方案,涵盖服务注册、配置管理、负载均衡、熔断限流等核心功能,助力开发者构建高可用、易扩展的分布式系统,并持续向云原生演进。
|
3月前
|
监控 安全 Java
Spring Cloud 微服务治理技术详解与实践指南
本文档全面介绍 Spring Cloud 微服务治理框架的核心组件、架构设计和实践应用。作为 Spring 生态系统中构建分布式系统的标准工具箱,Spring Cloud 提供了一套完整的微服务解决方案,涵盖服务发现、配置管理、负载均衡、熔断器等关键功能。本文将深入探讨其核心组件的工作原理、集成方式以及在实际项目中的最佳实践,帮助开发者构建高可用、可扩展的分布式系统。
237 1
|
3月前
|
jenkins Java 持续交付
使用 Jenkins 和 Spring Cloud 自动化微服务部署
随着单体应用逐渐被微服务架构取代,企业对快速发布、可扩展性和高可用性的需求日益增长。Jenkins 作为领先的持续集成与部署工具,结合 Spring Cloud 提供的云原生解决方案,能够有效简化微服务的开发、测试与部署流程。本文介绍了如何通过 Jenkins 实现微服务的自动化构建与部署,并结合 Spring Cloud 的配置管理、服务发现等功能,打造高效、稳定的微服务交付流程。
496 0
使用 Jenkins 和 Spring Cloud 自动化微服务部署
|
3月前
|
Kubernetes Java 微服务
Spring Cloud 微服务架构技术解析与实践指南
本文档全面介绍 Spring Cloud 微服务架构的核心组件、设计理念和实现方案。作为构建分布式系统的综合工具箱,Spring Cloud 为微服务架构提供了服务发现、配置管理、负载均衡、熔断器等关键功能的标准化实现。本文将深入探讨其核心组件的工作原理、集成方式以及在实际项目中的最佳实践,帮助开发者构建高可用、可扩展的分布式系统。
461 0
|
6月前
|
负载均衡 Java API
基于 Spring Cloud 的微服务架构分析
Spring Cloud 是一个基于 Spring Boot 的微服务框架,提供全套分布式系统解决方案。它整合了 Netflix、Zookeeper 等成熟技术,通过简化配置和开发流程,支持服务发现(Eureka)、负载均衡(Ribbon)、断路器(Hystrix)、API网关(Zuul)、配置管理(Config)等功能。此外,Spring Cloud 还兼容 Nacos、Consul、Etcd 等注册中心,满足不同场景需求。其核心组件如 Feign 和 Stream,进一步增强了服务调用与消息处理能力,为开发者提供了一站式微服务开发工具包。
663 0

热门文章

最新文章