1 Jenkins+Docker+SpringCloud部署方案优化
- 前面部署方案存在的问题:
一次只能选择一个微服务部署。
只有一条生产部署服务器。
每个微服务只有一个实例,容错率低。
 - 优化方案:
在一个Jenkins工程中可以选择多个微服务同时发布。
在一个Jenkins工程中可以选择多台生产服务器同时部署。
每个微服务都是以集群高可用的形式部署。
 
2 Jenkins+Docker+SpringCloud集群部署流程说明

3 修改所有微服务配置
3.1 修改注册中心的配置
# 集群版spring:  application:    name: Eureka-HA---server:  port: 10086spring:  # 指定当profile=eureka-server1  profiles: eureka-server1eureka:  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: 10086spring:  profiles: eureka-server2eureka:  instance:    hostname: 192.168.18.104  client:    service-url:      defaultZone: http://192.168.18.103:10086/eureka,http://192.168.18.104:10086/eureka
- 在启动微服务的时候,加入参数
spring.profile.active来读取对应的配置。 
3.2 其他微服务配置
- 除了Eureka注册中心以外,其他微服务的配置都需要加入所有Eureka的服务。
 - 商品微服务的配置:
 
server:  port: 9001 # 微服务的端口号spring:  application:    name: service-product # 微服务的名称# 配置 eurekaeureka:  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
server:  port: 9002 # 微服务的端口号spring:  application:    name: service-order # 微服务的名称# 配置 eurekaeureka:  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
3.3 提交代码到Gitlab中
4 设计Jenkins集群项目的构建参数
4.1 Jenkins安装Extended Choice Parameter插件
4.2 创建流水线项目

4.3 添加参数
4.3.1 项目名称





4.3.2 最后效果

5 把多个项目提交SonarQube进行代码审核
//定义git凭证IDdef git_auth = "7d5c4945-2533-41e2-bd47-5dd97eb37f38"//git的url地址def git_url = "git@192.168.18.100:develop_group/jenkinscloud.git"//定义tagdef tag = "1.0"// 定义Harbor的URL地址def harbor_url = "192.168.18.102:85"// 镜像库项目名称def harbor_project = "xudaxian-mall"// Harbor的登录凭证iddef 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)])    }}
6 多个项目打包及构建镜像、上传私服
//定义git凭证IDdef git_auth = "7d5c4945-2533-41e2-bd47-5dd97eb37f38"//git的url地址def git_url = "git@192.168.18.100:develop_group/jenkinscloud.git"//定义tagdef tag = "1.0"// 定义Harbor的URL地址def harbor_url = "192.168.18.102:85"// 镜像库项目名称def harbor_project = "xudaxian-mall"// Harbor的登录凭证iddef 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)])    }}
7 完成微服务多服务器远程发布
7.1 前提说明
远程部署服务器(192.168.18.103和192.168.18.104)已安装Docker,并信任Harbor私有仓库。
关闭了防火墙。
在Publish over SSH那边增加一台远程部署服务器。

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

7.3 在192.168.18.103和192.168.18.104服务器中创建deployCluster.sh
- 在远程部署服务器的/usr/local目录下,创建deployCluster.sh文件:
 
cd /usr/local
vim deployCluster.sh
#! /bin/sh#接收外部参数harbor_url=$1harbor_project_name=$2project_name=$3tag=$4port=$5profile=$6imageName=$harbor_url/$harbor_project_name/$project_name:$tagecho "$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# 登录Harbordocker login -u xudaxian -p Xudaxian12345678 $harbor_url# 下载镜像docker pull $imageName# 启动容器docker run -di -p $port:$port $imageName $profileecho "容器启动成功"
chmod +x deployCluster.sh
7.4 修改Jenkinsfile文件
//定义git凭证IDdef git_auth = "7d5c4945-2533-41e2-bd47-5dd97eb37f38"//git的url地址def git_url = "git@192.168.18.100:develop_group/jenkinscloud.git"//定义tagdef tag = "1.0"// 定义Harbor的URL地址def harbor_url = "192.168.18.102:85"// 镜像库项目名称def harbor_project = "xudaxian-mall"// Harbor的登录凭证iddef 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)])              }        }    }}