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

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

5.1 Jenkins+Docker+SpringCloud部署方案优化

上面部署方案存在的问题:

  1. 一次只能选择一个微服务部署

  2. 只有一台生产者部署服务器

  3. 每个微服务只有一个实例,容错率低

优化方案:

  1. 在一个Jenkins工程中可以选择多个微服务同时发布

  2. 在一个Jenkins工程中可以选择多台生产服务器同时部署

  3. 每个微服务都是以集群高可用形式部署

5.2 Jenkins+Docker+SpringCloud集群部署流程说明

Jenkins+Docker+Harbor+SpringCloud微服务持续集成(下)_第1张图片

5.3 修改所有微服务配置

注册中心配置

# 集群版
spring:
  application:
    name: EUREKA-HA
        
---
server:
  port: 10086
spring:
  # 指定profile=eureka-server1
  profiles: eureka-server1
eureka:
  instance:
    # 指定当profile=eureka-server1时,主机名是eureka-server1
    hostname: 192.168.5.6
  client:
    service-url:
    # 将自己注册到eureka-server1、eureka-server2这个Eureka上面去
      defaultZone: http://192.168.5.6:10086/eureka,http://192.168.5.7:10086/eureka
​
---
server:
  port: 10086
spring:
  profiles: eureka-server2
eureka:
  instance:
    hostname: 192.168.5.7
  client:
    service-url:
      defaultZone: http://192.168.5.6:10086/eureka,http://192.168.5.7:10086/eureka

在启动微服务的时候,加入参数: spring.profiles.active 来读取对应的配置

Jenkins+Docker+Harbor+SpringCloud微服务持续集成(下)_第2张图片

修改其他微服务配置

除了Eureka注册中心以外,其他微服务配置都需要加入所有Eureka服务

# Eureka配置
eureka:
  client:
    service-url:
      defaultZone: http://192.168.5.6:10086/eureka,http://192.168.5.7:10086/eureka # 追加Eureka访问地址

Jenkins+Docker+Harbor+SpringCloud微服务持续集成(下)_第3张图片

提交代码

5.4 设计Jenkins集群项目的构建参数

1)安装Extended Choice Parameter插件

Extended Choice Parameter

Jenkins+Docker+Harbor+SpringCloud微服务持续集成(下)_第4张图片

2)创建流水线项目

Jenkins+Docker+Harbor+SpringCloud微服务持续集成(下)_第5张图片

Jenkins+Docker+Harbor+SpringCloud微服务持续集成(下)_第6张图片

3)添加参数 字符串参数:分支名称

Jenkins+Docker+Harbor+SpringCloud微服务持续集成(下)_第7张图片

多选框:项目名称

Jenkins+Docker+Harbor+SpringCloud微服务持续集成(下)_第8张图片

Jenkins+Docker+Harbor+SpringCloud微服务持续集成(下)_第9张图片

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

Jenkins+Docker+Harbor+SpringCloud微服务持续集成(下)_第10张图片

Jenkins+Docker+Harbor+SpringCloud微服务持续集成(下)_第11张图片

最后效果:

Jenkins+Docker+Harbor+SpringCloud微服务持续集成(下)_第12张图片

Jenkins+Docker+Harbor+SpringCloud微服务持续集成(下)_第13张图片

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

//gitlab的凭证
def git_auth = "14ae86e8-c3b4-4d7d-afe1-8c23d9fed317"
//gitlab的地址
def git_url = "[email protected]: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)编译镜像测试

    Jenkins+Docker+Harbor+SpringCloud微服务持续集成(下)_第14张图片

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

    1)环境配置

    • 安装docker

    • 拷贝公钥到远程服务器

    • 系统配置->添加远程服务器

    Jenkins+Docker+Harbor+SpringCloud微服务持续集成(下)_第15张图片

    2)添加参数

    多选框:部署服务器

    Jenkins+Docker+Harbor+SpringCloud微服务持续集成(下)_第16张图片

    Jenkins+Docker+Harbor+SpringCloud微服务持续集成(下)_第17张图片

    master_server,slave_server_01
    

    Jenkins+Docker+Harbor+SpringCloud微服务持续集成(下)_第18张图片

    Jenkins+Docker+Harbor+SpringCloud微服务持续集成(下)_第19张图片

    最终效果:

    Jenkins+Docker+Harbor+SpringCloud微服务持续集成(下)_第20张图片

    Jenkins+Docker+Harbor+SpringCloud微服务持续集成(下)_第21张图片

    3)修改Jenkinsfile构建脚本

    //gitlab的凭证
    def git_auth = "14ae86e8-c3b4-4d7d-afe1-8c23d9fed317"
    //gitlab的地址
    def git_url = "[email protected]: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网关中定义的服务器地址来进行远程部署

    Jenkins+Docker+Harbor+SpringCloud微服务持续集成(下)_第22张图片

    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"
      }
    
    这里可以理解为
    当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)修改代码连接后端微服务服务

    Jenkins+Docker+Harbor+SpringCloud微服务持续集成(下)_第23张图片

    Jenkins+Docker+Harbor+SpringCloud微服务持续集成(下)_第24张图片

    4)项目测试

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

    Jenkins+Docker+Harbor+SpringCloud微服务持续集成(下)_第25张图片

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

    Jenkins+Docker+Harbor+SpringCloud微服务持续集成(下)_第26张图片

    Jenkins+Docker+Harbor+SpringCloud微服务持续集成(下)_第27张图片

    Jenkins+Docker+Harbor+SpringCloud微服务持续集成(下)_第28张图片

    Jenkins+Docker+Harbor+SpringCloud微服务持续集成(下)_第29张图片

    5.8 Jenkins的Master-Slave分布式构建

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

    Jenkins+Docker+Harbor+SpringCloud微服务持续集成(下)_第30张图片

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

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

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

    Manage Jenkins -> Configure Global Security

    Jenkins+Docker+Harbor+SpringCloud微服务持续集成(下)_第31张图片

    Jenkins+Docker+Harbor+SpringCloud微服务持续集成(下)_第32张图片

    2)新建节点

    Manage Jenkins—Manage Nodes—新建节点

    Jenkins+Docker+Harbor+SpringCloud微服务持续集成(下)_第33张图片

    Jenkins+Docker+Harbor+SpringCloud微服务持续集成(下)_第34张图片

    Jenkins+Docker+Harbor+SpringCloud微服务持续集成(下)_第35张图片

    Jenkins+Docker+Harbor+SpringCloud微服务持续集成(下)_第36张图片

    Jenkins+Docker+Harbor+SpringCloud微服务持续集成(下)_第37张图片

    3)节点部署

    Jenkins+Docker+Harbor+SpringCloud微服务持续集成(下)_第38张图片

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

    Jenkins+Docker+Harbor+SpringCloud微服务持续集成(下)_第39张图片

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

    4)完成部署

    Jenkins+Docker+Harbor+SpringCloud微服务持续集成(下)_第40张图片

    5)拉取测试

    Jenkins+Docker+Harbor+SpringCloud微服务持续集成(下)_第41张图片

    Jenkins+Docker+Harbor+SpringCloud微服务持续集成(下)_第42张图片

     

你可能感兴趣的:(云原生架构搭建,docker,jenkins,git,nginx,微服务)