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



    Jenkins+Docker+SpringCloud部署方案优化

    ● 前面部署方案存在的问题:

    ○ 一次只能选择一个微服务部署。

    ○ 只有一条生产部署服务器。

    ○ 每个微服务只有一个实例,容错率低。

    ● 优化方案:

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

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

    ○ 每个微服务都是以集群高可用的形式部署。


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

    在这里插入图片描述


    修改所有微服务配置

    修改注册中心的配置

    • application.yaml
    # 集群版
    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.18.103
      client:
        service-url:
          defaultZone: http://192.168.18.103:10086/eureka,http://192.168.18.104:10086/eureka
    
    ---
    server:
      port: 10086
    spring:
      profiles: eureka-server2
    eureka:
      instance:
        hostname: 192.168.18.104
      client:
        service-url:
          defaultZone: http://192.168.18.103:10086/eureka,http://192.168.18.104:10086/eureka
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 在启动微服务的时候,加入参数spring.profile.active来读取对应的配置。

    其他微服务配置

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

    商品微服务的配置:

    server:
      port: 9001 # 微服务的端口号
    
    spring:
      application:
        name: service-product # 微服务的名称
    
    # 配置 eureka
    eureka:
      client:
        service-url: # Eureka Server的地址
          defaultZone: http://192.168.18.103:10086/eureka,http://192.168.18.104:10086/eureka
      instance:
        instance-id: service-product
        prefer-ip-address: true
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    订单微服务的配置:

    server:
      port: 9002 # 微服务的端口号
    
    spring:
      application:
        name: service-order # 微服务的名称
    
    # 配置 eureka
    eureka:
      client:
        service-url: # Eureka Server的地址
          defaultZone: http://192.168.18.103:10086/eureka,http://192.168.18.104:10086/eureka
      instance:
        instance-id: service-order
        prefer-ip-address: true
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

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

    Jenkins安装Extended Choice Parameter插件

    创建流水线项目

    在这里插入图片描述

    添加参数

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


    最后效果

    在这里插入图片描述


    把多个项目提交SonarQube进行代码审核

    • 修改Jenkinsfile文件,内容如下:
    //定义git凭证ID
    def git_auth = "7d5c4945-2533-41e2-bd47-5dd97eb37f38"
    //git的url地址
    def git_url = "git@192.168.18.100:develop_group/jenkinscloud.git"
    //定义tag
    def tag = "1.0"
    // 定义Harbor的URL地址
    def harbor_url = "192.168.18.102:85"
    // 镜像库项目名称
    def harbor_project = "xudaxian-mall"
    // Harbor的登录凭证id
    def harbor_auth = "b6cf3cb5-8a33-457d-93da-65c46f0135b2"
    node {
        // == 修改部分 获取当前选择的项目名称 ==
        def selectedProjectNames = "${project_name}".split(",")
        // == 修改部分
        stage('拉取代码') {
            checkout([$class: 'GitSCM', branches: [[name: '*/master']], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: "${git_auth}", url: "${git_url}"]]])
        }
        stage('代码审查') {
            // == 修改部分
            for(int i  = 0 ; i< selectedProjectNames.length ; i++) {
                //jenkinscloud-eureka@10086
                def projectInfo = selectedProjectNames[i];
                //当前的项目名称 jenkinscloud-eureka
                def currentProjectName = "${projectInfo}".split("@")[0]
                //当前的项目端口 10086
                def currentProjectPort = "${projectInfo}".split("@")[1]
                 //定义当前Jenkins的SonarQubeScanner工具的环境
                def scannerHome = tool 'sonarqube-scanner'
                //引用当前Jenkins的SonarQube环境
                withSonarQubeEnv('sonarqube-8.6.0') {
                  sh """
                       cd ${currentProjectName}
                       ${scannerHome}/bin/sonar-scanner
                  """
                }
            }
            // == 修改部分
    
        }
        //如果有公共子工程
    //     stage('编译,安装公共的子工程') {
    //        sh "mvn -f jenkinscloud-common clean install"
    //     }
        stage('编译,打包微服务工程') {
           // dockerfile:build 可以触发插件的执行
           sh "mvn -f ${project_name} clean install dockerfile:build "
        }
    
        stage('上传镜像') {
           //定义镜像的名称
           def imageName = "${project_name}:${tag}"
           //给镜像打上标签
           sh "docker tag ${imageName} ${harbor_url}/${harbor_project}/${imageName}"
           //把镜像推送到Harbor
           withCredentials([usernamePassword(credentialsId: "${harbor_auth}", passwordVariable: 'password', usernameVariable: 'username')]) {
               // 登录到Harbor
               sh "docker login -u ${username} -p ${password} ${harbor_url}"
               //镜像的上传
               sh "docker push ${harbor_url}/${harbor_project}/${imageName}"
    
               sh "echo '镜像上传到Harbor仓库中成功'"
    
           }
           //删除本地镜像
           sh "docker rmi -f ${imageName}"
           sh "docker rmi -f ${harbor_url}/${harbor_project}/${imageName}"
    
        }
        // 定义远程执行命令
        def execCommand = "/usr/local/deploy.sh $harbor_url $harbor_project $project_name $tag $port"
        stage('拉取镜像和发布应用') {
            // 远程部署调用进行项目部署
            sshPublisher(publishers: [sshPublisherDesc(configName: '192.168.18.103', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: "${execCommand}", execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: '')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77

    多个项目打包及构建镜像、上传私服

    • 修改Jenkinsfile文件,内容如下:
    //定义git凭证ID
    def git_auth = "7d5c4945-2533-41e2-bd47-5dd97eb37f38"
    //git的url地址
    def git_url = "git@192.168.18.100:develop_group/jenkinscloud.git"
    //定义tag
    def tag = "1.0"
    // 定义Harbor的URL地址
    def harbor_url = "192.168.18.102:85"
    // 镜像库项目名称
    def harbor_project = "xudaxian-mall"
    // Harbor的登录凭证id
    def harbor_auth = "b6cf3cb5-8a33-457d-93da-65c46f0135b2"
    node {
        // == 获取当前选择的项目名称 ==
        def selectedProjectNames = "${project_name}".split(",")
    
        stage('拉取代码') {
            checkout([$class: 'GitSCM', branches: [[name: '*/master']], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: "${git_auth}", url: "${git_url}"]]])
        }
        stage('代码审查') {
            for(int i  = 0 ; i< selectedProjectNames.length ; i++) {
                //jenkinscloud-eureka@10086
                def projectInfo = selectedProjectNames[i];
                //当前的项目名称 jenkinscloud-eureka
                def currentProjectName = "${projectInfo}".split("@")[0]
                //当前的项目端口 10086
                def currentProjectPort = "${projectInfo}".split("@")[1]
                 //定义当前Jenkins的SonarQubeScanner工具的环境
                def scannerHome = tool 'sonarqube-scanner'
                //引用当前Jenkins的SonarQube环境
                withSonarQubeEnv('sonarqube-8.6.0') {
                  sh """
                       cd ${currentProjectName}
                       ${scannerHome}/bin/sonar-scanner
                  """
                }
            }
        }
        //如果有公共子工程
    //     stage('编译,安装公共的子工程') {
    //        sh "mvn -f jenkinscloud-common clean install"
    //     }
        stage('编译,打包微服务工程') {
           // == 修改部分 ==
           for(int i  = 0 ; i< selectedProjectNames.length ; i++) {
                //jenkinscloud-eureka@10086
               def projectInfo = selectedProjectNames[i];
               //当前的项目名称 jenkinscloud-eureka
               def currentProjectName = "${projectInfo}".split("@")[0]
               //当前的项目端口 10086
               def currentProjectPort = "${projectInfo}".split("@")[1]
              // dockerfile:build 可以触发插件的执行
              sh "mvn -f ${currentProjectName} clean install dockerfile:build "
           }
           // == 修改部分 ==
    
        }
    
        stage('上传镜像') {
             // == 修改部分 ==
             for(int i  = 0 ; i< selectedProjectNames.length ; i++) {
               //jenkinscloud-eureka@10086
              def projectInfo = selectedProjectNames[i];
              //当前的项目名称 jenkinscloud-eureka
              def currentProjectName = "${projectInfo}".split("@")[0]
              //当前的项目端口 10086
              def currentProjectPort = "${projectInfo}".split("@")[1]
               //定义镜像的名称
               def imageName = "${currentProjectName}:${tag}"
               //给镜像打上标签
               sh "docker tag ${imageName} ${harbor_url}/${harbor_project}/${imageName}"
               //把镜像推送到Harbor
               withCredentials([usernamePassword(credentialsId: "${harbor_auth}", passwordVariable: 'password', usernameVariable: 'username')]) {
                   // 登录到Harbor
                   sh "docker login -u ${username} -p ${password} ${harbor_url}"
                   //镜像的上传
                   sh "docker push ${harbor_url}/${harbor_project}/${imageName}"
    
                   sh "echo '镜像上传到Harbor仓库中成功'"
    
               }
               //删除本地镜像
               sh "docker rmi -f ${imageName}"
               sh "docker rmi -f ${harbor_url}/${harbor_project}/${imageName}"
             }
             // == 修改部分 ==
        }
        // 定义远程执行命令
        def execCommand = "/usr/local/deploy.sh $harbor_url $harbor_project $project_name $tag $port"
        stage('拉取镜像和发布应用') {
            // 远程部署调用进行项目部署
            // sshPublisher(publishers: [sshPublisherDesc(configName: '192.168.18.103', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: "${execCommand}", execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: '')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94

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

    ● 远程部署服务器(192.168.18.103和192.168.18.104)已安装Docker,并信任Harbor私有仓库。

    ● 关闭了防火墙。

    ● 在Publish over SSH那边增加一台远程部署服务器。

    在这里插入图片描述


    在Jenkins项目中增加一个Extended Choice Parameter参数

    • 在Jenkins项目中增加一个Extended Choice Parameter参数的目的是可以选择部署到那个服务器,选择Check

    在这里插入图片描述

    在192.168.18.103和192.168.18.104服务器中创建deployCluster.sh

    在远程部署服务器的/usr/local目录下,创建deployCluster.sh文件:

    cd /usr/local
    
    • 1
    vim deployCluster.sh
    
    • 1
    #! /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 xudaxian -p Xudaxian12345678 $harbor_url
    
    # 下载镜像
    docker pull $imageName
    
    # 启动容器
    docker run -di -p $port:$port $imageName $profile
    
    echo "容器启动成功"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    设置权限:

    chmod +x deployCluster.sh
    
    • 1

    修改Jenkinsfile文件

    //定义git凭证ID
    def git_auth = "7d5c4945-2533-41e2-bd47-5dd97eb37f38"
    //git的url地址
    def git_url = "git@192.168.18.100:develop_group/jenkinscloud.git"
    //定义tag
    def tag = "1.0"
    // 定义Harbor的URL地址
    def harbor_url = "192.168.18.102:85"
    // 镜像库项目名称
    def harbor_project = "xudaxian-mall"
    // Harbor的登录凭证id
    def harbor_auth = "b6cf3cb5-8a33-457d-93da-65c46f0135b2"
    node {
        // == 获取当前选择的项目名称 ==
        def selectedProjectNames = "${project_name}".split(",")
        // 获取当前选择的服务器名称
        def selectedServers = "${publish_server}".split(",")
    
        stage('拉取代码') {
            checkout([$class: 'GitSCM', branches: [[name: '*/master']], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: "${git_auth}", url: "${git_url}"]]])
        }
        stage('代码审查') {
            for(int i  = 0 ; i< selectedProjectNames.length ; i++) {
                //jenkinscloud-eureka@10086
                def projectInfo = selectedProjectNames[i];
                //当前的项目名称 jenkinscloud-eureka
                def currentProjectName = "${projectInfo}".split("@")[0]
                //当前的项目端口 10086
                def currentProjectPort = "${projectInfo}".split("@")[1]
                 //定义当前Jenkins的SonarQubeScanner工具的环境
                def scannerHome = tool 'sonarqube-scanner'
                //引用当前Jenkins的SonarQube环境
                withSonarQubeEnv('sonarqube-8.6.0') {
                  sh """
                       cd ${currentProjectName}
                       ${scannerHome}/bin/sonar-scanner
                  """
                }
            }
        }
        //如果有公共子工程
    //     stage('编译,安装公共的子工程') {
    //        sh "mvn -f jenkinscloud-common clean install"
    //     }
        stage('编译,打包微服务工程') {
           for(int i  = 0 ; i< selectedProjectNames.length ; i++) {
                //jenkinscloud-eureka@10086
               def projectInfo = selectedProjectNames[i];
               //当前的项目名称 jenkinscloud-eureka
               def currentProjectName = "${projectInfo}".split("@")[0]
               //当前的项目端口 10086
               def currentProjectPort = "${projectInfo}".split("@")[1]
              // dockerfile:build 可以触发插件的执行
              sh "mvn -f ${currentProjectName} clean install dockerfile:build "
           }
        }
    
        stage('上传镜像') {
             for(int i  = 0 ; i< selectedProjectNames.length ; i++) {
               //jenkinscloud-eureka@10086
              def projectInfo = selectedProjectNames[i];
              //当前的项目名称 jenkinscloud-eureka
              def currentProjectName = "${projectInfo}".split("@")[0]
              //当前的项目端口 10086
              def currentProjectPort = "${projectInfo}".split("@")[1]
               //定义镜像的名称
               def imageName = "${currentProjectName}:${tag}"
               //给镜像打上标签
               sh "docker tag ${imageName} ${harbor_url}/${harbor_project}/${imageName}"
               //把镜像推送到Harbor
               withCredentials([usernamePassword(credentialsId: "${harbor_auth}", passwordVariable: 'password', usernameVariable: 'username')]) {
                   // 登录到Harbor
                   sh "docker login -u ${username} -p ${password} ${harbor_url}"
                   //镜像的上传
                   sh "docker push ${harbor_url}/${harbor_project}/${imageName}"
    
                   sh "echo '镜像上传到Harbor仓库中成功'"
    
               }
               //删除本地镜像
               sh "docker rmi -f ${imageName}"
               sh "docker rmi -f ${harbor_url}/${harbor_project}/${imageName}"
             }
        }
    
        stage('拉取镜像和发布应用') {
            for(int i  = 0 ; i< selectedProjectNames.length ; i++) {
                   //jenkinscloud-eureka@10086
                  def projectInfo = selectedProjectNames[i];
                  //当前的项目名称 jenkinscloud-eureka
                  def currentProjectName = "${projectInfo}".split("@")[0]
                  //当前的项目端口 10086
                  def currentProjectPort = "${projectInfo}".split("@")[1]
                  for(int j =0;j<selectedServers.length;j++){
                      //获取当前遍历的服务器名称
                      def currentServerName = selectedServers[j]
                      //加上参数格式: --spring.profiles.active=eureka-server1/eureka-server2
                      def activeProfiles = "--spring.profiles.active="
                      if(currentServerName =="192.168.18.103"){
                         activeProfiles = activeProfiles + "eureka-server1"
                      }else if(currentServerName == "192.168.18.104"){
                         activeProfiles = activeProfiles + "eureka-server2"
                      }
    
                      // 定义远程执行命令
                      def execCommand = "/usr/local/deployCluster.sh $harbor_url $harbor_project $currentProjectName $tag $currentProjectPort $activeProfiles"
                      // 远程部署调用进行项目部署
                      sshPublisher(publishers: [sshPublisherDesc(configName: "${currentServerName}", transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: "${execCommand}", execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: '')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
                  }
            }
    
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
  • 相关阅读:
    JVM学习四
    PMP考试提分必刷题
    AFG EDI 解决方案
    uni-upgrade-center 升级中心详细流程
    从零开始学习 Java:简单易懂的入门指南之HashMap及TreeMap源码解读(二十四)
    PX4模块设计之三十三:Sensors模块
    灵性图书馆:好书推荐-《在荷欧波诺波诺中遇见真正的自己》
    第三章 常用Shell(Bash)命令
    生信豆芽菜-预后模型比较c-index计算
    Linux CPU拓扑
  • 原文地址:https://blog.csdn.net/m0_53157173/article/details/126798522