时间 标题 内容



上午



版本控制系统(GitLab)

1. GitLab安装部署配置
2. GitLab web页面的基本功能
3. 从创建项目到提交代码的开发过程
4. GitLab WebHook简介与应用
5. 配置代码提交触发CI流水线(限制分支)



下午 |


构建工具与消息通知 |
1. 提交流水线的优化(避免创建分支/tag后自动触发)
2. CI流水线中的Clone Code 阶段实现
3. 配置CI流水线中的构建反馈(邮件)
4. 更新Gitlab中commit的提交状态
| | | |
1. 常用的项目构建工具简介(maven/npm)
2. CI流水线集成代码Build阶段
| |

|

资源清单
Postman下载: https://www.postman.com/downloads/
Gitlab接口文档:https://docs.gitlab.com/13.10/ee/api/README.html | |


jenkins共享库代码:devops-library-service-day3-init-version.tar.gz

postman 实验接口文件: day3-gitlab.postman_collection.json 下载这个文件然后导入到你的本地postman
image.png

image.png

1. 预备知识

目标:学习HTTP基础知识,掌握如何使用Postman和Curl调用接口的方法。

1.1 Web HTTP基础知识

HTTP请求是什么?

HTTP超文本传输协议,是确保服务器(Server)和客户端(Client)之间的正确通信。

一个请求和响应的过程:
image.png

  • Request 用户通过浏览器向我们的服务端发起请求。
  • Response服务端将客户端请求的资源数据进行响应。

调用接口的方法

postman下载:

请求类型:

  • GET 向指定的URL请求资源,可携带参数(明文)。
  • POST 向指定的URL提交资源,表单数据提交,数据进行封装(比Get方法安全)。
  • PUT 与POST类似,通常用于对资源数据的更新修改。
  • DELETE 删除指定的资源。

演示:以baidu为例,用Postman调用一个接口的过程。并结合使用Curl操作。

image.png


HTTP常见的错误码

  • 1xx : 服务已收到请求,请求者继续执行操作。
  • 2xx:请求成功,常见(201)
  • 3xx:请求成功,页面发生重定向(301)
  • 4xx:客户端发生了错误
    • 常见的是资源找不到了(404)
    • 资源请求需要认证认证失败(401)
    • 认证成功但是权限不够(403)
  • 5xx: 服务端发生了错误
    • 网关超时(504)
    • 内部错误(500)
    • 网关错误(502)
    • 服务不可用(503)

1.2 Jenkins Generic Webhook实践

目的: 为Jenkins作业添加触发器,便于其他系统调用。

安装配置Generic WebHook

插件名称:Generic Webhook Trigger
image.png

重启后,进入一个Pipeline项目设置,已经可以选择这个触发器了….
image.png

插件文档https://plugins.jenkins.io/generic-webhook-trigger/
image.png

Jenkins作业配置触发器

启动Generic Webhook触发器后, 相当于给Jenkins加了一个新的接口http://JENKINS_URL/generic-webhook-trigger/invoke)。
image.png
调用的时候:这里要把 **JENKINS_URL** 换成自己真实的Jenkins 服务器地址,有端口就加上端口,是域名就写域名。下面是一个参考的URL:

  1. http://192.168.1.200:8080/generic-webhook-trigger/invoke

Post content parameters: 获取调用接口传进来的数据

JsonPath语法文档: https://github.com/json-path/JsonPath
image.png

Header parameters: 获取Header中的参数
image.png

**

  1. curl http://192.168.1.200:8080/generic-webhook-trigger/invoke?runopts=gitlab

image.png

**

  1. curl http://192.168.1.200:8080/generic-webhook-trigger/invoke?token=devops-service

image.png

打印调试信息到日志中
image.png

触发条件过滤:仅满足条件才能触发此作业
image.png

实践:demo
image.png

解析GET/POST请求数据

传参注意,第一个参数使用?号连接, 后面的参数使用&符号连接。

  1. ?token=demo-pipeline-service&user=jenkins&a=1&b=2

演示将postman中的请求转换curl方式。
image.png

  1. [root@zeyang-nuc-service ~]# curl --location --request GET 'http://192.168.1.200:8080/generic-webhook-trigger/invoke?token=demo-pipeline-service'
  2. {"jobs":{"demo-pipeline-service":{"regexpFilterExpression":"","triggered":true,"resolvedVariables":{},"regexpFilterText":"","id":209,"url":"queue/item/209/"}},"message":"Triggered jobs."}[root@zeyang-nuc-service ~]#

解析GET数据

  • 客户端发送Get请求, 带有两个参数 versionusername

    1. http://192.168.1.200:8080/generic-webhook-trigger/invoke?token=demo-pipeline-service&version=1.1.1&username=jenkins
  • Jenkins 配置Generic hook,获取请求参数versionusername参数名称要一致

image.png

  • 验证测试(Jenkins日志中能够打印出获取的值,则正常

image.png

  • 通过jenkinsfile读取传递的参数
    1. println("${username}")
    2. println("${version}")

解析HEADER参数数据

  • 客户端发送请求(什么请求都可以,这里的header与请求类型无关), 带有两个参数 header_name 和 header_id 。

    1. curl --location --request GET 'http://192.168.1.200:8080/generic-webhook-trigger/invoke?token=demo-pipeline-service' \
    2. --header 'header_name: jenkins' \
    3. --header 'header_id: 100'
  • Jenkins 配置Generic hook,获取请求参数header_nameheader_id

image.png

  • 通过jenkinsfile读取传递的参数 ```groovy println(“${header_id}”) println(“${header_name}”) println(header_id) println(header_name)

String headerName = “${header_id}” println(headerName)

//pipeline {
//}

  1. ---
  2. <a name="rGjNj"></a>
  3. #### 解析POST数据
  4. - 客户端发送POST请求, 参数存储在body体中(**参考POSTMAN中的样例**)
  5. ```groovy
  6. curl --location --request POST 'http://192.168.1.200:8080/generic-webhook-trigger/invoke?token=demo-pipeline-service' \
  7. --header 'Content-Type: application/json' \
  8. --data-raw '{
  9. "name": "zhangsan",
  10. "id": "123",
  11. "group1": {
  12. "name": "jenkins",
  13. "id" : "001",
  14. "age": "40"
  15. }
  16. }'
  • Jenkins 配置触发器来获取Post参数。

获取所有数据
image.png
获取username字段
image.png
获取group1Name字段
image.png

  • Jenkinsfile中使用参数 ```groovy

println(“所有body数据 —> ${allData}”) println(‘最外层name —-> $.name’ + “${userName}”) println(‘第二层name —-> $.group1.name’ + “${group1Name}”)

  1. ---
  2. <a name="2i5ow"></a>
  3. #### 扩展流水线解析JSON数据
  4. 安装插件: [Pipeline Utility Steps](https://plugins.jenkins.io/pipeline-utility-steps)<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/2584012/1618021428857-6b4b02a9-c2ec-4ab2-aacc-0372cdb1735b.png#height=141&id=xGkE4&originHeight=282&originWidth=1005&originalType=binary&size=27395&status=done&style=none&width=502.5)
  5. readJSON 处理json数据
  6. ```groovy
  7. println("所有body数据 --> ${allData}")
  8. def webHookData = readJSON text: "${allData}"
  9. String userName = webHookData["name"]
  10. String userName2 = webHookData.name
  11. String group1Name = webHookData["group1"]["name"]
  12. String group1Name2 = webHookData.group1.name
  13. println('最外层name ---> $.name' + "${userName}")
  14. println('最外层name ---> $.name' + "${userName2}")
  15. println('第二层name ---> $.group1.name' + "${group1Name}")
  16. println('第二层name ---> $.group1.name' + "${group1Name2}")

Rebuilder 插件使用

可以直接携带原触发参数进行触发,不用重复触发(不用在重复的提交代码了)。
image.png

进入某一次构建后,可以点击rebuild。
image.png


休息20分钟, 11点继续

2. 基于GitLab的开发工作流

root /admin123

创建一个项目组

配置group名称最好与项目组有关的,例如业务的简称等等。项目组的类型分为 Private、Internal、Public三种类型。

  • Private 私有类型(当group为私有类型,后面组下面的项目都是私有类型)
  • Public 公开类型

image.png

创建一个项目

在这个页面可以创建一个空的项目、根据一个模板创建项目、导入一个已存在的项目(Gitlab、GitHub等系统)
image.png

创建一个 demo-hello-service 项目,私有类型。

image.png

将代码导入项目

在执行git 客户端命令之前,可以先设置当前的用户信息,名称和邮箱。(可选、规范化)

  1. git config --global user.name "Administrator"
  2. git config --global user.email "admin@example.com"

下载代码库,并在代码库中创建文件提交。

  1. ### 下载项目
  2. # git clone http://192.168.1.200/devops/demo-hello-service.git
  3. Cloning into 'demo-hello-service'...
  4. Username for 'http://192.168.1.200': root
  5. Password for 'http://root@192.168.1.200':
  6. warning: You appear to have cloned an empty repository.
  7. ### 查看项目
  8. # ls | grep demo
  9. demo-hello-service
  10. demo-java-service
  11. microservicecicd-demo-service
  12. spinnaker-canary-demo
  13. ### 进入项目中
  14. #### 创建文件
  15. # cd demo-hello-service/
  16. # echo devopsdevops >> jenkins.txt
  17. # ls
  18. jenkins.txt
  19. ### 提交文件到远程仓库
  20. # git add jenkins.txt
  21. # git commit -m "add jenkins.txt "
  22. [master (root-commit) d74d541] add jenkins.txt
  23. 1 file changed, 1 insertion(+)
  24. create mode 100644 jenkins.txt
  25. [root@zeyang-nuc-service demo-hello-service]# git push origin master
  26. Username for 'http://192.168.1.200': root
  27. Password for 'http://root@192.168.1.200':
  28. Enumerating objects: 3, done.
  29. Counting objects: 100% (3/3), done.
  30. Writing objects: 100% (3/3), 221 bytes | 221.00 KiB/s, done.
  31. Total 3 (delta 0), reused 0 (delta 0)
  32. To http://192.168.1.200/devops/demo-hello-service.git
  33. * [new branch] master -> master

扩展:将本地已存在的代码提交到远程仓库

  1. cd existing_folder
  2. git init
  3. git remote add origin http://192.168.1.200/devops/demo-hello-service.git
  4. git add .
  5. git commit -m "Initial commit"
  6. git push -u origin master

代码提交后的效果:发现 jenkins.txt 文件已经从本地同步到了远程的gitlab仓库中了。
image.png


拉取特性分支

为什么要拉取分支? 一个分支不够吗? 一般我们使用 master 主干分支存放最新的能够发布生产的代码,而单独创建一些特性分支来做项目需求任务的开发分支。 这样的好处是防止主干分支污染,对分支起到了保护的作用。

下面进入 demo-hello-service 项目主页,然后基于主干分支master,创建特性分支feature-1-DEV。操作如下:

image.png

image.png

特性分支开发与提交

查看当前本地分支,发现没有刚刚远程创建的 feature-1-DEV 分支。

  1. [root@zeyang-nuc-service demo-hello-service]# git branch -a
  2. * master
  3. remotes/origin/master

git pull 同步远程仓库所做的更新到本地, 这样远程的feature-1-DEV 分支就同步到了本地。然后我们使用 git checkout feature-1-DEV 切换到特性分支。

  1. root@zeyang-nuc-service demo-hello-service]# git pull
  2. Username for 'http://192.168.1.200': root
  3. Password for 'http://root@192.168.1.200':
  4. From http://192.168.1.200/devops/demo-hello-service
  5. * [new branch] feature-1-DEV -> origin/feature-1-DEV
  6. Already up to date.
  7. [root@zeyang-nuc-service demo-hello-service]# git branch -a
  8. * master
  9. remotes/origin/feature-1-DEV
  10. remotes/origin/master
  11. [root@zeyang-nuc-service demo-hello-service]# git checkout feature-1-DEV
  12. Branch 'feature-1-DEV' set up to track remote branch 'feature-1-DEV' from 'origin'.
  13. Switched to a new branch 'feature-1-DEV'
  14. [root@zeyang-nuc-service demo-hello-service]# git branch -a
  15. * feature-1-DEV
  16. master
  17. remotes/origin/feature-1-DEV
  18. remotes/origin/master

我们更改了Jenkins.txt文件内容,并创建一个新的version文件,然后将更改内容提交到远程仓库。

  1. ### 更改文件内容
  2. [root@zeyang-nuc-service demo-hello-service]# ls
  3. jenkins.txt
  4. [root@zeyang-nuc-service demo-hello-service]# vi jenkins.txt
  5. [root@zeyang-nuc-service demo-hello-service]# cat jenkins.txt
  6. pipeline {
  7. agent { label "master"}
  8. stages{
  9. stage("Build"){
  10. steps {
  11. script {
  12. echo "hello"
  13. }
  14. }
  15. }
  16. }
  17. }
  18. [root@zeyang-nuc-service demo-hello-service]# echo 1.1.1 >version
  19. [root@zeyang-nuc-service demo-hello-service]# ls
  20. jenkins.txt version
  21. ### 提交到远程仓库分支
  22. [root@zeyang-nuc-service demo-hello-service]# git add jenkins.txt version
  23. [root@zeyang-nuc-service demo-hello-service]# git commit -m "add pipeline "
  24. [feature-1-DEV 97ae23e] add pipeline
  25. 2 files changed, 14 insertions(+), 1 deletion(-)
  26. create mode 100644 version
  27. [root@zeyang-nuc-service demo-hello-service]# git push origin feature-1-DEV
  28. Username for 'http://192.168.1.200': root
  29. Password for 'http://root@192.168.1.200':
  30. Enumerating objects: 6, done.
  31. Counting objects: 100% (6/6), done.
  32. Delta compression using up to 8 threads.
  33. Compressing objects: 100% (3/3), done.
  34. Writing objects: 100% (4/4), 380 bytes | 380.00 KiB/s, done.
  35. Total 4 (delta 0), reused 0 (delta 0)
  36. remote:
  37. remote: To create a merge request for feature-1-DEV, visit:
  38. remote: http://192.168.1.200/devops/demo-hello-service/-/merge_requests/new?merge_request%5Bsource_branch%5D=feature-1-DEV
  39. remote:
  40. To http://192.168.1.200/devops/demo-hello-service.git
  41. d74d541..97ae23e feature-1-DEV -> feature-1-DEV

这样我们就把本地的特性分支开发的代码提交到了远程特性分支中了, 接下来对应该对该特性分支进行测试验证,没问题后合并到主干分支。


特性分支合并操作

将特性分支 feature-1-DEV 代码合并到主干分支master Merge Request。

image.png
在这个页面,选择源分支和目标分支。

image.png
在这个页面:

  • 1 指定合并请求的标题
  • 2 描述信息,一般都是变更信息
  • 3 指定主管进行审核(最终该用户决定是否合并)
  • 4 指定进行代码审查的同事
  • 5 合并成功后删除源分支(最后很定要删除源分支,可以先保留一个版本后再删除,此处最好取消勾选)

image.png

提交合并后,由管理员审查进行合并。
image.png

合并后的效果: 特性分支的更改已经同步到了主干分支。
image.png

到此一个基本的项目开发提交代码过程就已经完成了。(多熟悉一下这个过程)


3. GitLab触发器与提交流水线

Jenkins WebHook配置

新建一个“gitlab-test-pipeline”项目, 开启Generic webhook。 配置触发token 为作业名称gitlab-test-pipeline。
image.png
生成的触发URL

  1. http://192.168.1.200:8080/generic-webhook-trigger/invoke?token=gitlab-test-pipeline

GitLab WebHook 配置

进入项目设置, gitlab webhook的配置页面:
image.png

添加Jenkins 触发器地址,选择对应的事件。最后点击image.png
这里选择的是代码提交事件, 过滤的分支是以feature开头的所有分支。

image.png

事件:

  • Push 提交事件
  • Tag Push 创建事件
  • MergeRequest 合并事件
  • Issue 问题创建更新事件

模拟事件触发,点击test按钮选择push事件,此时去看下Jenkins是否成功被触发。
image.png

触发成功提示: Hook executed successfully: HTTP 200

出现此FAQ:Url is blocked: Requests to the local network are not allowed

解决方法:进入admin管理页面设置 > network
image.png

找到”Outbound requests”勾选允许请求webhooks和服务。(更改后,重启触发即可)

image.png

Webhook问题排查调试

进入webhook添加页面的最下方,点击你所创建的webhook的 Edit按钮
image.png
可以查看到webhook的历史记录, 可以看到本次提交是否正常产生了webhook事件。

image.png
点击 View details 可以看到此webhook发送给对端Jenkins的数据信息,和请求状态。
image.png

如果此次请求由于xxx原因导致没有触发jenkins构建, 可以在这里点击 Resend Request 按钮进行重新发送请求,而不是再次提交代码。

image.png

如果Jenkins触发成功了之后,我们可以在Jenkins的构建日志中查看效果。 这些数据就是gitlab post传递过来的。
image.png

到此:你基本上已经知道了Gitlab如何触发Jenkins的了。(多看几遍,多练习几遍)


4. GitLab提交流水线优化

过滤新建分支和tag的触发

你可能发现问题了,新建一个分支或者标签也会出现构建,这个构建是没有意义的。我们需要排除掉。没错,jenkins 的 Generic webHook 也是支持的。
参考官方的说明:https://github.com/jenkinsci/generic-webhook-trigger-plugin/blob/master/src/test/resources/org/jenkinsci/plugins/gwt/bdd/gitlab/gitlab-push-ignore-create-remove-branch.feature

image.png
添加三个变量,获取当前的提交信息 $object_kind $before $after
此步骤一定要注意下参数名和值后面的空格,要去掉
image.png
通过正则表达式配置触发条件:Expression ^push\s(?!0{40}).{40}\s(?!0{40}).{40}$ Text $object_kind $before $after。 push请求只有after和before的值都不是40个0的时候触发构建(为40个0的情况是删除分支或者新建分支)

image.png

如何支持多个分支触发构建?


创建多个gitlab webhook 指向同一个jenkins 作业就可以了。
image.png


下午继续!

5. GitLab & Jenkins集成

代码下载部分

找一个pipeline 类型的项目进入流水线语法, 找到片段生成器中的 checkout 。 我们使用checkout方法来进行代码下载(svn也是支持的哦)
image.png

我们的代码库是私有类型的,需要一个具有clone 代码权限的用户账号来下载代码。(测试场景你可以将gitlab admin用户的账号存储在Jenkins的凭据中用于后期代码下载, 线上环境最好创建一个单独的reporter角色的用户进行代码下载,机器人账号。)

image.png

然后我们来生成代码片段吧,

  • 1 填写项目仓库地址
  • 2 指定访问项目仓库的账号
  • 3 指定要下载的代码库分支
  • 4 点击生成代码

image.png

一个基本的下载代码的demo示例:

  1. pipeline {
  2. agent { label "build" }
  3. stages {
  4. stage("GetCode"){
  5. steps{
  6. script{
  7. checkout([$class: 'GitSCM', branches: [[name: '*/master']],
  8. extensions: [],
  9. userRemoteConfigs: [[credentialsId: 'b874381c-20e9-4578-be9d-f1f691c25b23',
  10. url: 'http://192.168.1.200/devops/demo-hello-service.git']]])
  11. }
  12. }
  13. }
  14. }
  15. post {
  16. always {
  17. script{
  18. echo "always......"
  19. }
  20. }
  21. success {
  22. script {
  23. echo "success....."
  24. }
  25. }
  26. }
  27. }

一个动态传递分支参数的示例: (这部分最好要看下视频)

try 语句块中的是解析Gitlab传递过来的数据的,然后将 checkout 中的分支字符串和仓库地址使用变量替换。 branchNamegitHttpURL 都是解析的Gitlab hook数据。

如果想让这个作业同时支持gitlab自动触发和手动点击触发, 可以为项目添加 parameters 字段,生成UI参数。(注意这种将参数定义在Jenkinsfile的方式第一次运行需要初始化,第一次失败可以忽略,第二次以及后续可以正常使用的)

添加 try 语句块的目的也是为了忽略这些错误的, 因为手动触发是拿不到gitlab 传递的数据的,这是两种不同的触发方式所以一定要注意。 所以最后我们在UI定义两个参数作为手动触发使用的。

  1. try {
  2. // Gitlab提交触发
  3. println("${webHookData}")
  4. def webHookData = readJSON text: "${webHookData}"
  5. env.branchName = webHookData["ref"] - "refs/heads/"
  6. env.gitHttpURL = webHookData["project"]["git_http_url"]
  7. }
  8. catch(Exception e) {
  9. println(e)
  10. //String branchName = "${env.branchName}"
  11. }
  12. pipeline {
  13. agent { label "build" }
  14. // 手动触发
  15. parameters {
  16. string defaultValue: 'http://192.168.1.200/devops/demo-hello-service.git', description: '', name: 'gitHttpURL', trim: true
  17. string defaultValue: 'master', description: '', name: 'branchName', trim: true
  18. }
  19. stages {
  20. stage("GetCode"){
  21. steps{
  22. script{
  23. println("下载代码 --> 分支: ${env.branchName}")
  24. checkout([$class: 'GitSCM', branches: [[name: "${env.branchName}"]],
  25. extensions: [],
  26. userRemoteConfigs: [[credentialsId: 'b874381c-20e9-4578-be9d-f1f691c25b23',
  27. url: "${env.gitHttpURL}"]]])
  28. }
  29. }
  30. }
  31. }
  32. post {
  33. always {
  34. script{
  35. echo "always......"
  36. }
  37. }
  38. success {
  39. script {
  40. echo "success....."
  41. }
  42. }
  43. }
  44. }

效果:
image.png


邮件通知反馈

默认情况可能每个Gitlab用户没有配置邮箱的, 需要Gitlab用户要配置好邮箱。 点击头像进入 **edit profile**

image.png
在这个页面配置好邮箱地址,最好这几个email都配置上吧……最后image.png
image.png


Jenkins解析Gitlab的POST参数来获取用户邮箱地址, 我们这里获取到然后使用 currentBuild 来增加描述信息。这样更加醒目直观的看到此次构建的主要信息。

此段代码没有加到Pipeline{}中, 直接写就可以运行的。

  1. println("${webHookData}")
  2. def webHookData = readJSON text: "${webHookData}"
  3. env.branchName = webHookData["ref"] - "refs/heads/"
  4. env.gitHttpURL = webHookData["project"]["git_http_url"]
  5. env.userEmail = webHookData["user_email"]
  6. env.userName = webHookData["user_username"]
  7. currentBuild.description = "Trigger by ${env.userName} ${env.branchName} \n Email: ${env.userEmail}"

image.png


Jenkins需要配置邮件通知,安装插件Email Extension安装后重启Jenkins。
image.png
然后进入系统管理-> 系统设置 , 先配置下全局的admin的邮箱地址。(最后配置下不然可能会出错的)

image.png

->Extended E-email Notification。设置邮件系统配置信息。

登入邮箱拿到授权码

image.png

发送短信之后获取授权码
image.png

这里我使用的是QQ邮箱,填写SMTP服务器地址smtp.qq.com 和端口 465注意要开启SSL,密码为授权码。
image.png

换个选项注意下: 不选择 HTML 就是普通的文本, HTML 可以支持html网页,更加美观。这里选择 HTML

image.png

pipeline as code , 进入片段生成器,生成邮件通知代码。

image.png

  1. emailext body: 'hello world!....jenkins', subject: 'test.....', to: '2560350642@qq.com'

然后我们将此段代码加入到Jenkins pipeline 中运行, 可以看到效果:

image.png

jenkins as code 将email 写成一个函数。这个通知信息是一个html格式的。

  1. try {
  2. // Gitlab提交触发
  3. println("${webHookData}")
  4. def webHookData = readJSON text: "${webHookData}"
  5. env.branchName = webHookData["ref"] - "refs/heads/"
  6. env.gitHttpURL = webHookData["project"]["git_http_url"]
  7. env.userEmail = webHookData["user_email"]
  8. env.userName = webHookData["user_username"]
  9. currentBuild.description = "Trigger by ${env.userName} ${env.branchName} \n Email: ${env.userEmail}"
  10. }
  11. catch(Exception e) {
  12. println(e)
  13. //String branchName = "${env.branchName}"
  14. }
  15. pipeline {
  16. agent { label "build" }
  17. // // 手动触发
  18. // parameters {
  19. // string defaultValue: 'http://192.168.1.200/devops/demo-hello-service.git', description: '', name: 'gitHttpURL', trim: true
  20. // string defaultValue: 'master', description: '', name: 'branchName', trim: true
  21. // }
  22. stages {
  23. stage("GetCode"){
  24. steps{
  25. script{
  26. println("下载代码 --> 分支: ${env.branchName}")
  27. checkout([$class: 'GitSCM', branches: [[name: "${env.branchName}"]],
  28. extensions: [],
  29. userRemoteConfigs: [[credentialsId: 'b874381c-20e9-4578-be9d-f1f691c25b23',
  30. url: "${env.gitHttpURL}"]]])
  31. }
  32. }
  33. }
  34. }
  35. post {
  36. always {
  37. script{
  38. echo "always......"
  39. }
  40. }
  41. success {
  42. script {
  43. echo "success....."
  44. EmailUser("${env.userEmail}","${currentBuild.currentResult}")
  45. }
  46. }
  47. }
  48. }
  49. def EmailUser(userEmail,status){
  50. emailext body: """
  51. <!DOCTYPE html>
  52. <html>
  53. <head>
  54. <meta charset="UTF-8">
  55. </head>
  56. <body leftmargin="8" marginwidth="0" topmargin="8" marginheight="4" offset="0">
  57. <img src="http://192.168.1.200:8080/static/0eef74bf/images/headshot.png">
  58. <table width="95%" cellpadding="0" cellspacing="0" style="font-size: 11pt; font-family: Tahoma, Arial, Helvetica, sans-serif">
  59. <tr>
  60. <td><br />
  61. <b><font color="#0B610B">构建信息</font></b>
  62. </td>
  63. </tr>
  64. <tr>
  65. <td>
  66. <ul>
  67. <li>项目名称:${JOB_NAME}</li>
  68. <li>构建编号:${BUILD_ID}</li>
  69. <li>构建状态: ${status} </li>
  70. <li>项目地址:<a href="${BUILD_URL}">${BUILD_URL}</a></li>
  71. <li>构建日志:<a href="${BUILD_URL}console">${BUILD_URL}console</a></li>
  72. </ul>
  73. </td>
  74. </tr>
  75. <tr>
  76. </table>
  77. </body>
  78. </html> """,
  79. subject: "Jenkins-${JOB_NAME}项目构建信息 ",
  80. to: userEmail
  81. }

最后收到的邮件效果:
image.png


6. 常用的项目构建工具集成

image.png

6.1 Java项目构建工具

maven项目实验代码: devops-maven-service-master.zip
gradle 项目实验代码: devops-gradle-service-master.zip

初始化一个springboot项目 https://start.spring.io/

6.1.1 Maven

官网:http://maven.apache.org/download.cgi Maven是一个项目的构建依赖管理工具。通常项目的根目录会存在一个pom.xml文件(该文件用于定义项目的依赖包信息和构建配置)
image.png

安装配置

  1. ### 下载
  2. wget https://mirrors.bfsu.edu.cn/apache/maven/maven-3/3.8.1/binaries/apache-maven-3.8.1-bin.tar.gz
  3. tar zxf apache-maven-3.8.1-bin.tar.gz -C /usr/local/
  4. cd /usr/local/apache-maven-3.8.1/
  5. pwd /usr/local/apache-maven-3.8.1
  6. ### 配置环境变量
  7. vi /etc/profile
  8. export M2_HOME=/usr/local/apache-maven-3.8.1
  9. export PATH=$M2_HOME/bin:$PATH
  10. source /etc/profile
  11. ### 验证
  12. mvn -v
  13. Apache Maven 3.8.1 (05c21c65bdfed0f71a2f2ada8b84da59348c4c5d)
  14. Maven home: /usr/local/apache-maven-3.8.1
  15. Java version: 1.8.0_282, vendor: AdoptOpenJDK, runtime: /usr/local/jdk8u282-b08/jre
  16. Default locale: en_US, platform encoding: ANSI_X3.4-1968
  17. OS name: "linux", version: "4.18.0-80.el8.x86_64", arch: "amd64", family: "unix"

将这个项目上传项目到Git仓库中。(提前创建一个 devops-maven-service 的仓库)

  1. localhost:~ zeyang$ cd Desktop/demo
  2. localhost:demo zeyang$ ls
  3. HELP.md mvnw mvnw.cmd pom.xml src
  4. localhost:demo zeyang$ git init
  5. Initialized empty Git repository in /Users/zeyang/Desktop/demo/.git/
  6. localhost:demo zeyang$ git remote add origin http://192.168.1.200/devops/devops-maven-service.git
  7. localhost:demo zeyang$ git add .
  8. localhost:demo zeyang$ git commit -m "Initial commit"
  9. [master (root-commit) f907177] Initial commit
  10. 10 files changed, 712 insertions(+)
  11. create mode 100644 .gitignore
  12. create mode 100644 .mvn/wrapper/MavenWrapperDownloader.java
  13. create mode 100644 .mvn/wrapper/maven-wrapper.jar
  14. create mode 100644 .mvn/wrapper/maven-wrapper.properties
  15. create mode 100755 mvnw
  16. create mode 100644 mvnw.cmd
  17. create mode 100644 pom.xml
  18. create mode 100644 src/main/java/com/example/demo/DemoApplication.java
  19. create mode 100644 src/main/resources/application.properties
  20. create mode 100644 src/test/java/com/example/demo/DemoApplicationTests.java
  21. localhost:demo zeyang$ git push -u origin master
  22. Username for 'http://192.168.1.200': devopsadmin
  23. Password for 'http://devopsadmin@192.168.1.200':
  24. Enumerating objects: 26, done.
  25. Counting objects: 100% (26/26), done.
  26. Delta compression using up to 4 threads.
  27. Compressing objects: 100% (16/16), done.
  28. Writing objects: 100% (26/26), 52.35 KiB | 8.72 MiB/s, done.
  29. Total 26 (delta 0), reused 0 (delta 0)
  30. To http://192.168.1.200/devops/devops-maven-service.git
  31. * [new branch] master -> master
  32. Branch 'master' set up to track remote branch 'master' from 'origin'.

常用命令

  • mvn clean 清理构建目录
  • mvn clean package 打包
  • mvn clean install 打包部署
  • mvn clean test 单元测试
  • mvn clean package -f ../pom.xml -f指定pom位置\
  • mvn clean package -DskipTests / -Dmaven.test.skip=true 跳过单测
  • mvn deploy 发布包到制品库

配置阿里maven源 、 本地仓库

**

  1. stage("build"){
  2. steps {
  3. script{
  4. sh "mvn clean package"
  5. }
  6. }
  7. }

更加严谨的方式:使用绝对路径

  1. def buildTools = ["maven": "/usr/local/apache-maven-3.8.1"]
  2. pipeline {
  3. agent { label "build" }
  4. stages {
  5. stage("GetCode"){
  6. steps{
  7. script{
  8. println("下载代码 --> 分支: ${env.branchName}")
  9. checkout([$class: 'GitSCM', branches: [[name: "${env.branchName}"]],
  10. extensions: [],
  11. userRemoteConfigs: [[credentialsId: 'b874381c-20e9-4578-be9d-f1f691c25b23',
  12. url: "${env.gitHttpURL}"]]])
  13. }
  14. }
  15. }
  16. stage("Build"){
  17. steps {
  18. script {
  19. //sh "/usr/local/apache-maven-3.8.1/bin/mvn clean package"
  20. sh "${buildTools["maven"]}/bin/mvn clean package"
  21. }
  22. }
  23. }
  24. stage("UnitTest"){
  25. steps{
  26. script{
  27. sh "${buildTools["maven"]}/bin/mvn test"
  28. }
  29. }
  30. post {
  31. success {
  32. script{
  33. junit 'target/surefire-reports/*.xml'
  34. }
  35. }
  36. }
  37. }
  38. }
  39. post {
  40. always {
  41. script{
  42. echo "always......"
  43. }
  44. }
  45. success {
  46. script {
  47. echo "success....."
  48. }
  49. }
  50. }
  51. }
  52. def EmailUser(userEmail,status){
  53. emailext body: """
  54. <!DOCTYPE html>
  55. <html>
  56. <head>
  57. <meta charset="UTF-8">
  58. </head>
  59. <body leftmargin="8" marginwidth="0" topmargin="8" marginheight="4" offset="0">
  60. <img src="http://192.168.1.200:8080/static/0eef74bf/images/headshot.png">
  61. <table width="95%" cellpadding="0" cellspacing="0" style="font-size: 11pt; font-family: Tahoma, Arial, Helvetica, sans-serif">
  62. <tr>
  63. <td><br />
  64. <b><font color="#0B610B">构建信息</font></b>
  65. </td>
  66. </tr>
  67. <tr>
  68. <td>
  69. <ul>
  70. <li>项目名称:${JOB_NAME}</li>
  71. <li>构建编号:${BUILD_ID}</li>
  72. <li>构建状态: ${status} </li>
  73. <li>项目地址:<a href="${BUILD_URL}">${BUILD_URL}</a></li>
  74. <li>构建日志:<a href="${BUILD_URL}console">${BUILD_URL}console</a></li>
  75. </ul>
  76. </td>
  77. </tr>
  78. <tr>
  79. </table>
  80. </body>
  81. </html> """,
  82. subject: "Jenkins-${JOB_NAME}项目构建信息 ",
  83. to: userEmail
  84. }

6.1.2 Gradle

官网:https://gradle.org/releases/ Gradle基于Groovy,具有更灵活更强大的构建系统,能帮助我们构建更复杂的项目。

image.png

上传git

  1. localhost:~ zeyang$ cd Desktop/demo
  2. localhost:demo zeyang$ ls
  3. HELP.md build.gradle gradle gradlew gradlew.bat settings.gradle src
  4. localhost:demo zeyang$ build.gradl
  5. localhost:demo zeyang$ git init
  6. Initialized empty Git repository in /Users/zeyang/Desktop/demo/.git/
  7. localhost:demo zeyang$ git remote add origin http://192.168.1.200/devops/devops-gradle-service.git
  8. localhost:demo zeyang$ git add .
  9. localhost:demo zeyang$ git commit -m "Initial commit"
  10. [master (root-commit) c27be12] Initial commit
  11. 10 files changed, 366 insertions(+)
  12. create mode 100644 .gitignore
  13. create mode 100644 build.gradle
  14. create mode 100644 gradle/wrapper/gradle-wrapper.jar
  15. create mode 100644 gradle/wrapper/gradle-wrapper.properties
  16. create mode 100755 gradlew
  17. create mode 100644 gradlew.bat
  18. create mode 100644 settings.gradle
  19. create mode 100644 src/main/java/com/example/demo/DemoApplication.java
  20. create mode 100644 src/main/resources/application.properties
  21. create mode 100644 src/test/java/com/example/demo/DemoApplicationTests.java
  22. localhost:demo zeyang$ git push -u origin master
  23. Username for 'http://192.168.1.200': devopsadmin
  24. Password for 'http://devopsadmin@192.168.1.200':
  25. Enumerating objects: 26, done.
  26. Counting objects: 100% (26/26), done.
  27. Delta compression using up to 4 threads.
  28. Compressing objects: 100% (15/15), done.
  29. Writing objects: 100% (26/26), 57.35 KiB | 8.19 MiB/s, done.
  30. Total 26 (delta 0), reused 0 (delta 0)
  31. To http://192.168.1.200/devops/devops-gradle-service.git
  32. * [new branch] master -> master
  33. Branch 'master' set up to track remote branch 'master' from 'origin'.
  34. localhost:demo zeyang$

**

  1. unzip gradle-6.8.3-bin.zip -d /usr/local/
  2. export GRADLE_HOME=/usr/local/gradle-6.8.3/
  3. export PATH=$GRADLE_HOME/bin:$PATH
  4. gradle -v

常用命令

  • gradle build 构建项目
  • gradle build -x test 构建项目跳过测试
  • gradle clean 清空构建目录

**

  1. stage("build"){
  2. steps{
  3. script {
  4. sh "gradle clean && gradle build "
  5. }
  6. }
  7. }

6.1.3 Ant(扩展)

官网:https://ant.apache.org/bindownload.cgi Ant现在用的比较少了,也是一个项目构建依赖管理工具,使用xml的文件保存配置(build.xml)

安装配置

  1. wget
  2. tar
  3. export
  4. export
  5. source
  6. ant -version

常用命令

  • ant
  • ant -f ../build.xml

Jenkins集成

  1. stage("build"){
  2. steps{
  3. script {
  4. sh "ant -f build.xml"
  5. }
  6. }
  7. }

6.2 Go项目构建工具

https://studygolang.com/dl

此处代码和Jenkinsfile参考这个代码库 devops-golang-service-master.zip

**

  1. [root@zeyang-nuc-service cicd]# tar zxf go1.16.3.linux-amd64.tar.gz -C /usr/local/
  2. [root@zeyang-nuc-service cicd]# cd /usr/local/go/
  3. [root@zeyang-nuc-service go]# ls
  4. AUTHORS CONTRIBUTORS PATENTS SECURITY.md api doc lib pkg src
  5. CONTRIBUTING.md LICENSE README.md VERSION bin favicon.ico misc robots.txt test
  6. [root@zeyang-nuc-service go]# pwd
  7. /usr/local/go
  8. [root@zeyang-nuc-service go]# vi /etc/profile
  9. export GOROOT=/usr/local/go
  10. export GOPATH=/opt/godir
  11. export PATH=$GOROOT/bin:$PATH
  12. [root@zeyang-nuc-service go]# source /etc/profile
  13. [root@zeyang-nuc-service go]# go version
  14. go version go1.16.3 linux/amd64
  • GOROOT设置golang的安装位置
  • GOBIN目录是执行 go install 后生成可执行文件的目录
  • GOPATH工作目录

常用命令

  • go clean 清空构建
  • go build 构建源文件
  • go doc 生成godoc文档
  • go install 编译并安装指定的代码包
  • go fmt 代码格式化
  • go get 获取一个包
  • go run 运行一个go文件
  • go test 运行测试

6.3 前端项目构建工具

实验项目代码和Jenkinsfile: devops-web-service-master.zip

6.3.1 npm

官网:https://nodejs.org/en/download/

安装npm

  1. wget https://nodejs.org/dist/v14.16.1/node-v14.16.1-linux-x64.tar.xz
  2. tar xf node-v14.16.1-linux-x64.tar.xz -C /usr/local/
  3. [root@zeyang-nuc-service node-v14.16.1-linux-x64]# vi /etc/profile
  4. export NODE_HOME=/usr/local/node-v14.16.1-linux-x64
  5. export PATH=$NODE_HOME/bin:$PATH
  6. [root@zeyang-nuc-service node-v14.16.1-linux-x64]# source /etc/profile
  7. [root@zeyang-nuc-service node-v14.16.1-linux-x64]#
  8. [root@zeyang-nuc-service node-v14.16.1-linux-x64]# node -v
  9. v14.16.1
  10. [root@zeyang-nuc-service node-v14.16.1-linux-x64]# npm -v
  11. 6.14.12

通过下面的命令,初始化一个vue项目

  1. [root@zeyang-nuc-service node-v14.16.1-linux-x64]# npm install -g vue
  2. + vue@2.6.12
  3. added 1 package from 1 contributor in 3.342s
  4. [root@zeyang-nuc-service node-v14.16.1-linux-x64]# vue-init webpack vuedemo
  5. ? Project name vuedemo
  6. ? Project description A Vue.js project
  7. ? Author adminuser <2560350642@qq.com>
  8. ? Vue build standalone
  9. ? Install vue-router? No
  10. ? Use ESLint to lint your code? No
  11. ? Set up unit tests No
  12. ? Setup e2e tests with Nightwatch? No
  13. ? Should we run `npm install` for you after the project has been created? (recommended) npm
  14. vue-cli · Generated "vuedemo".

常用命令

  • npm install -g 包安装到全局
  • npm list:查看当前已安装的包。
  • npm config set registry https://registry.npm.taobao.org 设置淘宝源
  • npm config set cache “/opt/npmcache/“ 设置缓存路径

6.3.2 yarn

yarn,facebook取代npm的包管理工具,速度快。Yarn 缓存包,无需重复下载。 并行下载,安装速度快。

安装配置

  1. npm install -g yarn
  2. yarn info

常用命令

  • yarn / yarn install
  • yarn clean
  • yarn config set cache-folder “/opt/yarncache”
  1. yarn config set registry https://registry.npm.taobao.org
  2. yarn config set sass_binary_site "https://npm.taobao.org/mirrors/node-sass/"

7. 总结与共享库封装

共享库实验代码:devops-library-service-master.zip

创建共享库

image.png

配置共享库

进入Jenkins 设置, 定义共享库名称 devopslib 默认的版本 master
image.png

设置共享库的Git地址 [http://192.168.1.200/devops/devops-library-service.git](http://192.168.1.200/devops/devops-library-service.git) , 设置共享库的凭据。(如果没有提前创建好凭据,需要先去创建好然后再重新配置这个页面。)
image.png
保存设置,共享库就配置好了。

共享库内容分析

builds.groovy 存放构件相关的代码配置, 这里面定义了一个 Build 函数, 接受两个参数 buildToolsbuildType , 前者定义的是一个构建工具的位置map,后者是定义的构建类型例如 maven/ant/gradle/web/golang

代码主体是根据不同的构建类型执行不同的构建命令,默认打印信息。

  1. package org.devops
  2. // 构建函数
  3. def Build(buildTools, buildType){
  4. switch(buildType){
  5. case "maven":
  6. sh "${buildTools["maven"]}/bin/mvn clean package"
  7. break
  8. case "gradle":
  9. sh "${buildTools["gradle"]}/bin/gradle build -x test"
  10. break
  11. case "golang":
  12. sh "${buildTools["golang"]}/bin/go build demo.go"
  13. break
  14. case "web":
  15. sh """ ${buildTools["web"]}/bin/npm install && ${buildTools["web"]}/bin/npm run build """
  16. break
  17. default :
  18. println("buildType ==> [maven|gralde|golang|web]")
  19. break
  20. }
  21. }

mytools.groovy用于存放一些小工具, 这里定义了 GetCode 函数用于下载代码,接受三个参数:

  • srcType 代码库类型(git/svn)
  • branchName 代码库的分支名称
  • gitHttpURL 代码库的地址

EmailUser 函数用于发送邮件通知, 接受两个参数:

  • userEmail 邮件接收人(多个使用逗号分隔)
  • status 作业状态(成功、失败)
  1. package org.devops
  2. //下载代码
  3. def GetCode(srcType,branchName,gitHttpURL){
  4. if (srcType == "git"){
  5. println("下载代码 --> 分支: ${branchName}")
  6. checkout([$class: 'GitSCM', branches: [[name: "${branchName}"]],
  7. extensions: [],
  8. userRemoteConfigs: [[credentialsId: 'b874381c-20e9-4578-be9d-f1f691c25b23',
  9. url: "${gitHttpURL}"]]])
  10. }
  11. }
  12. // 邮件通知
  13. def EmailUser(userEmail,status){
  14. emailext body: """
  15. <!DOCTYPE html>
  16. <html>
  17. <head>
  18. <meta charset="UTF-8">
  19. </head>
  20. <body leftmargin="8" marginwidth="0" topmargin="8" marginheight="4" offset="0">
  21. <img src="http://192.168.1.200:8080/static/0eef74bf/images/headshot.png">
  22. <table width="95%" cellpadding="0" cellspacing="0" style="font-size: 11pt; font-family: Tahoma, Arial, Helvetica, sans-serif">
  23. <tr>
  24. <td><br />
  25. <b><font color="#0B610B">构建信息</font></b>
  26. </td>
  27. </tr>
  28. <tr>
  29. <td>
  30. <ul>
  31. <li>项目名称:${JOB_NAME}</li>
  32. <li>构建编号:${BUILD_ID}</li>
  33. <li>构建状态: ${status} </li>
  34. <li>项目地址:<a href="${BUILD_URL}">${BUILD_URL}</a></li>
  35. <li>构建日志:<a href="${BUILD_URL}console">${BUILD_URL}console</a></li>
  36. </ul>
  37. </td>
  38. </tr>
  39. <tr>
  40. </table>
  41. </body>
  42. </html> """,
  43. subject: "Jenkins-${JOB_NAME}项目构建信息 ",
  44. to: userEmail
  45. }

Jenkinsfile

  1. // 加载名称为devopslib的共享库的master版本
  2. @Library("devopslib@master") _
  3. //导入共享库中的方法类
  4. def mytools = new org.devops.mytools()
  5. def builds = new org.devops.builds()
  6. //定义构建工具类型与路径map
  7. def buildTools = [ "maven": "/usr/local/apache-maven-3.8.1",
  8. "gradle": "/usr/local/gradle-6.8.3/",
  9. "golang": "/usr/local/go",
  10. "web" : "/usr/local/node-v14.16.1-linux-x64/"]
  11. //定义UI上面的参数(用户去选择构建那个项目的那个分支的构建类型)
  12. String branchName = "${env.branchName}"
  13. String gitHttpURL = "${env.gitHttpURL}"
  14. String buildType = "${env.buildType}"
  15. // 以下是流水线阶段
  16. pipeline {
  17. agent { label "build" }
  18. options {
  19. skipDefaultCheckout true
  20. }
  21. stages {
  22. stage("GetCode"){
  23. steps{
  24. script{
  25. // 调用GetCode方法进行代码下载
  26. mytools.GetCode("git",branchName,gitHttpURL)
  27. }
  28. }
  29. }
  30. stage("Build"){
  31. steps {
  32. script {
  33. // 调用Build方法进行代码构建
  34. builds.Build(buildTools, buildType)
  35. }
  36. }
  37. }
  38. }
  39. post {
  40. always {
  41. script{
  42. echo "always......"
  43. }
  44. }
  45. success {
  46. script {
  47. echo "success....."
  48. }
  49. }
  50. }
  51. }

Jenkins 作业配置

newbuild-library-service.tar.gz 这是课程中作业的配置, 下载后解压到Jenkinshome的jobs目录中,然后重启Jenkins就可以加载了。

定义一个选项参数: 存放gitlab仓库地址
image.png

定义一个选项参数选择代码库类型
image.png

定义一个选项参数选择构建工具类型
image.png