在前一章中,我们研究了与 Jenkins 交互并扩展其使用的几种方式,以便开发人员可以直接从他们的开发环境中受益。
我们看到的插件和附加组件显然能够从 Jenkins 那里获取“实时”数据,以便将这些数据直接传送到客户端环境(开发人员的集成开发环境)。
在本章中,我们将了解这些插件是如何访问这些信息的,并且我们将探索 Jenkins 为编程交互提供的各种机制和接口,例如 Jenkins 应用编程接口 ( API )。我们还将探索 Jenkins 命令行界面 ( CLI ),它提供了一种机制,您可以通过该机制以编程方式和/或交互方式与 Jenkins 进行远程交互。
这两个特性都非常强大,是扩展 Jenkins 的基本工具。
有三个主要功能,您通常会使用 Jenkins 应用编程接口;这些措施如下:
- 从 Jenkins 那里检索和消费信息
- 基于外部事件触发构建
- 创建、复制和更改 Jenkins 配置
为了说明如何使用 Jenkins API 以编程方式从 Jenkins 中提取实时信息,我们将从高层次上看一个实际的例子——创建一个信息辐射器,获取 Jenkins 的信息并将其显示在外部网页中。我们不会为这个详细地写所有的代码;但是,我们将充分详细地分析基本的构建模块,以便您能够采用通用的方法,并以自己选择的语言开发自己的定制解决方案。
信息辐射器很简单,但很有用实时网页,让人们可以轻松地实时监控你最关键的 Jenkins 工作的状态。这与我们之前看到的 IDE 插件非常相似,但是这些指示器显示在办公室的电视屏幕上,以辐射信息
信息辐射器的惯例是保持简单—尽可能少的工作,如果一切正常,显示绿色指示灯,如果有问题,显示红色指示灯。如果构建正在进行,有时显示一个琥珀色的指示器是很有用的。这个简单的系统有助于突出需要作为最高优先级进行修复的紧急问题,并且当人们可以清楚地看到构建当前不稳定时,它还可以阻止他们签入新的更改;对一个已经崩溃的系统进行进一步的改造只会使问题更加复杂。
在我们的高级演练中,我们将监控一个 Jenkins 构建的当前状态。您将能够重用和扩展相同的方法来监控您想要的任意多的构建,并且您将看到如何从您的 Jenkins 工作中获取和报告其他细节。
请注意,您可以使用许多预构建的解决方案,包括各种不同需求的插件——我们在这里有意采用一种自己动手的方法,以展示可能性并向您展示如何使用 Jenkins 应用编程接口。
第一步是获取我们(程序化)手中的信息。最简单的方法是通过 XML 应用编程接口。这只是将/api/xml
字符串附加到您想要监控的作业的网址上,如下所示:http://yourjenkinsserver:8080/job/YourJob/api/xml
。
注意还有一个 JSON API 可用;如果这更符合您的需求——只需将api/xml
替换为api/json
即可接收 JSON 格式的相同信息。
如果您在浏览器中这样做,您应该会看到与我的非常基本的工作有点相似的 XML:
API 返回的文本简单,XML 很不言自明;快速浏览一下就会发现,它包含了您想要的关于刚刚查询的工作的所有信息,只需要对其进行处理和解释。这些 XML 元素的文档化方式似乎不多;但是,如果您从尽可能简单的工作开始,然后对其进行更改和添加,您应该能够弄清楚每个元素的作用以及可能的值。
XML 处理器是处理这个问题的最佳方式,您选择的脚本或编程语言应该为您提供几个选择。例如,Perl 有 XML::Simple ,Python 有 ElementTree ,Groovy 有 XmlParser ,Java 有 JAXP 等等。如果您没有这些,您可以使用grep
和awk
在 shell 脚本中找到您想要的行和值。
因此,我们现在有了一个我们想要监视的作业,一种获取该作业的所有当前信息的方法,一种处理 XML 的合适方法,以及一种提取我们想要的信息的机制。
对于这个例子,我们真正想知道的是构建的当前状态——对应于红色、琥珀色和绿色健康指标的值——这些在 XML 例子中作为作业的当前color
属性出现。
例如,考虑下面的 XML 标签:<color>blue</color>
。这表明我们当前有一个非运行且稳定的作业,而<color>blue_anime</color>
指的是上一次构建健康且当前正在构建的作业的蓝色动画健康指示器图标。
在我们的散热器中,我们可以简单地将anime
显示为琥珀色。<color>red</color>
和<color>red_anime</color>
分别是失败和运行(但以前失败过)作业的显而易见的等价物。如果您查看各种不同作业类型和状态的 XML,您将能够发现并解释所使用的命名约定——只需将/api/xml
添加到各种作业选择中并进行比较。
我们简单的信息辐射器的下一个障碍是自动化和调度工作,正如您所期望的,我们可以在 Jenkins 非常快速和容易地做到这一点。
只需创建一个新的 Jenkins 作业,获取相应的 URL(末尾有/api/xml
)并将其输入到您的 XML 解析脚本中,以提取当前值。
许多编程和脚本语言都有内置的 XML 或 web 获取能力,但是如果您愿意,您可以使用 curl 或 wget 来获取数据,然后将其传递给您的脚本。
可以将 Jenkins 作业安排为以适合您的频率运行——您可以使用标准 cron 符号通过内置的 cron 函数来实现这一点;例如,您可以将作业设置为每两分钟运行一次,如下所示:
在这个条目中,我已经指定H/2 * * * *
让这个作业每两分钟运行一次。符号H
,是一个方便的 Jenkins 内置机制,允许 Jenkins 平衡和管理作业调度。Jenkins 能够为您分配负载,而不是同时触发所有作业。更多详情,点击**?图标在时间表**输入框旁边,显示如下:
**为了让定期调度的任务在系统上产生均匀的负载,应该尽可能使用符号 H(表示散列)。例如,对十几个日常工作使用 0 0 * * 将导致午夜出现大峰值。相比之下,使用 H H * * 仍然会每天执行一次每个作业,但不是同时执行,这样可以更好地利用有限的资源。
如果您不熟悉 cron 语法,请查看任何 Linux 盒子上的 cron 手册页(在终端中键入man cron
)。网上也有几个有用的 cron 生成器,比如这个在http://crontab-generator.org/的,可以很有用。
请注意,强烈建议您在决定和设置重复构建的频率之前测试和微调您的工作*。例如,如果你的工作需要 3 分钟来运行,而你设置它每分钟运行一次,事情就不会进展顺利!*
这一步剩下的最后一个任务是将数据存储在某个地方——我通常更喜欢一个简单的 MySQL 数据库,我可以在作业结束时简单地通过将当前运行时参数传递给 MySQL 二进制文件来更新它。
最后一步是将数据库中的信息显示为一种颜色“辐射器”——这仅仅包括生成一个查询数据的网页,并将该信息转换为适当的颜色——红色、琥珀色或绿色。这可以用多种语言来完成,如果您愿意的话,包括 PHP、JSP 和 ASP,但是最简单的方法可能是让您的 Jenkins 工作为您将原始 HTML 写出到一个文件中,可能是这样的:
<html>
<head>
<meta http-equiv="refresh" content="5">
<style type="text/css">
.myclass{
width:270px;
height:150px;
position:absolute;
left:50%;
top:50%;
margin:-75px 0 0 -135px;
}
</style>
</head>
<body style="background:#088A08">
<div class="myclass">Status of my VerySimpleJob</div>
</body>
</html>
作业会根据需要更新背景颜色的值。请注意,在前面的代码中有一个 Meta refresh 标记,每 5 秒钟重新加载一次页面——您将需要实现类似这样的东西,否则您将长时间查看相同的信息!
您甚至可以让 Jenkins 充当一个简单的 web 服务器,并托管我们为您创建的网页—如果您将由作业生成的文件复制到运行您的 Jenkins 实例的系统上您的JENKINS_HOME
位置内的userContent
目录中,您将看到该文件出现在以下网址:http://myjenkins:8080/userContent
这应该如下所示:
点击**【inforad.html】**链接,您将看到以下页面——我们非常简单的 DIY 信息辐射器:
这个简单的练习说明了如何通过应用编程接口查询 Jenkins 来实时检索和使用信息。
在本节中,我们将回顾 Jenkins 命令行界面——这是另一个在某些情况下非常有用的 Jenkins 扩展点——通常用于对远程 Jenkins 服务器运行命令,执行触发构建或更新配置等功能。
要使用 Jenkins 命令行界面,需要“Jenkins-命令行界面. jar”文件。
这可以从您自己的 Jenkins 服务器上快速轻松地获取。如果您将“/cli”附加到 Jenkins 实例的网址上,您应该会看到类似如下的页面:
该网址提供了开始使用 Jenkins 命令行界面所需的一切。
有一个指向 Jenkins Wiki 主题的链接以获取更多信息,还有一个从您的服务器(http://{yourserverand:port}/jnlpJars/jenkins-cli.jar
)直接下载 Jenkins-cli.jar 文件的链接,以及一个可用的 cli 命令列表和简要描述。
一旦您在本地保存了 CLI jar(您可以通过浏览器下载或使用命令行工具,如wget
或curl
),您只需要设置您的 Java 环境,然后执行帮助页面开始处详述的行,如下所示:
java -jar jenkins-cli.jar -s http://{yourserverand:port}/ help
假设你在当前目录中有Jenkins-cli.jar
并且你更新了地址来反映你的服务器,你应该会得到基本的帮助信息,你就可以走了。
最常见的也可能是 CLI 最简单的任务是触发远程作业在进程中的某个点运行。当您将 Jenkins 与其他遗留系统集成并逐步将自动化引入手动流程时,这将非常有用。您可能无法立即自动化所有事情,或者让 Jenkins 同时控制所有事情,但是如果您可以设置一个 Jenkins 作业来自动化现有手动工作流的各个部分,那么您就可以逐步引入 Jenkins,并逐步从链中删除手动步骤。
例如,假设我们有一个运行某种形式的数据处理的遗留批处理作业。该处理可能需要一些时间来运行,并且可能会出现另一个步骤来检查处理是否完成,如果完成,则将新数据传输到另一个位置或将它传递给另一个进程。我们可以从创建一个 Jenkins 作业开始,当调用该作业时,它会获取数据,检查数据是否有效,然后将其传递给流程的下一部分。这可以消除手动步骤并减少整体处理时间。但是 Jenkins 的工作怎么知道什么时候运行?判断处理是否已经完成可能效率不高,但我们可以对流程进行一个小的更新,在初始处理之后自动调用 Jenkins 作业。
远程触发作业在简单的 Jenkins 服务器上运行就像这样简单:
这里,我们首先使用curl
将 Jenkins CLI jar 文件下载到当前目录:
curl -O http://minty:8080/jnlpJars/jenkins-cli.jar
这一步只需要做一次。然后,我们将使用以下命令调用远程作业:
java -jar jenkins-cli.jar -s http://minty:8080/ build VeryBasicJob
使用这个简单的配置,你不会得到发生什么的反馈;但是,在 Jenkins 服务器上查看VeryBasicJob
应该会发现作业被触发,在控制台输出中,还应该提到以下内容:
匿名从命令行启动
所以,我们可以看到这种方法很有效,但是缺乏反馈没有太大帮助。如果我们将–s
和–v
参数添加到命令中,我们将获得正在发生的事情的完整细节,如下所示:
这看起来要好得多——我们现在可以看到我们已经启动了VeryBasicJob
的第 9 次运行,它在成功退出之前简单地休眠了 20 秒。
该输出信息可以在客户端脚本中用于检查成功或失败,或者您可以记下作业号,或者记录任何其他有用的输出。
显然,如果没有某种形式的身份验证,我们通常不会运行 Jenkins,所以在现实世界中事情会稍微复杂一点。首先需要在 Jenkins 的“配置用户”页面中为您运行 CLI 命令的用户授予“总体/读取”帐户权限。然后,您可以在命令行末尾简单地添加用户名和密码,如下所示:
--username don --password MyPassword123
这足以让事情运转起来,但从安全性的角度来看,它仍然不够好;在添加凭据的脚本中,或者在您使用的 shell 的历史记录中,或者在不使用 HTTPS 的 HTTP 流中,凭据将以纯文本形式可见。当用户在同一台主机上运行ps
或top
等时,凭据也可以作为传递给运行进程的参数可见。
一种更安全的方法是设置 SSH 密钥,将传入私钥中作为公钥。如果您在 Jenkins 中为您的用户名进行“配置”,您可以在提供的文本框中为您的帐户设置 SSH 密钥。这里有设置 SSH 的详细说明:
https://help.github.com/articles/generating-ssh-keys/
完成后,根据您使用的 Jenkins 版本,Jenkins 可能会从以下任何位置自动检查并使用您的 SSH 凭据:
~/.ssh/identity, ~/.ssh/id_dsa, ~/.ssh/id_rsa
然后,您可以显式提供密钥的路径,如下所示(将其附加到命令行,而不是前面的用户名和密码详细信息):
-i ~/.ssh/id_rsa
对于需要参数的作业(也就是说,您已经设置为在运行时向用户请求信息的作业),您可以提供额外的“-p”参数,如下所示:
-p sprint=1.7
这将被传递给该作业,就像用户通过用户界面输入数据一样,并假设为该作业配置了名为“sprint”的输入元素。
Jenkins 命令行界面的另一个非常有用的功能是以编程方式远程更新 Jenkins 配置。
从帮助页面中,我们看到当我们之前将/cli
附加到服务器 URL 时,get-job
和create-job
这两个命令非常有用。
运行 get-job 将请求 Jenkins 为该作业提供 XML 定义。
例如,考虑以下命令:
java -jar jenkins-cli.jar -s http://minty:8080/ get-job VeryBasicJob
在我的服务器上运行时,它将返回以下输出:
例如,这个 XML 也可以通过在命令的末尾附加“> VeryBasicJob.xml
”来重定向到一个文件,然后该文件可以作为定期备份在您的版本控制软件中添加或更新。
同样,您可以选择使用如下所示的 create-job 命令创建新作业:
java -jar jenkins-cli.jar -s http://{yourserverand:port} create-job MyNewJobName < MyNewJob.xml
例如,MyNewJob.xml
文件可以通过结合 Jenkins 作业、一个 XML 模板和一些用户指定的输入以编程方式创建。
我们还可以使用 update-job 结合现有作业名称来更新现有作业:
java -jar jenkins-cli.jar -s http://{yourserverand:port} update-job VeryBasicJob < VeryBasicJob_v2.xml
这种方法可以用来构建一种机制,将 Jenkins 配置的全部或部分备份到版本控制,然后以编程方式(通过 Jenkins)重新加载它们。
如果需要更改,还可以扩展这种方法,对 XML 文件(以及它们创建的作业配置)进行一些修改;例如,定期更新版本或 sprint 细节。
在本章中,我们探讨了 Jenkins 应用编程接口和 Jenkins 命令行界面带来的可能性。
我们已经研究了一些高级示例,并展示了如何使用 XML API 开发自己的定制信息辐射器。
我们还概述了 CLI 提供的一些功能,并演示了如何使用它们。
从这两个特性的开放性中可以看出,Jenkins 的灵活性相当惊人——这是一个开放的平台,为您提供了许多适应和扩展它的方法,以满足您的需求和要求,无论它们是什么。
我们之前检查的插件能够在远程 Jenkins 服务器上显示实时信息的方式现在很有希望变得非常明显,当我们稍后查看如何为 Jenkins 开发我们自己的插件时,我们将进一步使用 API 和 CLI。
在下一章中,我们将探索 Jenkins 扩展点,看看它们背后的理论,并回顾开发的最佳实践。