第 5 章 DevOps 实践

作者: admin 分类: ARM64 Linux CICD 研发 发布时间: 2022-08-27 11:59

前面几个章节介绍了跟 DevOps 生态圈相关的一些应用工具,下面借助这些应用工具来实现 DevOps 生产作业。另外,以下实践中会直接使用 kubernetes 和 docker 应用工具,而不会过多地介绍其原理,可自行查阅相关资料。

5.1、DevOps 概念

软件开发最初是由两个团队组成:

  1. 开发者组成的团队,专门负责软件的设计和代码编写,并且不断迭代更新代码。
  2. 运维者组成的团队,测试人员包括在运维团队中,专门负责将开发团队研发的软件进行测试部署和反馈迭代。

看似两个目标不同的团队需要协同完成一个软件的开发。在开发团队指定好计划并完成 coding 后,需要提供到运维团队。运维团队向开发团队反馈需要修复的 BUG 以及一些需要返工的任务。这时开发团队需要经常等待运维团队的反馈。这无疑延长了整个软件开发的周期。

基于现在的互联网现状,更推崇敏捷式开发,这样就导致项目的迭代速度更快,但是由于开发团队与运维团队的沟通问题,会导致新版本上线的时间成本很高。这又违背了敏捷式开发的最初目的。

实际上这种现状在历史上维持了很长一段时间,包括现在依然有很多传统企业保持着这种工作模式。

随着 IT 生态圈的不断发展,引领 IT 行业的一些高科技公司迫切需要打通开发团队与运维团队,降低二者之间的时间成本,DevOps 开发运维模式应运而生,并且随着 Docker 和 Kubernetes 的出现,可以将 DevOps 理念很好地执行落地。

DevOps 字面意思是 Development & Operations 的缩写,也就是开发 & 运维。当然这个 DevOps 里面隐含测试。

devops_log

这表明 DevOps 是一个不断提高效率并且持续不断工作的过程。DevOps 的方式可以让公司能够更快地应对更新和市场发展变化,开发可以快速交付,部署也更加稳定。核心就在于简化 Dev 和 Ops 团队之间的流程,使整体软件开发过程更快速。

整体的软件开发流程包括:

  • PLAN:开发团队根据客户的需求制定开发计划。
  • CODE:根据 PLAN 将业务流程转化成代码逻辑,需要将不同版本的代码存储在一个库中。
  • BUILD:编码完成后,需要将代码构建并且运行。
  • TEST:成功构建项目后,需要测试代码是否存在 BUG 或错误。
  • DEPLOY:代码经过手动测试和自动化测试后,认定代码已经准备好部署并且交给运维团队。
  • OPERATE:运维团队将代码部署到生产环境中。
  • MONITOR:项目部署上线后,需要持续的监控产品。
  • INTEGRATE:然后将监控阶段收到的反馈发送回 PLAN 阶段,整体循环的流程就是 DevOps 的核心,即持续集成、持续部署。

为了保证整体流程可以高效的完成,各个阶段都有比较常见的工具,如下图:

devops_tools

最后我们给 DevOps 下一个定义:DevOps 强调的是通过自动化的工具贯穿开发、测试和运维之间的协同工作来完成软件的生命周期管理,从而更快、更高效地交付更稳定的软件。

5.2、Coding 阶段

在 coding 阶段,我们需要将不同版本的代码存储到一个仓库中,常见的版本控制工具有 SVN 或者 Git,这里我们采用 Git 作为版本控制工具,GitLab 作为远程仓库。

5.2.1、Git 安装

# step 1:删除centos自带yum源
[root@jenkins ~]# rm -rf /etc/yum.repos.d/*

# step 2:添加aliyun的Base源
[root@jenkins ~]# curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo
[root@jenkins ~]# sed -i -e '/mirrors.cloud.aliyuncs.com/d' -e '/mirrors.aliyuncs.com/d' /etc/yum.repos.d/CentOS-Base.repo

# step 3:添加aliyun的epel源
[root@jenkins ~]# wget -O /etc/yum.repos.d/epel.repo https://mirrors.aliyun.com/repo/epel-7.repo

# step 4:安装git
[root@jenkins ~]# yum install git -y

5.2.2、GitLab 安装

准备好服务器,采用 Docker 安装容器化的 GitLab。

  1. 安装 docker-ce

    # step 1: 安装必要的一些系统工具
    [root@jenkins ~]# yum install -y yum-utils device-mapper-persistent-data lvm2
    
    # Step 2: 添加软件源信息
    [root@jenkins ~]# yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
    
    # Step 3
    [root@jenkins ~]# sed -i 's+download.docker.com+mirrors.aliyun.com/docker-ce+' /etc/yum.repos.d/docker-ce.repo
    
    # Step 4: 更新并安装Docker-CE
    [root@jenkins ~]# yum makecache fast
    [root@jenkins ~]# yum -y install docker-ce
    
    # Step 5: 开启Docker服务
    [root@jenkins ~]# service docker start
  2. 国内 docker 镜像加速

    [root@jenkins ~]# vim /etc/docker/daemon.json
    {
     "registry-mirrors": [
       "https://hub-mirror.c.163.com",
       "https://mirror.baidubce.com",
       "https://registry.docker-cn.com",
       "https://reg-mirror.qiniu.com",
       "https://dockerhub.azk8s.cn",
       "https://docker.mirrors.ustc.edu.cn"
     ]
    }
    [root@jenkins ~]# systemctl daemon-reload
    [root@jenkins ~]# systemctl restart docker
  3. 拉取 GitLab 镜像

    [root@jenkins ~]# docker pull gitlab/gitlab-ce
  4. 安装 docker-compose 工具

    [root@jenkins ~]# curl -L "https://github.com/docker/compose/releases/download/1.29.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
  5. 启动 gitlab 服务

    我们使用的是容器化安装 gitlab,参见 gitlab 官方文档:https://docs.gitlab.cn/jh/install/docker.html

    文档介绍有 Docker Engine,Docker Compose,Docker Swarn 三种方式都可以安装,我们这里使用 docker-compose 方式安装。

    # 创建gitlab家目录
    [root@jenkins ~]# mkdir /usr/local/gitlab_home
    [root@jenkins ~]# export GITLAB_HOME=/usr/local/gitlab_home
  6. GitLab 容器使用主机装载的卷来存储持久数据:

    本地位置 容器位置 使用
    $GITLAB_HOME/data /var/opt/gitlab 用于存储应用程序数据。
    $GITLAB_HOME/logs /var/log/gitlab 用于存储日志。
    $GITLAB_HOME/config /etc/gitlab 用于存储极狐 GitLab 配置文件。
  7. 编写 docker-compose.yml 文件

    [root@jenkins gitlab_home]# pwd
    /usr/local/gitlab_home
    
    [root@jenkins gitlab_home]# vim docker-compose.yml
    version: '3.6'
    services:
     web:
       image: 'gitlab/gitlab-ce:latest'
       restart: always
       container_name: gitlab
       environment:
         GITLAB_OMNIBUS_CONFIG: |
           external_url 'http://192.168.95.165:8929'
           gitlab_rails['gitlab_shell_ssh_port'] = 2224
       ports:
         - '8929:8929'
         - '2224:22'
       volumes:
         - '$GITLAB_HOME/config:/etc/gitlab'
         - '$GITLAB_HOME/logs:/var/log/gitlab'
         - '$GITLAB_HOME/data:/var/opt/gitlab'
       shm_size: '256m'
  8. 启动容器 gitlab

    [root@jenkins gitlab_home]# docker-compose up -d
    
    # 生成相应目录和文件
    [root@jenkins gitlab_home]# ll
    total 8
    drwxrwxr-x  3 root root  268 Aug 15 05:25 config
    drwxr-xr-x 20 root root 4096 Aug 15 05:30 data
    -rw-r--r--  1 root root  480 Aug 15 05:24 docker-compose.yml
    drwxr-xr-x 20 root root  326 Aug 15 05:27 logs
  9. 访问 gitlab 首页

    先查看初始用户名和密码

    [root@jenkins gitlab_home]# docker exec -it gitlab bash
    root@ff87dc0665e0:/# cat /etc/gitlab/initial_root_password | grep Password:
    Password: 4FLDE1usK2WqFnbGQe3wJBAhYrjvChDzSvSDSGGnMh8=

gitlab_page

  1. 修改密码

gitlab_repassword

5.3、Build 阶段

当下应用最广泛的开发语言依然是 Java,所以我们还是以 java 项目为例进行 DevOps 实践。在 Build 阶段,常用的构建工具有两种,一个是 Maven,一个是 Gradle。这里我们选择 Maven 作为项目的构建工具。

5.3.1、安装 Java JDK 环境

下载地址:https://www.oracle.com/java/technologies/downloads/

[root@jenkins ~]# wget https://download.oracle.com/java/17/latest/jdk-17_linux-x64_bin.rpm
[root@jenkins ~]# ls
jdk-17_linux-x64_bin.rpm
[root@jenkins ~]# rpm -ivh jdk-17_linux-x64_bin.rpm 
warning: jdk-17_linux-x64_bin.rpm: Header V3 RSA/SHA256 Signature, key ID ec551f03: NOKEY
Preparing...                          ################################# [100%]
Updating / installing...
   1:jdk-17-2000:17.0.4-ga            ################################# [100%]
[root@jenkins ~]# 
[root@jenkins ~]# java -version
java version "17.0.4" 2022-07-19 LTS
Java(TM) SE Runtime Environment (build 17.0.4+11-LTS-179)
Java HotSpot(TM) 64-Bit Server VM (build 17.0.4+11-LTS-179, mixed mode, sharing)
[root@jenkins ~]#

5.3.2、Maven 安装

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

# 下载安装
[root@jenkins ~]# wget https://dlcdn.apache.org/maven/maven-3/3.8.6/binaries/apache-maven-3.8.6-bin.tar.gz --no-check-certificate
[root@jenkins ~]# tar xf apache-maven-3.8.6-bin.tar.gz -C /usr/local/
[root@jenkins ~]# ln -svf /usr/local/apache-maven-3.8.6/ /usr/local/maven
‘/usr/local/maven’ -> ‘/usr/local/apache-maven-3.8.6/’

# 配置环境
[root@jenkins ~]# vim /etc/profile.d/maven.sh
export MAVEN_HOME=/usr/local/maven
export PATH=$PATH:$MAVEN_HOME/bin
[root@jenkins ~]# source /etc/profile.d/maven.sh

# 最后验证是否安装成功,出现如下信息,说明安装成功
[root@jenkins ~]# mvn --version
Apache Maven 3.8.6 (84538c9988a25aec085021c365c560670ad80f63)
Maven home: /usr/local/maven
Java version: 17.0.4, vendor: Oracle Corporation, runtime: /usr/java/jdk-17.0.4
Default locale: en_US, platform encoding: UTF-8
OS name: "linux", version: "3.10.0-1160.el7.x86_64", arch: "amd64", family: "unix"
[root@jenkins ~]#

5.4、Integrate 工具

5.4.1、Jenkins 介绍

我们这里使用 jenkins 进行项目集成,前面章节对 jenkins 进行了详细的讲解,请参考 https://brinnatt.com/projects/cicd/第-3-章-jenkins-自动化系统/

Jenkins 最强大的就在于插件,Jenkins 官方提供了大量的插件库,来自动化 CI/CD 过程中的各种琐碎功能。

Jenkins 最主要的工作就是将 GitLab 上的工程代码拉取下来进行构建,再根据流程可以选择发布到测试环境或者生产环境。一般是 GitLab 上的代码经过大量的测试后,确定发行版本,再发布到生产环境。

CI/CD 可以理解为:

  • CI 过程即是通过 Jenkins 将代码拉取、构建、制作镜像交给测试人员测试。
    • 持续集成:让软件代码可以持续地集成到主干上,并自动构建和测试。
  • CD 过程即是通过 Jenkins 将打好标签的发行版本代码自动推送到服务器上,并且进行脚本自动化部署。
    • 持续交付:让经过持续集成的代码可以进行手动部署。
    • 持续部署:让可以持续交付的代码随时随地的自动化部署。

devops_cicd

5.4.2、Jenkins 安装

  1. 拉取 jenkins 镜像

    [root@jenkins ~]# docker pull  jenkins/jenkins
  2. 编写 docker-compose.yml

    [root@jenkins ~]# mkdir /usr/local/jenkins
    [root@jenkins ~]# export JENKINS_HOME=/usr/local/jenkins/
    [root@jenkins ~]# cd /usr/local/jenkins/
    [root@jenkins jenkins]# 
    [root@jenkins jenkins]# vim docker-compose.yml
    version: "3.6"
    services:
     jenkins:
       image: jenkins/jenkins
       container_name: jenkins
       ports:
         - 8080:8080
         - 50000:50000
       volumes:
         - $JENKINS_HOME/data/:/var/jenkins_home/
  3. 启动 jenkins 容器

    [root@jenkins jenkins]# docker-compose up -d
    
    [root@jenkins jenkins]# docker-compose logs -f
    Attaching to jenkins
    jenkins    | touch: cannot touch '/var/jenkins_home/copy_reference_file.log': Permission denied
    jenkins    | Can not write to /var/jenkins_home/copy_reference_file.log. Wrong volume permissions?
    jenkins exited with code 1
  4. 第 3 步启动 jenkins 失败,因为生成的 data 目录是 root 属主属组,jenkins 容器以 jenkins 身份对 data 没有写的权限,增加 data 其它用户写权限,再启动。

    [root@jenkins jenkins]# chmod o+w data/
    
    [root@jenkins jenkins]# docker-compose down
    Removing jenkins ... done
    Removing network jenkins_default
    [root@jenkins jenkins]# 
    [root@jenkins jenkins]# docker-compose up -d
    Creating network "jenkins_default" with the default driver
    Creating jenkins ... done
  5. 浏览器输入 http://192.168.95.165:8080/ 地址,输入密码进入 jenkins,初始化密码使用下面方式查看。

    [root@jenkins jenkins]# docker-compose logs -f | grep -A 2 "following password to proceed"
    jenkins    | Please use the following password to proceed to installation:
    jenkins    | 
    jenkins    | c020872b3591419c89fcd419b072c3d2
  6. 在以下界面点击选择插件来安装。

    jenkins_select_plugin

  7. 取消所有插件,如下所示,点击安装,按要求顺利进入 jenkins。这里不安装插件是因为国内网络原因,下载很慢,以后按需安装,缺什么补什么。

    jenkins_none_plugin

  8. 代理配置,Dashboard --> Manage Jenkins --> Plugin Manager --> Advanced,科学上网,安装插件有保障。

    jenkins_proxy

5.4.3、Jenkins 入门配置

由于 Jenkins 需要从 Gitlab 拉取代码、需要本地构建、甚至需要直接发布自定义镜像到 Docker 仓库,所以 Jenkins 需要配置大量内容。

5.4.3.1、创建任务

  1. 创建一个简单的 spring web 小程序上传到 gitlab,结果如下:

    jenkins_spring_app

  2. Dashboard --> Manage Jenkins --> Plugin Manager --> Available,安装 Localization: Chinese (Simplified)Git。后面直接指定安装的插件名,不再引导安装。

    Dashboard --> 新建任务 --> 选择构建一个自由风格的软件项目,取名 mytest,点击确定。

    jenkins_freestyle

5.4.3.2、配置源码拉取地址

Jenkins 在任务构建的时候,需要将 Git 服务器上的源码拉取下来存储到 jenkins 服务所在的本地磁盘上。

  1. 配置任务源码拉取的地址。

    gitlab_git

  2. 立即构建

    gitlab_build

  3. 查看构建工程的日志,点击上述 2 的任务条即可。

    jenkins_log

    可以看到源码已经拉取到 Jenkins 本地,可以根据第三行日志信息,查看 Jenkins 本地拉取到的源码。

    注意:如果安装插件时配置了代理,最好是取消代理或者本地网段不走代理,避免出错。

  4. 查看 Jenkins 容器中 /var/jenkins_home/workspace/mytest 的源码。

    [root@jenkins ~]# docker exec -it jenkins bash
    jenkins@a46a36c57e16:/$ 
    jenkins@a46a36c57e16:/$ ls -l /var/jenkins_home/workspace/mytest/
    total 4
    -rw-r--r-- 1 jenkins jenkins 2922 Aug 17 00:11 pom.xml
    drwxr-xr-x 4 jenkins jenkins   30 Aug 17 00:11 src

5.4.3.3、配置 Maven 构建代码

代码拉取到 Jenkins 本地后,需要在 Jenkins 中对代码进行构建,这里需要 Maven 的环境,而 Maven 需要 Java 的环境,接下来需要在 Jenkins 中安装 JDK 和 Maven,并且配置到 Jenkins 服务。

  1. 因为容器和主机环境是隔离的,最简单的方式是准备 JDK、Maven 二进制压缩包解压出来,通过数据卷映射到 Jenkins 容器内部。但是我们前面安装了 jdk rpm 包和 maven 二进制包,所以我们复用这个环境,如下:

    # 映射 maven 到容器卷
    [root@jenkins ~]# ln -sv /usr/local/maven/ /usr/local/jenkins/data/
    ‘/usr/local/jenkins/data/maven’ -> ‘/usr/local/maven/’
    [root@jenkins ~]# ls -l /usr/local/jenkins/data/maven
    lrwxrwxrwx 1 root root 17 Aug 17 09:11 /usr/local/jenkins/data/maven -> /usr/local/maven/
    
    # 映射 jdk 到容器卷
    [root@jenkins ~]# ln -sv /usr/java/jdk-17.0.4/ /usr/local/jenkins/data/
    ‘/usr/local/jenkins/data/jdk-17.0.4’ -> ‘/usr/java/jdk-17.0.4/’
    [root@jenkins ~]#
    [root@jenkins ~]# ls -l /usr/local/jenkins/data/jdk-17.0.4
    lrwxrwxrwx 1 root root 21 Aug 17 09:15 /usr/local/jenkins/data/jdk-17.0.4 -> /usr/java/jdk-17.0.4/
    
    # docker-compose.yaml 修改
    [root@jenkins jenkins]# docker-compose down
    [root@jenkins jenkins]# vim docker-compose.yml 
    version: "3.6"
    services:
     jenkins:
       image: jenkins/jenkins
       container_name: jenkins
       ports:
         - 8080:8080
         - 50000:50000
       volumes:
         - $JENKINS_HOME/data/:/var/jenkins_home/
         - $JENKINS_HOME/data/maven:/var/jenkins_home/maven         # 显式指定挂载
         - $JENKINS_HOME/data/jdk-17.0.4:/var/jenkins_home/jdk-17.0.4   # 显式指定挂载
    [root@jenkins jenkins]# docker-compose up -d
    • 注意,在 5.4.2 小节安装 jenkins 时将宿主机的 /usr/local/jenkins/data/ 目录映射到了 jenkins 容器的 /var/jenkins_home/ 目录,正因为如此,jenkins 容器才可以使用宿主机的目录,从而链接 jdk 和 maven 环境。
    • 注意,宿主机的目录软链接必须要通过卷映射显式配置才能生效,否则容器只会识别成一个软链接字符文件,而非想要的目录数据结构。
    • 生产环境不要这么干,最好把二进制文件放进容器映射目录,然后直接使用。
  2. 配置 Maven 的 settings.xml。
    jenkins_build

  3. Jenkins 配置 JDK&Maven 并保存。

    Dashboard --> 系统配置 --> 全局工具配置。

    jenkins_jdk_maven

  4. 配置 Jenkins 任务构建代码。

    Dashboard --> mytest --> 配置。

    jenkins_build

  5. 立即构建测试,查看 target 下的 jar 包。

    jenkins_mvn_pkg

    [root@jenkins ~]# docker exec -it jenkins bash
    jenkins@700688700a33:/$ ls /var/jenkins_home/workspace/mytest/target/
    classes          maven-archiver         mytest-0.0.1-SNAPSHOT.jar.original
    generated-sources    maven-status           test-classes
    generated-test-sources   mytest-0.0.1-SNAPSHOT.jar

5.4.4、远程发布

jar 包构建好之后,就可以根据情况发布到测试或生产环境,需要安装插件 Publish Over SSH。

  1. 配置 Publish Over SSH 连接测试、生产环境。

    Dashboard --> 系统管理 --> 系统配置。

    jenkins_remote_server

  2. 配置任务的构建后操作,发布 jar 包到目标服务。

    Dashboard --> mytest --> 配置 --> 构建后操作 --> Send build artifacts over SSH。

    jenkins_remote_server_deploy

  3. 立即构建任务,并去目标服务查看。

    [root@jenkins ~]# ssh 192.168.95.166
    root@192.168.95.166's password: 
    Last login: Mon Aug 15 19:30:13 2022 from 192.168.95.1
    [root@webserver ~]# 
    [root@webserver ~]# ls /opt/target/
    mytest-0.0.1-SNAPSHOT.jar

5.5、CICD 入门操作

基于 Jenkins 拉取 GitLab 上指定版本的 SpringBoot 代码进行构建,实现 CI 持续集成;然后发布到生产环境实现 CD 持续部署。

5.5.1、持续集成

为了让程序代码可以自动推送到测试环境基于 Docker 服务运行,需要添加 Docker 配置和脚本文件让程序可以在集成到主干的同时运行起来。

  1. 添加 Dockerfile 文件。

    jenkins_dockerfile

  2. 添加 docker-compose.yml 文件。

    jenkins_docker-compose

  3. 前提条件:远程 webserver 安装 docker 和 docker-compose-plugin 环境;git commit and push 源码中新创建的 docker 文件至 gitlab;另外,为了方便操作,maven 构建出来的 jar 包名字很长,自定义为 mytest.jar,需要在 pom.xml 中添加嵌套字段 <build> <finalName>mytest</finalName></build>

    追加 Jenkins 构建后操作脚本命令。

    jenkins_maven_postscript

  4. Jenkins 立即构建并推送到目标服务器,使用上面自定义脚本将 jar 包容器化并运行起来。下面验证一下 webserver 上的应用是否成功运行。

    # 验证webserver环境是否符合预期
    [root@webserver docker]# pwd
    /opt/docker
    [root@webserver docker]# ls
    docker-compose.yml  Dockerfile  mytest.jar
    [root@webserver docker]# 
    [root@webserver docker]# docker ps
    CONTAINER ID   IMAGE           COMMAND                  CREATED          STATUS          PORTS                                       NAMES
    15f5dd263596   mytest:v1.0.0   "/bin/sh -c 'java -j…"   13 minutes ago   Up 13 minutes   0.0.0.0:8888->8080/tcp, :::8888->8080/tcp   mytest
    
    # 请求一下页面
    [root@webserver docker]# curl 192.168.95.166:8888/test
    Hello Jenkins

5.5.2、持续交付

程序代码在经过多次集成操作后,达到最终可交付的目标。持续交付整体流程和持续集成类似,不过需要选取指定的发行版本。

  1. 给项目添加 tag 版本。注意,应该是源代码有更新并合并到 master 分支,然后想添加新的 tag 或 branch,才手动操作 gitlab web 界面。另外,在 IDE 工具中可以直接分出 tag 或者 branch,然后上传到 gitlab,避免手动操作分支。

    Gitlab --> 项目 btest --> Repository --> Tags --> New Tag。

    gitlab_release_v100

  2. 下载 Git Parameter 插件,设置项目参数化构建。

    Dashboard --> mytest --> 配置 --> 参数化构建过程 --> 添加参数 --> Git 参数。

    jenkins_git_para

  3. 在 gitlab 中创建的 tag 或者 branch 版本,根据第 2 步中的参数类型选择,都会反映到 jenkins 的 Build with Parameters 工程项目当中,然后选择 tag 或者 branch 进行参数化构建。

    jenkins_para_tag_or_branch

  4. 任务构建时,采用 Shell 方式构建,拉取指定 tag 版本代码。

    Dashboard --> mytest --> 配置 --> Build Steps --> 增加构建步骤 --> 执行 shell。

    jenkins_shell_para_build

  5. 基于 Parameter 构建任务,任务发布到目标服务器上。我们把测试源码稍微修改一下,反映出 v2.0.0 版本。

    jenkins_test_git_para

    # 构建v2.0.0版本
    [root@jenkins ~]# curl 192.168.95.166:8888/test
    Hello Jenkins -- v2.0.0
    
    # 构建v1.0.0版本
    [root@jenkins ~]# curl 192.168.95.166:8888/test
    Hello Jenkins -- v1.0.0

5.6、集成 Sonar Qube

5.6.1、Sonar Qube 介绍

Sonar Qube 是一个开源的代码分析平台,支持 Java、Python、PHP、JavaScript、CSS 等 25 种以上的语言,可以检测出重复代码、代码漏洞、代码规范和安全性漏洞的问题。

Sonar Qube 可以与多种软件整合进行代码扫描,比如 Maven,Gradle,Git,Jenkins 等,并且会将代码检测结果推送回 Sonar Qube,并且在系统提供的 UI 界面上显示出来。

5.6.2、Sonar Qube 环境搭建

5.6.2.1、Sonar Qube 安装

Sonar Qube 在 7.9 版本中已经放弃了对 MySQL 的支持,并且建议在商业环境中采用 PostgreSQL,那么安装 Sonar Qube 时需要依赖 PostgreSQL。

这里会安装 Sonar Qube 的长期支持版本 v8.9。

  1. 拉取镜像。

    [root@jenkins ~]# docker pull postgres
    [root@jenkins ~]# docker pull sonarqube:8.9.3-community
  2. 编写 docker-compoe.yml。

    [root@jenkins ~]# mkdir /usr/local/sonarqube
    [root@jenkins ~]# cd /usr/local/sonarqube
    [root@jenkins sonarqube]# vim docker-compose.yml
    version: "3.6"
    services:
     db:
       image: postgres
       container_name: db
       ports:
         - 5432:5432
       networks:
         - sonarnet
       environment:
         POSTGRES_USER: sonar
         POSTGRES_PASSWORD: sonar
     sonarqube:
       image: sonarqube:8.9.3-community
       container_name: sonarqube
       depends_on:
         - db
       ports:
         - "9000:9000"
       networks:
         - sonarnet
       environment:
         SONAR_JDBC_URL: jdbc:postgresql://db:5432/sonar
         SONAR_JDBC_USERNAME: sonar
         SONAR_JDBC_PASSWORD: sonar
    networks:
     sonarnet:
       driver: bridge
  3. 启动容器。

    [root@jenkins sonarqube]# docker-compose up -d
    Creating network "sonarqube_sonarnet" with driver "bridge"
    Creating db ... done
    Creating sonarqube ... done
  4. 查看 sonarqube 启动日志,发现启动失败,需要设置 sysctl.conf 文件配置信息。

    [root@jenkins sonarqube]# docker-compose logs -f
    sonarqube    | ERROR: [1] bootstrap checks failed. You must address the points described in the following [1] lines before starting Elasticsearch.
    sonarqube    | bootstrap check failure [1] of [1]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]
    sonarqube    | ERROR: Elasticsearch did not exit normally - check the logs at /opt/sonarqube/logs/sonarqube.log
    sonarqube    | 2022.08.17 15:07:48 INFO  es[][o.e.n.Node] stopping ...
    sonarqube    | 2022.08.17 15:07:48 INFO  es[][o.e.n.Node] stopped
    sonarqube    | 2022.08.17 15:07:48 INFO  es[][o.e.n.Node] closing ...
    sonarqube    | 2022.08.17 15:07:48 INFO  es[][o.e.n.Node] closed
    sonarqube    | 2022.08.17 15:07:49 WARN  app[][o.s.a.p.AbstractManagedProcess] Process exited with exit value [es]: 78
    sonarqube    | 2022.08.17 15:07:49 INFO  app[][o.s.a.SchedulerImpl] Process[es] is stopped
    sonarqube    | 2022.08.17 15:07:49 INFO  app[][o.s.a.SchedulerImpl] SonarQube is stopped
    
    [root@jenkins ~]# vim /etc/sysctl.conf
    vm.max_map_count=262144
    [root@jenkins ~]# sysctl -p
  5. 重新启动需要一定时间,可以查看容器日志,看到如下内容代表启动成功。

    sonarqube    | 2022.08.17 15:15:18 INFO  app[][o.s.a.SchedulerImpl] Process[ce] is up
    sonarqube    | 2022.08.17 15:15:18 INFO  app[][o.s.a.SchedulerImpl] SonarQube is up
  6. 访问 Sonar Qube 首页。

    sonarqube_page

  7. 重新设置密码,然后进入首页。

    sonarqube_repass

5.6.2.2、安装中文插件

Administration --> Marketplace --> I understand the risk --> search "chinese" --> Chinese Pack LOCALIZATION --> Install --> Restart Server。

sonarqube_localization

5.6.3、Sonar Qube 基本使用

Sonar Qube 的使用方式很多,Maven 可以整合,也可以采用 sonar-scanner 的方式,再查看 Sonar Qube 的检测效果。

5.6.3.1、Maven 实现代码检测

  1. 修改 Maven 的 settings.xml 文件配置 Sonar Qube 信息。
   <profile>
       <id>sonar</id>
       <activation>
           <activeByDefault>true</activeByDefault>
       </activation>
       <properties>
           <sonar.login>admin</sonar.login>
           <sonar.password>helloworld</sonar.password>
           <sonar.host.url>http://192.168.95.165:9000</sonar.host.url>
       </properties>
   </profile>
  1. 在源代码位置执行命令:mvn sonar:sonar。直接进入 jenkins 容器操作即可,jenkins 在前面集成测试的时候里面环境很齐全。

    [root@jenkins ~]# docker exec -it jenkins bash
    jenkins@1c9970622799:/$ cd /var/jenkins_home/workspace/mytest/
    jenkins@1c9970622799:~/workspace/mytest$ /var/jenkins_home/maven/bin/mvn sonar:sonar
    ......
    [INFO] Analysis report generated in 47ms, dir size=106 KB
    [INFO] Analysis report compressed in 26ms, zip size=18 KB
    [INFO] Analysis report uploaded in 524ms
    [INFO] ANALYSIS SUCCESSFUL, you can browse http://192.168.95.165:9000/dashboard?id=com.brinnatt%3Amytest
    [INFO] Note that you will be able to access the updated dashboard once the server has processed the submitted analysis report
    [INFO] More about the report processing at http://192.168.95.165:9000/api/ce/task?id=AYKshoFNitn0n-6-mh3_
    [INFO] Analysis total time: 9.476 s
    [INFO] ------------------------------------------------------------------------
    [INFO] BUILD SUCCESS
    [INFO] ------------------------------------------------------------------------
    [INFO] Total time:  15.545 s
    [INFO] Finished at: 2022-08-17T15:57:52Z
    [INFO] ------------------------------------------------------------------------
  2. 查看 Sonar Qube 界面检测结果。

    sonarqube_analyze

5.6.3.2、Sonar-scanner 实现代码检测

  1. 下载Sonar-scanner:https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/,下载 4.6.x 版本即可,要求 Linux 版本。

  2. 解压并配置 sonar 服务端信息。将 sonar-scanner 解压到 jenkins 容器映射的目录以便直接在容器中使用。

    [root@jenkins ~]# yum install unzip
    [root@jenkins ~]# unzip sonar-scanner-cli-4.6.2.2472-linux.zip
    [root@jenkins ~]# mv sonar-scanner-4.6.2.2472-linux/ /usr/local/jenkins/data/sonar-scanner462
  3. 配置 sonarQube 服务端地址,修改 conf 下的 sonar-scanner.properties。

    [root@jenkins ~]# cd /usr/local/jenkins/data/sonar-scanner462/
    [root@jenkins sonar-scanner462]# 
    [root@jenkins sonar-scanner462]# vim conf/sonar-scanner.properties
    sonar.host.url=http://192.168.95.165:9000
    sonar.sourceEncoding=UTF-8
  4. 执行命令检测代码。执行之前需要登陆 sonarqube 界面,生成 token 供命令使用。

    我的账号 --> 安全 --> 取一个名字生成令牌,记住令牌。

    [root@jenkins ~]# docker exec -it jenkins bash 
    jenkins@1c9970622799:/$ cd /var/jenkins_home/workspace/mytest
    jenkins@1c9970622799:~/workspace/mytest$ ../../sonar-scanner462/bin/sonar-scanner -Dsonar.sources=./ -Dsonar.projectname=casually_name -Dsonar.projectKey=casually_key -Dsonar.java.binaries=target/ -Dsonar.login=ac155e09db5f2b0017acc5b1c8456a017f009664
    ......
    INFO: ANALYSIS SUCCESSFUL, you can browse http://192.168.95.165:9000/dashboard?id=casually_key
    INFO: Note that you will be able to access the updated dashboard once the server has processed the submitted analysis report
    INFO: More about the report processing at http://192.168.95.165:9000/api/ce/task?id=AYKsviZoitn0n-6-mh4O
    INFO: Analysis total time: 5.050 s
    INFO: ------------------------------------------------------------------------
    INFO: EXECUTION SUCCESS
    INFO: ------------------------------------------------------------------------
    INFO: Total time: 5.995s
    INFO: Final Memory: 9M/30M
    INFO: ------------------------------------------------------------------------
  5. 查看 SonarQube 界面检测结果。

    sonarqube_analyze1

5.6.4、Jenkins 集成 Sonar Qube

5.6.4.1、Jenkins 配置 Sonar Qube

首先要安装 SonarQube Scanner 插件,安装插件的方法前面已介绍。

  1. 开启 Sonar Qube 权限验证。

    sonarqube_jenkins_allow

  2. 获取 Sonar Qube 的令牌。复用上一个实验的令牌。

  3. 配置 Jenkins 的 Sonar Qube 信息。

    Dashboard --> 系统管理 --> 系统配置 --> SonarQube servers。

    jenkins_sonarqube_conf

    jenkins_sonarqube_proof

5.6.4.2、配置 Sonar-scanner

  1. 将 Sonar-scaner 添加到 Jenkins 数据卷中并配置全局配置。

    jenkins_sonar-scanner_conf

  2. 配置任务的 Sonar-scanner。

    jenkins_sonar-scanner_exec

5.6.4.3、构建任务

构建项目时,控制台输出信息如下,省略部分次要内容。

Started by user brinnatt
Running as SYSTEM
Building in workspace /var/jenkins_home/workspace/mytest
......
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  3.804 s
[INFO] Finished at: 2022-08-17T19:32:15Z
[INFO] ------------------------------------------------------------------------
[mytest] $ /var/jenkins_home/sonar-scanner462/bin/sonar-scanner -Dsonar.host.url=http://192.168.95.165:9000 ******** -Dsonar.projectKey=mytest -Dsonar.projectname=mytest -Dsonar.sources=./ -Dsonar.java.binaries=target/ -Dsonar.projectBaseDir=/var/jenkins_home/workspace/mytest
INFO: Scanner configuration file: /var/jenkins_home/sonar-scanner462/conf/sonar-scanner.properties
......
INFO: ------------- Run sensors on module mytest
INFO: Load metrics repository
......
INFO: ------------- Run sensors on project
......
INFO: ANALYSIS SUCCESSFUL, you can browse http://192.168.95.165:9000/dashboard?id=mytest
INFO: Note that you will be able to access the updated dashboard once the server has processed the submitted analysis report
INFO: More about the report processing at http://192.168.95.165:9000/api/ce/task?id=AYKtSuBXitn0n-6-mh4a
INFO: Analysis total time: 5.291 s
INFO: ------------------------------------------------------------------------
INFO: EXECUTION SUCCESS
INFO: ------------------------------------------------------------------------
INFO: Total time: 6.377s
INFO: Final Memory: 9M/34M
INFO: ------------------------------------------------------------------------
WARN: Found multiple 'report-task.txt' in the workspace. Taking the first one.
/var/jenkins_home/workspace/mytest/.scannerwork/report-task.txt
/var/jenkins_home/workspace/mytest/target/sonar/report-task.txt
SSH: Connecting from host [1c9970622799]
SSH: Connecting with configuration [webserver] ...
SSH: EXEC: completed after 2,408 ms
SSH: Disconnecting configuration [webserver] ...
SSH: Transferred 4 file(s)
Finished: SUCCESS

jenkins_sonar-scanner_result

5.7、集成 Harbor

5.7.1、Harbor 介绍

前面在部署项目时,我们主要采用 Jenkins 推送 jar 包到指定服务器,再通过脚本命令让目标服务器对当前 jar 包进行部署。

这种方式在项目较多时,每个目标服务器都需要将 jar 包制作成自定义镜像再通过 docker 进行启动,重复操作比较多,会降低项目部署效率。

我们可以通过 Harbor 作为私有的 Docker 镜像仓库。让 Jenkins 统一将项目打包并制作成 Docker 镜像发布到 Harbor 仓库中,只需要通知目标服务器,让目标服务器统一去 Harbor 仓库上拉取镜像并在本地部署即可。

Docker 官方提供了 Registry 镜像仓库,但是 Registry 的功能相对简陋。Harbor 是 VMware 公司提供的一款镜像仓库,提供了权限控制、分布式发布、强大的安全扫描与审查机制等功能。

5.7.2、Harbor 安装

这里采用原生的方式安装 Harbor。

  1. 下载 Harbor 安装包:https://github.com/goharbor/harbor/releases/download/v2.3.4/harbor-offline-installer-v2.3.4.tgz 上传到 linux 服务器并解压。

    [root@webserver ~]# wget https://github.com/goharbor/harbor/releases/download/v2.3.4/harbor-offline-installer-v2.3.4.tgz
    [root@webserver ~]# tar xf harbor-offline-installer-v2.3.4.tgz -C /usr/local/
  2. 修改 Harbor 配置文件:

    [root@webserver ~]# cd /usr/local/harbor/
    [root@webserver harbor]# 
    [root@webserver harbor]# ls
    common.sh  harbor.v2.3.4.tar.gz  harbor.yml.tmpl  install.sh  LICENSE  prepare
    [root@webserver harbor]# 
    [root@webserver harbor]# cp harbor.yml.tmpl harbor.yml
    
    [root@webserver harbor]# vim harbor.yml
    hostname: 192.168.95.166
    # 不需要https,注释掉
    # https:
    # port: 443
    # certificate: /your/certificate/path
    # private_key: /your/private/key/path
  3. 启动 Harbor。

    # 要提前安装docker和docker-compose
    [root@webserver harbor]# ./install.sh
  4. 登陆 Harbor,yaml 文件中默认的用户名密码。

    harbor_login

  5. 首页信息。

    harbor_page

5.7.3、Harbor 使用

Harbor 作为镜像仓库,主要的交互方式就是将镜像上传到 Harbor 上,以及从 Harbor 上下载指定镜像。

在传输镜像前,可以先使用 Harbor 提供的权限管理,将项目设置为私有项目,并对不同用户设置不同角色,从而更方便管理镜像。

5.7.3.1、添加用户构建项目

  1. 创建用户。

    harbor_user

  2. 构建项目,设为私有。

    harbor_new_project

  3. 给项目追加用户。

    harbor_adduser_project

  4. 切换测试用户。

    harbor_normal_user

5.7.3.2、发布镜像到 Harbor

  1. 修改镜像名称,名称要求:harbor地址:端口/项目名/镜像名:版本。

    [root@webserver ~]# docker images mytest
    REPOSITORY   TAG       IMAGE ID       CREATED        SIZE
    mytest       v1.0.0    0f5295fa48a4   2 hours ago    488MB
    mytest       v2.0.0    3065ccb0b778   10 hours ago   488MB
    [root@webserver ~]# 
    [root@webserver ~]# docker tag mytest:v1.0.0 192.168.95.166:80/btest/mytest:v1.0.0
    [root@webserver ~]# 
    [root@webserver ~]# docker images 192.168.95.166:80/btest/mytest
    REPOSITORY                       TAG       IMAGE ID       CREATED       SIZE
    192.168.95.166:80/btest/mytest   v1.0.0    0f5295fa48a4   2 hours ago   488MB
  2. 修改 daemon.json,支持 Docker 仓库,并重启 Docker。

    [root@webserver ~]# vim /etc/docker/daemon.json
    {
     "registry-mirrors": [
       "https://hub-mirror.c.163.com",
       "https://mirror.baidubce.com",
       "https://registry.docker-cn.com",
       "https://reg-mirror.qiniu.com",
       "https://dockerhub.azk8s.cn",
       "https://docker.mirrors.ustc.edu.cn"
     ],
     "insecure-registries": ["192.168.95.166:80"]
    }
    [root@webserver ~]# systemctl restart docker.service
  3. 设置登录仓库信息。

    [root@webserver ~]# docker login -uBrinnatt -p"helloworld" 192.168.95.166:80
  4. 推送镜像到 Harbor。

    [root@webserver ~]# docker push 192.168.95.166:80/btest/mytest:v1.0.0 
    The push refers to repository [192.168.95.166:80/btest/mytest]
    5f70bf18a086: Pushed 
    2e12daf62b02: Pushed 
    dc9fa3d8b576: Pushed 
    27ee19dc88f2: Pushed 
    c8dd97366670: Pushed 
    v1.0.0: digest: sha256:7632bcead9a3ab0cce360e887eb3c0ecf3f262f9c5da4c42646f8efda225b605 size: 1372

    harbor_push_image

5.7.3.3、从 Harbor 拉取镜像

跟传统方式一样,不过需要先配置 /etc/docker/daemon.json 文件。

[root@webserver ~]# vim /etc/docker/daemon.json
{
  "registry-mirrors": [
    "https://hub-mirror.c.163.com",
    "https://mirror.baidubce.com",
    "https://registry.docker-cn.com",
    "https://reg-mirror.qiniu.com",
    "https://dockerhub.azk8s.cn",
    "https://docker.mirrors.ustc.edu.cn"
  ],
  "insecure-registries": ["192.168.95.166:80"]
}
[root@webserver ~]# systemctl restart docker.service

直接通过镜像的命名方式进行拉取。

[root@webserver ~]# docker pull 192.168.95.166:80/btest/mytest:v1.0.0
v1.0.0: Pulling from btest/mytest
Digest: sha256:7632bcead9a3ab0cce360e887eb3c0ecf3f262f9c5da4c42646f8efda225b605
Status: Downloaded newer image for 192.168.95.166:80/btest/mytest:v1.0.0
192.168.95.166:80/btest/mytest:v1.0.0
[root@webserver ~]# 
[root@webserver ~]# docker images 192.168.95.166:80/btest/mytest
REPOSITORY                       TAG       IMAGE ID       CREATED       SIZE
192.168.95.166:80/btest/mytest   v1.0.0    0f5295fa48a4   3 hours ago   488MB

5.7.3.4、Jenkins 容器使用宿主机 Docker

Jenkins 构建镜像和发布镜像到 harbor 都需要使用到 docker 命令。而在 Jenkins 容器内部安装 Docker,官方推荐直接采用宿主机带的 Docker 即可。

注意:Docker 实际上有客户端和服务器端之分,只不过一般同时安装在一台 linux 主机上的。Docker 与 Docker Server 使用 Unix Domain Socket 进行本地进程通信。网络 Socket 是绑定 IP:PORT 通信,Unix Domain Socket 是绑定本地 socket 文件通信。

所以想让 Jenkins 使用宿主机 Docker,只需要将 /usr/bin/docker 和 /var/run/docker.sock 映射进 Jenkins 容器即可,当然,也可以将 /etc/docker/daemon.json 配置文件映射进去。

  1. 设置宿主机 docker.sock 权限:

    [root@jenkins ~]# useradd -M -s /sbin/nologin -c "for docker jenkins" jenkins
    [root@jenkins ~]# 
    [root@jenkins ~]# setfacl -m u:jenkins:rw /var/run/docker.sock
  2. 添加数据卷。

    [root@jenkins ~]# vim /usr/local/jenkins/docker-compose.yml
    version: "3.6"
    services:
     jenkins:
       image: jenkins/jenkins
       container_name: jenkins
       ports:
         - 8080:8080
         - 50000:50000
       volumes:
         - /usr/local/jenkins/data/:/var/jenkins_home/
         - /usr/local/jenkins/data/maven:/var/jenkins_home/maven
         - /usr/local/jenkins/data/jdk-17.0.4:/var/jenkins_home/jdk-17.0.4
         - /usr/bin/docker:/usr/bin/docker
         - /var/run/docker.sock:/var/run/docker.sock
         - /etc/docker/daemon.json:/etc/docker/daemon.json

5.7.3.5、添加构建后操作

jenkins_post_push_harbor

5.7.3.6、编写部署脚本

部署项目需要通过 Publish Over SSH 插件,让目标服务器执行命令。为了方便一次性实现拉取镜像和启动服务,推荐采用脚本文件的方式执行。

添加脚本文件到目标服务器,再通过 Publish Over SSH 插件让目标服务器执行脚本即可。

  1. 编写脚本文件,添加到目标服务器。

    #!/bin/bash
    # 这个脚本只是解决远程服务器从harbor下载镜像并运行,没有检查和异常处理机制,传参时一定要准确;
    
    harbor_socket=$1
    harbor_project_name=$2
    project_name=$3
    tag=$4
    port=$5
    imageName=$harbor_socket/$harbor_project_name/$project_name:$tag
    
    msg(){
       echo "=================================================="
       echo $1
       echo "=================================================="
    }
    
    clear_ifexists(){
       DOCKERNAME=docker ps | grep ${project_name} | awk '{print $NF}'
       IMAGEID=docker images -q -f reference=${imageName}
    
       if [[ -n $DOCKERNAME ]]; then
           docker rm -f $DOCKERNAME
           msg "Deleting existed container finished!"
       else
           msg "This Docker does not exist!"
       fi
    
       if [[ -n $IMAGEID ]]; then
           docker rmi -f ${imageName}
           msg "Deleting existed image finished!"
       else
           msg "This Image does not exist!"
       fi
    }
    
    run_container(){
       docker login -u Brinnatt -p "HelloWorld123" $harbor_socket
       docker pull $imageName
       docker run -d -p $port:$port --name $project_name $imageName
    }
    
    clear_ifexists
    run_container
    
    msg "Start Container Success"
    msg "Project's name is $project_name"
  2. 设置脚本可执行权限。

    [root@webserver ~]# chmod +x /usr/local/bin/deploy.sh

5.7.3.7、配置构建后操作

jenkins_script_dist

5.8、Jenkins 流水线

5.8.1、Jenkins 流水线介绍

前面采用 Jenkins 的自由风格构建项目,每个步骤流程都要通过不同的方式设置,并且构建过程中整体流程是不可见的,无法确认每个流程花费的时间,并且不方便定位问题。

Jenkins 的 Pipeline 可以让项目的发布流程可视化,明确执行的阶段,可以快速的定位问题。并且整个项目的生命周期可以通过一个 Jenkinsfile 文件管理,而且 Jenkinsfile 文件是可以放在项目中维护。

所以 Pipeline 相对自由风格或者其他的项目风格更容易操作。

5.8.2、Jenkins 流水线任务

5.8.2.1、创建 Jenkins 流水线

首先安装 PipelinePipeline: Stage ViewPipeline: Groovy Libraries 插件。

  1. 构建任务。

    pipeline_create

  2. 生成 Groovy 脚本。

    pipeline_groovy

  3. 构建后查看视图。

    pipeline_run

5.8.2.2、Groovy 脚本

  1. Groovy 脚本基础语法。

    // 所有脚本命令包含在pipeline{}中
    pipeline {  
    // 指定任务在哪个节点执行(Jenkins支持分布式)
       agent any
    
       // 配置全局环境,指定变量名=变量值
       environment{
        host = '192.168.11.11'
       }
    
       // 存放所有任务的集合
       stages {
        // 单个任务
           stage('任务1') {
            // 实现任务的具体流程
               steps {
                   echo 'do something'
               }
           }
        // 单个任务
           stage('任务2') {
            // 实现任务的具体流程
               steps {
                   echo 'do something'
               }
           }
           // ……
       }
    }
  2. 编写测试用例。

    pipeline {
       agent any
    
       // 存放所有任务的集合
       stages {
           stage('拉取Git代码') {
               steps {
                   echo '拉取Git代码'
               }
           }
    
           stage('检测代码质量') {
               steps {
                   echo '检测代码质量'
               }
           }
    
           stage('构建代码') {
               steps {
                   echo '构建代码'
               }
           }
    
           stage('制作自定义镜像并发布到Harbor') {
               steps {
                   echo '制作自定义镜像并发布到Harbor'
               }
           }
    
           stage('基于Harbor部署工程') {
               steps {
                   echo '基于Harbor部署工程'
               }
           }
       }
    }
  3. 查看效果。

    pipeline_stages

  4. 利用 Jenkins 自带的流水线语法快速生成 Groovy 语句。配置流水线脚本的时候,有一个流水线语法链接,点进去,根据自己的需求生成 Groovy 脚本。

    pipeline_generate_groovy

5.8.2.3、Jenkinsfile 实现

Jenkinsfile 方式需要将脚本内容编写到项目中的 Jenkinsfile 文件中,每次构建会自动拉取项目并且获取项目中的Jenkinsfile 文件,然后根据其中的脚本对项目进行构建。

  1. 配置 pipeline 流水线。

    pipeline_configure

  2. 准备 Jenkinsfile。在项目根目录下创建一个 Jenkinsfile 文件,编写脚本。然后上传到 gitlab。

    pipeline_Jenkinsfile

    gitlab_Jenkinsfile

  3. 立即构建,查看效果。

    pipeline_Jenkinsfile_finished

5.8.3、Jenkins 流水线实现

5.8.3.1、参数化构建

添加参数化构建,方便选择不同项目版本。

pipeline_git_param

5.8.3.2、拉取 Git 代码

通过流水线语法生成 Checkout 代码的脚本。

pipeline_checkout_generate_groovy

*/master 更改为标签 ${tag}

pipeline {
    agent any
    stages {

        stage('拉取Git代码') {
            steps {
                checkout([$class: 'GitSCM', branches: [[name: '${tag}']], extensions: [], userRemoteConfigs: [[url: 'http://192.168.95.165:8929/root/btest.git']]])
            }
        }
    }
}

5.8.3.3、完善后面步骤

按照上面这个思路,可以生成如下代码:

  • mvn 构建代码

    stage('构建代码') {
              steps {
                  sh '/var/jenkins_home/maven/bin/mvn clean package -DskipTests'
              }
          }
  • sonar-scanner 代码质量检测

    stage('检测代码质量') {
              steps {
                  sh '/var/jenkins_home/sonar-scanner462/bin/sonar-scanner -Dsonar.sources=./ -Dsonar.projectname=${JOB_NAME} -Dsonar.projectKey=${JOB_NAME} -Dsonar.java.binaries=target/ -Dsonar.login=ac155e09db5f2b0017acc5b1c8456a017f009664' 
              }
          }
  • 生成自定义镜像脚本

    pipeline {
      agent any
      environment{
          harborSocket = '192.168.95.166:80'
          harborRepo = 'btest'
          harborUser = 'Brinnatt'
          harborPasswd = 'helloworld'
      }
    
      // 存放所有任务的合集
      stages {
          stage('制作自定义镜像并发布Harbor') {
              steps {
                  sh '''cp ./target/*.jar ./docker/
                  cd ./docker
                  docker build -t ${JOB_NAME}:${tag} ./'''
    
                  sh '''docker login -u ${harborUser} -p ${harborPasswd} ${harborSocket}
                  docker tag ${JOB_NAME}:${tag} ${harborSocket}/${harborRepo}/${JOB_NAME}:${tag}
                  docker push ${harborSocket}/${harborRepo}/${JOB_NAME}:${tag}'''
              }
          }
      }
    }
  • 生成 Publish Over SSH 脚本

    pipeline {
      agent any
      environment{
          harborSocket = '192.168.95.166:80'
          harborRepo = 'btest'
          harborUser = 'Brinnatt'
          harborPasswd = 'helloworld'
      }
    
      // 存放所有任务的合集
      stages {
          stage('目标服务器拉取镜像并运行') {
              steps {
                  sshPublisher(publishers: [sshPublisherDesc(configName: 'webserver', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: "deploy.sh $harborSocket $harborRepo $JOB_NAME $tag $servicePort", execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: '')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
              }
          }
      }
    }

最终合版如下:

pipeline {
    agent any
    environment{
        harborSocket = '192.168.95.166:80'
        harborRepo = 'btest'
        harborUser = 'Brinnatt'
        harborPasswd = 'HelloWorld123'
        servicePort = '8080'
    }

    // 存放所有任务的合集
    stages {
        stage('拉取Git代码') {
            steps {
                checkout([$class: 'GitSCM', branches: [[name: '${tag}']], extensions: [], userRemoteConfigs: [[url: 'http://192.168.95.165:8929/root/btest.git']]])
            }
        }
        stage('构建代码') {
            steps {
                sh '/var/jenkins_home/maven/bin/mvn clean package -DskipTests'
            }
        }
        stage('检测代码质量') {
            steps {
                sh '/var/jenkins_home/sonar-scanner462/bin/sonar-scanner -Dsonar.sources=./ -Dsonar.projectname=${JOB_NAME} -Dsonar.projectKey=${JOB_NAME} -Dsonar.java.binaries=target/ -Dsonar.login=4787dbf134aaaf08ec56208dce768114d8eed7d8'
            }
        }
        stage('制作自定义镜像并发布Harbor') {
            steps {
                sh '''cp ./target/*.jar ./docker/
                cd ./docker
                docker build -t ${JOB_NAME}:${tag} ./'''

                sh '''docker login -u ${harborUser} -p ${harborPasswd} ${harborSocket}
                docker tag ${JOB_NAME}:${tag} ${harborSocket}/${harborRepo}/${JOB_NAME}:${tag}
                docker push ${harborSocket}/${harborRepo}/${JOB_NAME}:${tag}'''
            }
        }

        stage('目标服务器拉取镜像并运行') {
            steps {
                sshPublisher(publishers: [sshPublisherDesc(configName: 'webserver', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: "deploy.sh $harborSocket $harborRepo $JOB_NAME $tag $servicePort", execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: '')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
            }
        }
    }
}

注意:一定要检查脚本中的单双引号问题,如果是单引号引用 bash 变量,则属于强引用,变量不生效。

至此,流水线已经定制完成,只需要将该脚本替换到 gitlab 上的 Jenkinsfile 即可。

5.8.4、Jenkins 流水线整合钉钉

在程序部署成功后,可以通过钉钉的机器人及时向群众发送部署的最终结果通知。

  1. 安装插件 DingTalk。

  2. 钉钉内部创建群组并构建机器人。

    通讯录 --> 我的群组 --> 进群 --> 群设置 --> 智能群助手 --> 添加机器人 --> 自定义。

    dingtalk_define

    最终获取 webhook 信息如下:

    https://oapi.dingtalk.com/robot/send?access_token=0cba3354e2fd2e9438707d28c6031170084ffba2929154f663622768504adf9c
  3. 系统配置添加钉钉通知。

    Dashboard --> 系统管理 --> 系统配置 --> 钉钉。

    jenkins_dingding

  4. pipeline_test 流水线项目中添加钉钉通知。

    pipeline {
       agent any
       environment{
           harborSocket = '192.168.95.166:80'
           harborRepo = 'btest'
           harborUser = 'Brinnatt'
           harborPasswd = 'HelloWorld123'
           servicePort = '8080'
       }
    
       // 存放所有任务的合集
       stages {
           stage('拉取Git代码') {
               steps {
                   checkout([$class: 'GitSCM', branches: [[name: '${tag}']], extensions: [], userRemoteConfigs: [[url: 'http://192.168.95.165:8929/root/btest.git']]])
               }
           }
           stage('构建代码') {
               steps {
                   sh '/var/jenkins_home/maven/bin/mvn clean package -DskipTests'
               }
           }
           stage('检测代码质量') {
               steps {
                   sh '/var/jenkins_home/sonar-scanner462/bin/sonar-scanner -Dsonar.sources=./ -Dsonar.projectname=${JOB_NAME} -Dsonar.projectKey=${JOB_NAME} -Dsonar.java.binaries=target/ -Dsonar.login=4787dbf134aaaf08ec56208dce768114d8eed7d8'
               }
           }
           stage('制作自定义镜像并发布Harbor') {
               steps {
                   sh '''cp ./target/*.jar ./docker/
                   cd ./docker
                   docker build -t ${JOB_NAME}:${tag} ./'''
    
                   sh '''docker login -u ${harborUser} -p ${harborPasswd} ${harborSocket}
                   docker tag ${JOB_NAME}:${tag} ${harborSocket}/${harborRepo}/${JOB_NAME}:${tag}
                   docker push ${harborSocket}/${harborRepo}/${JOB_NAME}:${tag}'''
               }
           }
    
           stage('目标服务器拉取镜像并运行') {
               steps {
                   sshPublisher(publishers: [sshPublisherDesc(configName: 'webserver', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: "deploy.sh $harborSocket $harborRepo $JOB_NAME $tag $servicePort", execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: '')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
               }
           }
       }
       post {
           success {
               dingtalk (
                   robot: 'Jenkins_DingDing',
                   type:'MARKDOWN',
                   title: "success: ${JOB_NAME}",
                   text: ["- 成功构建:${JOB_NAME}项目!\n- 版本:${tag}\n- 持续时间:${currentBuild.durationString}\n- 任务:#${JOB_NAME}"]
               )
           }
           failure {
               dingtalk (
                   robot: 'Jenkins_DingDing',
                   type:'MARKDOWN',
                   title: "fail: ${JOB_NAME}",
                   text: ["- 失败构建:${JOB_NAME}项目!\n- 版本:${tag}\n- 持续时间:${currentBuild.durationString}\n- 任务:#${JOB_NAME}"]
               )
           }
       }
    }
  5. 验证可以成功发出通知。

标签云