2 Star 6 Fork 8

wubin28 / devops-kata-jenkins-pipeline-as-code

Create your Gitee Account
Explore and code with more than 6 million developers,Free private repositories !:)
Sign up
This repository doesn't specify license. Without author's permission, this code is only for learning and cannot be used for other purposes.
Clone or download
Cancel
Notice: Creating folder will generate an empty file .keep, because not support in Git
Loading...
README.adoc

DevOps编程操练:用Docker、Jenkins和GitLab操练代码质量预警机制

Table of Contents

解决痛点

  • 代码上线故障多

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

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

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

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

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

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

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

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

  • Group: devops.katas

  • Artifact: adminprovider

  • Name: adminprovider

  • Description: Demo project for Jenkins pipeline as code

  • Dependencies: Web, Lombok

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

因为把maven中的Java版本设置为11后,在Jenkins里运行会报错,所以需要把pom.xml文件中的 java.version11 改为 1.8

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

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

首先创建 AdminController

AdminproviderApplication.java
@RestController
class AdminController {
	@GetMapping("/admin/{id}")
	Admin admin(@PathVariable int id) {
		return new Admin("First" + id, "Last" + id);
	}
}

然后创建 Admin 类。

AdminproviderApplication.java
@Data
@AllArgsConstructor
class Admin {
	private String firstName;
	private String lastName;
}

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

application.properties
server.port=8765

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

httpie 8765 admin 1
Figure 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("First" + id, "Last" + 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("First4");
        BDDAssertions.then(admin.getLastName()).isEqualTo("Last4");
    }

}

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

在命令行上运行命令 mvn clean test ,单元测试也应该运行通过

使用docker安装Jenkins

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

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

实测表明,如果将GitLab和Jenkins同时运行在同一台电脑的两个docker容器里,无论电脑的内存是8G还是16G,两者都会运行很慢。所以推荐把Jenkins和GitLab分别运行在两台电脑的docker容器中,且每台电脑的内存至少8G

本操练使用ubuntu和mac这两台电脑,其中

  • ubuntu电脑(ip地址:192.168.71.244)的容器中安装了GitLab

  • mac电脑(ip地址:192.168.71.243)的容器中安装了Jenkins

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

安装docker

参见 Install Docker Engine ,在两台电脑上安装Docker

安装Kitematic

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

安装Jenkins

安装好docker后,在mac电脑的命令行运行以下命令,就能下载并启动jenkins容器。其中 ~/docker-volumes/jenkins 文件夹是我的个人目录,需要替换成你的本机某个目录。 Bins-MacBook-Pro.local 是我的电脑的主机名,也需要换成你的主机名

docker run --detach --hostname Bins-MacBook-Pro.local --publish 8080:8080 --publish 50000:50000 --name jenkins -v ~/docker-volumes/jenkins:/var/jenkins_home jenkins/jenkins

打开Kitematic,查看jenkins是否已经运行起来。如果已经运行起来,就可以在浏览器中访问 http://localhost:8080/ ,进入Jenkins安装页面,安装Jenkins。安装第一步所需要的admin管理员密码,能在Kitematic的 Home 签中的log内容中找到。安装Jenkins插件时,选择默认的即可。

使用docker安装并配置GitLab

由于GitLab运行所需要的80端口已经被ubuntu上的apache2占用了,所以需要运行命令 sudo service apache2 stop 先把apache2停掉

在ubuntu电脑的命令行运行以下命令,就能下载并启动GitLab容器。其中以 ~/docker-volumes/gitlab/ 开头的文件夹是我的个人目录,需要替换成你的本机某个目录。 ben-ZenBook-UX393EA-UX3000EA 是我的电脑的主机名,也需要换成你的主机名

docker run --detach --hostname ben-ZenBook-UX393EA-UX3000EA --publish 443:443 --publish 80:80 --publish 22:22 --name gitlab -v ~/docker-volumes/gitlab/etc:/etc/gitlab -v ~/docker-volumes/gitlab/var-log:/var/log/gitlab -v ~/docker-volumes/gitlab/var-opt:/var/opt/gitlab gitlab/gitlab-ce:latest

安装完成后,在浏览器中访问 http://localhost:80/ ,就能进入GitLab页面,设置 root用户密码。然后注册一个用户,并以该用户身份登录GitLab

登录GitLab后,进入用户的 Settings 页面,创建 Access Token ,并记录下来,以便后面设置webhook时使用。下面是我创建Access Token时使用的参数:

  • Name: token-for-asus-3

  • expires at: 2023-11-30

  • Scopes: (全部勾选5个scope)

点击 Create personal access token 进行创建,然后把页面顶部 Your new personal access token 下面的值记录下来,以备后用。我的access token是 pHt2XifyorWQeFTHs8e-

现在可以在GitLab中,创建一个名为 devops-kata-jenkins-pipeline-as-code 的空的项目,然后把前面编写的代码push到该项目中,以便后面操练中的Jenkins流水线读取代码来运行自动化测试。下面是我创建该项目时填的参数:

  • Project Name: devops-kata-jenkins-pipeline-as-code

  • Visibility Level: Public

点击 Create Project 按钮,创建代码库

要将代码push到GitLab,需要把你的代码所在的电脑的ssh key配置到GitLab中,以便push代码

点击 Add SSH key 按钮,进入 SSH Keys 页面

此时,在命令行窗口运行命令 ls ~/.ssh/ 查看电脑上的ssh key。如果没有发现 id_ed25519.pubid_rsa.pub 文件,那么推荐优先用以下命令创建ED25519 ssh key (比下面的RSA ssh key更安全高效),把其中的 email@company.com 换成你的email

ssh-keygen -t ed25519 -C "email@company.com>"

也可以用以下命令创建RSA ssh key,把其中的 email@company.com 换成你的email

ssh-keygen -t rsa -b 2048 -C "email@company.com>"

再次在命令行窗口运行命令 ls ~/.ssh/ 查看电脑上的ssh key,此时就能看到 id_ed25519.pubid_rsa.pub 文件了

现在可以在 SSH Keys 页面,创建ssh key了。下面是我创建时使用的参数

  • Key: (先运行 cat ~/.ssh/id_ed25519.pub ,然后把该命令的输出内容复制粘贴到此次)

  • Title: For TW-MacBookPro-3

  • Expires at: 2023/11/30

点击 Add Key 按钮创建ssh key

下面push代码的命令中的 192.168.71.244 是我的ubuntu电脑ip地址,需要换成你的电脑ip

git init
git add .
git commit -m "AdminService with a test"
git remote add origin git@192.168.71.244:wuzhenben/devops-kata-jenkins-pipeline-as-code.git
git push -u origin master

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

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

虽然本操练的一个目标,是要用保存在GitLab中进行版本化管理的Jenkinsfile脚本来定义流水线,但为了调试脚本方便,所以先在Jenkins界面上把脚本调试好,然后再把这些脚本写入Jenkinsfile

创建文件夹

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

点击 New Item

jenkins new item
Figure 2. 点击 New Item

创建文件夹

jenkins folder
Figure 3. 创建文件夹

不需要配置,直接点 Save

文件夹创建完毕

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

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

进入 Global Tool Configuration 页面

jenkins tool
Figure 4. 进入 Global Tool Configuration 页面

把Maven命名为M3

jenkins tool maven
Figure 5. 把Maven命名为M3

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

jenkins tool git
Figure 6. 把git命令的路径设置为 /usr/bin/git

创建名为adminprovider的流水线

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

jenkins pipeline create
Figure 7. 创建名为 adminprovider 的流水线

修改流水线的脚本

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

jenkins pipeline sample
Figure 8. 选择 GitHub + Maven 流水线样例脚本,作为修改的基础

将第13行的git代码库的地址改为本操练的代码库的地址 http://username:pHt2XifyorWQeFTHs8e-@192.168.71.244/wuzhenben/devops-kata-jenkins-pipeline-as-code.git

其中, 192.168.71.244 是我的ubuntu电脑ip地址,需要替换成你的电脑地址

将第16行的mvn命令,改为 ./mvnw -s /var/jenkins_home/maven/settings.xml clean package

mvnw命令能够在没有安装maven的情况下,运行maven命令。之后,点击 Save 按钮保存

/var/jenkins_home/ 对应mac电脑的 ~/docker-volumes/jenkins/ 目录,这已经在上面在mac电脑上所运行的docker run命令中的 -v 参数设置好了。这种volume的设置,能在你的电脑上设置一个文件夹,供docker和你的电脑操作系统共同访问,方便使用

/maven/settings.xml 其实是把电脑本地的带有阿里云maven镜像的settings.xml文件,复制过来,加快构建的速度

./mvnw -s 命令,就能读取后面跟随的settings.xml文件

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

jenkins pipeline build now
Figure 9. 点击 Build Now 手工触发流水线构建
jenkins pipeline console output
Figure 10. 点击左下角 #1 左侧的小圆点,能够跳转到控制台输出页面

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

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

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

jenkins pipeline create new
Figure 11. 创建名为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 'http://username:pHt2XifyorWQeFTHs8e-@192.168.71.244/wuzhenben/devops-kata-jenkins-pipeline-as-code.git'

                // Run Maven on a Unix agent.
                sh "./mvnw -s /var/jenkins_home/maven/settings.xml 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'
                }
            }
        }
    }
}

上面代码中的 http://username:pHt2XifyorWQeFTHs8e-@ 包含了前面配置的GitLab的 access token。如果没有这个token,那么在Jenkins里构建时会报 Authentication failed 错误

使用以下命令,将代码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

jenkins pipeline script from SCM
Figure 12. 选择 Pipeline script from SCM

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

jenkins pipeline script from SCM Git
Figure 13. 选择 Git ,填写代码库地址

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

jenkins pipeline script from SCM build now
Figure 14. 点击 Build Now 手工触发流水线构建

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

jenkins pipeline script from SCM hello from scm
Figure 15. 观察运行结果中包含了上面添加的那句 hello from scm

配置web hook触发流水线

现在Jenkins能从代码库中读取Jenkinsfile了。这意味着流水线的配置,都可以用有版本控制的脚本来完成。但如何让流水线的构建自动进行,从而尽早频繁小批地发现代码集成的问题,以便修复呢?

一种方法是使用cron来每隔一段时间来轮询。但这样作无论代码是否更新,轮询总是会触发构建,比较耗费资源。另一种更有优势的方法是在GitLab里配置web hook。这样代码库一旦有代码push上来, GitLab 就能通过 web hook 通知Jenkins进行构建,从而把频繁小批构建做到极致。

在Jenkins里配置GitLab

首先确认Jenkins安装了GitLab插件和Git插件

在Jenkins的 Configure System ,找到GitLab的配置区域,把 Enable authentication for /project end-point 的勾选取消掉。如不取消勾选,则会在web hook测试时,报403 forbidden error

下面是我配置的两个参数:

在下面的Credentials里,把GitLab的api access token粘贴进去。点击 Add 按钮,选择 Jenkins 选项,出现Jenkins Credentials Provider: Jenkins对话框。下面是我输入的一些参数:

  • Kind: GitLab API token

  • API token: pHt2XifyorWQeFTHs8e-

  • ID: token-for-asus-3

  • Description: token for asus 3

点击 Add 创建credential

在Jenkins的System Configuration里,确保GitLab中的Credentials下拉菜单选择了上面配置的名为 token for asus 3 的GitLab API token

由于Jenkinsfile的triggers声明暂不支持gitlab的web hook,没法在Jenkinsfile里编写脚本。所以下面用直接在Jenkins界面里写脚本的adminprovider流水线来实验web hook

进入adminprovider流水线的配置页面,在 Build Triggers 区域,勾选 Build when a change is pushed to GitLab. GitLab webhook URL: http://192.168.71.243:8080/project/jenkins-pipeline-as-code-kata/adminprovider ,并勾选下面两个选项:

  • Accepted Merge Request Events

  • Closed Merge Request Events

注意,这个选项已经提示你GitLab webhook URL了

在GitLab里配置web hook

进入GitLab里刚刚创建的代码库项目 devops-kata-jenkins-pipeline-as-code,点击左下角的 Settings ,再点击 Web hooks,下面是我配置的一些参数:

点击 Add webhook 按钮。然后在页面底部,点击 Test 按钮,并选择 Push events。如果一切顺利,会在页面顶部出现 Hook executed successfully: HTTP 200 成功信息

在流水线上引入一个编译错误,并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分钟后,流水线被轮询程序自动触发。把鼠标放到有提交的出错构建处,能看到导致这次构建失败的提交人和提交信息。点击相应提交左边的小圆球,能看到具体的错误信息

jenkins pipeline script from SCM compilation error committer
Figure 16. 等1分钟后,流水线被轮询程序自动触发。把鼠标放到有提交的出错构建处,能看到导致这次构建失败的提交信息
jenkins pipeline script from SCM compilation error logs
Figure 17. 点击相应提交左边的小圆球,能看到具体的错误信息

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

git log
git revert 131f54ebb5554aef43fc823d5d8d6fb7aaa8898c
git push

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

jenkins pipeline script from SCM compilation error reverted
Figure 18. revert并且push,流水线自动构建,故障消失

在流水线上引入一个自动化单元测试失败,并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]");
    }

}

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

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

作业

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

反馈

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

Comments ( 0 )

Sign in for post a comment

About

No description spread retract
Cancel

Releases

No release

Contributors

All

Activities

load more
can not load any more
1
https://gitee.com/wubin28/devops-kata-jenkins-pipeline-as-code.git
git@gitee.com:wubin28/devops-kata-jenkins-pipeline-as-code.git
wubin28
devops-kata-jenkins-pipeline-as-code
devops-kata-jenkins-pipeline-as-code
master

Search