美文网首页DevOps
DevOps编程操练:用Jenkins流水线建立代码质量预警机制

DevOps编程操练:用Jenkins流水线建立代码质量预警机制

作者: 程序员吾真本 | 来源:发表于2020-10-04 22:49 被阅读0次

    解决痛点

    • 不知如何用docker搭建Jenkins操练环境

    • 不知如何开始为Java代码编写自动化单元测试

    • 不知如何将单元测试运行在Jenkins流水线上

    • 不知如何将繁琐的手工Jenkins流水线配置,简化为编写一个Jenkinsfile脚本,并进行版本控制

    • 当流水线出现故障后,不知如何revert导致故障的代码提交,来解决故障

    使用docker搭建Jenkins操练环境

    当然也可以不用docker,直接在本机安装Jenkins。但对于操练DevOps技能来说,Docker是一个必修项目。所以本操练使用docker来搭建操练环境

    本操练是从“CI搭建兽”到“流水线即代码”的升级版,除了使用docker来运行Jenkins之外,还将 Jenkinsfile的写法,从原来的脚本式(以 node 开头),升级为声明式(以 pipeline开头)

    安装docker

    参见 Install Docker Engine安装Docker

    下面以Ubuntu 20.04为例进行操练,其他操作系统操练步骤类同

    安装Kitematic

    Kitematic是一个为了方便使用docker而精心设计的图形化工具。参见Kitematic发布页面安装Kitematic

    安装Jenkins

    在Kitematic里下载jenkins/jenkins的image,启动容器并安装Jenkins

    打开Kitematic,在搜索框中输入 jenkins来搜索所有Jenkins的镜像。选择镜像名字第一行和第二行都是jenkins的那个镜像。点击CREATE 按钮下载镜像,并启动容器。参见下图

    点击 `CREATE` 按钮下载镜像,并启动容器

    点击左上角 jenkins 容器,然后点击右上角 Settings 页签,将容器改名为jenkins-kata ,参见下图

    将容器改名为 `jenkins-kata`

    点击右上角 Home 页签,浏览容器的log,等待jenkins重启

    在本机创建文件夹~/OOR/docker-volumes/jenkins-kata,并将其配置为docker的volume,以便保存Jenkins运行后的输出文件,且能同时被docker和本机访问。参见下图

    设置docker的volume

    点击右上角 Home 页签,浏览容器的log,等待jenkins重启

    点击右上角 Settings 页签,再点击下面左侧的 Hostname/Ports页签,记下页面左侧中间第一个带有 localhost 的端口号,如下图所示的localhost:32769,然后打开浏览器,在地址栏中访问这个地址和端口号,就能进入Jenkins安装页面,安装Jenkins。安装第一步所需要的admin管理员密码,能在Home页签中的log内容中找到。安装Jenkins插件时,选择默认的即可。参见下图

    查看Jenkins运行的端口号

    用spring boot编写一个web应用程序并手工测试

    本操练的代码和文档参见
    devops-katas-jenkins-pipeline-as-code-kata

    start.spring.io 下载web空白应用

    下载前的选项,参见下面的列表。其中Dependencies添加Web

    • Group: devops.katas
    • Artifact: adminprovider
    • Name: adminprovider
    • Description: Demo project for Jenkins pipeline as code
    • Dependencies: Web

    编写adminprovider的Web应用,可以按id号一次返回一位管理员

    将刚才下载的adminprovider.zip解压,用IntelliJ IDEA打开该Maven项目,开始编写一个Web应用

    为方便起见,本操练所创建的类,都写在AdminproviderAppication类中

    首先创建 AdminController

    AdminproviderApplication.java.

    @RestController
    class AdminController {
            @GetMapping("/admin/{id}")
            Admin admin(@PathVariable int id) {
                    return new Admin("firstName [" + id + "]", "lastName [" + id + "]");
            }
    }
    

    然后创建 Admin类。其中的两个getter是必须的,否则在运行时会报HttpMessageNotWritableException

    AdminproviderApplication.java.

    class Admin {
    
            private final String firstName;
            private final String lastName;
    
            public Admin(String firstName, String lastName) {
                    this.firstName = firstName;
                    this.lastName = lastName;
            }
    
            // The public getters are mandatory to fix the issue "org.springframework.http.converter.
            // HttpMessageNotWritableException: No converter found for return value of type:
            // class devops.katas.adminprovider.Admin"
            public String getFirstName() {
                    return firstName;
            }
    
            public String getLastName() {
                    return lastName;
            }
    }
    

    最后在 application.properties 文件中,添加该Web应用启动的端口号 8765

    application.properties.

    server.port=8765
    

    此时,在Intellij IDEA中运行 AdminproviderApplication类。然后用浏览器或 HTTPie工具来访问地址localhost:8765/admin/1 。应该能得到1号管理员的姓和名,参见下图

    用HTTPie工具访问

    编写AdminService的自动化单元测试

    为了让Jenkins流水线起到质量预警的作用,必须在上面运行自动化测试,来检测每一次代码push是否有缺陷。让我们先从单元测试开始。

    目前要测试的单元,是根据 id 号生成 Admin 对象。这段逻辑写在了AdminController
    类中,而这个设计是不好的。因为Controller类本来的用途,是起“传达室”的作用,即将用户的请求,分配给相应的服务来处理。所以良好的设计,应该是把这段逻辑交给AdminService 来处理。而对这段逻辑的单元测试,也就是对 AdminService的单元测试。

    第一步,先把上述逻辑交给 AdminService 来处理

    AdminproviderApplication.java.

    @Configuration
    class AdminConfiguration {
            @Bean
            AdminService adminService() {
                    return new AdminService();
            }
    }
    
    class AdminService {
            public Admin retrieveAdmin(int id) {
                    return new Admin("firstName [" + id + "]", "lastName [" + id + "]");
            }
    }
    
    @RestController
    class AdminController {
            @Autowired
            AdminService adminService;
    
            @GetMapping("/admin/{id}")
            Admin admin(@PathVariable int id) {
                    return adminService.retrieveAdmin(id);
            }
    
    }
    

    第二步,为 AdminService 编写单元测试

    AdminServiceTest.java.

    class AdminServiceTest {
        @Test
        public void should_retrieve_an_admin_with_correct_names() {
            AdminService adminService = new AdminService();
    
            Admin admin = adminService.retrieveAdmin(4);
    
            BDDAssertions.then(admin.getFirstName()).isEqualTo("firstName [4]");
            BDDAssertions.then(admin.getLastName()).isEqualTo("lastName [4]");
        }
    
    }
    

    在IntelliJ IDEA中运行单元测试,应该运行通过

    现在可以把上述代码push到码云中,以便后面操练中的Jenkins流水线读取代码来运行自动化测试

    可以在码云自己的帐号中,创建一个名为devops-katas-jenkins-pipeline-as-code-kata
    的空的代码库。然后在代码根目录中,使用下述命令push代码

    git init
    git add .
    git commit -m "AdminService with a test"
    git remote add origin https://gitee.com/wubin28/devops-katas-jenkins-pipeline-as-code-kata.git
    git push -u origin master
    

    本文代码的码云地址为 https://gitee.com/wubin28/devops-katas-jenkins-pipeline-as-code-kata.git

    下面的任务,就是要把上述单元测试,运行在Jenkins流水线上

    在Jenkins界面上编写流水线脚本并运行流水线

    虽然本操练的最终目标,是要用Jenkinsfile脚本来定义流水线,但为了调试脚本方便,所以先在Jenkins界面上把脚本调试好,然后再把这些脚本写入Jenkinsfile

    创建文件夹

    为方便管理操练内容,首先在Jenkins主页上创建jenkins-pipeline-as-code-kata文件夹,以后的操作都在该文件夹中

    点击 New Item

    点击 `New Item`

    创建文件夹

    创建文件夹

    不需要配置,直接点 Save

    不需要配置,直接点 `Save`

    文件夹创建完毕

    文件夹创建完毕

    确认Maven与git都已经在Jenkins中配置好

    因为运行流水线需要Maven和Git这两个工具,所以需要事先在Jenkins里配置好

    进入 Global Tool Configuration 页面

    进入 `Global Tool Configuration` 页面

    把Maven命名为M3

    把Maven命名为M3

    把git命令在Jenkins容器里的路径设置为 /usr/bin/git。这一点可以通过执行命令 docker container exec -it jenkins-katas bash进入容器内部查看,查看有按 Ctrl + PQ 退出

    把git命令的路径设置为 `/usr/bin/git`

    创建名为adminprovider的流水线

    进入jenkins-pipeline-as-code-kata文件夹,点击 New Item ,创建名为adminprovider 的流水线

    创建名为 `adminprovider` 的流水线

    修改流水线的脚本

    在流水线配置页面的底部, script 输入框的右上角try sample Pipeline... ,选择 GitHub + Maven
    流水线样例脚本,作为修改的基础

    选择 `GitHub + Maven`流水线样例脚本,作为修改的基础

    将第13行的git代码库的地址改为本操练的代码库的地址 https://gitee.com/wubin28/devops-katas-jenkins-pipeline-as-code-kata.git

    将第13行的git代码库的地址改为本操练的代码库的地址

    将第16行的mvn命令,改为./mvnw clean package'。mvnw命令能够在没有安装maven的情况下,运行maven命令。之后,点击 `Save按钮保存

    将第16行的mvn命令,改为 ‘./mvnw clean package’

    点击 Build Now 手工触发流水线构建。点击左下角 #1 左侧的小圆点,能够跳转到控制台输出页面,观察运行结果。

    点击 `Build Now` 手工触发流水线构建 点击左下角 `#1`左侧的小圆点,能够跳转到控制台输出页面

    如果一切正常,那么构建应该成功。这表明在界面上编写的脚本没有问题。下面可以把这些脚本写到
    Jenkinsfile文件中,以便让Jenkins读取该文件中的流水线配置信息。从而实现用Jenkinsfile脚本文件来定义流水线,减轻配置的工作量。

    根据脚本创建Jenkinsfile,并配置Jenkins,使其读取Jenkinsfile来运行流水线

    因为流水线脚本要从git版本库中读取,需要重新配置,所以现在创建一个名为adminprovider-from-scm新的流水线

    创建名为adminprovider-from-scm的流水线

    准备好Jenkinsfile

    在流水线配置页面的底部, script 输入框的右上角 try sample Pipeline... ,选择 GitHub + Maven
    流水线样例脚本,将其内容复制粘贴到代码根目录下新创建的Jenkinsfile文件中,并把其中的git版本库地址和maven命令如上所示更改过来。为了验证Jenkins确实从Jenkinsfile读取了流水线配置,在 steps 第一句增加了 echo 'hello from scm。修改完Jenkinsfile后,就可以点击流水线配置页面底部的 Save按钮,保存配置。

    Jenkinsfile.

    pipeline {
        agent any
    
        tools {
            // Install the Maven version configured as "M3" and add it to the path.
            maven "M3"
        }
    
        stages {
            stage('Build') {
                steps {
                    echo 'hello from scm'
                    // Get some code from a GitHub repository
                    git 'https://gitee.com/wubin28/devops-katas-jenkins-pipeline-as-code-kata.git'
    
                    // Run Maven on a Unix agent.
                    sh "./mvnw clean package"
    
                    // To run Maven on a Windows agent, use
                    // bat "mvn -Dmaven.test.failure.ignore=true clean package"
                }
    
                post {
                    // If Maven was able to run the tests, even if some of the test
                    // failed, record the test results and archive the jar file.
                    success {
                        junit '**/target/surefire-reports/TEST-*.xml'
                        archiveArtifacts 'target/*.jar'
                    }
                }
            }
        }
    }
    

    使用以下命令,将代码push到git版本库

    git add .
    git commit -m "add Jenkinsfile"
    git pull --rebase
    git push -u origin master
    

    配置Jenkins使其读取代码库中的Jenkinsfile来配置流水线

    进入刚刚创建的流水线 adminprovider-from-scm 配置页面,在页面底部的
    Pipeline 配置区域,点击 Definition 下拉框,选择
    Pipeline script from SCM

    选择`Pipeline script from SCM`

    SCM 下拉框中,选择 Git。在 Repository URL中,填入Jenkinsfile所在的代码库的地址
    https://gitee.com/wubin28/devops-katas-jenkins-pipeline-as-code-kata.git。确保Branch Specifier 中填写了 */masterScript Path 中填写了Jenkinsfile 。点击 Save 保存

    选择 `Git`,填写代码库地址

    点击 Build Now
    手工触发流水线构建,让Jenkins读取代码库中的Jenkinsfile。

    点击 `Build Now`手工触发流水线构建

    点击左下角 #1
    左侧的小圆点,能够跳转到控制台输出页面,观察运行结果中包含了上面添加的那句 hello from scm 。说明Jenkins确实读取了Jenkinsfile

    观察运行结果中包含了上面添加的那句 `hello from scm`

    触发流水线

    现在Jenkins能从代码库中读取Jenkinsfile了。这意味着流水线的配置,都可以用有版本控制的脚本来完成。但如果想让Jenkins定时轮询代码库,以便做到频繁小批地构建代码,从而尽早频繁小批地定位代码质量问题,更容易地修复问题,这该如何用脚本实现呢?(当然,使用web hook会比轮询更有优势——能实现代码库一旦有代码push上来,就能通知Jenkins进行构建,从而把频繁小批构建做到极致。有关web hook的操练,我们以后再做)

    在jenkinsfile中配置轮询

    为了验证Jenkins对代码库的轮询,确实来自Jenkinsfile,可以先打开流水线配置页面中的build
    trigger配置,确认没有任何选项被勾选了

    打开流水线配置页面中的build trigger配置,确认没有任何选项被勾选了

    在Jenkinsfile中的 agent any 下面,添加五个星号的 cron,表示Jenkins每隔1分钟就轮询一次代码库,无论是否有新代码,都会执行构建

    triggers {
      cron('* * * * *')
    }
    

    使用以下命令,将代码push到git版本库

    git commit -am "add triggers with 5 stars into Jenkinsfile"
    git pull --rebase
    git push -u origin master
    

    点击 Build Now手工触发流水线构建,让Jenkins读取代码库中的Jenkinsfile

    确认流水线配置页面中的Build Triggers配置区域中,Build periodically已经被勾选,且五个星出现在 Schedule输入框中。这表明Jenkins确实读取了Jenkinsfile

    确认流水线配置页面中的Build Triggers配置区域中,Build periodically已经被勾选,且五个星出现在 `Schedule` 输入框中

    在流水线上引入一个编译错误,并revert来解决问题

    现在操练一下当流水线遇到编译错误时,会报什么错

    在测试代码中,加一句 abc(); ,然后push代码到代码库

    AdminServiceTest.java.

    class AdminServiceTest {
        @Test
        public void should_retrieve_an_admin_with_correct_names() {
            abc();
            AdminService adminService = new AdminService();
    
            Admin admin = adminService.retrieveAdmin(4);
    
            BDDAssertions.then(admin.getFirstName()).isEqualTo("firstName [4]");
            BDDAssertions.then(admin.getLastName()).isEqualTo("lastName [4]");
        }
    }
    

    等1分钟后,流水线被轮询程序自动触发。把鼠标放到有提交的出错构建处,能看到导致这次构建失败的提交人和提交信息。点击相应提交左边的小圆球,能看到具体的错误信息

    等1分钟后,流水线被轮询程序自动触发。把鼠标放到有提交的出错构建处,能看到导致这次构建失败的提交人和提交信息 点击相应提交左边的小圆球,能看到具体的错误信息

    使用下述命令来查看上次提交的hash号,revert刚才引起流水线故障的提交

    git log
    git revert 131f54ebb5554aef43fc823d5d8d6fb7aaa8898c
    git push
    

    revert并且push,1分钟后,流水线自动构建,故障消失

    revert并且push,1分钟后,流水线自动构建,故障消失

    在流水线上引入一个自动化单元测试失败,并revert来解决问题

    现在操练一下当流水线遇到测试失败时,会报什么错

    在测试代码中,将断言中的 firstName [4] 改为 firstName [40],然后push代码到代码库

    AdminServiceTest.java.

    class AdminServiceTest {
        @Test
        public void should_retrieve_an_admin_with_correct_names() {
            AdminService adminService = new AdminService();
    
            Admin admin = adminService.retrieveAdmin(4);
    
            BDDAssertions.then(admin.getFirstName()).isEqualTo("firstName [40]");
            BDDAssertions.then(admin.getLastName()).isEqualTo("lastName [4]");
        }
    
    }
    

    等1分钟后,流水线被轮询程序自动触发。把鼠标放到有提交的出错构建处,能看到导致这次构建失败的提交人和提交信息。点击相应提交左边的小圆球,能看到具体的错误信息

    等1分钟后,流水线被轮询程序自动触发。把鼠标放到有提交的出错构建处,能看到导致这次构建失败的提交人和提交信息 点击相应提交左边的小圆球,能看到具体的错误信息

    可以使用上面提到的命令来查看上次提交的hash号,revert刚才引起流水线故障的提交

    将Jenkinsfile中的cron改为不那么频繁地构建

    每分钟构建一次十分耗费资源,所以可以把轮询次数改为工作时间每2小时构建一次

    Jenkinsfile.

    pipeline {
        agent any
    
        triggers {
            cron('H H(8-15)/2 * * 1-5')
        }
    

    push代码,1分钟后自动构建,Jenkins会把修改后的轮询配置自动更新到配置页面

    作业

    操练到此结束。现在该轮到你操练了。可以换一个业务场景操练一下。比如可以将根据id号获取管理员的业务场景,换成根据id号获取学生,从头到尾操练一遍。愿你有所收获

    反馈

    为了让下次DevOps编程操练让你更有收获,不妨花2分钟填写4个问题

    相关文章

      网友评论

        本文标题:DevOps编程操练:用Jenkins流水线建立代码质量预警机制

        本文链接:https://www.haomeiwen.com/subject/sycouktx.html