• 还在手动发包?手把手教你 Jenkins 自动化部署SpringBoot



    大家好,我是一航!

    程序员每天的工作,几乎都是围绕着开发打包发布测试这些点循环往复,但这些过程中,有很大一部分重复性的苦力活,不仅消耗了我们大量的时间,但有不得不做;纯手工去集成、部署,既苦力,还容易出错;此时就需要通过自动化,来解放双手。

    在做自动化集成、部署前,先引入两个概念CI/CD,其核心分为三点:

    • 持续集成(Continuous integration)

      频繁地(一天多次)将代码集成到主干。 每次集成都通过自动化的构建(包括编译、发布、自动化测试)来验证,从而尽快地发现集成错误。

    • 持续交付(Continuous delivery)

      频繁地将软件的新版本,交付给质量团队或者用户,以供评审。如果评审通过,代码就进入生产阶段。

    • 持续部署(Continuous deployment)

      持续部署是持续交付的下一步,指的是代码通过评审以后,自动部署到生产环境。

    Jenkins就是专门为CI/CD而生的一款Java开源软件,因功能强大跨平台插件丰富扩展性强分布式等特点,深受大家的喜爱;

    但是,好用归好用,可他对新手来说稍微有一点点不太友好,环境部署总是会遇到一些莫名其妙的问题,哪怕是我部署过这么多次了,但每一次重新部署的时候,总会遇到各种各样不同的问题;交流群里面经常有铁子在交流Jenkins,这里就索性带从0开始,带大家完整部署一套 Jenkins 自动化环境。

    本次的目标,就是将我们从集成、部署的苦力活中解放出来,专心写代码就好了

    文章尽可能的细化到每个细节,稍微有点点长,建议收藏,以便日后查看;

    准备

    本文所有的部署都是在CentOS 7上完成,软件安装都采用的是Docker,如果你想跟着这个教程搭建,需要准备一下环境:

    • 2台CentOS 7 的电脑 / 虚拟机

      没有物理机的,可以使用虚拟机的方式来搭建,本文就是采用的虚拟机来配置;

      CentOS 7 虚拟机搭建教程:https://blog.lupf.cn/articles/2020/04/04/1586001434581.html

      以下两台测试机器的IP和功能点

      • 192.168.1.253 安装Jenkins、GitLab
      • 192.168.1.237 运行打包后的项目
    • Docker 安装

      服务编排采用的docker-compose

      安装教程:https://blog.lupf.cn/articles/2019/11/23/1574503815568.html

    • JDK安装

      全版本JDK下载:https://blog.lupf.cn/category/jdkdl

    • Maven安装

      下载地址:https://maven.apache.org/download.cgi

      wget http://dlcdn.apache.org/maven/maven-3/3.8.6/binaries/apache-maven-3.8.6-bin.tar.gz
      tar -zxvf apache-maven-3.8.6-bin.tar.gz
      mv apache-maven-3.8.6 /usr/local/
      
      • 1
      • 2
      • 3
    • SSH工具

      用于远程Linux服务器,虚拟机,直接操作起来比较的麻烦;

      之前给大家推荐过多款SSH;【5款SSH工具】、【】

    软件安装

    由于是工具类的软件,为了减少对机器的侵入, Jenkins 和代码仓库 GitLab的安装都是采用的Docker的方式,所以请提前安装好Docker

    Jenkins

    安装

    • 准备挂载目录

      用于持久化数据

      mkdir -p /var/jenkins/data
      chmod 777 /var/jenkins/data
      cd /var/jenkins/data
      
      • 1
      • 2
      • 3
    • 查看Docker的组ID

      **重要!**这个步骤很重要,因为后续Jenkins使用的是宿主机的 Docker ,所以在启动的时候,就需要做好Docker的映射以及权限组ID的配置;

      cat /etc/group | grep docker
      
      • 1

      每台电脑都会有所差异,下面是我两台虚拟机对应的ID;

      记住你装Jenkins那一台的ID,在下个步骤配置docker-compose时,需要在group_add中配置上对应的ID;

      这里务必要配置正确,否则后面在Jenkins容器中使用Docker命令时,会报权限不足的错误。

    • 准备docker-compose-jenkins.yml配置文件

      vim docker-compose-jenkins.yml  
      
      • 1

      添加以下配置

      version: '2'
      services:
        jenkins:
          container_name: 'jenkins'
          image: 'jenkins/jenkins'
          restart: always
          environment:
            TZ: 'Asia/Shanghai'
          ports:
            - '8880:8080'
            - '50000:50000'
          group_add:
            - 994
          volumes:
            - /var/run/docker.sock:/var/run/docker.sock
            - /usr/bin/docker:/usr/bin/docker
            - /var/jenkins/data:/var/jenkins_home
            - /usr/local/apache-maven-3.8.6:/usr/local/apache-maven-3.8.6
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18

      参数说明

      • ports 映射的端口

      • restart 自启动

      • group_add Docker的组ID,就是前一个步骤获取出来的ID

      • volumes 挂在目录

        /usr/bin/docker:/usr/bin/docker ,将宿主机的Docker挂在给Jenkins,方便后续在Jenkins中使用宿主机Docker,就不需要在Jenkins容器中再安装一个Docker了;

        /var/jenkins/data:/var/jenkins_home 将Jenkins的工作目录/var/jenkins_home挂在到宿主机的/var/jenkins/data的目录下;

        /usr/local/apache-maven-3.8.6:/usr/local/apache-maven-3.8.6 挂在的Maven,可以根据个人使用的调整;

    • 启动

      # 启动
      docker-compose -f docker-compose-jenkins.yml up -d
      # 查看容器
      docker ps | grep jenkins
      
      • 1
      • 2
      • 3
      • 4

    初始化

    容器启动,大约等1分钟之后,就可以来初始化Jenkins了

    • 访问jenkins

      直接在浏览器访问http://你jenkins机器的ip:8880,我本地地址:http://192.168.1.253:8880

    • 查看初始密码

      默认密码是保存在/var/jenkins_home/secrets/initialAdminPassword

      获取命令如下:

      docker exec -it jenkins cat /var/jenkins_home/secrets/initialAdminPassword
      
      • 1

      命令的意思是在名称为jenkins的Docker容器中执行cat /var/jenkins_home/secrets/initialAdminPassword;也可以在本地挂在的目录下去查找;

    安装推荐插件

    安装插件

    在初始化的时候,默认给我们安装了一些插件,后续还需要用到一些插件,这里提前安装一下;

    安装入口:系统管理–>插件管理–>可选插件

    注意!所有插件在重启之后才会生效

    • 汉化插件

    • Maven插件

      本文演示的下项目是通过Maven管理,搜索Maven Integration安装Maven相关插件

    • Git 插件

    • GitLab插件

    • SSH 插件

      用于将文件上传到远端服务器

    • webhook

    全局配置

    主要是配置一些基础的环境,比如Maven、Git、JDK等工具,便于后续构建的时候,能够直接使用这些全局的基础配置

    • Maven配置

      这里的Maven是容器启动时,映射的宿主机的包,如果你是下载的其他的版本或者映射的其他路径,请根据实际情况调整;

      Maven优化

      可以通过配置 MAVEN_OPTS 避免编译复杂项目时出现内存泄漏等问题

    • JDK配置

      Docker安装的Jenkins容器自带了JDK,所以这里只需要拿到容器内的JDK路径,配置上即可;获取当时如下:

      # 进去容器
      docker exec -it jenkins /bin/bash
      # 输出JDK的环境变量
      echo $JAVA_HOME
      
      • 1
      • 2
      • 3
      • 4

    • SSH Server配置

      这里配置的是远端服务器的信息(也就是代码运行的服务器信息);

    Gitlab

    GitLab 是一个用于仓库管理系统的开源项目,使用 Git 作为代码管理工具,并在此基础上搭建起来的Web服务。

    介于很多人在实际工作的时候,都是使用GitLab来搭建的私有代码仓库,这里就使用GitLab来演示;当然你使用GithubGitee也是可以的,不过由于本地Jenkins没有公网IP,后续配置webhook的时候,如果使用Github、Gitee的话,就需要做内网穿透,又会增加一些麻烦,所以这里索性就直接安装一个本地GitLab,方便后续的使用,反正Docker安装起来也比较的容易。

    安装

    • 搜索中文版的gitlab-ce-zh

      docker search gitlab-ce-zh
      
      • 1

      这里是用的是twang2218/gitlab-ce-zh镜像

    • 准备持久化目录

      mkdir -p /var/gitlab
      
      • 1
    • 准备docker-compose

      version: '2'
      services:
          gitlab:
            container_name: 'gitlab-ce-zh'
            image: 'twang2218/gitlab-ce-zh'
            restart: unless-stopped
            hostname: 'gitlab'
            environment:
              TZ: 'Asia/Shanghai'
              GITLAB_OMNIBUS_CONFIG: |
                external_url 'http://192.168.1.253:880'
            ports:
              - '880:880'
              - '8443:443'
              - '2212:22'
            volumes:
              - /var/gitlab/etc:/etc/gitlab
              - /var/gitlab/log:/var/log/gitlab
              - /var/gitlab/data:/var/opt/gitlab
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19

      其中external_url中的IP为你本地机器的IP;其中的端口和容器映射的端口要保持一致,否则将无法访问。

    • 启动

      docker-compose -f docker-compose-gitlab.yml up -d
      docker ps | grep gitlab
      
      • 1
      • 2

      状态变成healthy,说明服务已经正常。

    初始化

    • 访问

      访问上一步的external_url(本机:http://192.168.1.253:880)

    • 设置管理员密码

      设置完之后,就使用root和你新设置的密码,登录即可进入gitlab的主页

    配置Jenkins、GitLab关联

    由于后续Jenkins需要自动在Gitlab中获取最新的代码,因此,需要提前配置身份认证令牌。

    GitLab令牌

    • 创建

      hYw-Qy6KxGFsdzGA96Ux
      
      • 1

      每次创建的都会不一样;而且,这个令牌只会显示一次,之后就不会再显示了,所以请保存好;否则就只能重新生成了;

    Jenkins配置GitLab的凭据

    将前面步骤生成的GitLab令牌配置成Jenkins的全局凭据,以方便后续的使用。

    • 选择管理凭据

    • 点击Jenkins

    • 点击全局凭据

    • 点击添加凭据

    • 输入Token

      选择GitLab API token,然后输入前一步在GitLab中创建好的token

    • 创建成功

      点击Create按钮即可创建凭据

    Jenkins配置GitLab的基础信息

    构建Maven项目

    准备项目

    • 测试源码

      本教程使用的源码:https://github.com/vehang/ehang-spring-boot

    • 本地GitLab创建一个项目

      源码创建了一个jenkins分支,推送到本地Gitlab仓库

    准备脚本

    此脚本的作用就是将Jenkins传到运行服务器的Jar包给跑起来;

    单模块的项目脚本写起来会比较的简单;当我在群里面说在写Jenkins教程的时候,有群友专门@我,说要求把多模块的写一下,然后这里的脚本就变长了;因为多模块的情况稍微复杂一些,比如:一个项目有10个模块,本次提交,仅仅修改了1个模块,其他9个模块没做任何改动,就不需要发布那9个,仅仅更新有修改的哪一个包就好了;

    下面只是一个最基础的脚本,个人可以根据实际的使用过程,再进行调整;

    #!/bin/sh
    
    # JDK的环境变量
    export JAVA_HOME=/usr/local/jdk-11.0.14
    export PATH=$JAVA_HOME/bin:$PATH
    
    # 基础路径,由参数传入
    # 多模块的时候,需要在路径中使用*统配一下多模块
    # 比如/opt/ehang-spring-boot是多模块,下面由module1和module2
    # 那么执行shell的时候使用:sh restart.sh /opt/ehang-spring-boot/\*  注意这里的*需要转义一下
    JAR_BATH=$1
    echo "基础路径:"$JAR_BATH
    JAR_PATH=${JAR_BATH}/target/*.jar
    
    # 获取所有的JAR 开始遍历
    for JAR_FILE in $JAR_PATH
    do
    if [ -f $JAR_FILE ]
    then
      echo ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"
      echo "JAR路径:"$JAR_FILE
      JAR_FILE_MD5=${JAR_FILE}.md5
    
      # 用于标记是否需要重启的标识
      RESTART=false
    
      # 判断MD5文件是否存在,存在就校验MD5值
      if [ -f $JAR_FILE_MD5 ]; then
        # 校验MD5
        md5sum --status -c $JAR_FILE_MD5
        # = 0表示校验成功 =1 表示校验失败
        if [ $? = 1 ];then
          echo "MD5校验失败,安装包已经更新!"
          RESTART=true
        else
          echo "安装包没有更新!"
        fi
      else
        echo "没有MD5值,说明是第一次启动"
        RESTART=true
      fi
    
      # 获取进程号
      PROCESS_ID=`ps -ef | grep $JAR_FILE | grep -v grep | awk '{print $2}'`
      # 如果不需要重启,但是进程号没有,说明当前jar没有启动,同样也需要启动一下
      if [ $RESTART == false ] && [ ${#PROCESS_ID} == 0 ] ;then
         echo "没有发现进程,说明服务未启动,需要启动服务"
         RESTART=true
      fi
    
      # 如果是需要启动
      if [ $RESTART == true ]; then
          # kill掉原有的进程
          ps -ef | grep $JAR_FILE | grep -v grep | awk '{print $2}' | xargs kill -9
    
          #如果出现Jenins Job执行完之后,进程被jenkins杀死,可尝试放开此配置项
          #BUILD_ID=dontKillMe
          #启动Jar
          nohup java -jar $JAR_FILE  > ${JAR_FILE}.log 2>&1 &
          # =0 启动成功 =1 启动失败
          if [ $? == 0 ];then
              echo "restart success!!! process id:" `ps -ef | grep $JAR_FILE | grep -v grep | awk '{print $2}'`
          else
              echo "启动失败!"
          fi
    
          # 将最新的MD5值写入到缓存文件
          echo `md5sum $JAR_FILE` > $JAR_FILE_MD5
      fi
      echo "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"
      echo ""
    fi
    done
    
    • 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

    脚本细节,注释已经写的很清楚了;简单说一下脚本的流程:

    1. 执行脚本传入单模块/多模块的路径

      注意:多模块的时候,需要使用*通配一下各个模块,执行命令的时候,需要通过\*转义一下;详情见下图

    2. 遍历目录中target目录下的所有jar包

    3. 校验MD5,MD5没有或者对不上,说明更新了,否则对应包没有更新

    4. 当不需要更新时,校验进程是否存在,如果进程不存在,同样需要启动

    5. 启动jar

    6. 将最新的MD5值缓存起来

    以下仅仅是后续测试截图,此处可以跳过,继续下一步

    Jenkins创建Maven任务

    • 创建Maven任务

    Maven任务配置

    设置gitlab凭据

    选择前面步骤配置的凭据

    设置GitLab项目地址

    首次配置,这里需要先添加Git仓库的凭据信息,步骤如下:

    配置构建命令

    clean package  -DskipTests=true
    
    • 1

    注意这里本身就是使用的Maven构建,所以命令不需要在前面加上mvn

    构建完后上传文件

    上传脚本

    在上传Jar包之前,我们需要将项目的启动/重启脚本传到服务器,方便后续jar上传完之后,就能直接执行了

    • Transfer Set Source files:表示要传输的文件/文件规则

    • Remove prefix:要去掉文件的前缀,比如以上配置,默认文件路径是:script/jenkins/abc.sh,在这里配置script/jenkins/就只会将abc.sh拷贝到远端目录,否则,script/jenkins/abc.sh 完整的拷贝过去。

    • Remote directory:拷贝的远端路径,如果不配置,就是有SSH Server中配置的路径,我ssh server配置的远端路径为/opt/jenkins/package

    • Exec command:拷贝完之后,执行的脚本

    上传Jar包

    由于项目是多模块的,打包之后,每个模块都会打出一个jar包,分别放在各个模块的target目录下;我们需要作的就是将所有模块的Jar都拷贝到服务器并启动;

    上传成功之后,会在/opt/jenkins/package目录下看到所有的包信息:

    拷贝完之后,将执行shell脚本,单模块和多模块执行的命令仅仅为参数上的区别:

    • 单模块

      spring-boot-001-hello-world/target/spring-boot-001-hello-world-0.0.1-SNAPSHOT.jar为单模块的包进行测试,执行的重启脚本为:

      sh /opt/jenkins/package/jenkins_restart.sh /opt/jenkins/package/spring-boot-001-hello-world
      
      • 1

      其中参数/opt/jenkins/package/spring-boot-001-hello-world为单模块的根目录

    • 多模块

      多模块仅仅只是路径不同而已

      sh /opt/jenkins/package/jenkins_restart.sh /opt/jenkins/package/\*
      
      • 1

      其中/opt/jenkins/package/\*用于指明/opt/jenkins/package路径下的所有子模块Jar包都重启;其中\*表示通配。

    注意:由于这里执行了shell脚本,所以配置的时候,一定要在高级选项中将Exec in pty勾选上,否则shell命令执行不会终止,直到超时结束,如下日志:

    ....
    SSH: Connecting with configuration [centos_server] ...
    SSH: Disconnecting configuration [centos_server] ...
    ERROR: Exception when publishing, exception message [Exec timed out or was interrupted after 120,000 ms]
    Build step 'Send files or execute commands over SSH' changed build result to UNSTABLE
    Finished: UNSTABLE
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    开启控制台的输出日志

    初次配置的时候,容易出现异常情况,为了方便排查问题,可以勾上下图左侧的选项,输出远端服务器执行日志;下图右侧中的日志就是远端执行shell脚本的输出;不勾选将不会有这些日志。

    手动构建

    选择项目,点击“立即构建”即可开始,执行完如果是绿色,说明构建成功,红色表示失败。

    自动构建

    Jenkins任务开启监听

    前面的手动构建已经完成了,我们最终的目的是希望能够做到代码一提交,就自动构建并发布。

    Jenkins中开启Gitlab监听,并设置一个token(可以不设置)

    配置好之后,得到了以下的信息:

    • 钩子地址
      http://192.168.1.253:8880/project/ehang-spring-boot

    • token

      97a6a4c1601cebe83241a08a67fd3755

      token是为了安全性,也可以不设置,根据个人的需要来定

    GitLab配置钩子

    找到项目之后,按着上图的步骤,将jenkins的地址、token以及触发条件配置好后,点击添加按钮;

    手动触发事件

    成功添加之后,可以通过测试(Test)按钮,可以手动测试一下事件触发,如果出现10的提醒,说明触发成功;

    然后前往Jenkins查看,即可发现自动多了一条构建任务,触发条件是:Started by GitLab push by Administator

    自动触发事件

    上面通过手动触发的方式,已经能够正常构建了,下面就来通过git提交一段代码,看能否自动触发

    动图中可以看到,当代码成功push2秒之后,Jenkins就已经自动开始构建任务了

    执行完之后,查看Jenkins的日志与Linux的服务进程,服务已经正常启动。

    使用Docker部署

    上面演示了Maven + SSH 的构建与部署,以直接运行Jar的方式,部署到服务器;当服务容器化之后,以Docker的方式将更方便管理,下面就来说一下,如何将项目构建成Docker镜像,来部署服务;

    阿里云镜像仓库

    有Docker镜像,就需要有仓库管理,本地局域网使用,可以通过Harbor搭建私有仓库,不过要想能在公网下使用,就必须有公网IP;

    其实也可以不用那么麻烦,阿里云为我们提供了免费的镜像仓库,Jenkins服务器可以将打好的镜像直接推送到阿里云的仓库,业务服务器直接在阿里云仓库拉取镜像,运行起来即可;

    • 地址

      https://cr.console.aliyun.com/cn-shenzhen/instances

    • 创建命名空间

      免费3个

    • 创建仓库

    • Docker登录阿里云仓库

      上一步,创建完仓库之后,根据引导,在服务器登录阿里云的Docker仓库

      docker login --username=你的用户名 registry.cn-guangzhou.aliyuncs.com
      
      • 1

      Jenkins共享的Docker,也需要进入容器类登录一下,由于容器内用户不一样,所以在宿主机上登录的,容器内无法使用

      # 进入容器
      docker exec -it jenkins /bin/bash
      # 容器内登录
      docker login --username=你的用户名 registry.cn-guangzhou.aliyuncs.com
      
      • 1
      • 2
      • 3
      • 4

    准备脚本

    在Maven构建完之后,就需要构建对应的 Docker 镜像,并将镜像推送到远端服务器,因此这里需要提前准备好一些脚本,以方便构建时使用,过程如下:

    • Jenkins本地构建Docker镜像
    • 将镜像推送的阿里云仓库
    • 推送下载脚本、启动脚本到运行服务器
    • 执行下载、启动脚本

    脚本详情如下:

    Dockerfile

    用来构建镜像时用的

    FROM openjdk:8
    
    # 同步时区
    ENV TZ=Asia/Shanghai
    RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
    
    # 将当前目录下的jar拷贝到容器类
    ADD ./*.jar /app.jar
    
    # 监听端口
    EXPOSE 8081
    
    # 启动
    ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom" \
    ,"-XX:+UnlockExperimentalVMOptions","-XX:+UseCGroupMemoryLimitForHeap" \
    ,"-jar", "/app.jar" ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    构建脚本

    Jenkins任务使用!

    docker-image-build.sh

    构建镜像时执行的shell脚本;

    # 考虑到多模块的情况 这里创建一个临时目录,来汇总配置
    rm -rf ./tmp
    mkdir ./tmp
    
    # 将脚本 jar包拷贝的临时目录中
    cp ./spring-boot-001-hello-world/docker/* ./tmp
    cp ./spring-boot-001-hello-world/target/*.jar ./tmp
    cd ./tmp
    
    # 构建镜像
    docker build -t registry.cn-guangzhou.aliyuncs.com/ehang_jenkins/ehang-sping-boot-hello-world:latest .
    # 将镜像推送到阿里云
    docker push registry.cn-guangzhou.aliyuncs.com/ehang_jenkins/ehang-sping-boot-hello-world:latest
    
    # 删除临时文件
    cd ..
    rm -rf ./tmp
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    镜像下载脚本

    运行服务器使用;

    docker-image-pull.sh;用于运行服务器下载最新的镜像脚本

    # 更新最新的镜像
    docker pull registry.cn-guangzhou.aliyuncs.com/ehang_jenkins/ehang-sping-boot-hello-world:latest
    
    • 1
    • 2

    docker-compose配置

    运行服务器使用;

    docker-compose.yaml

    模块的服务编配配置,用于启动/重启Docker容器

    version: '2'
    services:
      ehang-hello-world:
        container_name: ehang-hello-world
        image: registry.cn-guangzhou.aliyuncs.com/ehang_jenkins/ehang-sping-boot-hello-world:latest
        restart: always
        volumes:
          - /opt/ehang:/opt/ehang
        ports:
          - "8081:8081"
        environment:
          - --spring.profiles.active=dev
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    多模块构建脚本

    Jenkins任务使用!

    用于遍历构建所有的子模块

    #!/bin/sh
    
    BUILD_SHELL_PATH=./*/docker/docker-image-build.sh
    
    # 获取所有的JAR 开始遍历
    for BUILD_SHELL in $BUILD_SHELL_PATH
    do
    if [ -f $BUILD_SHELL ]
    then
      echo ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"
      echo "构建脚本:"$BUILD_SHELL
      sh $BUILD_SHELL
      echo "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"
      echo ""
    fi
    done
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    启动/重启脚本

    运行服务器使用;

    jenkins_restart_docker.sh

    用于所有模块打包并随送到Docker仓库之后,启动/重启Docker容器的脚本;

    #!/bin/sh
    
    # 基础路径,由参数传入
    # 多模块的时候,需要在路径中使用*统配一下多模块
    # 比如/opt/ehang-spring-boot是多模块,下面由module1和module2
    # 那么执行shell的时候使用:sh restart.sh /opt/ehang-spring-boot/\*  注意这里的*需要转义一下
    BASE_PATH=$1
    echo "基础路径:"$BASE_PATH
    DOCKER_COMPOSE_FILES=${BASE_PATH}/docker/docker-compose.yaml
    
    DOCKER_PULL_SHELL=${BASE_PATH}/docker/docker-image-pull.sh
    
    # 获取最新的镜像
    for PULL_SHELL in $DOCKER_PULL_SHELL
    do
    if [ -f $PULL_SHELL ]
    then
      echo ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"
      echo "更新最新的镜像:"$PULL_SHELL
      sh $PULL_SHELL
      echo "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"
      echo ""
    fi
    done
    
    # 启动镜像
    for DOCKER_COMPOSE_FILE in $DOCKER_COMPOSE_FILES
    do
    if [ -f $DOCKER_COMPOSE_FILE ]
    then
      echo ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"
      echo "docker compose 配置路径:"$DOCKER_COMPOSE_FILE
    
      docker-compose -f $DOCKER_COMPOSE_FILE up -d
    
      echo "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"
      echo ""
    fi
    done
    
    • 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

    所有的脚本,功能都很单一,逻辑也比较的简单,看看注释,很容易就看明白了;

    Maven任务配置

    使用Docker部署的步骤和前面使用SSH上传的步骤差不多,唯一的区别就在Maven打包完之后,脚本执行的差异,所以关于创建Maven任务设置GitLab凭据设置GitLab地址以及配置构建命令这些一样的配置,这里就不再重复了,忘了的可以翻回去看看;

    这里详细说一下不同的地方!

    构建后的操作

    构建Docker镜像

    Jenkins 执行本地构建所有子模块Docker镜像的脚本jenkins_docker_build.sh

    上传基础脚本

    上传启动/重启模块的脚本,这里的配置使用通配符上传了所有脚本,但服务器用到的只有jenkins_restart_docker.sh

    上传所有模块Docker相关的脚本

    这里上传的主要是各个子模块服务编排的配置以及下载最新镜像的脚本

    上传成功之后,开始执行jenkins_restart_docker.sh,后面的参数需要根据项目结构来定,单模块,直接传项目的根路径;多模块需要使用\*通配所有子模块

    手动构建、自动触发

    手动构建、自动触发和前面配置的方式一样,这里就不再重复了;

    由于资源有限,下面使用了两个子模块进行构建测试;成功之后,服务端的Docker出现以下两个容器,说明服务已经正常了。

    异常情况

    • Docker命令权限不否

      dial unix /var/run/docker.sock: connect: permission denied
      
      • 1

      解决方案:Jenkins启动时,group_add未配置或者配置有误,配置正确后即可正常;

    • denied: requested access to the resource is denied

      上传/下载镜像时,错误

      解决方案:需要进入 Jenkins 容器,登录一下Docker仓库;

    Pipeline流水线

    前面了Maven任务操作下来,可以将其看成一个框架,基于那个框架,去填入个性化的东西;这种构建方式,几乎能满足我们大部分的需求场景了;Pipeline流水线提供的就一种更加灵活的构建方式;

    什么是Pipeline?

    pipeline,即流水线,是jenkins2.X的新特性,是jenkins官方推荐使用的持续集成方案。与传统的自由风格项目不同,它是通过 jenkins DSL 编写代码来实现。相比于之前用户只能通过Web界面进行配置的方式来定义Jenkins任务,现在通过使用 jenkins DSLGroovy 语言编写程序,用户可以定义流水线并执行各种任务。

    简单测试

    Pipeline 是Jinkins 一块比较大的功能点,涉及的内容很多,由于时间关系,这里就先不深入了,后续再针对 Pipeline 再进行详细的讲解。

    下面就简单演示一下,通过Pipeline 编译打包构建Docker镜像的过程;

    • 创建Pipeline任务

    • 配置脚本

      pipeline {
          agent any
          stages {
              stage('下载源码'){
                  options { timeout(time: 60, unit: 'SECONDS') }
                  steps {
                      git branch: 'jenkins',
                      credentialsId: '6b7f5874-52b0-4859-ab83-14b20d68db50',
                      url: 'http://192.168.1.253:880/root/ehang-spring-boot.git'
                  }
                  
              }
              stage('Maven编译'){
                  steps {
                      sh '''
                        /usr/local/apache-maven-3.8.6/bin/mvn clean package -DskipTests=true
                      '''
                  }
              }
              
              stage("构建/上传镜像"){
                  steps {
                      sh '''
                        sh ./script/jenkins/jenkins_docker_build.sh
                      '''
                  }
              }
          }
      }
      
      • 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
      • git branch 表示代码分支的名称
      • credentialsId GitLab凭据ID,在 系统管理–>凭据中添加或者查看
    • 测试

    总结

    虽然花了这么长的篇幅来写这个教程,但也只是讲解了一些最基本,最常用的功能;Jenkins的功能实在太强了,原本计划本文会将Pipeline以及自动化测试相关一并整理了,但一路写下来,发现基础的内容就占了如此大的篇幅,决定剩下的部分就留到下一篇文章再来写吧!

    希望本文能够让你的日常工作变的更加“丝滑”;

    我是一航!码字不易!求个三连!

  • 相关阅读:
    2023-11-9
    641. 设计循环双端队列 队列模拟
    【面试题】什么是跨域?及7种跨域解决方法
    SpingBoot整合Quartz框架实现动态定时任务(支持实时增删改查任务)
    STLINK-V3 STDC14座转2.54mm排针转接板Kicad工程
    【Router】PC连接到路由LAN,但是无法获取到IP地址问题分析及解决方案
    volatile 变量是什么?volatile 变量和 atomic 变量有什么不同?
    「Python循环结构」利用while循环求1~n的平方和
    C 练习实例75-输入一个整数,并将其反转后输出
    【数据结构】绪论
  • 原文地址:https://blog.csdn.net/lupengfei1009/article/details/126495241