Jenkins pipeline是什么意思啊 系列 什么是Jenkins pipeline是什么意思啊

自动化部署之jenkins的Pipeline(代码流水线管理)
<span type="1" blog_id="2053844" userid='
分享到朋友圈
好的文章,和好友一起分享热门标签:
基于Docker的Jenkins Pipeline工作流
点击量:5826
本文内容来源于4月25日有容云Docker技术交流群微信群分享活动,根据有容云技术实施团队原创分享内容整理而成。对Docker技术感兴趣、或对本文中细节需继续探讨的朋友,欢迎加入有容云Docker技术交流群参与讨论!(关注微信公众号,回复&加群&)
一个软件产品的开发周期中,尤其是敏捷开发,持续集成和持续部署是必不可少的环节,而随着产品的丰富,模块的增多。随即带来了更加多的问题,各模块间编译环境的准备,编译复杂,耗时增加,还需要专人去负责这个流程。而Jenkins则可以很好的解决这个单一而容易出错的(持续集成)工作。
Jenkins也存在着编译环境不隔离的问题,虽然可以通过集群的方式解决,可是需要为每种环境甚至是一种语言的不同版本准备多台机器,这个利用率很低很低。
本次线上活动主要分享在基于的Jenkins pipeline工作流的一些经验和见解:
1、什么是jenkins?jenkins pipeline如何为我们工作带来便利;
2、Jenkins pipeline 的基础概念;
3、基于容器的方式Jenkins CI流程;
4、Jenkins和Docker、Kubernetes整合、完成集成部署。
传统交付方案
传统我们的项目开发模式是产品调研提出需求,开发团队研究决定开发方案选型。然后开始一个周期的开发,模块开发完成之后开始模块间的联调。联调结束之后打包交付给测试团队。测试团队,系统测试或自动化测试,然后提交bug,开发团队修复bug,周而复始。
传统的模式中,存在着较多的不确定因素。例如,开发环境、编译环境、测试环境、生产环境,等不确定因素。人为介入打包中的不确定因素,缺乏单元测试和自动化测试的整合。从而导致的结果是,开发-测试-修复的周期较长,而且很多小的问题完全可以由单元测试进行覆盖。
持续交付并不是某个特定的软件,而是一个结果。这个结果要求团队可以随时的发布一个新的准确版本,而且要求在编译发布的过程中进行自动化测试,通过自动化测试可以及时的发现并定位存在的bug,修复bug之后再进行快速的发布到测试环境,测试团队直接进行测试。
与传统模式的区别在于持续交付可以提前发现bug的存在和快速修复而不必等到测试人员的介入之后才发现。持续交付分解出来就是&持续&和&交付&。
持续:持续要求任何时,候任何情况都能进行准确的发布,做到准确的发布需要注意以下几个关键点。
1、持续应该是一个周期性的,可以是每天的某个时间点,也可以是某次代码的提交,或者某次人为触发。所以人工进行构建是不可能的,需要自动化的构建,自动化要求构建的任何一个流程都必须以脚本的形式运行,代码检出、代码构建、各模块代码单元测试、集成测试、UI自动化测试等。
2、发布的程序版本不允许是各个模块在开发环境编译出一个版本作为交付,而要求在一个纯净的编译环境中进行构建。
3、构建的过程应该要求最大可能的固化,例如操作系统的版本,构建环境的版本,相关的依赖等。
4、避免从网络获取相关的文件,这点以nodejs为开发或编译的项目尤其重要,安装node的依赖包总是一个漫长的过程,就算有国内的源,一般的项目也需要一两分钟的node依赖包,这不符合快速构建。
交付:在持续编译的过程,使用自动化已经可以避免大多数的错误了。但是还是需要人为介入的系统测试,毕竟自动化的测试一般只能覆盖到70%左右。
根据我们团队内部推广这种工作方式的效果来看,持续集成确实让我们工作便利了许多, 每次代码的构建和自动化测试让我们及时发现存在的bug。好的工作模式也需要团队成员的遵守,团队成员应该积极的拥抱这种工作方式,团队成员需要做好以下几点。
1、使用版本工具例如git。git有强大的版本回溯,成员每次完成一个小的功能点进行代码提交。合并到master分支,持续交付工具应该配置为代码更新触发。团队内部应该等到持续交付流程结束之后,确认编译、自动化测试通过之后方可进行下一个版本的提交,这样容易定位bug。而不会导致这次bug影响团队内其他成员的工作。
2、主分支的代码bug不应该存留时间过长,避免团队内其他成员合并代码的时候引入其他问题。
3、测试驱动开发,任何一个新的功能开发都应该先写好单元测试脚本,并积极更新自动化测试脚本。并且积极地拥抱测试,虽然你明白这个测试不通过的问题并不会引起很大的系统性问题 ,但是还是应该进行修复而不是想方设法的跳过这个自动化测试。
4、临近下班的时候不要提交代码,这主要是因为遵守第2点。
一个解决方案
使用Docker
已经越来越火,CICD和Devops也是Docker一个重要的场景。在持续交付中使用Docker有一下优点。
1、Docker强大的环境隔离性可以将环境和程序打包在一起,测试、运维,人员无需知道我们的程序是如何配置的,只需要一条Docker 的命令就可以将我们的程序运行起来,这也更加容易实现持续部署。
2、减少编译环境的污染,因为Docker天然的隔离性,也避免了传统编译环境难以配置多套编译环境的问题。在基于Docker的持续发布中,我们可以在同一台宿主机上同时编译不同版本的Java项目,不同版本的Python项目,而无需任何配置,镜像也只是从docker hub中获取。
在持续集成方面,我们选择Jenkins。Jenkins是一款开源软件,拥有众多优秀的插件,依靠这些插件,我们可以完成一些周期、繁琐、复杂的任务。例如我们今天分享的持续发布,虽然Jenkins解决了我们繁琐复杂周期性的操作,但是没有解决我们在多种环境下编译构建的需求。而这个场景正是Docker的强项。
通过Jenkins的pipeline我们可以实现代码检出、单元测试、编译、构建、发布、测试等流程的自动化,而最终通过Jenkins的Docker插件将产出物构建成镜像,方便部署到Docker环境。
持续集成让我们新的代码源源不断的构建成了镜像,这些镜像经历了单元测试,自动化测试,但还没有接受过测试团队的严格测试。Jenkins是一个强大的持续集成工具,然而持续部署并不是Jenkins的强项,但是Jenkins拥有很多强大的插件。2而且我们持续集成产出的是镜像,所以持续的部署,我们只需要将镜像运行起来,或者利用第三方的容器管理平台提供的API进行部署。
1、本地部署应用到Docker
本地部署到Docker容器可以使用Jenkins的docker插件,下面会介绍。
2、部署到远程主机的Docker、Appsoar。
Docker和Appsoar都支持开启API调用。通过现有的API我们可以运行我们生成镜像版本。从而达到持续的部署最新版本。
3、部署到kubernetes
kubernetes除了可以通过API调用还可以在jenkins中配置kubectl的方式创建或更新deployments。
Docker中运行Jenkins
Docker部署Jenkins的方式简单方便,下面我们介绍用Docker的方式运行Jenkins。
docker run -d -u root \
-v/var/run/docker.sock:/var/run/docker.sock \
-v $(which docker):/bin/docker \
-v /var/jenkins_home:/var/jenkins_home \
1、这里将docker.sock和docker的可执行文件挂载到jenkins容器中,这样我们就可以在容器中使用docker了。
2、&jenkins容器,默认的用户是jenkins因为我们需要使用docker所以我们需要使用root用户。
3、/var/jenkins_home的挂在卷是可选的,jenkins_home存放了所有任务、日志、认证、插件等jenkins运行后的文件。可做数据恢复使用。
配置Jenkins
1、解锁jenkins
&解锁的密码在容器的log中可以查看,或者直接查看jenkins_home指定文件。
2、选择插件
创建Pipeline
下面我们创建一个的Jenkins的Pipeline完成简单的cicd流程。
1、新建pipeline,在左侧新建选择pipeline。
2、在左侧的Credentials中新建git和镜像仓库的credentials
3、配置pipeline,例如定时触发,代码更新触发,webhook触发等。
4、在pipeline script中填入下面的demo.
以下是伪代码,仅提供思路
& // 代码检出
& stage(&#39;get Code&#39;) {
&&& git credentialsId: &#39;git-credentials-id&#39;, url: &#39;http://192.168.19.250/ufleet/uflow.git&#39;
&&& // 镜像中进行单元测试
& stage(&#39;unit testing&#39;){&
&&& // 启动golnag:1.7并在golang内编译代码
&&& docker.image(&#39;golang:1.7&#39;).inside {
&&&&& sh &#39;./script/unittest.sh&#39;
& // 镜像中代码构建
& stage(&#39;Build&#39;){&&&&
&&& // 将配置文件中的运行模式由&dev&改成&prod&
&&& def confFilePath = &#39;conf/app.conf&#39;
&&& def config = readFile confFilePath
&&& config = config.replaceFirst(/runmode = dev/, &runmode = prod&)&&
&&& writeFile file: confFilePath, text: config
&&& // 启动golnag:1.7并在golang内编译代码
&&& docker.image(&#39;golang:1.7&#39;).inside {
&&&&& sh &#39;./script/build.sh&#39;
& // 编译镜像并push到仓库
& def imagesName = &#39;192.168.18.250:5002/ufleet/uflow:v0.9.1.${BUILD_NUMBER}&#39;&&
& stage(&#39;Image Build And Push&#39;){
&&& docker.withRegistry(&#39;http://192.168.18.250:5002&#39;, &#39;registry-credentials-id&#39;) {
&&&&& docker.build(imagesName).push()
& // 启动刚运行的容器
& stage(&#39;deploy iamegs&#39;){&&&&
&&& // 需要删除旧版本的容器,否则会导致端口占用而无法启动。
&&&&& sh &#39;docker rm -f cicdDemo&#39;
&&& }catch(e){
&&&&&&& // err message
&& &docker.image(imagesName).run(&#39;-p 9091:80 --name cicdDemo&#39;)&
Jenkins pipeline的脚本语法是groovy的语法,其中docker、Git是插件提供的能力。代码的执行流程如下:
1、通过Git插件获取最新代码到jenkins的工作区,例如/var/jenkins_home/workspace/pipelineDemo。
2、docker.image().inside是如何编译我们的代码呢,通过查看Jenkins的console可以看到如下log.
[Pipeline] withDockerContainer
$ docker run -t -d -u 0:0 -w /var/jenkins_home/workspace/pipelineDemo --volumes-from d732ae2a92cbb082e85616abff5cefbd782e -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** --entrypoint cat 192.168.18.250:5002/ufleet-build/golang:1.7
[Pipeline] {
[Pipeline] sh
[ufleet-uflow] Running shell script
+ ./script/build.sh
熟悉Docker命令的朋友应该很容易理解了,原来是docker.image().inside启动的时候会将当前的目录挂在到容器中,然后在容器中执行./script/build.sh,这样我们就完成了利用容器中存在的环境做单元测试或构建编译了。
3、通过docker插件提供的能力构建镜像,Dockerfile存放在代码目录中。构建镜像后push到镜像仓库,私有仓库需要自行配置镜像仓库。
4、镜像构建完成之后就可以删掉旧版本,并重新运行一个新的版本。
通过简单的例子,可见Jenkins和Docker的结合给带来了足够的便利和强大。我们需要准备的只是一个编译的脚本,在编译脚本中可以使用任何的环境和任何的版本。
Pipeline&介绍
Jenkins的任务两个主要版本。
free style只是一个自动化的脚本,脚本类型为shell。所有的脚本在一台机器上运行,需要的环境需要提前准备。配置不集中,混乱。但是一般情况下还是够用的。
pipeline是jenkins2的版本使用了一个基于groovy脚本的任务类型,通过一系列的stage将构建的不同部分组合成一个pipline。而且配合step可以完成异步操作。因为基于groovy可编程性更加强大,而且脚本可以存放在源码中,脚本的更改不需要直接到jenkins中修改。
pipeline的一些使用经验和技巧
1、jenkins的资料较少,官网可以查看的内容也不多,一般的需求Jenkins内置的pipeline-syntax里面就有常用的命令生成器。可以满足大多数需求。
2、在pipeline脚本调试完成之后应该将脚本以文件的形式放在源码目录中,这样子方便修改。和多分支需要编译的情况下进行互相隔离。
3、应该多查找下相应的插件,而不是使用sh用执行脚本的方式来解决问题。
4、应该将jenkins_home目录挂在出来,如果遇上了Jenkins崩溃了可以及时的恢复数据。
5、应该新建一个定时的pipeline用来清理生成的镜像,减少硬盘资源的占用。
6、页面新建的pipeline,在页面删除之后,jenkins_home/workspace中对应的项目文件并不会被删除。
Q1:请问kubernetes怎么结合jenkins做持续集成呢?
A:部署到kubernetes。kubernetes除了可以通过API调用还可以在jenkins中配置kubectl的方式创建或更新deployments。
Q2:必须通过pipeline才能实现jenkins把代码构建成Docker镜像么?
A:不一定,使用Docker主要是方便进行编译环境的隔离,也可以配置好NFS,构建完成之后复制到固定的服务器上,这个我们一般叫制品库。
Q3:Docker目前官方的私有仓库registry并没有提供镜像删除功能,请问你们的镜像是如何进行版本管理的呢?
A:AppHouse是我们公司的一个镜像仓库产品基于Docker的registry,我们扩展了删除、复制,等功能。如果有兴趣的话可以到我们公司官网获取我们的AppHouse。
Q4:Pipeline如何通过Docker容器部署应用到不同的节点上去?发布遇到问题如何回滚版本的?
A:就如我前面的稿件中提到的,jenkins的能力更多的是做持续集成(CI)的功能,部署和回滚都需要容器管理平台并不是Jenkins的强项,特别是回滚单依靠jenkins很难做到完美的方案。但是部署到不同的Docker的节点上,可以使用第三方的管理平台,例如AppSoar和k8s提供的API能力,可以进行部署。jenkins直接调用curl命令执行容器管理平台提供的API。
Q5:pipeline的每个环节的报告如何快速获取?比如代码静态检查,工程构建,测试报告等等
A:&http://jenkins:8080/job/clearImages/86/wfapi/&通过jenkins这个API,可以获取一些状态和时间信息,至于详细的代码静态检查,每种语言都有不同的语法检查。需要自行配置。当然详细的需要查看输出日志。
Q6:关于测试驱动开发,在开发之前写好的用例一定要是自动化的吗?为什么?
A:一个系统由若干的方法组成,单元测试就是测试你写的方法是否符合你的业务要求。所以先写合理的单元测试,只要你的方法通过了这个单元测试就表示你写的这个方法是正确的,单元测试代码是需要开发人员编写,每种语言有不同的单元测试框架例如nodejs的mocha,golang 的go test 。自动化测试由测试人员编写,单元测试应该需要脱离外部因素,不依赖数据库,不依赖外部API。
Q7:怎么触发工作流的?
A:&jenkins pipeline 提供了三种方式(如果安装了SCM的插件可能有其他的方式触发),进入到pipeline的设置页面中的分别有。wbhook(触发远程构建 (例如,使用脚本))、定时触发(Build periodically)、代码更新触发(Poll SCM)。
Q8:jenkins的编译环境是怎么处理的?实际用户的编译需求和环境都不一样。
A:用户需要清楚你使用的编译环境的基本情况,例如golang的编译环境,容器中的GOPATH是在什么位置。你需要将你的代码ln到什么目录才能进行编译,等这些细节都是需要用户提前知晓。
Q9:jenkins里的有用户权限管理吗?贵公司的CI CD是怎么实现用户隔离的,每个用户只能看到自己的项目。
A:jenkins当中并没有用户权限。公司在研发的产品中,有一个虚拟的概念叫用户组,对应的是k8s中的一个或多个namespaces。管理员将成员用户添加到这个用户组中,组内成员创建的资源(pipeline、集群、服务,等)在组内是可见,用户组来进行逻辑概念上的隔离。
Q10:贵公司jenkins和kubernetes是怎么结合使用的?是什么的部署形式?如何回滚?
A:我看到很多朋友都提问了,jenkins如何跨主机部署或者如何部署到kubernetes集群,如何回滚。jenkins对这方面的能力比较弱,仅仅能够支持kube-api-server的调用而已,如果完全依靠jenkins是很难完成需求,所以我们的产品当中有一个专门对接kubernetes的deploy的模块,一个应用商店的模块,一个封装了jenkins的uflow模块,uflow模块向应用商店获取模板并根据当前编译构建出来的镜像tag号替换模板,并交付给deploy模块创建。回滚和升级都由deploy模块负责。这样各自分开,各司其职。
Q11:多个php 项目,在Docker 应用中,需要逐个拆分吗?一个项目对应一个镜像管理?还是使用文件夹映射的方式构建镜像?
A:多个项目服务是放在一个容器中还是分开容器中,这个并没有强制的限定。但是建议还是分为多个容器进行部署。Docker的理念就是一个容器完成一个单独的事情。
Q12:Jenkins PIpeline input指令可以复杂的参数化么?
A:input是一个比较强大的指令,可以在pipeline的运行过程中确认操作,字符输入,文件上传等功能。详细的可以看下jenkins的pipeline-syntax有使用说明和脚本的生成。
Q13:jenkins自动触发job到build docker image,自动触发是怎么实现的,wedhook 定时触发有没遇到过问题?不能正常触发的。
A:自动触发的原理的原理是,我们在pipeline中配置一个定时器,这个定时器是用cron表达式表示。例如你设置了 &* * * * * &就表示每分钟检查一次,那么检查什么呢,检查每次提交的ID,例如git的commit ID 。只要检测到了这个ID和上一次的不一致就会触发pipeline的构建。从目前使用并没有出现过不能触发的情况。如果出现了请检查是否是配置的错误。
Q14:CD过程中,重造的轮子和开源组件是一个什么样的比例?个人推崇哪个?
A:自己重复造轮子和开源组件,应该如何选择。这个是很有意思的一个问题。因为开发者都说不要重复造轮子,这是因为很多轮子经过了很多项目考验和众多开发者提交代码和fix的bug。这些项目肯定是比自己从头开始造一个轮子更加有效率而且使用风险低,毕竟所有人都想完成工作上的任务早点下班。但是从个人发展来说,有些轮子还是值得自己去制造一次的,这样子你才会了解到这个组件的工作原理,底层的东西。所以我个人的推崇的是,假如你找到了合适接近完美的轮子那就直接用,如果找到了一个可用但是总觉得用起来不太爽的组件,那么你就把轮子造起来吧。
持续发布很多团队想有这样的工具达到这个效果,有些团队觉得不需要。任何工具、流程都需要符合自身团队的实际。从我开始参与团队内的这个和持续发布有关的项目,查看了许多资料,结合团队项目内的实践。给出的一些经验的和见解和大家一起分享,如有错误或者建议欢迎大家及时沟通。谢谢大家的参与。
有容云-构筑企业容器云 www.youruncloud.com
对Docker容器技术或容器生产实施感兴趣的朋友欢迎加群讨论。我们汇集了Docker容器技术落地实施团队精英及业内技术派高人,在线为您分享Docker技术干货。我们的宗旨是为了大家拥有更专业的平台交流Docker实战技术,我们将定期邀请嘉宾做各类话题分享及回顾,共同实践研究Docker容器生态圈。
加微信群方法:
1.关注【有容云】公众号
2.留言”我要加群”
全国免费咨询电话:400-998-1227
CopyRight by 有容云
工作日9:00~18:00Jenkins 用户手册 - 使用 Pipelinefile
Jenkins 用户手册 - 使用 Pipelinefile,本节基于 入门章节 中的信息,并介绍了更多有用的步骤,常见模式,并演示了一些不重要的 Jenkinsfile 示例。
创建 Jenkinsfile 并上传到源代码控制,会带来几个直接的好处:
基于 Pipeline 的代码审查和迭代 审核追踪 Pipeline Pipeline 的来源单一真实,可以由项目的多个成员查看和编辑
Pipeline 支持两种语法,声明式和脚本式。这两种方法都支持构建持续交付流水线,都可以通过 web UI 或 Jenkinsfile 文件来定义 Pipeline(通常认为创建 Jenkinsfile 文件并上传到源代码控制仓库是最佳实践)。
1. 创建 Jenkinsfile
入门章节中讨论过如何在 SCM 中定义 Pipeline,Jenkinsfile 就是一个包含对 Jenkins Pipeline 定义的文本文件,会上传到版本控制中。下面的 Pipeline 实现了基本的 3 段持续交付流水线。
声明式 Pipeline:
Jenkinsfile (Declarative Pipeline)
pipeline {
stage(&#39;Build&#39;) {
echo &#39;Building..&#39;
stage(&#39;Test&#39;) {
echo &#39;Testing..&#39;
stage(&#39;Deploy&#39;) {
echo &#39;Deploying....&#39;
对应的脚本式 Pipeline:
Jenkinsfile (Scripted Pipeline)
stage(&#39;Build&#39;) {
echo &#39;Building....&#39;
stage(&#39;Test&#39;) {
echo &#39;Building....&#39;
stage(&#39;Deploy&#39;) {
echo &#39;Deploying....&#39;
注意,所有的 Pipeline 都会有这三个相同的 stage,可以在所有项目的一开始就定义好它们。下面演示在 Jenkins 的测试安装中创建和执行一个简单的 Pipeline。
假设项目已经设置好了源代码控制仓库,并且已经按照入门章节的描述在 Jenkins 中定义好了 Pipeline。
使用文本编辑器(最好支持 Groovy 语法高亮显示),在项目根目录中创建 Jenkinsfile。
上面的声明式 Pipeline 示例包含了实现一个持续交付流水线所需的最少步骤。必选指令 agent 指示 Jenkins 为 Pipeline 分配执行程序和工作空间。没有 agent 指令的话,声明式 Pipeline 无效,无法做任何工作!默认情况下 agent 指令会确保源代码仓库已经检出,并且可用于后续步骤。
stage 和 step 指令在声明式 Pipeline 中也是必须的,用于指示 Jenkins 执行什么及在哪个 stage 中执行。
对于脚本式 Pipeline 的更高级用法,上面的示例节点是至关重要的第一步,因为它为 Pipeline 分配了一个执行程序和工作空间。如果没有 node,Pipeline 不能做任何工作!在 node 内,业务的第一阶段是检出此项目的源代码。由于 Jenkinsfile 是直接从源代码控制中提取的,因此 Pipeline 提供了一种快速简单的方法来访问源代码的正确版本:
Jenkinsfile (Scripted Pipeline)
checkout scm
/* .. snip .. */
这个 checkout 步骤会从源代码控制中检查代码,scm 是特殊变量,它指示运行检出步骤,复制触发了这次 Pipeline 运行的指定版本。
1.1 Build 构建
对于大多数项目,Pipeline 流水线的第一步是构建。通常在这一步中,源代码会被组装、编译或打包。Jenkinsfile 不是现有构建工具比如 GNU/Make、Maven、Gradle 等的替代品,而是可以被视为粘合层来将项目开发生命周期的多个阶段(构建,测试,部署等)绑定在一起。
Jenkins 有一系列插件可以调用几乎所有常用的构建工具,这个例子中会在 shell (sh)步骤中调用 make。sh 步骤假设是基于 Unix/,对于基于 Windows 的系统,可以使用 bat。
声明式 Pipeline:
Jenkinsfile (Declarative Pipeline)
pipeline {
stage(&#39;Build&#39;) {
sh &#39;make&#39;
archiveArtifacts artifacts: &#39;**/target/*.jar&#39;, fingerprint: true
对应的脚本式 Pipeline:
Jenkinsfile (Scripted Pipeline)
stage(&#39;Build&#39;) {
sh &#39;make&#39; // 调用 make 命令
archiveArtifacts artifacts: &#39;**/target/*.jar&#39;, fingerprint: true
// 匹配并保存文件供以后检索
sh 步骤调用 make 命令,并且只有在 make 命令返回代码是 0 时才会继续。任何非 0 的返回代码都会使 Pipeline 失败。 archiveArtifacts 捕捉构建时匹配模式 **/target/*.jar 的文件并保存到 Jenkins 主进程供以后检索。
Jenkins 的文件存档不能代替 Artifactory 或 Nexus 等外部文件存储库,应仅用于基本报告和文件存档。
1.2 Test 测试
自动化测试是任何成功的持续交付流程的重要部分。为此,Jenkins 通过一系列插件提供测试记录、报告和可视化功能。在基本层面上,当测试失败时,Jenkins 会记录失败并在 Web UI 中可视化。下面的例子使用由 JUnit 插件提供的 junit step。
下面的例子中,如果测试失败,Pipeline 会被标记为不稳定&unstable&,web UI 中会标记黄球。根据记录的测试报告,Jenkins 还可以提供历史趋势分析和可视化。
声明式 Pipeline:
Jenkinsfile (Declarative Pipeline)
pipeline {
stage(&#39;Test&#39;) {
/* 测试失败时,`make check` 返回非 0 值
尽管如此,使用 `true` 使 Pipeline 继续
sh &#39;make check || true&#39;
junit &#39;**/target/*.xml&#39;
对应的脚本式 Pipeline:
Jenkinsfile (Scripted Pipeline)
/* .. snip .. */
stage(&#39;Test&#39;) {
/* `make check` returns non-zero on test failures,
* using `true` to allow the Pipeline to continue nonetheless
sh &#39;make check || true&#39;
junit &#39;**/target/*.xml&#39;
/* .. snip .. */
sh &#39;make check || true&#39;:使用内联 shell 条件(sh &#39;make || true&#39;)可确保 sh 步骤总是得到值为 0 的退出代码,从而为 junit 步骤提供捕获和处理测试报告的机会。下面的处理失败部分将更详细地介绍这方面的其他方法。 junit &#39;**/target/*.xml&#39;:junit 捕获并关联与包含模式匹配的 JUnit XML 文件(**/target/*.xml)
1.3 Deploy 部署
根据项目或组织的需求,部署可能意味着各种步骤,可能是将构建的文件发布到 Artifactory 服务器,或将代码推送到生产系统等。
在 Pipeline 示例的这一步,构建和测试阶段都已经成功完成了。实质上,&部署&阶段只会在前一阶段成功完成的情况下执行,否则管道将提早退出。
声明式 Pipeline:
Jenkinsfile (Declarative Pipeline)
pipeline {
stage(&#39;Deploy&#39;) {
expression {
currentBuild.result == null || currentBuild.result == &#39;SUCCESS&#39; // 判断是否发生测试失败
sh &#39;make publish&#39;
对应的脚本式 Pipeline:
Jenkinsfile (Scripted Pipeline)
/* .. snip .. */
stage(&#39;Deploy&#39;) {
if (currentBuild.result == null || currentBuild.result == &#39;SUCCESS&#39;) {
// 判断是否发生测试失败
sh &#39;make publish&#39;
/* .. snip .. */
访问 currentBuild.result 变量使得 Pipeline 可以判断是否发生测试失败的情况。失败时这个变量的值是 UNSTABLE。
假设 Jenkins Pipeline 示例中的所有内容都已成功执行,每个成功的 Pipeline 运行都将存档关联的构建文件、测试结果的报告以及 Jenkins 中完整的控制台输出。
脚本式 Pipeline 可以报考测试条件(上面例子中有)、循环、try/catch/finally 代码块和函数。下一部分会讲解脚本式 Pipeline 高级语法。
2. 使用 Jenkinsfile
以下部分提供详细信息用于处理:
Jenkinsfile 文件中的具体 Pipeline 语法 Pipeline 语法的特性和函数,这对于构建应用程序或 Pipeline 项目非常重要。
2.1 字符串插值(interpolation)
Jenkins Pipeline 使用与 Groovy 相同的规则进行字符串插值。Groovy的字符串插值支持可能会让许多这门语言的新手感到困惑。虽然 Groovy 支持使用单引号或双引号来声明一个字符串,例如:
def singlyQuoted = &#39;Hello&#39;
def doublyQuoted = &World&
只有双引号字符串才支持基于美元符号($)的字符串插值,例如:
def username = &#39;Jenkins&#39;
echo &#39;Hello Mr. ${username}&#39;
echo &I said, Hello Mr. ${username}&
Hello Mr. ${username}
I said, Hello Mr. Jenkins
了解如何使用字符串插值对于使用 Pipeline 的一些更高级功能至关重要。
2.2 使用环境变量
Jenkins Pipeline 通过全局变量 env 暴露环境变量,该变量可以在 Jenkinsfile 中的任何地方使用。假设 Jenkins 主机在 localhost:8080 运行,在 Jenkins Pipeline 中可访问的环境变量的完整列表记录在 localhost:8080/pipeline-syntax/globals#env 中,包括:
BUILD_ID:当前的构建 ID,对于在 Jenkins 1.597 以上的版本中创建的构建,与 BUILD_NUMBER 相同。 JOB_NAME:当前构建的项目名,例如&foo&或&foo/bar&。 JENKINS_URL:完整的 Jenkins URL,例如 example.com:port/jenkins/(注意:只有在&System Configuration&中设置了 Jenkins URL 后才可用)。
引用或使用这些环境变量可以像访问 Groovy Map 中的任何键一样,例如:
声明式 Pipeline:
Jenkinsfile (Declarative Pipeline)
pipeline {
stage(&#39;Example&#39;) {
echo &Running ${env.BUILD_ID} on ${env.JENKINS_URL}&
对应的脚本式 Pipeline:
Jenkinsfile (Scripted Pipeline)
echo &Running ${env.BUILD_ID} on ${env.JENKINS_URL}&
2.2.1 设置环境变量
在 Jenkins Pipeline 中设置环境变量时,声明式和脚本式 Pipeline 中完全不同。
声明式 Pipeline 支持 environment 指令,而脚本式 Pipeline 中必须使用 withEnv 步骤。
声明式 Pipeline:
Jenkinsfile (Declarative Pipeline)
pipeline {
environment { // 顶级上下文中的 environment 指令会用于 Pipeline 中的所有步骤
CC = &#39;clang&#39;
stage(&#39;Example&#39;) {
environment { // stage 中的 environment 指令仅用于当前 stage 中的步骤
DEBUG_FLAGS = &#39;-g&#39;
sh &#39;printenv&#39;
对应的脚本式 Pipeline:
Jenkinsfile (Scripted Pipeline)
/* .. snip .. */
withEnv([&PATH+MAVEN=${tool &#39;M3&#39;}/bin&]) {
sh &#39;mvn -B verify&#39;
2.3 处理凭证
Jenkins 中配置的凭证 可以在 Pipeline 中处理用于临时用途。关于在 Jenkins 中使用凭证的资料请参考 Using credentials 页面。
2.3.1 处理密码文本、用户名密码和密码文件
声明式 Pipeline 语法使用支持密码文本、用户名密码和密码文件的 credentials() 辅助函数(在 environment 指令中使用)。如果要处理其他类型的凭证,参考下一部分。
2.3.1.1 密码文本(Secret text)
下面的 Pipeline 代码示例显示了如何使用和密码文本凭证相关的环境变量创建 Pipeline。
在这个例子中,两个密码文本凭证被分配到不同的环境变量来访问 Amazon Web Services (AWS)。这些凭证将在 Jenkins 中以其各自的证书 ID jenkins-aws-secret-key-id 和 jenkins-aws-secret-access-key 进行配置。
Jenkinsfile (Declarative Pipeline)
pipeline {
// 定义代理详情
environment {
AWS_ACCESS_KEY_ID
= credentials(&#39;jenkins-aws-secret-key-id&#39;)
AWS_SECRET_ACCESS_KEY = credentials(&#39;jenkins-aws-secret-access-key&#39;)
stage(&#39;Example stage 1&#39;) {
stage(&#39;Example stage 2&#39;) {
可以引用这两个环境变量凭证(在这个 Pipeline 的 environment 指令中定义),在这个阶段(stege)的步骤(step)中使用 $AWS_ACCESS_KEY_ID 和 $AWS_SECRET_ACCESS_KEY。例如,可以授权 AWS 使用和这个凭证变量相关的密码文本凭证。
要维护这些证书的安全性和匿名性,如果你尝试从 Pipeline 内检索这些凭证变量的值(例如,echo $AWS_SECRET_ACCESS_KEY),则 Jenkins 仅返回值 **** 以防止秘密信息写入到控制台输出和任何日志。凭证 ID 本身的任何敏感信息(例如用户名)在 Pipeline 运行的输出中也以 **** 的形式返回。
在这个 Pipeline 示例中,分配到两个 AWS_... 环境变量的凭证都对 Pipeline 有全局作用域,因此这些凭证变量可以在这个阶段的每个步骤中使用。然而,如果这个 Pipeline 中的 environment 指令被移到某个特定的 stage 中(参考下面用户名和密码 Pipeline 示例),那这些 AWS_... 环境变量将只能在这个 stage 中使用。
2.3.1.2 用户名密码
下面的 Pipeline 代码片段是关于如何创建使用与用户名密码相关的环境变量的 Pipeline 示例。
在此示例中,用户名和密码凭证分配给环境变量,以访问组织中常用帐户或团队中的 Bitbucket 仓库;这些凭证将在 Jenkins 中用凭证 ID jenkins-bitbucket-common-creds 进行配置。
在 environment 指令中设置凭证环境变量时:
environment {
BITBUCKET_COMMON_CREDS = credentials(&#39;jenkins-bitbucket-common-creds&#39;)
这实际上设置了三个环境变量:
BITBUCKET_COMMON_CREDS - 包含用冒号分割的用户名和密码,格式为 username:password BITBUCKET_COMMON_CREDS_USR - 只包含用户名的额外变量 BITBUCKET_COMMON_CREDS_PSW - 只包含密码的额外变量
按照惯例,环境变量的变量名通常以大写形式指定,单个单词由下划线分隔。但是,也可以使用小写字符指定任何合法的变量名称。请记住,由credentials() 方法创建的附加环境变量将始终附加 _USR 和 _PSW(下划线后跟三个大写字母)。
下面的代码片段显示了完整的 Pipeline:
Jenkinsfile (Declarative Pipeline)
pipeline {
// Define agent details here
stage(&#39;Example stage 1&#39;) {
environment {
BITBUCKET_COMMON_CREDS = credentials(&#39;jenkins-bitbucket-common-creds&#39;)
stage(&#39;Example stage 2&#39;) {
凭证环境变量(在这个 Pipeline 的 environment 指令中定义的)可以在所有阶段的每个 step 中用下面的语法引用:
$BITBUCKET_COMMON_CREDS $BITBUCKET_COMMON_CREDS_USR $BITBUCKET_COMMON_CREDS_PSW
例如,在这里可以用分配给这些凭证变量的用户名和密码对 Bitbucket 进行身份验证。
为了维护这些凭证的安全性和匿名性,如果你尝试在 Pipeline 内检索这些凭证变量的值,则以上密码文本示例中描述的相同行为也适用于这些用户名和密码凭证变量类型(即返回 ****)。
在这个 Pipeline 示例中,分配给这三个环境变量的凭证的作用域仅限于当前阶段。然而,如果将当前 Pipeline 中的 environment 指令移到顶级上下文中,那就可以在所有的 stage 阶段中使用了。
2.3.1.3 密码文件(Secret files)
就管道而言,密码文件的处理方式与上文中的密码文本完全相同。
密码文本和密码文件凭证之间的唯一区别:对于密码文本,凭证本身直接输入 Jenkins,而对于密码文件,凭证最初存储在文件中,然后上传到 Jenkins。
与密码文本不同,密码文件符合以下凭证:
过于笨重,无法直接传到 Jenkins 二进制格式,例如 GPG 文件
2.3.2 其他类型的凭证
如果需要在 Pipeline 中为密码文本、用户名密码或密码文件以外的任何内容设置凭据 - 即 SSH 密钥或证书,则可以通过 Jenkins 的 classic UI 使用 Jenkins 的片段生成器功能。
要为你的项目使用 Snippet Generator 片段生成器:
在 Jenkins 的首页(例如 Jenkins classic UI 的 Dashboard)点击你的 Pipeline 项目名。 点击左侧的 Pipeline Syntax 并确保 Snippet Generator 链接在左上角以粗体显示。(如果没有,请点击链接) 从 Sample Step 字段中,选择 withCredentials: Bind credentials to variables。
在 Bindings 中点击 Add,从下拉列表中选择:
SSH User Private Key - 处理 SSH 公私钥对凭证,可以指定为:
Key File Variable - 要被绑定到这些凭证的环境变量的名字。Jenkins 实际上将此临时变量分配给 SSH 公私钥对认证过程中所需的私钥文件的安全位置。 Passphrase Variable - 可选,要被绑定到与 SSH 公私钥对关联的密码的环境变量的名称。 Username Variable - 可选,要被绑定到与 SSH 公私钥对关联的用户名的环境变量的名称。 Credentials - 选择存储在 Jenkins 中的 SSH 公私钥凭证。 该字段的值是由 Jenkins 写入生成的代码片段中的凭证 ID。 Certificate - 处理 PKCS#12 证书,可以指定为:
Keystore Variable - 要被绑定到这些凭据的环境变量的名称。Jenkins 实际上将此临时变量分配给证书认证过程中所需的证书密钥库的安全位置。 Password Variable - 可选,要被绑定到和证书相关的密码的环境变量的名字。 Alias Variable - 可选,要被绑定到和证书相关的唯一别名的环境变量的名字。 Credentials - 选择 Jenkins 中存储的证书凭证。 该字段的值是由 Jenkins 写入生成的代码片段中的凭证 ID。 Docker client certificate - 处理 Docker 主机证书认证。
点击 Generate Pipeline Script ,Jenkins 会为你指定的凭证生成,可以复制粘贴到你的声明式或脚本式 Pipeline 代码中。
Credentials 字段显示了 Jenkins 中配置的凭证名称。然而,在点击 Generate Pipeline Script 之后,这些值会转换为凭证 ID。 要在一个 withCredentials( ... ) { ... } Pipeline 步骤的代码片段整合多个凭证,参考下一小节。
SSH User Private Key 示例
withCredentials(bindings: [sshUserPrivateKey(credentialsId: &#39;jenkins-ssh-key-for-abc&#39;, \
keyFileVariable: &#39;SSH_KEY_FOR_ABC&#39;, \
passphraseVariable: &#39;&#39;, \
usernameVariable: &#39;&#39;)]) {
// some block
The optional passphraseVariable and usernameVariable definitions can be deleted in your final Pipeline code.
Certificate 示例
withCredentials(bindings: [certificate(aliasVariable: &#39;&#39;, \
credentialsId: &#39;jenkins-certificate-for-xyz&#39;, \
keystoreVariable: &#39;CERTIFICATE_FOR_XYZ&#39;, \
passwordVariable: &#39;XYZ-CERTIFICATE-PASSWORD&#39;)]) {
// some block
可选的 aliasVariable 和 passwordVariable 变量定义可以在你最终的 Pipeline 代码中删除。
下面的代码片段是完整的 Pipeline 示例,包括了上面提到的 SSH User Private Key 和 Certificate 片段:
Jenkinsfile (Declarative Pipeline)
pipeline {
// define agent details
stage(&#39;Example stage 1&#39;) {
withCredentials(bindings: [sshUserPrivateKey(credentialsId: &#39;jenkins-ssh-key-for-abc&#39;, \
keyFileVariable: &#39;SSH_KEY_FOR_ABC&#39;)]) {
withCredentials(bindings: [certificate(credentialsId: &#39;jenkins-certificate-for-xyz&#39;, \
keystoreVariable: &#39;CERTIFICATE_FOR_XYZ&#39;, \
passwordVariable: &#39;XYZ-CERTIFICATE-PASSWORD&#39;)]) {
stage(&#39;Example stage 2&#39;) {
①:在这个步骤中,可以通过 $SSH_KEY_FOR_ABC 引用凭证环境变量。例如,可以使用其已经配置的 SSH 公私钥对凭证向 ABC 应用授权,该应用的 SSH User Private Key 文件已经分配到了 $SSH_KEY_FOR_ABC 变量。
②:在这个步骤中,可以通过 $CERTIFICATE_FOR_XYZ 和 $XYZ-CERTIFICATE-PASSWORD 引用凭证环境变量。例如,可以使用其已经配置的证书凭证向 XYZ 应用授权,其证书的密钥库文件和密码分别分配给变量 $CERTIFICATE_FOR_XYZ 和 $XYZ-CERTIFICATE-PASSWORD。
③:在这个 Pipeline 示例中,分配给 $SSH_KEY_FOR_ABC、$CERTIFICATE_FOR_XYZ 和
$XYZ-CERTIFICATE-PASSWORD 环境变量的凭证仅作用域对应的 withCredentials( ... ) { ... } 步骤。因此无法在第二个阶段中使用。
为了维护这些凭证的安全性和匿名性,如果你尝试在 within these withCredentials( ... ) { ... } 内检索这些凭证变量的值,则以上密码文本示例中描述的相同行为也适用于这些用户名和密码凭证变量类型(即返回 ****)。
在代码段生成器 Snippet Generator 中使用 Sample Step 字段的 withCredentials: Bind credentials to variables 选项时,只能从凭证字段列表中选择当前 Pipeline 项目有权访问的凭证。虽然可以手动为 Pipeline 编写 withCredentials( ... ) { ... } 步骤,但建议使用 Snippet Generator 以避免指定超出此 Pipeline 项目范围的凭据, 这在运行时会使这个步骤失败。 也可以使用 Snippet Generator 来生成 withCredentials( ... ) { ... } 来处理密码文本、用户名密码和密码文件。然而,如果只需要处理这些类型的凭证,建议为了 Pipeline 代码的可读性使用上一章节描述的相关过程。
2.3.2.1 一个步骤中整合多个凭证
使用 Snippet Generator 时,可以通过下面步骤在一个 withCredentials( ... ) { ... } 步骤中使用多个凭证:
在 Jenkins 首页(例如 classic UI 的 Dashboard),点击你的 Pipeline 项目名。 点击左侧的 Pipeline Syntax 并确保 Snippet Generator 链接在左上角变成粗体(若没有,点击这个链接)。 在 Sample Step 字段中选择 withCredentials: Bind credentials to variables。 点击 Bindings 下面的 Add。 通过下列列表选择要添加到 withCredentials( ... ) { ... } 步骤的凭证类型。 指定凭证的 Bindings 详情。参考上面部分的凭证类型。 为每个要添加到 withCredentials( ... ) { ... } 步骤的凭证重复 Click Add &。 点击 Generate Pipeline Script 生成用于 withCredentials( ... ) { ... } 步骤的最终片段。
2.4 处理参数
声明式 Pipeline 支持开箱即用的参数,允许 Pipeline 通过 parameters 指令 在运行时接受用户指定的参数。使用脚本式 Pipeline 配置参数是通过 properties 步骤完成的,该步骤可以在代码片段生成器中找到。
如果通过 Build with Parameters 选项来配置你的流水线接受参数,那些参数可以作为 params 变量的成员来访问。
假设在 Jenkinsfile 中已经配置了一个名为&Greeting&的字符串参数,可以通过 ${params.Greeting} 来访问该参数:
声明式 Pipeline:
Jenkinsfile (Declarative Pipeline)
pipeline {
parameters {
string(name: &#39;Greeting&#39;, defaultValue: &#39;Hello&#39;, description: &#39;How should I greet the world?&#39;)
stage(&#39;Example&#39;) {
echo &${params.Greeting} World!&
对应的脚本式 Pipeline:
Jenkinsfile (Scripted Pipeline)
properties([parameters([string(defaultValue: &#39;Hello&#39;, description: &#39;How should I greet the world?&#39;, name: &#39;Greeting&#39;)])])
echo &${params.Greeting} World!&
2.5 处理失败
声明式 Pipeline 默认通过 post 部分 支持强大的故障处理,post 中支持 always、unstable、success、failure 和 changed 等各种不同的结果状态(post conditions)。教程的 Pipeline 语法部分 提供了关于如何使用各种 post condition 的更多细节。
声明式 Pipeline:
Jenkinsfile (Declarative Pipeline)
pipeline {
stage(&#39;Test&#39;) {
sh &#39;make check&#39;
junit &#39;**/target/*.xml&#39;
mail to: , subject: &#39;The Pipeline failed :(&#39;
对应的脚本式 Pipeline:
Jenkinsfile (Scripted Pipeline)
/* .. snip .. */
stage(&#39;Test&#39;) {
sh &#39;make check&#39;
junit &#39;**/target/*.xml&#39;
/* .. snip .. */
脚本式 Pipeline 依赖于 Groovy 的内置 try/catch/finally 语义来处理 Pipeline 执行过程中的失败。
上面的一个示例中,sh 步骤被设置为永不返回非 0 代码(sh &#39;make check || true&#39;)。这种方法虽然有效,但意味着以下阶段需要检查 currentBuild.result 以确定是否存在测试失败。
处理这个问题的另一种方法是通过使用一系列 try/finally 代码块保留 Pipeline 中失败的早期退出行为,同时仍然给 junit 提供捕获测试报告的机会。
2.6 使用多个代理
前面的所有例子都只使用了一个代理。这意味着 Jenkins 会在任何可用的地方分配一个执行者,而不管它是如何标记或配置的。这种行为可以被重写,而且 Pipeline 允许在同一个 Jenkinsfile 中使用 Jenkins 环境中的多个代理,这对于更高级的用例(例如在多个平台上执行构建/测试)可能会有所帮助。
在下面的例子中,将在一个代理上执行&构建&阶段,而在&测试&阶段期间,将分别在两个后续代理上标记&linux&和&windows&使用这个构建的结果。
声明式 Pipeline:
Jenkinsfile (Declarative Pipeline)
pipeline {
agent none
stage(&#39;Build&#39;) {
checkout scm
sh &#39;make&#39;
stash includes: &#39;**/target/*.jar&#39;, name: &#39;app&#39;
stage(&#39;Test on Linux&#39;) {
label &#39;linux&#39;
unstash &#39;app&#39; // ③
sh &#39;make check&#39;
junit &#39;**/target/*.xml&#39;
stage(&#39;Test on Windows&#39;) {
label &#39;windows&#39;
unstash &#39;app&#39;
bat &#39;make check&#39; // ④
junit &#39;**/target/*.xml&#39;
对应的脚本式 Pipeline:
Jenkinsfile (Scripted Pipeline)
stage(&#39;Build&#39;) {
checkout scm
sh &#39;make&#39;
stash includes: &#39;**/target/*.jar&#39;, name: &#39;app&#39; // ①
stage(&#39;Test&#39;) {
node(&#39;linux&#39;) { // ②
checkout scm
unstash &#39;app&#39; // ③
sh &#39;make check&#39;
junit &#39;**/target/*.xml&#39;
node(&#39;windows&#39;) {
checkout scm
unstash &#39;app&#39;
bat &#39;make check&#39; // ④
junit &#39;**/target/*.xml&#39;
①:stash 这一步允许捕获匹配模式(**/target/*.jar)的文件以在同一 Pipeline 内重用。一旦 Pipeline 完成执行,隐藏的文件将从 Jenkins 主文件中删除。
②:agent/node 中的参数允许任何有效的 Jenkins 标签表达式。更多详细信息,请参阅 管道语法部分。
③:unstash 将从主 Jenkins 中检索名为&stash&并放入 Pipeline 的当前工作空间(unstash will retrieve the named &stash& from the Jenkins master into the Pipeline&s current workspace)。
④:bat 脚本允许在基于 Windows 的平台上执行批处理脚本。
2.7 可选的步骤参数
Pipeline 遵循 Groovy 语言约定,允许在方法参数周围省略括号。
许多 Pipeline 步骤还使用命名参数(named-parameter)语法作为在 Groovy 中创建 Map 的简写,使用的语法为 [key1:value1,key2:value2]。Making statements like the following functionally equivalent:
git url: &#39;git://example.com/amazing-project.git&#39;, branch: &#39;master&#39;
git([url: &#39;git://example.com/amazing-project.git&#39;, branch: &#39;master&#39;])
为方便起见,当调用只有一个参数的步骤(或只有一个必需参数)时,参数名称可以省略,例如:
sh &#39;echo hello&#39; /* short form
sh([script: &#39;echo hello&#39;])
/* long form */
2.8 高级脚本式 Pipeline
脚本式 Pipeline 是一种基于 Groovy 的领域特定语言,大多数 Groovy 语法都可以不用修改直接用于脚本式 Pipeline。
2.8.1 并行执行
上一个示例以线性顺序跨两个不同平台的测试执行。在实践中,如果执行 make check 需要30分钟完成,那么&测试&阶段现在需要 60 分钟才能完成!
幸运的是,Pipeline 具有内置的功能,可以通过适当命名的并行步骤并行执行部分脚本式 Pipeline。
重构上面的例子以使用并行步骤:
Jenkinsfile (Scripted Pipeline)
stage(&#39;Build&#39;) {
/* .. snip .. */
stage(&#39;Test&#39;) {
parallel linux: {
node(&#39;linux&#39;) {
checkout scm
unstash &#39;app&#39;
sh &#39;make check&#39;
junit &#39;**/target/*.xml&#39;
windows: {
node(&#39;windows&#39;) {
/* .. snip .. */
假设在 Jenkins 环境中资源足够,它们现在将并行执行&linux&和&windows&标记节点上的测试。

我要回帖

更多关于 pipeline 什么功能 的文章

 

随机推荐