前言

通过前几章的学习,了解了持续集成概念以及相关技术的学习和使用

接下来学习构建一整套SpringCloud微服务+Docker+Jenkins的持续集成案例

环境

架构图

jenkins docker部署springboot jenkins docker springcloud_Docker

流程说明:

  1. 开发人员将代码提交到代码托管平台,如Gitlab、Github
  2. Jenkins 从代码托管平台拉取代码、编译、打包后,通过Docker容器技术构建成镜像
  3. jenkins将构建好的镜像上传到远程容器管理平台Harbor
  4. Jenkins通过连接项目部署服务器,拉取Harbor上的项目镜像,创建容器,启动服务
  5. 用户远程访问部署服务器上的服务

服务列表

服务器名称

IP地址

安装的软件

代码托管服务器

192.168.2.217

Gitlab

持续集成服务器

192.168.2.244

Jenkins、Maven、Docker

Docker仓库服务器

192.168.2.217

Docker Harbor

项目部署服务器

192.168.2.219/192

Docker

微服务项目

项目技术

SpringCloud + SpringBoot +Mysql

微服务项目结构

jenkins docker部署springboot jenkins docker springcloud_微服务_02

  • cloud2022 父工程
  • cloud-api-common 项目公共类、通用代码
  • cloud-consume-order80 消费者,通过注册中心调用后端微服务 ,端口为80
  • cloud-eureka-server7001 Eureka技术,微服务的注册中心1, 端口为7001
  • cloud-eureka-server7002 Eureka技术,微服务的注册中心2,端口为7002
  • cloud-provide-payment8001 服务提供者1,端口为8001
  • cloud-provide-payment8002 服务提供者2,端口为8002

数据库结构

jenkins docker部署springboot jenkins docker springcloud_Docker_03

项目部分代码

@RestController
@Slf4j
public class OrderController {

    public static final String PAYENT_URL = "http://CLOUD-PAYMENT-SERVICE";
    @Resource
    private RestTemplate restTemplate;
    // 注入自定义的负载均衡规则
    @Resource
    private MyLoadBlance myLoadBalancer;

    @Resource
    private DiscoveryClient discoveryClient;

    @GetMapping (value = "/consume/payment/create")
    public CommonResult<Payment> create(Payment payment) {
        return restTemplate.postForObject(PAYENT_URL+"/payment/create",payment,CommonResult.class);
    }
    @GetMapping(value = "/consume/payment/get/{id}")
    public CommonResult<Payment> getPayment(@PathVariable("id") long id) {
        return restTemplate.getForObject(PAYENT_URL+"/payment/get/"+id,CommonResult.class);
    }

    /**
     * 测试自定义负载均衡算法,测试时,需要取消  restTemplate配置类的注解  @LoadBalanced
     * @LoadBalanced 让RestTemplate具备默认轮询的负载策略,所以会跟自定义负载策略冲突
     * @return
     */
    @GetMapping(value = "/consume/payment/lb")
    public String getPaymentLB() {
        List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");

        if (instances == null || instances.isEmpty()) {
            return null;
        }
        // 调用自定义的负载均衡策略
        ServiceInstance serviceInstance = myLoadBalancer.instance(instances);
        URI uri = serviceInstance.getUri();
        System.out.println(uri.toString());
        //http://192.168.109.1:8001/payment/lb
        return restTemplate.getForObject(uri + "/payment/lb", String.class);
    }
}

通过Http客户端工具RestTemplate工具访问Eureka注册中心的后端微服务(负载均衡)

实际测试接口

@GetMapping(value = "/consume/payment/get/{id}")

代码地址:

https://gitee.com/lucky_sungit/spring-cloud2022_jenkins

上述仓库是包含了后续的Jenkinsfile脚本以及shell脚本的完结版本的代码

本地部署

SpringCloud微服务部署

本地运行微服务

1)逐一启动微服务

2)使用 postman 测试功能是否可用

本地部署微服务

1)Springboot微服务项目打包

在每个微服务的pom.xml中导入打包插件

cloud-api-common无需配置该插件

<build>
        <!-- 定义项目的上下文 -->
        <finalName>cloud_80</finalName>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.2.1.RELEASE</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal><!--可以把依赖的包都打包到生成的Jar包中 -->
                        </goals>
                    </execution>
                </executions>
            </plugin>
    </build>

打包

如果在父pom下打包,会报错,所以需要整个install

jenkins docker部署springboot jenkins docker springcloud_微服务_04

然后clean 、package即可全部打包,或者单个微服务下打包

测试

先启动注册中心集群,然后启动服务提供者集群,最后启动消费者服务,通过http访问接口即可

Docker仓库

docker介绍以及用法暂不介绍

Harbor

简介

jenkins docker部署springboot jenkins docker springcloud_docker_05

Harbor(港口,港湾)是一个用于存储和分发Docker镜像的企业级Registry服务器。

除了Harbor这个私有镜像仓库之外,还有Docker官方提供的Registry。相对Registry,Harbor具有很多优势:

1).提供分层传输机制,优化网络传输 Docker镜像是是分层的,而如果每次传输都使用全量文件(所以用FTP的方式并不适合),显然不经济。必须提供识别分层传输的机制,以层的UUID为标识,确定 传输的对象。

2).提供WEB界面,优化用户体验 只用镜像的名字来进行上传下载显然很不方便,需要有一个用户界面可以支持登陆、搜索功能,包括区分公有、私有镜像。

3).支持水平扩展集群 当有用户对镜像的上传下载操作集中在某服务器,需要对相应的访问压力作分解。

4).良好的安全机制 企业中的开发团队有很多不同的职位,对于不同的职位人员,分配不同的权限, 具有更好的安全性。

安装

Harbor需要安装在192.168.2.219上面

1)先安装Docker并启动Docker(已完成) 参考之前的安装过程

2)再安装Docker-compose

一键安装脚本

#! /bin/bash

echo "---------开始安装---------"
sleep 1
echo "############判断是否安装了docker##############"
if ! type docker >/dev/null 2>&1; then

        cat >/etc/yum.repos.d/docker.repo<<EOF
[docker-ce-edge]
name=Docker CE Edge - \$basearch
baseurl=https://mirrors.aliyun.com/docker-ce/linux/centos/7/\$basearch/edge
enabled=1
gpgcheck=1
gpgkey=https://mirrors.aliyun.com/docker-ce/linux/centos/gpg
EOF

    echo 'docker 未安装';
        echo '开始安装Docker....';
        yum -y install docker-ce

        echo '配置Docker开启启动';
        systemctl enable docker
        systemctl start docker

cat >> /etc/docker/daemon.json << EOF
{
  "registry-mirrors": ["https://b9pmyelo.mirror.aliyuncs.com"]
}
EOF

        systemctl restart docker

else
    echo 'docker 已安装';
fi

echo "############判断是否安装了docker-compose##############"
if ! type docker-compose >/dev/null 2>&1; then
    echo 'docker-compose 未安装';
        echo '开始安装docker-compose....';
        wget http://oss.moguit.cn/script//docker-compose-Linux-x86_64
        mv docker-compose-Linux-x86_64  docker-compose
        chmod +x docker-compose
        mv docker-compose /usr/local/bin/
        docker-compose -v

else
    echo 'docker-compose 已安装';
fi

3)安装Harbor

下载安装包

https://github.com/goharbor/harbor/releases

上传服务器并解压

tar -xvf harbor-offline-installer-v1.9.2.tgz -C /opt
cd  harbor

修改Harbor的配置

vi harbor.yml

hostname: 192.168.2.217
port:8888

安装Harbor

./prepare
/inistakk.sh

启动Harbor

docker-compose up -d 启动
docker-compose stop 停止
docker-compose restart 重启

访问Harbor

http://192.168.2.217:8888

默认密码: admin Harbor12345 (可以在harbor.yml中修改)

jenkins docker部署springboot jenkins docker springcloud_Docker_06

操作

1) 创建项目

Harbor的项目分为公开和私有的:

公开项目:所有用户都可以访问,通常存放公共的镜像,默认有一个library公开项目。

私有项目:只有授权用户才可以访问,通常存放项目本身的镜像。

我们可以为微服务项目创建一个新的项目

jenkins docker部署springboot jenkins docker springcloud_微服务_07

2)创建用户

jenkins docker部署springboot jenkins docker springcloud_Docker_08

jenkins docker部署springboot jenkins docker springcloud_微服务_09

1) 给私有项目分配用户

进入cloud2022项目->成员

jenkins docker部署springboot jenkins docker springcloud_docker_10

jenkins docker部署springboot jenkins docker springcloud_docker_11

角色

权限说明

访客

对于指定项目拥有只读权限

开发人员

对于指定项目拥有读写权限

维护人员

对于指定项目拥有读写权限,创建 Webhooks

项目管理员

除了读写权限,同时拥有用户管理/镜像扫描等管理权限

测试

把镜像上传到Harbor

1)配置harbor信任列表

vi /etc/docker/daemon.json

{
  "registry-mirrors": ["https://b9pmyelo.mirror.aliyuncs.com"],
  "insecure-registries": ["192.168.2.217:8888"]
}

2)重启docker

systemctl restart docker

3)登陆harbor服务器

docker login -u admin -p harbor12345 http://192.168.2.217:8888

4)给镜像打上标签

docker tag eureka:v1 192.168.2.217:8888/cloud2022/eureka:v1

5) 推送镜像

docker push 192.168.2.217:8888/cloud2022/eureka:v1

6)拉取镜像

docker pull 192.168.2.217:8888/cloud2022/eureka:v1

7)创建实例

docker run -it -d --name=test =p 7001:7001 192.168.2.217:8888/cloud2022/eureka:v1

Jenkins持续集成

微服务项目上传到Gitlab

jenkins docker部署springboot jenkins docker springcloud_Docker_12

jenkins从Gitlab拉取源码

jenkins docker部署springboot jenkins docker springcloud_docker_13

//gitlab的凭证
def jenkins_Id="b6ccfe64-2c07-4b33-a39c-beec91d4a026"
node{
	 stage('拉取代码'){
      checkout([$class: 'GitSCM', branches: [[name: "*/${branch}"]], extensions: [], userRemoteConfigs: [[credentialsId: "${jenkins_Id}", url: "${git_url}"]]])
    }
}

拉取jenkinsfile文件

jenkins docker部署springboot jenkins docker springcloud_微服务_14

提交到SonarQube代码审查

添加两个参数

jenkins docker部署springboot jenkins docker springcloud_docker_15

每个项目的根目录下添加sonar-project.properties

sonar.projectKey=cloud-eureka-server7002
sonar.projectName=cloud-eureka-server7002
sonar.projectVersion=1.0

sonar.sources=.
sonar.exclusions=**/test/**,**/target/**
sonar.java.binaries=.

sonar.java.source=1.8
sonar.java.target=1.8
#sonar.java.libraries=**/target/classes/**

sonar.sourceEncoding=UTF-8

注意:修改sonar.projectKey和sonar.projectName

修改Jenkinsfile构建脚本

//gitlab的凭证
def jenkins_Id="b6ccfe64-2c07-4b33-a39c-beec91d4a026"
node{
	 stage('拉取代码'){
      checkout([$class: 'GitSCM', branches: [[name: "*/${branch}"]], extensions: [], userRemoteConfigs: [[credentialsId: "${jenkins_Id}", url: "${git_url}"]]])
    }
     stage('代码审查'){
        def scannerHome = tool 'sonarqube-scanner' //全局工具
        withSonarQubeEnv('sonar-env'){ //全局环境变量
           sh """
             cd ${current_project_name}
             ${scannerHome}/bin/sonar-scanner
           """
        }
      }
}

使用Dockerfile编译、生成镜像

利用dockerfile-maven-plugin插件构建Docker镜像

在每个微服务项目的pom.xml加入dockerfile-maven-plugin插件

<!--docker插件-->
            <plugin>
                <groupId>com.spotify</groupId>
                <artifactId>dockerfile-maven-plugin</artifactId>
                <version>1.3.6</version>
                <configuration>
                    <repository>${project.artifactId}</repository>
<!--                    <tag>${project.parent.version}</tag>-->
                    <buildArgs>
                        <JAR_FILE>target/${project.build.finalName}.jar</JAR_FILE>//该参数对应dockefile的 ARG
                    </buildArgs>
                </configuration>
            </plugin>

在每个微服务项目根目录下建立Dockerfile文件

#FROMjava:8
FROM openjdk:8-jdk-alpine
ARG JAR_FILE
COPY ${JAR_FILE} app.jar
EXPOSE 7002
ENTRYPOINT ["java","-jar","/app.jar"]

修改Jenkinsfile构建脚本

//gitlab的凭证
def jenkins_Id="b6ccfe64-2c07-4b33-a39c-beec91d4a026"
//定义上传的镜像标签
def tag="latest"
node{
	 stage('拉取代码'){
      checkout([$class: 'GitSCM', branches: [[name: "*/${branch}"]], extensions: [], userRemoteConfigs: [[credentialsId: "${jenkins_Id}", url: "${git_url}"]]])
    }
     stage('代码审查'){
        def scannerHome = tool 'sonarqube-scanner' //全局工具
        withSonarQubeEnv('sonar-env'){ //全局环境变量
           sh """
             cd ${current_project_name}
             ${scannerHome}/bin/sonar-scanner
           """
        }
      }
    stage("安装父工程"){//找不到父工程依赖,需要将父工程打入仓库
      sh "mvn clean install"
    }
    stage("公共子服务安装"){
      sh "mvn -f cloud-api-common clean install"
    }
    stage("构建镜像"){
      //定义镜像标签
          def imageName="${project_name}:${tag}"
          sh "mvn -f ${project_name} clean package dockerfile:build"
    }
}

注意:步骤{安装父工程} 如果出现找不到父工程依赖,需要手动把父工程的依赖上传到仓库中

上传到Harbor镜像仓库

//gitlab的凭证
def jenkins_Id="b6ccfe64-2c07-4b33-a39c-beec91d4a026"
def git_url="http://192.168.2.217:8444/CICD/springcloud2022.git"
//Harbor的凭证
def harbor_url="192.168.2.217:8888"
//定义上传的镜像标签
def tag="latest"
//Harbor项目名称
def harbor_project_name="cloud2022"
//harbor的凭证
def harbor_auth="23d2b1c5-941f-4144-8aef-3228b11f24a7"
node{
	 stage('拉取代码'){
      checkout([$class: 'GitSCM', branches: [[name: "*/${branch}"]], extensions: [], userRemoteConfigs: [[credentialsId: "${jenkins_Id}", url: "${git_url}"]]])
    }
     stage('代码审查'){
        def scannerHome = tool 'sonarqube-scanner' //全局工具
        withSonarQubeEnv('sonar-env'){ //全局环境变量
           sh """
             cd ${current_project_name}
             ${scannerHome}/bin/sonar-scanner
           """
        }
      }
    stage("安装父工程"){//找不到父工程依赖,需要将父工程打入仓库
      sh "mvn clean install"
    }
    stage("公共子服务安装"){
      sh "mvn -f cloud-api-common clean install"
    }
    stage("构建镜像"){
      //定义镜像标签
      def imageName="${project_name}:${tag}"
      sh "mvn -f ${project_name} clean package dockerfile:build"
      //给本地镜像打标签
      sh "docker tag ${imageName} ${harbor_url}/${harbor_project_name}/${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_name}/${imageName}"
          }
      //删除本地镜像
      sh "docker rmi  ${imageName}"
      sh "docker rmi  ${harbor_url}/${harbor_project_name}/${imageName}"
    }
}

使用凭证管理Harbor私服账户和密码

先在凭证建立Harbor的凭证,在生成凭证脚本代码

jenkins docker部署springboot jenkins docker springcloud_微服务_16

拉取镜像和发布应用

结构

jenkins docker部署springboot jenkins docker springcloud_微服务_17

注意:192.168.66.103服务已经安装Docker并启动安装 Publish Over SSH 插件

安装以下插件,可以实现远程发送Shell命令

jenkins docker部署springboot jenkins docker springcloud_docker_18

配置远程部署服务器

拷贝公钥到远程服务器

ssh-copy-id 192.168.2.192

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

jenkins docker部署springboot jenkins docker springcloud_微服务_19

如果path to key报错的话,填入jenkins所在主机的key

修改Jenkinsfile构建脚本

jenkins docker部署springboot jenkins docker springcloud_Docker_20

添加一个port参数

jenkins docker部署springboot jenkins docker springcloud_docker_21

stage {构建镜像步骤} 添加模板代码

sshPublisher(publishers: [sshPublisherDesc(configName: "${host}", transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: "/opt/jenkins_shell/deploy.sh $harbor_url $project_name $project_name $tag $port", execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: '')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])

编写deploy.sh部署脚本

#!/bin/sh
harbor_url=$1
harbor_project_name=$2
project_name=$3
tag=$4
port=$5
imagename=$harbor_url/$harbor_project_name/$project_name:$tag

##判断本地是都有容器
container_id=`docker ps -qa | grep $project_name | awk '{print$1}'`

if [ $container_id != "" ];then
   echo "容器存在,即将删除"
   docker rm -f $container_id
fi

##判断镜像是否存在

image_id=`docker images | grep $project_name | awk '{print$3}'`

if [ $image_id] != "" ];then
  echo "删除镜像"
  docker rmi $imagename
fi

##登陆Harbor
docker login -u admin -p Harbor12345 $harbor_url
##拉取镜像
docker pull  $imagename

##启动实例

docker  run -it -d --name=$project_name -p $port:$port $imagename
echo "容器启动成功"

上传deploy.sh文件到/opt/jenkins_shell目录下,且文件至少有执行权限!

chmod +x deploy.sh 添加执行权限

测试

部署方案优化

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

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

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

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

优化方案:

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

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

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

jenkins docker部署springboot jenkins docker springcloud_微服务_22

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

1) 安装Extended Choice Parameter插件支持多选框

jenkins docker部署springboot jenkins docker springcloud_docker_23

2)添加参数

字符串参数:分支

jenkins docker部署springboot jenkins docker springcloud_docker_24

多选框:项目名称

jenkins docker部署springboot jenkins docker springcloud_Docker_25

注意:不填的话,默认‘,’ 区分中文和英文

jenkins docker部署springboot jenkins docker springcloud_docker_26

jenkins docker部署springboot jenkins docker springcloud_docker_27

//gitlab的凭证
def jenkins_Id="b6ccfe64-2c07-4b33-a39c-beec91d4a026"
def git_url="http://192.168.2.217:8444/CICD/springcloud2022.git"
//Harbor的凭证
def harbor_url="192.168.2.217:8888"
//定义上传的镜像标签
def tag="latest"
//Harbor项目名称
def harbor_project_name="cloud2022"
//harbor的凭证
def harbor_auth="23d2b1c5-941f-4144-8aef-3228b11f24a7"

node{
    //项目名称数组
    def selectedProjects="${project_name}".split(',')
    stage('拉取代码'){
      checkout([$class: 'GitSCM', branches: [[name: "*/${branch}"]], extensions: [], userRemoteConfigs: [[credentialsId: "${jenkins_Id}", url: "${git_url}"]]])
    }
    stage('代码审查'){
      for(int i = 0;i < selectedProjects.length;i++){
        //取出每个项目的名称和端口
        def project_info = selectedProjects[i]
        //项目名称
        def current_project_name = "${project_info}".split('@')[0]
        def scannerHome = tool 'sonarqube-scanner'
        withSonarQubeEnv('sonar-env'){
           sh """
             cd ${current_project_name}
             ${scannerHome}/bin/sonar-scanner
           """
        }
      }
    }
    stage("安装父工程"){//找不到父工程依赖,需要将父工程打入仓库
      sh "mvn clean install"
    }
    stage("公共子服务安装"){
      sh "mvn -f cloud-api-common clean install"
    }
    stage("编译、构建本地镜像"){
        for(int j = 0; j < selectedProjects.length; j++){
          def current_project = selectedProjects[j]
          def current_project_name = "${current_project}".split("@")[0]
          def current_port = "${current_project}".split("@")[1]
          //定义镜像标签
          def imageName="${current_project_name}:${tag}"
          sh "mvn -f ${current_project_name} clean package dockerfile:build"
          //给本地镜像打标签
          sh "docker tag ${imageName} ${harbor_url}/${harbor_project_name}/${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_name}/${imageName}"
          }
          //删除本地镜像
          sh "docker rmi  ${imageName}"
          sh "docker rmi  ${harbor_url}/${harbor_project_name}/${imageName}"
          //远程调用
          sshPublisher(publishers: [sshPublisherDesc(configName: 'master', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: "/opt/jenkins_shell/deploy.sh $harbor_url $harbor_project_name $current_project_name $tag $current_port", execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: '')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
            echo "${current_project_name}完成编译,构建镜像"
          }
        }
    }
}

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

1) 配置远程部署服务器

拷贝公钥到远程服务器

ssh-copy-id 192.168.66.104

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

jenkins docker部署springboot jenkins docker springcloud_微服务_28

3)修改Docker配置信任Harbor私服地址 重启Docker

4)添加参数

多选框:部署服务器

jenkins docker部署springboot jenkins docker springcloud_微服务_29

jenkins docker部署springboot jenkins docker springcloud_docker_30

jenkins docker部署springboot jenkins docker springcloud_Docker_31

最终效果

jenkins docker部署springboot jenkins docker springcloud_docker_32

5) 修改Jenkinsfile构建脚本

//gitlab的凭证
def jenkins_Id="b6ccfe64-2c07-4b33-a39c-beec91d4a026"
def git_url="http://192.168.2.217:8444/CICD/springcloud2022.git"
//Harbor的凭证
def harbor_url="192.168.2.217:8888"
//定义上传的镜像标签
def tag="latest"
//Harbor项目名称
def harbor_project_name="cloud2022"
//harbor的凭证
def harbor_auth="23d2b1c5-941f-4144-8aef-3228b11f24a7"

node{
    //项目名称数组
    def selectedProjects="${project_name}".split(',')
    //定义主机
    def servers="${server}".split(',')
    stage('拉取代码'){
      checkout([$class: 'GitSCM', branches: [[name: "*/${branch}"]], extensions: [], userRemoteConfigs: [[credentialsId: "${jenkins_Id}", url: "${git_url}"]]])
    }
    stage('代码审查'){
      for(int i = 0;i < selectedProjects.length;i++){
        //取出每个项目的名称和端口
        def project_info = selectedProjects[i]
        //项目名称
        def current_project_name = "${project_info}".split('@')[0]
        def scannerHome = tool 'sonarqube-scanner'
        withSonarQubeEnv('sonar-env'){
           sh """
             cd ${current_project_name}
             ${scannerHome}/bin/sonar-scanner
           """
        }
      }
    }
    stage("安装父工程"){//找不到父工程依赖,需要将父工程打入仓库
      sh "mvn clean install"
    }
    stage("公共子服务安装"){
      sh "mvn -f cloud-api-common clean install"
    }
    stage("编译、构建本地镜像"){
        for(int j = 0; j < selectedProjects.length; j++){
          def current_project = selectedProjects[j]
          def current_project_name = "${current_project}".split("@")[0]
          def current_port = "${current_project}".split("@")[1]
          //定义镜像标签
          def imageName="${current_project_name}:${tag}"
          sh "mvn -f ${current_project_name} clean package dockerfile:build"
          //给本地镜像打标签
          sh "docker tag ${imageName} ${harbor_url}/${harbor_project_name}/${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_name}/${imageName}"
          }
          //删除本地镜像
          sh "docker rmi  ${imageName}"
          sh "docker rmi  ${harbor_url}/${harbor_project_name}/${imageName}"
          for(int i = 0; i < servers.length; i++){
            def host = servers[i]
            echo host
            //远程调用
            sshPublisher(publishers: [sshPublisherDesc(configName: "${host}", transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: "/opt/jenkins_shell/deploy.sh $harbor_url $harbor_project_name $current_project_name $tag $current_port", execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: '')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
            echo "${current_project_name}完成编译,构建镜像"
          }
        }
    }
}

注意:多台部署服务器都需要创建目录以及脚本文件

/opt/jenkins_shell/deploy.sh

最终测试

jenkins docker部署springboot jenkins docker springcloud_docker_33