jenkins 脚本编写
DevOps的关键思想之一是“ 基础结构即代码”(具有用代码表示交付/部署管道的基础结构),就像流过它的产品一样。
Jenkins工作流工具是用于创建许多持续交付/部署管道的主要应用程序之一。 这通常是通过为各种管道任务定义一系列单独的作业来完成的。 每个作业都通过Web表单进行配置-填写文本框,从下拉列表中选择条目,等等。然后将一系列作业串在一起,每个作业触发下一个,进入管道。
尽管得到了广泛使用,但是这种创建和连接詹金斯工作以形成管道的方法具有挑战性。 它不符合“ 基础设施即代码”的定义。 作业配置仅作为XML文件存储在Jenkins配置区域中。 这意味着文件不容易阅读或直接修改。 Jenkins应用程序本身提供了用户的主视图以及对它们的访问。
我们通常将Jenkins 2命名为支持流水线代码功能以及其他功能的较新版本。)在本文中,我们将学习如何在Jenkins 2中创建流水线作为代码。这将使用两种不同样式的管道语法(脚本式和声明式)来完成。 我们还将介绍如何在Jenkins外部存储管道代码,但仍然需要Jenkins在更改时运行它,以实现实现DevOps的目标之一。让我们从基础开始。 Jenkins及其插件通过其自己的编程步骤(Jenkins领域专用语言(DSL))使构建基块可用于您的管道任务。
DSL步骤
当詹金斯开始向流水线代码模型发展时,一些最早的更改是以插件的形式出现的— 工作流插件。 这包括创建一组初始DSL步骤,以允许在Jenkins中编写简单的作业,并通过扩展对简单的管道进行编码。
如今,许多不同的插件提供了Jenkins中可用的不同DSL步骤。 例如,我们通过Git插件提供了一个git步骤,以从源代码控制系统中检索源代码。 清单1显示了其用法的示例:
node ('worker_node1') {
stage('Source') {
// Get some code from our Git repository
git 'http://github.com/brentlaster/roarv2'
}
}
清单1:使用Git步骤的示例管道代码行。
实际上,为了与Jenkins 2兼容,当前的插件有望提供用于管道的DSL步骤。 管道包含DSL步骤,以执行传统上通过Web表单执行的操作。 但是,要构成完整的管道,还需要其他支撑件和结构。 Jenkins 2允许两种样式的结构和语法来构建管道。 这些被称为脚本化和声明性 。
脚本管道
现在,在Jenkins 2中创建管道的原始方法称为脚本化 。 在最高级别, 脚本化管道被包装在一个节点块中。 这里的节点是指包含Jenkins 代理程序片段并且可以运行作业(以前称为从属实例)的系统。 该节点通过使用标签映射到系统。 标签只是在Jenkins中配置一个或多个节点(通过全局“ 管理节点”部分完成)时添加的标识符。 一个示例如图1所示。
![图1.带有标签的节点配置 Figure 1. Node configuration with labels](https://i-blog.csdnimg.cn/blog_migrate/c5cef0c92d3ce97e32f92a1021e599ab.png)
图1:带有标签的节点配置
节点块是Groovy编程语言中的一种称为闭包的构造(由开括号和闭括号表示)。 实际上,脚本化管道可以包括并利用任何有效的Groovy代码。 作为示例,清单2显示了执行以下操作的脚本管道代码:
- 检索源代码(通过
git
pipe步骤); - 获取全局定义的Gradle安装的值(通过
tool
管道步骤),并将其放入Groovy变量中; 和 - 调用外壳执行它(通过
sh
管道步骤):// Scripted Pipeline
node ('worker_node1') {
// get code from our Git repository
git 'http://github.com/brentlaster/roarv2'
// get Gradle HOME value
def gradleHome = tool 'gradle4'
// run Gradle to execute compile and unit testing
sh "'${gradleHome}/bin/gradle' clean compileJava test"
}
清单2:使用该工具和sh步骤的脚本语法
这里gradleHome是一个Groovy变量,用于支持DSL步骤。 可以通过Jenkins定义这样的管道,方法是创建一个新的Pipeline项目,并在新项目页面底部的Pipeline Editor部分中键入代码,如图2所示:
![图2:定义一个新的管道项目 Figure 2: Defining a new Pipeline project](https://i-blog.csdnimg.cn/blog_migrate/afedb16eb5e31bdb6aef82d0319ffcfe.png)
图2:定义一个新的管道项目
尽管此简单的节点块在技术上是有效的语法,但Jenkins管道通常具有更高级的粒度阶段。 阶段是将管道划分为逻辑功能单元的一种方式。 它还可以将DSL步骤和Groovy代码组合在一起以实现目标功能。 清单3显示了我们的脚本化管道,其中包含构建阶段的阶段定义和源管理阶段的阶段定义:
// Scripted Pipeline with stages
node ('worker_node1') {
stage('Source') { // Get code
// get code from our Git repository
git 'http://github.com/brentlaster/roarv2'
}
stage('Compile') { // Compile and do unit testing
// get Gradle HOME value
def gradleHome = tool 'gradle4'
// run Gradle to execute compile and unit testing
sh "'${gradleHome}/bin/gradle' clean compileJava test"
}
}
清单3:带有阶段的脚本语法
在新的默认Jenkins输出屏幕( 舞台视图)中,管道中的每个阶段都具有自己的输出区域。 如图3所示,“阶段视图”输出以矩阵形式组织,每一行代表作业的运行,每一列都映射到管道中定义的阶段:
![图3:定义的阶段和阶段视图输出 Figure 3: Defined stages and Stage View output](https://i-blog.csdnimg.cn/blog_migrate/a4690420c2fd3cfda4221da47d2ca0b8.png)
图3:定义的阶段和阶段视图输出
此矩阵中的每个单元格(行和列的交集)还显示计时信息,并使用颜色指示成功或失败。 表1显示了每种颜色的关键:
Color | 含义 |
White | 阶段尚未运行 |
Blue stripes | 处理中 |
Green | 阶段成功 |
Red | 阶段成功,但下游阶段失败 |
Red stripes | 阶段失败 |
另外,通过将鼠标悬停在该单元上,弹出窗口将提供有关该阶段执行的更多信息,并带有指向日志的链接,如图4所示:
![图4:钻取阶段输出中的日志 Figure 4: Drilling down into the logs in stage output](https://i-blog.csdnimg.cn/blog_migrate/813a559e3077d770bf36d6755a513df4.png)
图4:钻取阶段输出中的日志
尽管脚本化管道提供了很大的灵活性,并且使程序员感到舒适和熟悉,但是对于某些用户而言,它们确实有其缺点。 特别是:
- 感觉就像您需要了解Groovy才能创建和修改它们。
- 对于传统的詹金斯用户来说,学习如何使用它们可能具有挑战性。
- 它们提供Java样式的回溯以显示错误。
- 没有内置的生成后处理。
如果您习惯于执行诸如通过Freestyle作业中的后构建处理发送电子邮件通知或归档结果之类的任务,则上面列表中的最后一项可能是一件大事。 如果您的脚本管道代码失败并引发异常,则可能永远无法到达管道末端; 但是,您可以在主要的脚本管道代码周围使用Java样式的try-catch块来处理此问题,如图5所示:
![图5:try-catch块模拟构建后的通知 Figure 5: try-catch block to emulate post-build notifications](https://i-blog.csdnimg.cn/blog_migrate/9984f82aaf3c7e7bfb714059a6ba8d23.png)
图5:try-catch块模拟构建后的通知
在这里,我们有一个称为Notify的最后阶段。 无论管道的早期部分成功还是失败,都将始终执行此阶段。 这是因为try-catch结构将捕获由故障引起的任何异常,并允许控制随后继续。 (Jenkins 2也有一个catchError块,它是try-catch构造的简化的受限版本。)
这种解决方法增加了一种印象,即您需要了解一些编程才能在以前使用Freestyle免费获得的脚本管道中执行操作。 为了简化来自传统Jenkins的开发人员的工作,并从Java / Groovy-ism中脱颖而出,CloudBees和Jenkins社区创建了第二种语法风格来编写管道-声明式。
声明式管道
顾名思义,声明式语法更多地是在管道中声明您想要的内容,而不是编码要执行此操作的逻辑。 它仍然使用DSL步骤作为基础,但在步骤周围包括定义明确的结构。 该结构包括许多指令和部分,用于指定管道中所需的项目。 清单4中显示了一个示例性声明式管道:
pipeline {
agent { label 'worker_node1' }
stages {
stage('Source') { // Get code
steps {
// get code from our Git repository
git 'https://github.com/brentlaster/roarv2'
}
}
stage('Compile') { // Compile and do unit testing
tools {
gradle 'gradle4'
}
steps {
// run Gradle to execute compile and unit testing
sh 'gradle clean compileJava test'
}
}
}
}
清单4:声明性管道示例
您在这里可能会注意到的第一件事是,我们在代码周围有一个agent块,而不是一个node块。 您可以将代理程序和节点视为同一件事,即指定要在其上运行的系统。 (但是,代理程序还包括其他创建系统的方法,例如通过Dockerfiles。)
除此之外,我们在管道中还有其他指令块,例如工具一,在其中声明要使用Gradle的安装(而不是像脚本语法那样将其分配给变量)。 在声明性语法中,不能使用Groovy代码(例如赋值或循环)。 您仅限于结构化的部分/块和DSL步骤。
此外,在声明性管道中,必须将一个步骤中的DSL步骤包含在步骤指令中。 这是因为除了阶段中的步骤之外,您还可以声明其他内容。
图6显示了Jenkins 2:启动和运行手册中的图表,其中包含声明性管道中的所有部分。 读取此图表的方式是,需要带有实线边框的项目,而带有虚线边框的项目是可选的:
![图6:声明性管道部分的示意图 Figure 6: Diagram of Declarative Pipeline sections](https://i-blog.csdnimg.cn/blog_migrate/7967682146887bf40bc451072810abd6.png)
图6:声明性管道部分的示意图
您可能可以从它们的名称中了解不同部分的功能。 例如, 环境设置环境变量,而工具声明我们要使用的工具应用程序。 我们不会在这里详细介绍每一个细节,但是这本书有整整一章关于声明性管道。
请注意,这里有一个帖子部分。 这可以模拟Freestyle作业中的后生成处理功能,并且不需要像在脚本管道中使用的try-catch构造那样的语法。 还要注意, post块也可以在阶段结束时发生。
这种明确定义的结构为在Jenkins中使用声明性管道提供了多个优势。 它们包括:
- 从Freestyle到代码管道的转换更容易,因为您在声明所需内容(类似于在传统Jenkins中填写表单的方式);
- 更严格,更清晰的语法/要求检查; 和
- 与新的Blue Ocean图形界面更紧密地集成(管道图中结构良好的部分更易于单个图形对象使用)。
您可能想知道声明性管道的缺点是什么。 如果您需要超出声明性语法所允许的范围,则必须采用其他方法。 例如,如果您想为变量分配一个值并在管道中使用它,则不能使用声明性语法。 有些插件还可能需要某种东西才能在管道中使用它们。 因此,它们与声明性语法不直接兼容。
在那种情况下你会怎么做? 您可以在阶段中围绕少量非声明性代码添加脚本块。 声明性管道中脚本块中的代码可以是任何有效的脚本语法。
对于更大量的非声明性代码,推荐的方法是将其放入外部库(在Jenkins 2中称为共享管道库 )。 还有一些其他方法,这些方法受到的制裁较少-意味着将来可能不会支持它们。 (如果您有兴趣了解更多信息,那么“ Jenkins 2:启动并运行”一书详细介绍了共享库和其他替代方法。)
希望这可以使您很好地了解Jenkins 2中的脚本化管道和声明性管道之间的区别。在这里要考虑的最后一个方面是,对于Jenkins 2,您不需要将管道代码存储在Jenkins应用程序本身中。 您可以将其创建为(或复制到)具有特殊名称Jenkinsfile的外部文本文件中。 然后可以将该文件与源代码一起存储在所需分支的同一存储库中。
詹金斯档案
尽管Jenkins应用程序是开发和运行管道的主要环境,但是管道代码本身也可以放置在名为Jenkinsfile的外部文件中。 这样,我们便可以将管道代码视为其他任何文本文件,这意味着可以将其存储在源代码管理中并在源代码管理中进行跟踪,并进行常规的过程(例如代码审查)。
此外,流水线代码可以与产品源一起存储并与之一起使用,从而使我们可以将流水线作为基础结构即代码来实现。
图7显示了存储在源代码控制存储库中的Jenkins文件:
![图7:源代码管理中的Jenkinsfile Figure 7: Jenkinsfile in source control](https://i-blog.csdnimg.cn/blog_migrate/b49eca37ac1183a730e6b69775fcbac2.png)
图7:源代码管理中的Jenkinsfile
源代码管理中Jenkinsfile的内容如图8所示:
![图8:示例Jenkinsfile Figure 8: An example Jenkinsfile](https://i-blog.csdnimg.cn/blog_migrate/15235329ccdc20bbdd4c309cbcd67b8f.png)
图8:示例Jenkinsfile
您可能会认为这是我们较早版本的管道的精确副本,但是有一些细微的差异。 首先,我们在顶部添加了groovy标记。 但是,更重要的是,请注意从源代码控制系统获取代码的步骤。 代替我们先前使用的git步骤,我们有一个scm checkout步骤。 这是您可以在Jenkinsfile中使用的快捷方式。 由于Jenkinsfile与它将在其上运行的代码位于相同的存储库(和分支)中,因此它已经假定这是应从中获取源代码的位置。 这意味着我们不需要明确地告诉它该信息。
使用Jenkinsfile的优点是您的管道定义与通过管道的产品源一起使用。 然后,您可以在Jenkins中创建一个Multibranch Pipeline类型的项目,并通过作业配置的“ 分支源”部分将其指向带有Jenkinsfile的源存储库。 图9显示了两个步骤:
![图9:创建和配置多分支管道 Figure 9: Creating and configuring a Multibranch Pipeline](https://i-blog.csdnimg.cn/blog_migrate/b21b68c4be2273d6474ea27d62f2e34d.png)
![图9:创建和配置多分支管道 Figure 9: Creating and configuring a Multibranch Pipeline](https://i-blog.csdnimg.cn/blog_migrate/1d07d0b1795c1ca9be856fd7caff20e6.png)
图9:创建和配置多分支管道
然后,Jenkins将扫描存储库中的每个分支,并检查该分支是否具有Jenkinsfile。 如果是这样,它将在Multibranch Pipeline项目中自动创建并配置一个子项目,以运行该分支的管道。 图10显示了运行扫描并寻找Jenkinsfiles以在多分支管道中创建作业的示例:
![图10:Jenkins检查Jenkinsfiles以在多分支管道中自动设置作业 Figure 10: Jenkins checking for Jenkinsfiles to automatically setup jobs in a Multibranch Pipeline](https://i-blog.csdnimg.cn/blog_migrate/d160eabdb808ffe779e35bced00ef26c.png)
图10:Jenkins检查Jenkinsfiles以在多分支管道中自动设置作业
图11显示了针对Jenkins文件和源存储库执行后的多分支管道中的作业:
![图11:基于源代码分支中的Jenkinsfiles自动创建的多分支管道作业 Figure 11: Automatically created Multibranch Pipeline jobs based on Jenkinsfiles in source code branches](https://i-blog.csdnimg.cn/blog_migrate/0d40c1a31f43681b4077e3fbf679e6f9.png)
图11:基于源代码分支中的Jenkinsfiles自动创建的多分支管道作业
使用Jenkinsfiles,我们可以实现将基础架构(或至少是管道定义)视为代码的DevOps目标。 如果需要更改管道,则可以像在其他文件中一样在源代码控制中拉,编辑和推送Jenkins文件。 我们还可以在管道脚本上执行诸如代码审查之类的事情。
使用Jenkinsfiles有一个潜在的弊端:在外部文件中而不是在Jenkins应用程序环境中工作时,提前发现问题可能更具挑战性。 解决此问题的一种方法是首先在Jenkins应用程序中将代码开发为管道项目。 然后,您可以随后将其转换为Jenkinsfile。 此外,还有一个声明性管道短绒应用程序,您可以针对Jenkins之外的Jenkinsfiles运行它,以及早发现问题。
结论
希望本文对脚本化和声明性管道的区别和使用以及如何将它们作为Jenkinsfiles包含在源中提供了很好的了解。 如果您想在Jenkins 2中找到有关这些主题或相关主题的更多信息,可以在网上搜索或查阅《 Jenkins 2:启动并运行》一书,以获取有关使用此技术为DevOps赋权的更多示例和说明。在您的组织中。
jenkins 脚本编写