第 3 章 Jenkins 自动化系统
本文打算以实践为主,不过多涉及理论部分,主要想通过一系列实践,对 jenkins 有一个整体认识,然后深入官方文档:https://www.jenkins.io/doc/book/ 就可以很好地运用 jenkins。
3.1、Jenkins 介绍
Jenkins 的前身是 Hudson,采用 JAVA 编写的持续集成开源工具。Hudson 由 Sun 公司在 2004 年启动,第一个版本于 2005 年在 java.net 发布。
在 2010 年 11 月期间,因为 Oracle 对 Sun 的收购带来了 Hudson 的所有权问题。主要的项目贡献者和 Oracle 之间,尽管达成了很多协议,但有个关键问题就是商标名称 "Hudson"。甲骨文在 2010 年 12 月声明拥有该名称并申请商标的权利。
2011 年 1 月 11 日,有人要求投票将项目名称从 "Hudson" 改为 "Jenkins"。2011 年 1 月 29 日,该建议得到社区投票的批准,创建了 Jenkins 项目。
3.1.1、Jenkins 功能
- 持续的软件版本发布/测试项目。
- 监控外部调用执行的工作。
3.1.2、Jenkins 概念
Jenkins 是一个功能强大的应用程序,允许持续集成和持续交付项目,无论用的是什么平台。这是一个免费的开源项目,可以处理任何类型的构建或持续集成。集成 Jenkins 可以用于一些测试和部署技术。
3.1.4、Jenkins 目的
-
持续、自动地构建/测试软件项目。
-
监控软件开放流程,快速问题定位及处理,提高开发效率。
3.1.5、Jenkins 特性
-
开源的 java 语言开发持续集成工具,支持 CI,CD。
-
易于安装部署配置,可通过 yum 安装,或下载 war 包以及通过 docker 容器等快速实现安装部署,可方便 web 界面配置管理。
-
消息通知及测试报告,集成 RSS/E-mail 通过 RSS 发布构建结果或当构建完成时通过 e-mail 通知,生成 JUnit/TestNG 测试报告。
-
分布式构建,支持 Jenkins 能够让多台计算机一起构建/测试。
-
文件识别,Jenkins 能够跟踪何时构建生成哪些 jar,何时构建使用哪个版本的 jar 等。
-
丰富的插件支持,支持扩展插件,你可以开发适合自己团队使用的工具,如 git,svn,maven,docker 等。
3.1.6、产品发布流程
产品原型设计 -> 研发人员写代码 -> 测试人员测试功能 -> 运维人员发布上线
-
持续集成(Continuous integration,简称 CI)
-
持续交付(Continuous delivery)
-
持续部署(continuous deployment)
3.2、Jenkins CI/CD 流程
-
如上图所示,青色的线路是一个典型的 CICD 流程,上线之前先把代码 git 到版本仓库,然后通过 Jenkins 将 Java 项目通过 maven 去构建,最后通过自动化脚本或工具将可执行文件推送到 Tomcat,实现全自动化管理。
-
灰色的线路是用来对比青色的 CICD 过程,从研发到测试再到生产,全过程都需要人工参与,相对而言,成本很高,效率确很低。
在上面的基础上,我们借用容器化技术,使 CICD 流程可以进一步提高效率,这种效率的提升是由容器技术本身的优秀特性带来的;简单来说就是将程序本身及其所依赖的所有环境打包成一个独立的镜像,可以无缝迁移,快速高效地应用在开发、运维及自动化等众多领域。
至于时下最流行的 k8s 和 docker,那是另一个很大的话题,请自行翻阅相关文档。如下图是 CICD 改进流程。
3.3、Jenkins 部署
准备 arm64 架构服务器环境:
IP | 主机名 |
---|---|
172.16.4.16 | jenkins |
172.16.4.15 | web |
172.16.4.14 | git(server) |
172.16.4.17 | jenkins_slave |
172.16.4.100 | gitlab |
3.3.1、部署 Git
3.3.1.1、安装 git
[root@git ~]# yum install -y git
创建 git 用户并设置密码为 Greatwall#123
[root@git ~]# useradd git && echo "Greatwall#123" | passwd --stdin git
Changing password for user git.
passwd: all authentication tokens updated successfully.
[root@git ~]#
3.3.1.2、创建仓库
# 切换到git用户下
[root@git ~]# su - git
Last login: Tue Mar 15 04:28:48 CST 2022 on pts/0
[git@git ~]$
# 在git用户家目录下创建一个repos目录,repos目录下创建各个项目的目录
[git@git ~]$ mkdir -p repos/app.git
[git@git ~]$ cd repos/app.git
[git@git app.git]$
# 初始化仓库,如果不初始化,这仅仅就只是一个目录
[git@git app.git]$ git --bare init
Initialized empty Git repository in /home/git/repos/app.git/
[git@git app.git]$
# 查看初始化后仓库信息
[git@git app.git]$ ls
branches config description HEAD hooks info objects refs
[git@git app.git]$
3.3.1.3、安装 git 客户端
配置完仓库后,我们需要找一台机器测试是否能够成功从仓库中拉取代码,或者上传代码到该仓库。
IP:172.16.4.15,web 服务器进行测试。
# 首先还是要安装git
[root@web ~]# yum install -y git
[root@web ~]# mkdir -p code
[root@web ~]# cd code/
# 测试clone远端git仓库
[root@web code]# git clone git@172.16.4.14:/home/git/repos/app.git
Cloning into 'app'...
git@192.168.95.148's password:
warning: You appear to have cloned an empty repository.
[root@web code]#
[root@web code]# ls
app
3.3.1.4、push 测试
[root@web code]# cd app/
[root@web app]# touch index.html
[root@web app]# echo "<h1> www.brinnatt.com </h1>" > index.html
[root@web app]#
[root@web app]# git config user.email 'brinnatt@gmail.com'
[root@web app]# git config user.name 'Brinnatt'
[root@web app]# git add .
[root@web app]# git commit -m "first commit"
[master (root-commit) 62a9ae0] first commit
1 file changed, 1 insertion(+)
create mode 100644 index.html
[root@web app]#
# 查看一下当前分支,确认后可以推送到默认master分支
[root@web app]# git branch
* master
[root@web app]# git push origin master
git@192.168.95.148's password:
Counting objects: 3, done.
Writing objects: 100% (3/3), 234 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To git@192.168.95.148:/home/git/repos/app.git
* [new branch] master -> master
[root@web app]#
3.3.2、Jenkins 安装配置
3.3.2.1、Jenkins 安装
官方文档:https://www.jenkins.io/doc/book/getting-started/
官方插件:https://updates.jenkins-ci.org/download/plugins/
插件搜索:https://plugins.jenkins.io/
- 安装 java 环境(jenkins 依赖 java 环境)
[root@jenkins ~]# yum install java-11-openjdk-devel -y
[root@jenkins ~]# java -version
openjdk version "11.0.14.1" 2022-02-08 LTS
OpenJDK Runtime Environment 18.9 (build 11.0.14.1+1-LTS)
OpenJDK 64-Bit Server VM 18.9 (build 11.0.14.1+1-LTS, mixed mode, sharing)
[root@jenkins ~]#
- 注意:这里要安装 java-11-openjdk-devel,不能只安装 java-11-openjdk,因为 jenkins 要求具有 java 编译环境。
- yum 安装 jenkins
# 导入jenkins源
[root@jenkins ~]# curl -o /etc/yum.repos.d/jenkins.repo https://pkg.jenkins.io/redhat-stable/jenkins.repo
# 导入jenkins官方证书
[root@jenkins ~]# rpm --import https://pkg.jenkins.io/redhat-stable/jenkins.io.key
# 安装jenkins(安装的是最新的LTS版本)
[root@jenkins ~]# yum install jenkins -y
# 查看yum安装jenkins生成了哪些文件
[root@jenkins ~]# rpm -ql jenkins
/etc/init.d/jenkins # SysV 启动文件
/etc/logrotate.d/jenkins # 日志分割配置文件
/etc/sysconfig/jenkins # jenkins主配置文件
/usr/bin/jenkins # jenkins主程序
/usr/lib/systemd/system/jenkins.service # systemd 启动文件
/usr/sbin/rcjenkins
/usr/share/java/jenkins.war
/usr/share/jenkins
/usr/share/jenkins/migrate
/var/cache/jenkins
/var/lib/jenkins
/var/log/jenkins
[root@jenkins ~]#
3.3.2.2、配置文件
结合当前实验环境,修改 jenkins 主配置文件:
[root@jenkins ~]# grep -E -v "^#|^$" /etc/sysconfig/jenkins
JENKINS_HOME="/var/lib/jenkins"
JENKINS_JAVA_CMD=""
JENKINS_USER="jenkins"
JENKINS_JAVA_OPTIONS="-Djava.awt.headless=true"
JENKINS_PORT="8080"
JENKINS_LISTEN_ADDRESS=""
JENKINS_HTTPS_PORT=""
JENKINS_HTTPS_KEYSTORE=""
JENKINS_HTTPS_KEYSTORE_PASSWORD=""
JENKINS_HTTPS_LISTEN_ADDRESS=""
JENKINS_HTTP2_PORT=""
JENKINS_HTTP2_LISTEN_ADDRESS=""
JENKINS_DEBUG_LEVEL="5"
JENKINS_ENABLE_ACCESS_LOG="no"
JENKINS_HANDLER_MAX="100"
JENKINS_HANDLER_IDLE="20"
JENKINS_EXTRA_LIB_FOLDER=""
JENKINS_ARGS=""
[root@jenkins ~]#
3.3.2.3、启动 Jenkins
[root@jenkins ~]# systemctl start jenkins.service
[root@jenkins ~]# systemctl enable jenkins.service
Created symlink from /etc/systemd/system/multi-user.target.wants/jenkins.service to /usr/lib/systemd/system/jenkins.service.
[root@jenkins ~]#
3.3.2.4、验证安装
[root@jenkins ~]# ps aux | grep jenkins
jenkins 2136 91.1 26.4 5960232 2153968 ? Ssl 06:11 1:45 /usr/bin/java -Djava.awt.headless=true -jar /usr/share/java/jenkins.war --webroot=%C/jenkins/war --httpPort=8080
root 2314 0.0 0.0 112808 968 pts/0 S+ 06:13 0:00 grep --color=auto jenkins
[root@jenkins ~]#
[root@jenkins ~]# ss -tnlpu | grep 8080
tcp LISTEN 0 50 [::]:8080 [::]:* users:(("java",pid=2136,fd=115))
[root@jenkins ~]#
3.3.3、Jenkins 配置(web 页面)
3.3.3.1、管理员密码获取
[root@jenkins ~]# cat /var/lib/jenkins/secrets/initialAdminPassword
8c7f7d0a28a447af99814b57c2ce56cd
3.3.3.2、安装插件
这里选择安装推荐的插件,安装完成后也可以进入系统安装其他插件,我这里使用的是科学上网的方式安装的插件。如果你卡住了,或者安装很慢,请自行搜索解决方法。
3.3.3.3、创建用户
安装完成后,自动进入下面创建第一个管理员用户的界面。
3.3.3.4、url 配置
3.3.3.5、安装完成
出现如下页面时,表示安装完成
jenkins 是一个很有活力的项目,发展也很快,有很多功能插件,我们这里只介绍会用到的一部分功能,如果有更多需求,请参见官方文档。
3.3.3.6、配置 jdk、git、maven
系统管理 -> 全局工具配置
jdk:可以自动安装,但是我们已经安装了,这里写入 jdk 的路径即可,要求该路径具有 java 编译环境,yum install java-11-openjdk-devel 即可。
git:yum install git -y 即可。
maven:yum install maven -y 即可。
3.4、Jenkins 简单使用
3.4.1、用户权限管理
在项目当中,开发、运维、测试等不同的角色必须赋于不同的权限,否则会出现相互干扰的情况,阻碍项目进程。
Jenkins 基于插件: Role-based Authorization Strategy,Authorize Project 来实现权限管理。
3.4.1.1、 安装插件
系统管理 -> 插件管理 -> 可选插件 -> 搜索 authorization,选中插件直接安装即可。
3.4.1.2、 开启插件功能
系统管理 -> 全局安全配置 -> 授权策略 -> 选中该插件功能即可 -> 保存
3.4.1.3、验证用户管理
关闭用户管理功能来进行实践测试
- 策略改回默认状态(全局安全配置)
- 开启允许用户注册(全局安全配置)
- 注册一个新用户
- 登录之后,其默认就是管理员权限,可以进行任何操作,如下图可以随意修改安全配置,足见注册的 guest 用户权限之大
-
开启 Role-Based Strategy
-
重新登录新用户 guest,显示已经没有任何权限了
3.4.1.4、权限划分
安装 Role-based Authorization Strategy 插件后,系统管理中多了如下图所示的一个功能,用户权限的划分就是靠它来实现。
3.4.1.4.1、Manage Roles(管理角色)
Manage Roles:管理角色,相当于针对角色赋予不同权限,然后再将该角色分配给用户。默认提供 Global roles(全局)、Item roles(项目) 和 Node roles(节点)三个角色。
-
Global roles
默认是有一个 admin 用户的,可以看到所有权限都被勾选上了。
接下来我们来添加一个角色 reader,给他一个读的权限。
-
Item roles
Role to add:添加项目角色名称。
Pattern:用于正则匹配项目名称(jobs、nodes、slaves 等)。这意味着您分配给此角色的权限只对具有匹配此模式的名称的项有效。
接下来新建一个 itemA 项目角色,项目角色赋于构建、取消、读取和读取空间权限,一般配置这 4 个即可。
还可以再新建一个 itemB 项目角色:
-
Node roles
Role to add:添加节点角色名称。
Pattern:用于正则匹配项目名称(jobs、nodes、slaves 等)。这意味着您分配给此角色的权限只对具有匹配此模式的名称的项有效。
3.4.1.4.2、Assigin roles(分配角色)
-
给予 guest 用户分配 reader 角色。
这样 guest 就有 manage roles 中刚才创建的 reader 角色的权限了。
此时再去看 guest 用户,已有查看的权限了。
-
针对指定用户分配项目角色(一般最常用的就是针对不同用户进行项目角色分配)。
比如给 guest 用户分配 itemA 项目角色,这样 guest 用户就可以拥有 itemA 项目角色正则表达式匹配到的项目,并且对匹配到的项目有 itemA 角色权限。
- 通过 Brinnatt 管理员用户新建几个任务,重点是规划项目名称,方便角色正则匹配。
- 登陆 guest 用户,可以看到 itemA 项目角色正则匹配到的项目名称。
- 为了方便项目管理,管理员 Brinnatt 用户可以对不同项目进行分类,通过视图功能。
3.4.2、Jenkins 参数化构建
3.4.2.1、参数化构建背景
如果只是简单的构建,jenkins 自己默认的插件可以做,但是如果我们想要在构建过程中有更多功能,比如说:选择性构建、传参、项目指定变量等。
基础的参数化构建可以实现一些简单功能,但是要想使用更多功能,这时我们就需要借助参数化构建来实现交互的功能。
下面我们借助以下插件进行实现:
-
Extended Choice Parameter(更丰富的参数化构建插件)
-
Git Parameter
3.4.2.2、安装插件
左菜单系统管理 --> 插件管理 --> 可选插件 --> 搜索 Extended Choice Parameter,安装完成后重启 jenkins。
3.4.2.3、项目配置
- 前面我们构建了好几个自由风格的项目,选择 A-web 项目进行配置。
- 选择安装过的插件
- 进行配置
这里 branch 就相当于一个变量,然后来为其进行传参。
- 构建选择执行 shell 进行测试
- 参数化配置已经生效
-
构建 value02
可以发现控制台的输出信息是成功了的
-
数据来源选择文件
在 jenkins 所在服务器上进行如下操作:
[root@jenkins ~]# vim /opt/jenkins.property
var=parm1,parm2,parm3
web 端配置:
进行测试,构建前,可以发现也是生效的:
构建后查看结果,也是成功的:
3.4.3、Git 插件
3.4.3.1、安装插件
左菜单系统管理 --> 插件管理 --> 可选插件 --> 搜索 Git Parameter,安装完成后重启 jenkins。
3.4.3.2、配置风险
在配置之前我们先来说一个坑,当我们在配置 git repository url 时,会有如下报错:
这是因为 yum 安装的 jenkins,在运行时使用的是 jenkins 用户,此处是 jenkins 用户去 git 仓库进行拉取,而 jenkins 用户默认 shell 是 /bin/false 的,不但不能登录,也没有 git 命令权限,所以会报错。
# 查看是jenkins运行时是jenkins用户
[root@jenkins ~]# lsof -i:8080
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
java 17099 jenkins 125u IPv6 157014 0t0 TCP *:webcache (LISTEN)
[root@jenkins ~]#
# 查看jenkins默认的shell
[root@jenkins ~]# grep jenkins /etc/passwd | awk -F":" '{print $1,$7}'
jenkins /bin/false
解决此问题有两种办法:
-
更改 jenkins 用户为 root 用户;
- 修改 /etc/sysconfig/jenkins 文件,将 JENKINS_USER='jenkins' 改为 JENKINS_USER='root'。
- 需要重启 jenkins 服务。
-
更改 jenkins 用户为正常的普通用户,修改默认 shell 为 /bin/bash,将 jenkins 用户公钥传到 git 服务器作为密钥认证。
这里选择第二种方式:
# 更改jenkins shell
[root@jenkins ~]# chsh -s /bin/bash jenkins
Changing shell for jenkins.
Shell changed.
[root@jenkins ~]#
# 查看已经更改过来了
[root@jenkins ~]# grep jenkins /etc/passwd | awk -F":" '{print $1,$7}'
jenkins /bin/bash
[root@jenkins ~]#
[root@jenkins ~]# su - jenkins
-bash-4.2$ ssh-keygen
-bash-4.2$ ssh-copy-id -i git@172.16.4.14
3.4.3.3、配置实例
3.4.3.4、配置 git 仓库
Credentials 凭据有两种表达方式:
-
第一种:选择无,就是基于免秘钥认证(我用这种)。
-
第二种:基于用户名和密码(这里其实就是 git 用户)。
后面开始构建的时候会出现一个选择列表,是刚才定义 Git 参数变量传递过去的,这个变量是代表分支。
3.4.3.5、开始构建
- 列表中已经有了可选的分支。
- 构建成功。
- 新建分支增添内容再来验证插件。
[root@web app]# git branch
* master
[root@web app]# git branch test
[root@web app]# git checkout test
Switched to branch 'test'
[root@web app]#
[root@web app]# git branch
master
* test
[root@web app]# ls
cat.sh
[root@web app]# touch echo.sh
[root@web app]# vim echo.sh
[root@web app]# cat echo.sh
#!/bin/bash
echo "hello jenkins git parameter"
[root@web app]#
[root@web app]# git add .
[root@web app]#
[root@web app]# git commit -m "second comm"
[test acc6e1d] second comm
1 file changed, 2 insertions(+)
create mode 100644 echo.sh
[root@web app]#
[root@web app]# git push origin test
git@172.16.4.14's password:
Counting objects: 4, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 309 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To git@172.16.4.14:/home/git/repos/app.git
* [new branch] test -> test
- jenkins 构建查看
可以发现已经有了我们新建的分支。点击开始构建,可以构建成功。
3.4.4、Jenkins Master-Slave 架构
jenkins 的 Master-slave 分布式架构主要是为了解决 jenkins 单点构建任务多、负载较高、性能不足的场景。
Master/Slave 相当于 Server 和 Agent 的概念。Master 提供 web 接口让用户来管理 job 和 slave,job 可以运行在 master 本机或者被分配到 slave 上运行构建。一个 master(jenkins 服务所在机器)可以关联多个 slave,用来为不同的 job 或相同的 job 的不同配置服务。
Executor 执行器与编程的多进程/线程有关,可以简单理解为主节点和代理节点上用于执行任务的槽位;Executor 的数量定义了该节点可以执行任务的并发量,可自定义数量。Master 一旦分配任务给 Agent,必须有可用 Executor,否则要等空闲槽位可用。
节点标签(tag)指的是附加在节点之上的标识符,而后可由 pipeline 中的 agent 指令等进行过滤和选择;agent 节点较多时,标签 tag 应该定义的更有意义,便于筛选;一个 agent 可附加多个标签,标签名不能含空白字符,也不能使用标签表达式中预留的关键字 ! & | < > () 等
。
Master 与 Agent 之间的连接方式:为了能让 master 将一个主机识别为 agent,需要事先在各 agent 上运行特定的代理程序,以便 master 与 agent 可以建立双向通信。
- SSH 连接器:首先且最稳定的双向连接机制,它依赖于 jenkins 内置的 SSH 客户端,并要求 agent 启动 ssh 服务,认证支持密钥及口令两种方式。
- Inbound 连接器:在 agent 上以手动或系统服务的方式经由 JNLP 协议触发双向连接的建立。
- JNLP-HTTP 连接器:在 agent 上经由 JNLP 协议通过 HTTP 建立双向连接。
3.4.4.1、安装 slave 节点
安装 slave 节点时要注意,slave 所在服务器必须有 java 环境。在 jenkins web 端进行操作:系统管理 -> 节点管理 -> 新建节点。
-
进行基础配置
配置选项中的内容是可变的,根据实际情况填写。
配置完成后点击保存,jenkins 就会连接 slave 节点,成功后就会出现如下图所示,显示在节点列表中。当然如果没有成功,会出现一个红叉叉,这个时候打开日志,里面会有很清晰的错误提示,按照提示解决相应问题即可。
并且也可以在 slave 所在服务器上看到:
[root@jenkins_slave ~]# ps aux | grep jar
root 15478 0.0 0.0 111232 2752 ? Ss 08:31 0:00 bash -c cd "/var/lib/jenkins" && java -jar remoting.jar -workDir /var/lib/jenkins -jar-cache /var/lib/jenkins/remoting/jarCache
root 15485 1.1 0.8 8173056 139264 ? Sl 08:31 0:16 java -jar remoting.jar -workDir /var/lib/jenkins -jar-cache /var/lib/jenkins/remoting/jarCache
root 15584 0.0 0.0 110400 2496 pts/0 S+ 08:53 0:00 grep --color=auto jar
- jar 包就是负责接收 master 任务的。
3.4.4.2、配置 job 运行节点
在项目 job 中进行配置,可通过标签(标签可在安装时配置)或者名称进行匹配:
3.4.4.3、构建 job
注意,直接构建的话会失败的,这里有几个逻辑必须要搞清楚,不然会一直报错:
- 我们添加 jenkins slave 节点的时候,用的是 slave 节点的 root 用户(普通用户测试失败,权限不够)认证,所以启动 remoting.jar 代理程序时就会是 root 用户。
- 当 jenkins slave 被 master 分配构建任务时,自然会是 root 用户去 git 服务器下载代码,所以免私钥认证应该是 jenkins slave 节点 root 用户 --> git(server) 节点 git 用户。
- jenkins slave 节点要安装 git 客户端。
[root@jenkins_slave ~]# yum install git -y
[root@jenkins_slave ~]# ssh-keygen
......
[root@jenkins_slave ~]# ssh-copy-id -i git@172.16.4.14
......
下面可以开始构建,查看控制台的日志,就是 slave 构建的任务,符合预期。
再查看构建完的工作目录,也有预想中的文件。
[root@jenkins_slave ~]# ls /var/lib/jenkins/workspace/A-web/
cat.sh index.html
这样基本上就实现了借助 jenkins 的 slave 去构建 job了。目前是在 slave 节点上构建的,也在 slave 节点上部署。后面我们可以通过脚本,比如借助 rsync、ansible 等工具部署在其他服务器上。
3.4.4.4、扩展 slave 节点
可以在 slave 服务器配置时加上标签,这样也方便我们选择,用起来也不单单局限在一台 slave 服务器上,可以让多台 slave 节点去竞选。
3.5、Jenkins 项目
走完前面的步骤基本上可以很好的跨过 jenkins 门槛,但针对一些复杂的项目,还远远不够。下面梳理一下 jenkins 在项目上,针对不同的项目需求有哪些应用场景,并且就 jenkins pipeline 重要版块做一下深入理解。
3.5.1、Jenkins 项目类型
无论是哪个版本的 jenkins,都要通过新建一个指定类型的项目来添加一个新 "job";所谓项目类型,是指用于分类一个具体的 CI 任务,在其构建时使用不同的定义方式。比如:自由风格类型定义 job 时,可以使用 shell 脚本或 windows bat 批处理。
jenkins 的项目类型也可以根据不同的标准再次划分为不同的类型:
- 开放式配置可用于执行任何任务:"自由风格类型的项目"、"流水线项目";
- 针对高级或具有复杂场景的用例:"多配置项目";
- 针对组织的任务:"文件夹"、"多分支流水线"、"GitHub 组织"、"Bitbucket" 团队等项目;
- 自动配置和构建的任务:"多分支流水线"、"GitHub 组织"、"Bitbucket" 团队等项目。
3.5.1.1、自由风格项目类型
顾名思义,自由风格类型项目旨在提供一种相对开放的项目构建途径,以完成各种任务。事实上,在流水线风格的项目出现之前,自由风格类型项目是最为灵活的项目类型,是大多数 jenkins 任务的传统选择。
前面小节中所有实验都是使用自由风格类型,这些实验借助不同的插件可以实现更为丰富的功能,想必大家有一定的体会。如下图,再回顾一下:
3.5.1.2、流水线项目类型
这是 jenkins 2 最重要的项目类型之一,它用于将 CICD 流水线集成到 jenkins 中,从而由 jenkins 完成特定软件项目的自动化持续交付。
流水线项目的步骤和逻辑定义在结构化的 Groovy 脚本之中(而非 web 表单中),该脚本支持脚本式和声明式两种结构,并保存于名为 jenkinsfile 的文本文件中。
流水线项目也支持从 SCM 中获取 jenkins 脚本,而不是在如上图的文本输出区域输入脚本。因此,选定 SCM 时,还需要输入或选择额外的字段(从哪里获取脚本)。
注意:配置最下面还有一个"轻量级检出"选项,它是指 SCM 插件在流水线开始工作时仅检出 jenkinsfile 文件而不是整个项目,而后由 jenkinsfile 中的相关步骤检出整个项目,以避免重复检出。
3.5.1.3、多配置项目类型
多配置项目类型是为了简化运行一组仅在参数方面有所不同的项目构建而设计。
- 例如,在需要针对一组有 5 种不同的浏览器(IE、Firefox、Safari 和 Chrome等),以及一组 5 种不同操作系统(Debian、CentOS、Windows 和 MacOS X等)运行测试构建时,若没有多配置项目类型,将不得不运行 25 个任务来完成构建测试。
而使用多配置项目类型,只需要一个任务便能执行各种可能的组合。具体的做法,根据所使用的每个不同的"坐标轴"参数定义基础任务,以完成我们需要做的任何事情。
- 例如,我们使用 x 轴表示浏览器类型,而使用 y 轴表示操作系统类型,即能组合出所有可执行任务的矩阵。
提示:实际上,除了用户自定义的坐标轴外,jenkins 也允许用户选择节点坐标(slaves),以及通过节点标签进行节点筛选的坐标轴(Label Expression)。
3.5.1.4、多分支流水线项目类型
多分支流水线是 jenkins 2 提供的一种全新的项目类型,它支持针对源码版本控制仓库中检测到的每个分支创建新的流水线项目。
- 一旦将版本控制系统中的指定的项目识别为 jenkins 项目,多分支流水线可自动管理和构建所有的分支。
- 显然,在 jenkins 创建多分支流水线项目时,需要将任务指向 SCM 仓库,而非项目的某个特定分支。
目前,多分支流水线的 Build Configuration 部分只有 "by jenkinsfile" 一个选项,它的意思是 jenkins 会在检出的项目根目录中查找名为 jenkinsfile 的文件,以确认它是否可以自动构建项目的分支。
- jenkins 会在项目建立后通过运行 "分支索引" 功能,在项目的分支中查找 jenkinsfile 文件。
- 无论在任何分支中找出 jenkinsfile,它都将会自动为该分支创建并构建一个任务。因而,分支索引完成后,将为多分支流水线项目中的每个匹配分支各自创建一个单独的任务。
- 另外,一旦有新的分支进入,jenkins 也能自动检测新分支,并为它们创建相应的任务。
3.5.1.5、文件夹项目类型
不难想到,文件夹代表一个组织结构,而不是任务项目。
- jenkins 2 之前的版本使用视图来过滤仪表板上的项目列表,而视图提供了一种可以通过配置创建有限的任务列表的能力。
- 与视图不同的是,文件夹实际上增加了将项目分组到共同的名称空间、结构和环境中的能力。
具体来说,文件夹允许一组任务共享如下几个部分。
- 容器:创建文件夹时,将会创建一个容器来容纳一组任务。
- 名称空间:自定义名称空间也是任务路径的一部分。
- 共享库:文件夹可以有专用于内部项目间共享一组共享库。
- 单独权限:若 jenkins 安装了基于 RBAC 的授权策略插件,并配置了基于角色的权限,则可以使用这些插件。
创建完文件夹项目后,随后即可在其中创建新项目,这同直接创建项目的方式几乎没有什么区别,惟一的不同之处在于,这些项目均组织于文件夹提供的名称空间之下,新项目的全名将包含该名称空间。
- 因此,文件夹功能类似于操作系统上的目录,而于文件夹中创建新项目的功能也类似于在目录中创建其它类型的文件;而且,我们也可以将其它现存的项目移入到一个文件夹中,这同在目录中移动文件的功能类似。
- 另外,jenkins 文件夹也支持 RBAC 授权管理机制,我们可以在 jenkins 上安装 RBAC 授权插件后基于角色来分配文件夹的管理权限。
3.5.1.6、Github 组织或 Gitlab 组类型
GitHub 组织项目,是专用于管理托管于 GitHub 之上的项目集合的一种组织结构,而非专门的项目类型。
GitHub 组织的一个典型用途是,将一个公司或一个人的所有项目集合统一进行分组,而为了便于整合 GitHub 组织,jenkins 提供了 GitHub 组织项目类型。
GitLab Group 类型的功能与之类似,只不过,它对应的是 GitLab 上的 Group 的概念。
3.5.2、Jenkins pipeline 流水线
用 《持续交付——发布可靠软件的系统方法》这本书上的一句话解释什么是 jenkins pipeline,"从某种抽象层次上讲,部署流水线(Deployment pipeline) 是指从软件版本控制库到用户手中这一过程的自动化表现形式。"
jenkins 1.x 仅支持于界面中手动配置流水线;而 2.x 则实现了流水线即代码 pipeline as a code 的机制,它支持通过代码来描述部署整条流水线;使用代码而非 UI 完成 pipeline 定义的意义在于:
- 更好地版本化:支持版本控制;
- 更好地协作:pipeline 的修改对所有人可见,且支持代码审查;
- 更好的可重用性。
jenkins 2.x 中,用于保存 pipeline 代码并可被 jenkins 加载的文件称为 jenkinsfile;流水线即可以在 pipeline 类型的任务中创建,也可以定义在一个称为 jenkins 的外部文件中,它可以同代码保存在一起;jenkinsfile 就是一个文本文件,它是部署流水线概念在 jenkins 中的表现形式。
3.5.2.1、pipeline 语法
jenkins 2.x 支持两种 pipeline 语法:脚本式语法和声明式语法
- 脚本式语法:jenkins 最先支持 pipeline 的语法,它采用命令式风格,直接在流水线脚本中定义逻辑和程序流程;
//脚本式流水线:node用于脚本式流水线,从技术层面上来说,它是一个步骤,可以用于流水线中执行活动的资源
node('node01'){
stages{
stage('Build'){
steps{
echo "Building..."
}
}
stage('Test'){
steps{
echo "Testing..."
}
}
}
}
- 声明式语法:CloudBees 公司为 jenkins 引入的一种"流水线即代码"的 pipeline 语法,它允许用户在 pipeline 的定义中将更多的精力关注于期望 pipeline 的状态和输出之上,而非实现逻辑。
//声明式流水线:agent用于声明式流水线,它作为一个指令用于分配节点
pipeline {
agent {label 'node01'}
stages {
stage('Build') {
steps {
echo 'Building...'
}
}
stage('Test'){
steps{
echo 'Testing...'
}
}
}
}
pipeline 实际上就是基于 Groovy 语言实现的一种 DSL(Domain-Specific Language),用于描述代码编译到打包发布的整个流水线是如何进行的;
pipeline 的定义有一个明确的、必须遵循的结构,它由一些指令及嵌套的代码块组成,该结构存在以下几个基本元素:
- pipeline:流水线的最外层结构,代表整条 pipeline,包含 pipeline 的完整逻辑;
- stages:用于包含所有 stage 的定义;
- stage:阶段,代表流水线的一个单独的功能完成时期,例如编译等;
- steps:用于在 stage 中定义完成该阶段功能所需要经历的一系列步骤,并能够把这些步骤同该 stage 中的其它定义(如环境的定义等)分隔开;
- agent:pipeline 中的独立指令,用于指定流水线的执行位置,它可能代表 slaves 主机群中的某个物理机、虚拟机或者容器;
- post:用在 stage 代码块,或者是整个 pipeline 执行完成后附加的步骤;
- post 是配置语法中惟一可省略的,但在实际应用中,通常不能省略。
pipeline{
agent any
stages{
stage('name1'){
steps{
...
}
post{
...
}
}
stage('name2'){
...
}
}
post{
...
}
}
3.5.2.2、创建 pipeline 项目
Jenkins Dashboard --> 新建任务 --> 输入名称 --> 选择流水线 --> 确定后进入配置界面。
创建 pipeline 项目时,将首先打开一个基于 Web 表单形式的新项目配置页面,每个主要的配置部分都有一个对应的选项卡,底部还会有一个用于输入 pipeline code 的文本框,这是 jenkins 内置的流水线编辑器。
pipeline 编辑器的特性:
- 语法检查
- 编辑器会尝试检查 Groovy 语法和引用的有效性,所有问题都会在对应的代码行前面通过红色 "X" 方框标记提示出来;
- 然而并非所有的错误标记都代表真正的错误,在某些时候脚本可能无法解析依赖或最近创建的输入,这是一个异常而非错误。
- 完整的错误信息
- X 标记在提供快速识别问题行的同时,还支持鼠标悬停时展示完整的错误信息。
- 自动补全
- 编辑器还提供了一定程度上的自动补全功能,比如自动补全括号。
- 提供了"代码片断生成器"
- 代码片断生成器提供了一种搜索可用的 DSL 步骤的方法,以帮助用户随时借鉴和查阅。
3.5.2.3、pipeline 示例
pipeline {
agent {label 'node01'}
stages {
stage('Source'){
steps{
echo "Get code from Git Repository..."
}
}
stage('Build') {
steps {
echo 'Building...'
}
}
stage('Test'){
steps{
echo 'Testing...'
}
}
stage('Deploy'){
steps{
echo "Deploying..."
}
}
}
}
流水线一般由多个阶段组成,包括获取源代码、编译、集成测试、代码分析、应用打包和部署等。
- jenkins 2.x 能够代码化整个流水线;如上面的代码示例所示;
- 第一次保存流水线定义时,UI 会提示流水线尚未执行;在项目上通过"立即构建",可手动触发构建过程;
- 任务执行结果在阶段视图中以方块的形式显示;
- 一次构建用一行方块来表示,其中每个方块代表流水线中的一个 stage;
- 每个方块都代表了一个特定阶段的一次执行结果。
- 方块颜色的意义;
- 蓝色条纹:运行中。
- 白色:stage 尚未执行。
- 红色条纹:stage 执行失败。
- 绿色:stage 执行成功。
- 浅红色:stage 执行成功,但是下游的某个 stage 出现失败。
3.5.2.4、声明式 pipeline
pipeline 的定义有一个明确的、必须遵循的结构,它由一些 directive 和 section 组成,每一个 section 又可包含其它的 section、directive 和 step,以及一些 condition 的定义;
- Section:负责将那些在某个时间点需要一同运行的条目(item)组织在一起;
- agent:指定负责运行代码的节点;
- stages:组织一到多个 stage;
- steps:组织一到多个 DSL 格式的步骤;
- post:在 stage 或整个 pipeline 的尾部封装一些需要被执行的步骤或者检验条件;
- Directive(指令):负责完成特定功能的语句或代码块,如 environment、tools、triggers、input 和 when 等;
- Steps:steps 本身就是一个标识特定 section 的名称,其内部可以使用任何合法的 DSL 语句,例如 git、sh、bat 和 echo 等;
3.5.2.4.1、Section: agent
agent 用于指明使用哪一个节点去执行 pipeline 或 stage 中的代码。
- 在 pipeline 代码块的顶部,必须要有一个 agent 来指定"默认"的执行节点。
- 而一个 stage 的顶部也可以有一个 agent 的定义,用来指定负责运行该 stage 中的代码的节点。
agent 可接受多种形式的参数。
- any:任何可用节点。
- none:用于 pipeline 顶端时表示不定义默认的 agent,这就需要为每个 stage 单独指定。
- label { label '<label>' }:具有指定的标签的节点均为可用节点。
- node { label '<label>' }:与 label 相似,但可以指定额外的参数 customWorkspace。
- docker:在指定的容器中运行 pipeline 或 stage 代码,该容器动态创建并运行于预配置的可运行容器的 node 上,或能够匹配到指定 label 的 node 上;可用参数如下:
- image、label、args、registryUrl 和 registryCredentialsId。
- dockerfile:功能上类似于上面 docker 参数,但容器镜像通过指定的 docker 进行构建;该参数要求 jenkinsfile 必须从 Multibranch Pipeline 或者 Pipeline from SCM 中加载;可用参数如下:
- filename、dir、label、additionalBuildArgs、args、registryUrl 和 registryCredentialsId。
- kubernetes:于 Kubernetes 集群上指定的 Pod 中运行 stage 或 pipeline 代码,该参数同样要求 jenkinsfile 必须从 Multibranch Pipeline 或者 Pipeline from SCM 中加载。
- 需要在 kubernetes 参数中指定 Pod 模板。
3.5.2.4.2、Section: post
post section 在 stage 或 pipeline 的尾部定义一些 step,并根据其所在 stage 或 pipeline 的完成情况来判定是否运行这些 step;post section 支持的 condition 如下:
- always:总是运行;
- changed:其所处的 stage 或 pipeline 同前一次运行具有不同状态时,才运行该 post;
- fixed:stage 或 pipeline 本次运行成功,但前一次为 failed 或 unstable 时,才运行该 post;
- regression:stage 或 pipeline 前一次运行成功,但本次为 failure、unstable 或 aborted 时,才运行该 post;
- aborted:stage 或 pipeline 的运行状态为 aborted 时,才运行该 post;在 Web UI 中灰色显示;
- failure:stage 或 pipeline 的运行状态为 failed 时,才运行该 post;
- success:stage 或 pipeline 的运行状态为 success 时,才运行该 post;
- unstable:因测试失败或代码冲突导致 stage 或 pipeline 的运行状态为 unstable 时,才运行该 post;在 Web UI 中以黄色显示;
- unsuccessful:stage 或 pipeline 的运行不成功时,才运行该 post;
- cleanup:在其它所有的 post 的条件均被评估后(无论 stage 或 pipeline 的状态如何)才运行该 post;
3.5.2.4.3、Section: stages 和 steps
stages:封装了用于定义 pipeline 主体和逻辑的所有 stage 的定义,它包含一个或多个 stage 指令。
- stages 负责描述 pipeline 中绝大部分的实际工作(work);
- 事实上,stages 中至少需要包含一个 stage 指令来定义 CD 过程的每个离散部分,例如构建、测试和部署等;
steps:负责在 stage 中定义一个到多个 DSL 语句,这些语句负责完成该 stage 中特定的功能;但能同其它的语句分隔开,如 environment 等;
pipeline {
agent any
stages {
stage('Build') {
//stage 中必须要包含steps;
steps { //step中至少应该有一个DSL语句;
echo 'Building...'
}
}
stage('Test'){
steps{
echo 'Testing...'
}
}
}
}
Pipeline 的基本结构决定了 pipeline 的整体流程,但真正"做事"的还是其内部一个个具体的 step,因而 steps 是 pipeline 中最核心的组成部署;
- 除了 script,几乎所有的 step 在 pipeline 中都是不可拆分的原子操作;
- pipeline 内置了大量的 step,具体请参考 https://www.jenkins.io/doc/pipeline/steps;
- 除此之外,有相当一部分插件可直接当作 step 来用;
script{} 步骤负责将脚本引入到 steps{} 配置段中,但它为非必要步骤,且复杂的脚本应该单独组织为 Shared Libraries,并由 pipeline 导入后使用;
pipeline {
agent any
stages {
stage('Hello') {
steps {
echo 'Hello World'
script{
def browsers=['chrome','firefox']
for(int i=0; i < browsers.size(); ++i){
echo 'Testing the ${browsers[i]} browser'
}
}
}
}
}
}
Pipeline 内置与文件/目录相关 step:
- deleteDir:删除当前目录;
- dir("/path/to/dir"):切换到指定目录;
- fileExists("/path/to/dir"):判断文件是否存在;
- isUnix:判断是否为类 Unix 系统;
- pwd:打印当前目录;
- writeFile:将内容写入指定的文件中,支持如下几个参数:
- file:文件路径,支持相对路径和绝对路径;
- text:要写入的内容;
- encoding:目标文件的编码,空值为系统默认的编码;支持 base64 编码格式;可选参数;
- readFile:读取文件的内容;支持如下几个参数:
- file:文件路径,支持相对路径和绝对路径;
- encoding:读取文件内容时使用的编码格式;可选参数;
Pipeline 内置与消息或控制相关 step:
- echo("message"):打印指定的消息;
- error("message"):主动报错,并中止当前 pipeline;
- retry(count){}:重复执行 count 次在 {} 中定义的代码块;
- sleep:让 pipeline 休眠一段时间,支持如下参数:
- time:整数值,休眠时长;
- unit:时间单位,支持 NANOSECONDS、MICROSECONDS、MILLISECONDS、SECONDS、MINUTES、HOURS 和 DAYS,可选参数;
- timeout:代码块的超时时长,支持如下参数:
- time:整数值,休眠时长;
- unit:时间单位,支持 NANOSECONDS、MICROSECONDS、MILLISECONDS、SECONDS、MINUTES、HOURS 和 DAYS,可选参数;
- activity:布尔类型,值为 true 时,表示在该代码块不再有日志活动时才算真正超时;可选参数;
- waitUntil:等待指定的条件满足时执行定义的代码块;
- initialRecurrencePeriod:初始的重试周期,即测试条件是否满足的重试周期,默认为 250ms;可选参数;
- quiet:是否禁止将每次的条件测试都记入日志,默认为 false,即记入日志;可选参数;
Pipeline 内置与发送通知相关 step:
- mail:向指定邮箱发送邮件:
- subject:邮件标题;
- body:邮件正文;
- from(optional):发件人地址列表,逗号分隔;
- cc(optional):CC email 地址列表,逗号分隔;
- bcc(optional):BCC email 地址列表,逗号分隔;
- charset(optional):编码格式;
- mimeType(optional):Email 正文的 MIME 类型,默认为 text/plain;
- replyTo(optional):回件地址,默认为 jenkkins 设置的全局配置中的邮箱地址;
Pipeline 内置与 Node 和 Process 相关 step:
- bat:Windows 的批处理脚本;
- powershell:运行指定的 PowerShell 脚本,支持 Microsoft PowerShell 3+;
- pwsh:PowerShell Core Script;
- node:在指定的节点上运行后续的脚本;
- sh:运行 shell 脚本,支持的参数如下
- script{}:脚本代码块,支持指定脚本解释器,例如 "
#!/usr/bin/perl
,否则将使用系统默认的解释器,且使用了 -xe 选项"; - encoding(optional):脚本执行后输出的日志信息的编码格式,未定义时使用系统默认编码格式;
- label(optional):显示的 Web UI 中的详细信息;
- returnStdout(optional):布尔型值,true 表示任务的标准输出将作为 step 的返回值,而不是打印到日志中;若有错误,依然会记入日志;
- returnStatus(optional):正常情况下命令执行失败会返回非零状态码,设定该参数值为 true 时,表示将返回该 step 的结果,而非状态码;
- script{}:脚本代码块,支持指定脚本解释器,例如 "
- ws:分配工作空间;
3.5.2.4.4、pipeline 支持的指令
jenkins pipeline 支持指令主要有这些:
- environment:设定环境变量,可用于 stage 或 pipeline 代码块中;支持 credentials() 函数,用于通过标识符访问预定义的凭证;
- tools:指定需要在 agent 上下载并配置的工具,例如 git、maven、jdk 等,这些工具可经由 PATH 环境变量指定的位置访问到;可用于 stage 或 pipeline 中;
- parameters:用户在触发 pipeline 时应该提供的参数列表;仅可用于 pipeline 级别;
- options:仅可用在 pipeline 级别来配置 pipeline 自身的选项,支持的参数可由 pipeline 自身提供,也可由其他插件(例如 timestamps)提供;
- 例如:"retry(2)" 允许在 pipeline 失败时重试两次;
- triggers:用于指定负责自动启动 pipeline 的触发器,对于集成了 Github 或 Gitlab 等自带触发机制的系统场景来说,triggers 并非必须的指令;仅可用于 pipeline 级别;
- libraries:当前 pipeline 可以导入的共享库,该共享库内部的代码可被该 pipeline 调用;
- stage:负责在 stages 配置段中封装 steps 配置段,以及其它可用于 stage 中的指令;
- input:stage 中的专用指令,用于暂停 pipeline 并提示用户输入内容后继续;
- when:stage 中的专用指令,用于设定该 stage 的运行条件;
3.5.2.4.5、stages 高级用法
stages 是 pipeline 中最重要的 section,jenkins 会按照 stages 中定义的顺序自下而后执行各个 stage;
stage 内部还能再嵌套一个 stages{} 或一个 parallel{} 代码块,而后在这些代码块内再嵌套 stage,以指定 stage 的运行顺序;
- stage 内部嵌套 stages{},用于指定以顺序(串行)方式依次运行该 stages{} 内部的各 stage;
- 而 stage 内部嵌套的 parallel{},用于指定以并行方式运行该 parallel{} 内部的各 stage;
pipeline{
agent none
stages{
stage('Sequential'){
stages{
stage('In Sequential 1'){
steps{
echo "In Sequential 1"
}
}
stage('In Sequential 2'){
steps{
echo 'In Sequential 2'
}
}
stage('Parallel In Sequential'){
parallel{
stage('In Parallel 1'){
steps{
echo 'In Parallel 1'
}
}
stage('In Parallel 2'){
steps{
echo 'In Parallel 2'
}
}
}
}
}
}
}
}
需要注意以下几点:
- stage 内部仅能定义 steps、stages、parallel 或 matrix 四者其中之一,且多层嵌套只能用在最后一个 stage 中;
- 对于本身已经嵌套在 parallel 或 matrix 内部的 stage 来说,不支持在其内部再使用 parallel 或 matrix;但仍能使用 agent、tools 和 when 等其它指令,甚至是 stages{} 配置段以顺序运行 stage;
3.5.2.5、pipeline 代码生成器
对于初学者来说,pipeline 提供了很多内置的帮助文档,比如很多配置选项都带有 ?
,点一下就会显示帮助信息。不熟悉 pipeline 语法时,可以借助 pipeline 代码生成器生成代码样例。
pipeline 任务 --> 配置 --> 流水线 --> 流水线语法 --> 片段生成器:
3.5.2.6、pipeline 环境变量
环境变量可分为 jenkins 内置变量和用户自定义变量两类;
- pipeline{} 和 stage{} 中用于定义环境变量的指令是 environment,但定义位置的不同,也意味着其作用域的不同;
- 定义在 pipeline{} 顶部的环境变量可被其后的各 stage 所引用;
- jenkins 全局环境变量可被所有的 pipeline 引用,它们以 "env." 为前缀;
- 可将环境变量视作 pipeline 与 jenkins 系统交互的媒介;
- 引用全局环境变量格式有三种:
${env.<ENV_VAR_NAME>}
、$env.<ENV_VAR_NAME>
和${ENV_VAR_NAME}
下面是 jenkins 内置的几个常用环境变量;
- BUILD_NUMBER:构建号,递增的整数值;打包时,经常用作制品名称的一部分;
- BRANCH_NAME:在多分支 pipeline 中,需要根据不同的分支施加不同的操作时较为常用;
- BUILD_URL:当前构建页面的 URL,常用于邮件通知中;
- GIT_BRANCH:基于 git 拉取的源码进行构建时使用该变量;
- JENKINS_HOME:jenkins 的家目录;
- JENKINS_URL:jenkins 服务的 URL;
获取 jenkins 可用的内置环境变量方法:pipeline 任务 --> 配置 --> 流水线 --> 流水线语法 --> 全局变量参考。
自定义环境变量
在 pipeline{} 或 stage{} 中使用 environment 指令即可自定义环境变量;
自定义环境变量与全局环境变量同名时,全局环境变量将被覆盖;这可能会引起错误,必要时,可为自定义环境变量使用固定的前缀,例如__等;
pipeline {
agent any
environment{
CC='clang'
}
stages {
stage('stageONE') {
environment{
DEBUG_FLAGS='-g'
}
steps {
sh "echo ${CC} ${DEBUG_FLAGS}"
}
}
stage('stageTWO') {
steps{
//该step因引用了不在作用域中的环境变量而出现错误;
sh "echo ${CC} ${DEBUG_FLAGS}"
}
}
}
}
自定义全局环境变量
若要定义可通过 'env.' 前缀在所有 pipeline 中引用的全局环境变量,则需要通过以下路径定义:
系统管理 --> 系统配置 --> 全局属性 --> 勾选环境变量 --> 新增键值对:
3.6、Jenkins 邮件通知
3.6.1、安装 Email 插件
在配置 Email 之前我们需要下载两个插件:Email Extension Plugin
,Email Extension Template Plugin
,这两个插件可以帮助我们进行邮件的编写发送以及格式化等操作。
系统管理 --> 插件管理 --> 可选插件 --> 搜索 Email Extension --> 安装,安装完成后重启 jenkins。
3.6.2、邮箱配置
我们这里直接使用 QQ 邮箱作为我们的 SMTP 发送邮箱,所以需要事先配置好 QQ 邮箱,打开调用接口,生成授权码。
- 通过网页端 QQ 邮箱的设置进入到邮箱客户端设置页面。
- 点击相应服务后面括号中的如何设置,查看帮助信息。
3.6.3、jenkins 邮件通知配置
系统管理 --> 系统配置 --> 配置 <邮件通知> 和 <Jenkins Location>,完成配置如下:
直接测试的话会报如下错误:
Failed to send out e-mail
java.net.SocketException: Connection or outbound has closed
at sun.security.ssl.SSLSocketImpl$AppOutputStream.write(SSLSocketImpl.java:1181)
at com.sun.mail.util.TraceOutputStream.write(TraceOutputStream.java:140)
at java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:82)
at java.io.BufferedOutputStream.flush(BufferedOutputStream.java:140)
at com.sun.mail.smtp.SMTPTransport.sendCommand(SMTPTransport.java:2413)
Caused: javax.mail.MessagingException: Can't send command to SMTP host;
nested exception is:
java.net.SocketException: Connection or outbound has closed
at com.sun.mail.smtp.SMTPTransport.sendCommand(SMTPTransport.java:2415)
at com.sun.mail.smtp.SMTPTransport.sendCommand(SMTPTransport.java:2402)
at com.sun.mail.smtp.SMTPTransport.close(SMTPTransport.java:1409)
at javax.mail.Transport.send0(Transport.java:257)
at javax.mail.Transport.send(Transport.java:124)
需要将 Jenkins Location 中的系统管理员邮件地址改成 QQ 用户地址,也就是与上面保持一致。
再测试可以成功。
3.6.4、邮件模板
3.6.4.1、配置 Extended E-mail Notification
注意:网上的教程很混乱,配置邮件模板刚才安装的两个插件 Email Extension Plugin
,Email Extension Template Plugin
都可以完成,因为它们的功能有重复,前者 jenkins 默认安装了,可以直接按下面步骤操作即可;后者要通过系统管理 --> Editable Email Notification Templates 进行独立配置,然后在自由风格项目配置中通过 "构建后操作" 直接选择 Editable Email Notification Templates
模板即可。
Jenkins 系统设置里还可以通过 Extended E-mail Notification
对邮件发送进行模板格式的自定义,这是基于 jenkins 默认插件 Email Extension Plugin
来完成。
基础信息配置与上述一致,唯一要注意的是这里需要重新生成一个 QQ 授权码,在 Credentials 里面配置。
我们还可以在 Default Triggers
中根据自己的需要配置邮件的触发条件:
说明:这里为了防止因为构建往复尝试结果均为失败而造成的邮件轰炸,可以设置邮件仅第一次或第二次构建结果为失败时才发送邮件,后续失败的邮件将不再发送。
3.6.4.2、自由风格项目发邮件
上面两种插件的邮件模板定义都可以在自由风格项目中通过 "构建后操作" 直接选择,然后保存即可。当项目构建完成后,会按照模板定义的方式通过邮件服务器发送邮件到指定的邮箱。
下面还要设置 Triggers,这里设置邮件发给 Recipient List,注意,在 Extended E-mail Notification 中定义过 Default Triggers,会自动填充到这里的 Triggers 下面,这里只是定义发给谁,前面是定义成功或失败才触发邮件,当然这里也可以重新定义。
然后手动点击开始构建,即可收到邮件。
3.6.4.3、流水线项目发邮件
第一种:直接代码内部定义。
pipeline {
agent {
label 'centos79'
}
stages {
stage('ShowAgent') {
steps {
echo 'The node:${env.NODE_NAME}.'
echo "The node label:${env.NODE_LABELS}."
}
}
stage('Checkout'){
steps{
echo "Fetch codes from SCM..."
}
}
stage('Build'){
steps{
echo "Building..."
}
}
}
post {
success {
emailext (
subject: "'${env.JOB_NAME} [${env.BUILD_NUMBER}]' 更新正常",
body: """
详情:
SUCCESSFUL: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]'
状态:${env.JOB_NAME} jenkins 更新运行正常
URL :${env.BUILD_URL}
项目名称 :${env.JOB_NAME}
项目更新进度:${env.BUILD_NUMBER}
""",
to: "Brinnatt@163.com",
recipientProviders: [[$class: 'DevelopersRecipientProvider']]
)
}
failure {
emailext (
subject: "'${env.JOB_NAME} [${env.BUILD_NUMBER}]' 更新失败",
body: """
详情:
FAILED: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]'
状态:${env.JOB_NAME} jenkins 运行失败
URL :${env.BUILD_URL}
项目名称 :${env.JOB_NAME}
项目更新进度:${env.BUILD_NUMBER}
""",
to: "Brinnatt@163.com",
recipientProviders: [[$class: 'DevelopersRecipientProvider']]
)
}
}
}
第二种:安装下面两个插件。
Config File Provider Plugin:提供文件的存储插件。
Pipeline Utility Steps:提供文件的读写的插件。
系统管理 --> Managed Files --> Add a new Config,选择 "Extended Email Publisher Groovy Template" 类型,然后添加邮件模板。
content 代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>${ENV, var="JOB_NAME"}-第${BUILD_NUMBER}次构建日志</title>
</head>
<body leftmargin="8" marginwidth="0" topmargin="8" marginheight="4"
offset="0">
<table width="95%" cellpadding="0" cellspacing="0" style="font-size: 11pt; font-family: Tahoma, Arial, Helvetica, sans-serif">
<tr>
<td>各位同事,大家好,以下为${PROJECT_NAME }项目构建信息</td>
</tr>
<tr>
<td><br />
<b><font color="#0B610B">构建信息</font></b>
<hr size="2" width="100%" align="center" /></td>
</tr>
<tr>
<td>
<ul>
<li>项目名称 : ${PROJECT_NAME}</li>
<li>构建编号 : 第${BUILD_NUMBER}次构建</li>
<li>触发原因: ${CAUSE}</li>
<li>构建状态: ${BUILD_STATUS}</li>
<li>构建日志: <a href="${BUILD_URL}console">${BUILD_URL}console</a></li>
<li>构建 Url : <a href="${BUILD_URL}">${BUILD_URL}</a></li>
<li>工作目录 : <a href="${PROJECT_URL}ws">${PROJECT_URL}ws</a></li>
<li>项目 Url : <a href="${PROJECT_URL}">${PROJECT_URL}</a></li>
</ul>
</td>
</tr>
<tr>
<td><b><font color="#0B610B">历史变更记录:</font></b>
<hr size="2" width="100%" align="center" /></td>
</tr>
<tr>
<td>
${CHANGES_SINCE_LAST_SUCCESS,reverse=true, format="Changes for Build #%n:<br />%c<br />",showPaths=true,changesFormat="<pre>[%a]<br />%m</pre>",pathFormat=" %p"}
</td>
</tr>
</table>
</body>
</html>
然后配置 pipeline 项目,代码如下:
node () {
stage('email'){
echo "测试发送邮件"
// 设置生成模板文件
configFileProvider([configFile(fileId: 'c368dcc4-2e80-40c4-898e-e77287d5fb26',
targetLocation: 'email.html',
variable: 'Groovy_Email_Template')]) {
// 读取模板
template = readFile encoding: 'UTF-8', file: "${Groovy_Email_Template}"
// 发送邮件
emailext(subject: '测试',
attachLog: true,
recipientProviders: [requestor()],
to: 'Brinnatt@163.com',
body: """${template}""")
}
}
}
- body: 邮件内容
- subject: 邮件主题
- to: 指定邮件接收者
- attachLog: 附加构建日志信息到附件中
- recipientProviders: 设置邮件接收者,可以设置向任务请求触发人员、项目组、整个团体等发送邮件,这样既可以发送邮件到 to 中的设置人员,也可以发送给对应触发人员。