编写现代软件很困难,因为在软件交付中涉及许多团队,包括开发人员、质量保证、运维、产品所有者、客户支持和销售。需要有一个流程,通过该流程,软件的开发是以自动化的方式进行的。持续集成和持续交付的过程将有助于确保交付给最终用户的软件具有最高质量,并经过CI/CD流水线的一系列检查。在本书中,您将学习如何使用JenkinsCI,以及如何编写自由风格脚本、插件,以及如何使用更新的Jenkins2.0UI和流水线。您将了解TravisCI的UI、TravisCLI、高级日志记录和调试技术,以及TravisCI的最佳实践。您还将学习CircleCI的UI、CircleCLI、高级日志记录和调试技术,以及CircleCI的最佳实践。在整本书中,我们将讨论诸如容器、安全性和部署等概念。
本书适用于系统管理员、质量保证工程师、DevOps和站点可靠性工程师。您应该了解Unix编程、基本编程概念和Git等版本控制系统。
第一章,自动化测试的CI/CD,介绍了自动化的概念,并解释了与手动流程相比自动化的重要性。
第二章,持续集成的基础知识,介绍了持续集成的概念,解释了软件构建是什么,并介绍了CI构建实践。
第三章,持续交付的基础知识,介绍了持续交付的概念,特别是解释了软件交付、配置管理、部署流水线和脚本编写的问题。
第五章,Jenkins的安装和基础知识,帮助您在Windows、Linux和macOS操作系统上安装JenkinsCI。您还将学习如何在本地系统上运行Jenkins以及如何管理JenkinsCI。
第六章,编写自由风格脚本,介绍了如何在Jenkins中编写自由风格脚本,以及如何配置Jenkins中的自由风格脚本,包括添加环境变量和调试自由风格脚本中的问题。
第七章,开发插件,解释了软件中插件的概念,如何使用Java和Maven创建Jenkins插件,并介绍了Jenkins插件生态系统。
第八章,使用Jenkins构建流水线,详细介绍了Jenkins2.0,并解释了如何在Jenkins2.0(BlueOcean)中导航,还详细介绍了新的流水线语法。
第九章,TravisCI的安装和基础知识,向您介绍了TravisCI,并解释了TravisCI与JenkinsCI之间的区别。我们将介绍Travis生命周期事件和TravisYML语法。我们还将解释如何开始并在GitHub上设置。
第十章,TravisCICLI命令和自动化,向您展示如何安装TravisCICLI,详细解释CLI中的每个命令,展示如何在TravisCI中自动化任务,并解释如何使用TravisAPI。
第十一章,“TravisCIUI日志和调试”,详细解释了TravisWebUI,并展示了TravisCI中日志和调试的高级技术。
第十二章,“CircleCI的安装和基础知识”,帮助您在Bitbucket和GitHub上设置CircleCI,并展示如何使用CircleCIWebUI。我们还将解释CircleCIYML语法。
第十三章,“CircleCICLI命令和自动化”,帮助您安装CircleCICLI,并解释CLI中的每个命令。我们还将介绍CircleCI中的工作流程以及如何使用CircleCIAPI。
第十四章,“CircleCIUI日志和调试”,详细解释了作业日志,并展示了如何在CircleCI中调试缓慢的构建。我们还将介绍CircleCI中的日志记录和故障排除技术。
第十五章,“最佳实践”,涵盖了编写单元测试、集成测试、系统测试、CI/CD中的验收测试的最佳实践,以及密码和秘密管理的最佳实践。我们还将介绍部署的最佳实践。
为了充分利用本书,您需要熟悉Unix编程概念,比如使用Bashshell、环境变量和shell脚本,并了解Unix的基本命令。您应该熟悉版本控制的概念,知道提交是什么意思,并且需要了解如何使用Git。您应该了解基本的编程语言概念,因为我们将使用诸如Golang、Node.js和Java之类的语言,这些语言将作为我们在CI/CD流水线和示例中使用的构建语言。
您可以按照以下步骤下载代码文件:
下载文件后,请确保使用以下最新版本的解压缩或提取文件夹:
本书中使用了许多文本约定。
CodeInText:表示文本中的代码词、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟URL、用户输入和Twitter句柄。这是一个例子:“Chocolatey安装说明可以在chocolatey.org/install找到。”
代码块设置如下:
RulesupdatedRulesupdated(v6)粗体:表示一个新术语、一个重要词或屏幕上看到的词。例如,菜单或对话框中的单词会以这种方式出现在文本中。这是一个例子:“点击继续,确保点击同意按钮。”
警告或重要说明会显示为这样。提示和技巧会显示为这样。
在本书中,我们将探讨持续集成(CI)和持续交付(CD)的概念,并使用Jenkins、TravisCI和CircleCI等工具应用这些概念。我们将编写许多实用脚本,并探索真实世界的CI/CD自动化脚本和场景。本章将通过解释一个名为比利·鲍勃机械零件的虚构公司的当前实践来帮助阐明自动化的概念。比利·鲍勃机械零件公司有许多手动流程,并且由于软件发布只由首席开发人员完成,质量保证(QA)和开发团队之间存在一些紧张关系。
本章将涵盖以下主题:
本章将描述一个模拟的手动流程以及手动测试和手动流程中固有的缺陷,并将解释如何使用CI/CD可以大大提高开发人员的生产力。在这种情况下,每个成员都设置了一套手动流程,完成起来非常耗时。此外,如果QA在最新的发布版本中遇到问题,这些步骤必须重新执行。
以下图表显示了一些业务场景:
贝蒂·苏有几名QA工程师,并在星期一早上开始新版本的手动测试周期。贝蒂通知埃里克,她已经在最新版本中发现了几个问题。贝蒂准备了一个Excel电子表格,记录了最新版本引入的问题。在周末结束时,贝蒂已经将最新版本的问题列表分解为关键、高、中和低优先级的错误。
软件缺陷是软件产品中未按预期运行的缺陷。
在发布周期中,埃里克和贝蒂在解决问题时都要重新执行每个步骤。埃里克必须重新打包所有软件组件,并在本地工作站上重新运行所有测试。贝蒂必须重新进行测试周期,因为她必须检查回归,并确保最新的修复不会破坏软件组件中的现有功能。
迈克尔是团队中的初级开发人员,也在进行手动流程。迈克尔从埃里克那里得到了一个问题清单,并开始处理列表中更重要的错误。迈克尔试图解决每个错误,但没有编写任何回归测试,以确保新代码没有破坏现有功能。当迈克尔完成时,他告诉埃里克他这边一切都很好。不幸的是,埃里克在本地工作站上运行所有测试时看到了测试失败。埃里克告诉迈克尔,在处理错误列表时需要更加小心。
QA部门的成员迪伦开始测试新版本的部分,并告诉贝蒂该版本存在几个问题。他已经创建了一个问题清单,并将其发送给贝蒂。不幸的是,迪伦所做的一些工作已经被贝蒂重复,因为他们在两个不同的清单中都突出显示了相似的项目。贝蒂告诉迪伦,QA需要确保不会重复做相同的工作。迪伦回去突出显示他将要测试的版本的部分。
詹妮弗领导客户成功团队,并在质量保证部门通知新版本准备好向客户开放时收到通知。詹妮弗开始准备最新版本功能的视频,并因此向质量保证部门询问新版本的变化。
鲍比是客户成功团队的经验丰富的成员,并开始制作关于最新功能的视频。当发布公司博客上的版本视频时,质量保证部门意识到一些视频错误地说明了仍处于测试版本计划中的功能。詹妮弗现在迅速召集客户成功团队,并要求质量保证部门在将一些功能发送给客户成功团队之前清楚地标记为测试版本。
销售团队一直在通过电子邮件发送销售工程师在与潜在客户会议期间所做的笔记。桑迪手动输入了关于每个潜在客户的详细笔记,并使用Excel电子表格对重要的销售信息进行分类。不幸的是,销售团队将新更改的Excel电子表格发送给销售部门。有时会出现混乱,因为销售工程师会打开旧的Excel文档,并错误地向其他销售工程师提供过时的信息。
UI/UX团队倾向于使用大量的模型和线框图。通常,在原型阶段,UI/UX工程师会在模型中嵌入注释,详细说明验证状态和页面交互。维克多在其中一个线框图中看到一个注释,并意识到线框图中嵌入了重要的页面逻辑。维克多询问UI/UX团队是否可以与开发团队共享注释。UI/UX团队还使用艺术板并为每个功能工作创建ZIP文件。例如,桑迪被分配了关于新页面UI交互的工作,并一直在做详细的笔记。UI/UX团队的许多工作往往是高度视觉化的,颜色代表着不同的含义。工作的视觉方面往往意味着在UI流程的各个阶段应该发生某些动作。开发人员往往处理更具体的项目,因此并不总是清楚自然流程应该发生什么。例如,如果删除一个项目,是否会弹出一个模态,即一个小窗口进行确认,还是立即删除一个项目?提交表单时,UI是否以特定颜色显示错误指示,以另一种颜色显示警告?验证应该放在什么位置?有时,UI交互流程没有详细描述,开发人员必须与UI/UX来回沟通。记录决策文件中的决定原因是很重要的。
贝蒂·苏给维克多发了一份按优先级分类的问题列表。必须首先处理更高优先级的问题,而较低优先级的问题则稍后处理。维克多拿到了最新发布的问题列表,并通知开发团队他们必须立即停止正在进行的新功能工作,并开始修复最新发布的问题。大卫是团队中的一名高级开发人员,他感到沮丧,因为他之前进展顺利,现在又在匆忙地重新适应一个月前的工作。
迈克尔是团队中的一名初级开发人员,对代码库还不太熟悉,他担心列表上的一个更高优先级的问题。迈克尔急忙解决了更高优先级的问题,但没有考虑编写任何回归测试用例。迈克尔迅速为他的高优先级工单编写了一个补丁,并将补丁发送给了维克多。维克多很快发现了迈克尔的补丁中的回归和破损的测试用例。迈克尔不知道他应该编写回归测试用例来确保没有回归。
发布新补丁的流程没有得到适当的记录,而迈克尔这样的新开发人员经常会产生破坏现有工作的回归。维克多教迈克尔回归测试的概念,迈克尔迅速编写了带有回归测试用例的软件补丁。
一旦维克多准备好了所有的新软件补丁,他就开始进行热修复发布,并在本地机器上重新运行所有测试。贝蒂得到了最新发布的新ZIP文件,并再次开始手动测试流程。QA部门正在手动测试产品的部分,因此测试所有产品部分是一个耗时的任务。贝蒂发现了最新发布的一些问题,并给维克多发送了一个较小的列表,以便在本周晚些时候开始处理。
维克多此刻承认感到沮丧,他把所有开发人员都召集到一个房间里,并说除非所有问题都得到解决,否则谁也不能离开。在度过了一个漫长的晚上后,所有最新的问题都得到了解决,维克多让每个人回家。第二天早上,维克多打包了最新的发布,并给贝蒂发送了一个新的ZIP文件。贝蒂在上一次测试周期后有些担心,但她很高兴所有的错误都得到了解决,并给予了QA的批准,并告诉维克多最新发布已经准备就绪。开发团队和QA团队一起庆祝了一周工作的结束,并享受了公司赞助的午餐,然后回家过周末。
在QA部门测试热修复时,一些QA团队成员的工作出现了重叠。Dillon感到沮丧,因为他的一些工作与Betty的工作重叠。QA部门没有自动化,因此每次发布都需要手动完成所有工作,无论是补丁还是常规发布,QA都必须重新测试UI的所有部分。QA团队的新成员Nate问Dillon是否有比手动测试更好的工作方式,但被告知这些做法已经在QA部门实施。
销售工程师领队之一的Victor正在进行公司演示,并向潜在客户展示导出PDF功能。在演示过程中,Victor点击导出PDF功能,出现了一个显眼的错误消息。Victor迅速转移到产品的另一个方面,称这是一个暂时的故障,并表示他将在另一个演示中展示这个功能。Victor发现其中一个开发人员在后端服务中进行了一个本应该是简单更改的操作,却在生产环境中破坏了导出PDF功能。Victor发现潜在客户已决定选择另一种软件解决方案,现在他显然很沮丧,因为他指望这位新客户来获得年终奖金。
UI/UX团队的成员Samantha被告知她的一份模型缺少验证流程。Samantha在功能Z的原型设计阶段寻求澄清,被告知页面不需要任何验证,但David认为需要验证流程。Samantha显然很沮丧,决定休息一天,现在David在功能Z的工作上落后了进度。
QA的BettySue和开发团队的John之间有双向沟通。在寻找自动化帮助的领域时,沟通是至关重要的。随着各方之间的互动次数增加,参与方对手动流程的意识也在增加。手动流程一直隐藏着,直到更多的参与方,如营销、销售、客户成功和开发团队开始更频繁地合作。开发人员特别适合发现手动流程,因为非开发人员并不总能意识到一个流程是手动的,可以被自动化。
这是一个名为JohnnyTheAutomationBot的插图,用于描述公司的不同部门。Johnny的每个肢体代表公司的一个不同部门:
JohnnyTheAutomationBot是一个可以极大受益于自动化流程的领域的插图。自动化可以被视为一种程序或系统,其中机器完成了人类通常会做的工作。自动化需要了解正在进行的手动流程,与其他部门进行沟通,并找出哪些流程是手动进行的。CI和CD,正如我们将在本书中看到的,是极大增强公司生产力和流程的过程,因为它们消除了开发人员的假设和特定环境设置。
约翰尼自动化机器人的每个部分都有一个可以自动化的领域。销售部门目前正在向销售团队发送Excel工作表,并且很难跟上其他销售工程师所做的更改。约翰尼自动化机器人建议销售工程师有一个简单的方法将销售信息上传到公司内部网络,以更好地跟踪销售信息。约翰尼建议开发团队编写一个Excel集成,销售工程师可以轻松地将新的销售数据上传到公司内部网络。例如,可以添加一个菜单选项,连接到公司的API端点,自动将新的Excel更改上传到公司内部网络页面,该页面具有最新的销售信息。
质量保证部门正在手动测试产品,而手动测试是一项耗时且容易出错的活动。约翰尼建议质量保证部门开始使用SeleniumWebDriver编写验收测试。Selenium是一个浏览器自动化工具,质量保证部门可以使用诸如Python之类的语言来编写验收测试。约翰尼表示,使用Selenium编写自动化测试的优势在于可以编写一次并反复重用。这将带来额外的好处,即这些测试可以连接到CI/CD流水线中,我们将在本书的后面看到。
维克多意识到手动流程正在破坏生产力,并且手动流程具有明显的劣势。维克多可以在发布周期中引入一个自动化系统,任何开发人员都可以通过一个单击部署按钮来运行。与维克多目前正在做的在本地工作站上运行所有测试不同,每个软件构建都可以推送到诸如GitHub之类的版本控制系统,并且所有测试都可以在CI环境(如Jenkins)上运行,并且开发人员可以自动收到测试是否通过或失败的通知。例如,布鲁斯是团队中的一名新开发人员,他可以快速阅读开发人员文档,并在几乎没有指导的情况下开始进行下一个发布。约翰自动化机器人对这种做法表示赞许。
贝蒂也有机会自动化手动测试过程。使用诸如BrowserStack之类的工具,贝蒂可以编写一系列测试脚本,测试产品的每个部分。在一个小时内,贝蒂可以在测试环境中运行一套验收测试,并让维克多知道发布中的最新问题。维克多可以开始将问题分配给开发团队,并开始编写回归测试用例,以确保当前构建中没有回归。维克多对最新更改的工作原理感到自信,可以指导贝蒂到一个新的URL,她可以下载最新的软件发布。Johnny自动化机器人指出,创建ZIP文件并通过电子邮件发送的旧做法并不是一个好的做法,因为每次都需要额外的步骤,如果发送了错误的ZIP文件,可能会出现错误。约翰建议QA部门有一个专用的URL,所有最新的发布都在那里,并且每个发布都有版本和特定信息,比如热修复。例如,最新的热修复可以是v5.1.0-hotfix1,因此,对于每个热修复,QA部门都会有一个压缩文件,其中包含最新的构建和一个说明符,比如热修复。如果这个构建是一个常规构建,那么它可以被命名为v5.1.0。
维克多发现QA部门有一个BrowserStack账户。BrowserStack提供对整套浏览器和移动客户端的访问,这可以帮助自动化UI的负载测试。负载测试是使用开发团队用于特殊场景(如负载测试)的自定义服务器进行的。约翰自动化机器人建议使用诸如BrowserStack之类的服务,或者拥有一个可以提供必要资源进行负载测试的自定义服务。
维克多发现QA团队在测试开发团队编写的电子邮件服务时遇到了问题。约翰自动化机器人建议开发团队确保QA拥有可以帮助使用电子邮件服务的脚本。维克多告诉贝蒂,新的电子邮件服务正在代理到SendGrid服务,并且开发团队已经编写了一系列QA可以使用的脚本。这些脚本有助于编写测试电子邮件,并可以帮助QA测试在失败条件下会发生什么。
UI/UX团队正在将模型上传到Sketch-Sketch是一个原型工具-并嵌入有关页面可能的验证状态和流程的注释。这些注释非常详细,对开发团队在公司冲刺中开始功能工作时非常有帮助。Johnny自动化机器人建议开发团队编写一个插件,可以帮助UI/UX团队轻松共享这些信息。维克多决定创建一个Sketch插件,该插件可以创建一个带有嵌入式注释的PDF,UI/UX团队在原型制作完成后可以通过电子邮件发送给开发团队。这个插件对UI/UX团队来说很容易安装,因为他们只需双击文件,它就会自动安装插件。访问PDF和嵌入的注释将帮助开发人员了解新功能的用例和UI流程。
文森特,一位主要销售工程师,已经与开发团队沟通,他需要及时了解产品的流程变化,特别是在向潜在客户介绍公司路线图上的新功能时。约翰尼自动化机器人建议开发团队利用Git提交日志,其中包含有关最新功能更改的详细信息。维克多编写了一个脚本,用于抓取Git提交日志并编写一个包含所有最新功能工作的漂亮的Markdown文档。反过来,客户成功团队可以与开发团队合作,使用Markdown文件在公司博客上详细介绍所有最新功能。
这里有一个共同的主题。部门之间的沟通是发现手动流程并创建帮助自动化流程的合作伙伴关系的关键。除非了解手动流程,否则无法进行自动化,有时,唯一的自动化发生的方式是其他部门传达特定的痛点。
UI/UX团队决定创建一个样式指南,开发人员可以在软件产品中查找常见的UI模式。UI/UX团队发现不同页面中使用了许多不同的样式,这导致许多客户感到困惑。例如,零部件供应页面在一个页面上有一个大蓝色保存按钮和一个红色取消按钮,但在供应商详细信息页面上有一个大红色保存按钮和一个蓝色取消按钮。客户因为UI没有统一使用颜色而点击错误的按钮。有时,页面使用确认模态框来添加和删除项目;其他时候,没有确认模态框。UI/UX团队已经开始制作样式指南,并在公司内部网站上创建一个特殊的URL,用于存放实时样式指南。其目的是明确创建和列出页面可用的十六进制颜色,设计产品中的所有按钮,并决定页面上表单的外观和行为。
此外,将有一个特殊的小部件页面,其中包含产品中所有专用小部件的HTML标记和样式嵌入:
这个样式指南具有十六进制颜色值,并嵌入了一些HTML元素和一个切换开关,这是一个专门的小部件,有关闭状态和开启状态。样式指南的目的是让开发人员能够简单地右键单击并复制HTML标记和CSS,并建立统一的UI呈现。这是一种自动化形式,因为开发人员可以简单地重用现有的标记和样式,而不必手动创建HTML和自定义样式,其中统一性最好使用。每当产品的用户不得不猜测该做什么时,你肯定会招致灾难。
由于维克多将CI/CD管道引入了构建中,许多耗时的活动现在都被自动化管道所取代。每当软件被推送到版本控制系统(如Git)等上游时,Jenkins会触发自动构建,运行所有单元测试和集成测试。开发人员可以迅速知道他们编写的代码是否引入了缺陷。记住,维克多不得不整合所有软件补丁,并在本地工作站上手动运行所有测试。这是乏味、耗时且不必要的。
这里的螺栓象征着已经就位的流程;在这种情况下,需要自动化的发布流程。
客户成功团队已经向开发团队传达了一些客户在使用BillyBob'sMachineParts应用程序编程接口(API)服务时遇到问题。客户成功团队询问开发团队是否有任何方法可以帮助新的第三方API消费者上手。为了澄清,API消费者是指正在使用现有API的人,而API提供者是维护实际API服务的人;因此,在这方面,BillyBob'sMachinePart是提供运行API供第三方开发人员使用的API提供者。开发团队告诉客户成功团队,他们一直想要创建一个开发者门户,这将帮助API消费者轻松地使用API。然而,开发团队很难说服高层管理层开发者门户的价值,因为没有人要求这个特定的功能。客户成功团队迅速说服了高层管理层,开发者门户对BillyBob'sMachinePartsAPI消费者来说将是一项巨大的资产,API消费者可以开始使用API服务的数据构建漂亮的仪表板。
QA团队开始处理一个问题单,其中部分限制导致特定的UI错误。特别是,如果客户在产品详细页面上有超过10,000个零件列表,那么产品详细页面会崩溃,而没有任何有用的指示发生了什么。开发团队发现QA团队正在手动在新产品页面上创建新产品。开发团队通过让QA了解一个可以通过管理员端点来程序化创建语音邮件的端点来帮助QA团队。开发团队帮助编写了一个脚本,通过程序化生成新零件,从而节省了QA团队手动创建零件的耗时任务。
为了实现自动化,必须打破团队之间的沟通障碍。有时,不同的团队可能认为他们在同一个页面上,但实际上他们在谈论不同的事情。
打破沟通障碍是很重要的,为了解决误解:
有趣的是,在发布周期内仍然有更多的自动化空间。维克多询问贝蒂关于一些在源代码控制中的验收测试。维克多意识到他可以将验收测试集成到CI环境中,并创建一个次要构建,每晚运行所有验收测试,并且QA将在每天早上得到一份详细最新失败的报告。然后QA可以在每天早上审查失败的验收测试,并通知开发团队特定功能x已经出现问题,例如零件供应页面,正在处理这个新功能的开发人员需要重新检查新的业务逻辑。
大卫开始与UI/UX团队交谈,并发现新公开的API端点和构建新页面之间存在瓶颈。前端开发人员在这些页面中模拟数据,并经常对意外的JSON负载感到惊讶。前端开发人员有时要等待几周才能发布API端点,而他们开始模拟数据而不是坐等,这带来了意想不到的后果,他们开始假设数据模型将会是什么样子,这反过来使得更改页面变得更加困难。大卫让维克多知道存在可以快速为端点搭建数据模型并为API端点提供当前数据模型的工具。大卫开始使用Swagger作为构建API服务中的新API的工具。Swagger有助于减少开发团队和UI/UX团队之间的摩擦,因为UI/UX团队不必再等待数据模型。资深UI/UX开发人员杰森现在可以快速开始构建新页面,因为他确切地知道可以从新API端点期望的负载类型。
QA团队成员阿曼达已经开始与客户成功团队合作进行负载测试和用户验收测试。在用户验收测试周期中添加了验收测试,暴露了核心产品中可以改进UI/UX的地方。客户成功团队现在有了测试新页面和暴露可能的UI问题的额外责任。验收测试适用于测试顺利的路径场景,也就是当一切都按预期完成时,但用户验收测试可以暴露UI中不直观的工作流程。例如,拉里开始测试零部件供应页面中的新过滤功能,并发现为了开始过滤,需要点击复选框。拉里问QA为什么过滤不能默认进行,为什么需要复选框;然后开发人员开始着手添加默认过滤:
该图示没有复选框,而只是一个使用输入框的页面,每当用户输入Enter、逗号或Tab时,就会应用一个新的过滤器,然后页面会自动过滤。如果没有结果显示,则会显示文本“未找到结果”。
客户成功团队成员贾斯汀问QA团队成员弗朗西斯是否可以借用QA部门测试的新功能视频。贾斯汀意识到QA团队拥有一套非常有价值的视频,客户成功团队可以利用它来教客户如何使用最新功能。弗朗西斯为客户成功团队创建了一个内部门户网站,用于QA部门发布新视频时使用。客户成功团队一直在为新客户创建入职视频,并设计了一个知识门户,解释如何设置,例如,一个新的零部件供应页面。
销售团队可以为每个潜在客户创建一个项目展示。公司内部网正在帮助公司内部更多团队整合,因为团队成员正在打破沟通障碍。
CLI应用程序将与开发者门户倡议协同工作,并提高API消费者的采用率。除此之外,开发团队决定启动一个关于API消费者可以利用的软件开发工具包(SDK)的倡议来使用API。SDK可以极大地改善和增强API提供者的第三方采用,并因此增加API的采用率。SDK特别有用,因为开发人员和质量保证人员使用不同的编程语言。机器零件API的开发人员正在使用Golang编程语言,而质量保证人员大部分工作都在使用Python。SDK将支持许多编程语言,并将帮助API消费者快速上手,因为他们可以选择自己喜欢的语言来使用API服务。
为了使手动流程自动化,组织内部不同团队之间必须进行沟通。组织中的团队肯定会有手动流程。开发领导、质量保证领导、客户成功领导和UI/UX领导开始每月开会,讨论新的实践,并开始寻找公司内需要自动化的其他手动流程。
手动流程本质上并不是坏事,用户验收测试(UAT)在公司中仍然有效,并且可以帮助揭露自动化测试无法发现的问题。UAT对于测试自动化测试有时无法发现的边缘情况场景尤其有帮助,就像之前展示的例子一样,客户成功团队测试了一个新功能,并发现零件详情页面只有在复选框被选中时才能进行筛选。
营销、销售和客户成功团队通常使用电子表格应用程序,如Excel,来计算数字,以及演示应用程序,如PowerPoint,来创建图表。通常,在Excel电子表格中计算的数字会保存多个版本,但团队成员必须通过电子邮件向其他团队成员发送副本。开发人员可以要求营销、销售和客户成功团队以逗号分隔值(CSV)形式导出Excel中的值,这是一个文本文件,更容易处理。这样做的附加价值是公司内部网可以使用数据可视化工具创建漂亮的HTML图表和演示文稿。可以利用D3等库创建许多不同类型的可视化效果,非常强大。
为了使团队开始合作并公开讨论问题,必须存在一种开放的精神。团队很容易被隔离,意味着他们与其他团队的工作脱节。开发团队很容易选择与质量保证部门保持脱节。问题在于沟通是揭露团队之间手动流程的最重要元素。没有沟通,团队将独立地处理他们认为重要的事项。
不同团队参与的社交活动可以帮助打破障碍,建立友好的环境。通常,开发人员喜欢参加会议,仅仅是为了与其他开发人员进行社交互动,通常会有一个走廊轨道,开发人员在会议中站在外面,而不是参加会议中的活动。
公司可以赞助社交活动,并帮助在不同团队中任命代表,帮助打破团队之间的僵局。例如,在公司保龄球活动中,人们可能会被故意分配到与他们平时工作不同的团队中。一个小的保龄球团队可以由一个开发人员、一个客户成功团队成员、一个QA团队成员、一个营销团队成员和一个销售团队成员组成。这可以激发团队成员之间的工作关系,让他们彼此了解,并公开交流他们在公司活动之外遇到的问题。
比利鲍勃机械零件公司安排了一场棒球比赛活动,主要开发人员维克多和主要QA成员贝蒂与几名营销团队成员、销售团队成员和客户成功团队成员一起工作。为棒球比赛活动组成了两个团队,并安排了公司烧烤活动,让人们可以一起吃饭并交谈。
鼓励积极合作的另一种方式是改变公司的楼层平面图,使其更加开放。许多软件公司一直在采用开放式平面图,因为它们消除了隔间在人们之间产生的自然分隔。这个想法是,如果你有一个开放的平面图,你更有可能接近不同的团队,因为你可以轻松地走到不同的人面前,而不会感觉自己侵犯了他们的空间。
沟通是发现手动流程的关键,重要的是要找到手动流程以便自动化这些手动流程。手动流程往往容易出错且耗时,正如我们在各种业务场景中所阐述的那样。这就是自动化的价值所在,比如实施CI构建和编写脚本来自动化手动流程。开发人员和QA可以帮助开发可以惠及许多不同部门的自动化脚本,如销售、营销和客户成功。在本章中,您已经了解了自动化相对于手动流程的好处以及开放沟通的价值。
在下一章中,我们将学习CI的基础知识。
要更多地了解CI并使用流行的CI/CD工具,可以考虑阅读
本章将帮助介绍持续集成(CI)的概念,并为我们在后续章节中探讨的CI/CD概念奠定基础。了解CI构建的目的很重要,因为这些概念超越了您可能使用的任何特定的CI/CD工具。CI很重要,因为它有助于保持代码库的健康,并帮助开发人员保持软件系统独立于任何特定的开发人员机器运行。CI构建强制执行软件组件的独立性和本地环境配置。CI构建应该与任何一个开发人员的配置解耦,并且应该能够重复和隔离状态。每次运行的构建本质上是独立的,这可以保证软件系统正常工作。
本章只假设对版本控制系统有一定的了解,但读者至少应该了解配置文件是什么,并且对编程有基本的了解。我们将简查看一个示例makefile,并在本章中提供一些代码片段。
CI本质上是一个软件工程任务,其中源代码同时合并和测试在主干上。CI任务可以执行任何多种任务,包括测试软件组件和部署软件组件。CI的行为本质上是规定的,可以由任何开发人员、系统管理员或运维人员执行。持续集成是持续的,因为开发人员可以在开发软件时持续集成软件组件。
软件构建不仅仅是一个编译步骤。软件构建可以包括编译步骤、测试阶段、代码检查阶段和部署阶段。软件构建可以作为一种验证步骤,检查您的软件是否作为一个统一的单元工作。静态编译语言,如Golang和C++,通常具有生成二进制文件的构建工具。例如,Golang的构建命令,如gobuild,将生成一个静态编译的二进制文件,并对代码库运行linting。其他语言,如JavaScript,可以使用诸如gulp.js/grunt.js之类的工具来执行被认为是构建步骤的操作,例如缩小-将多个JavaScript源文件转换为一个文件-和丑化,它会剥离源文件的注释和任何空白,以及linting和运行测试运行器。
开发人员可以向版本控制项目(VCP)系统提交代码,例如GitHub和GitLab。CI服务器可以轮询仓库以获取更改,或者可以配置CI服务器通过WebHook触发软件构建。我们稍后将在Jenkins、Travis和CircleCI中进行讨论。CI服务器将从VCP系统获取最新的软件版本,然后可以执行集成软件系统的构建脚本。CI服务器应该生成反馈,将构建结果通过电子邮件发送给指定的项目成员。CI服务器将持续轮询更改,或者将从配置的WebHook响应。
CI有很多价值。首先,CI构建有价值,因为它可以减少风险,软件的健康状况变得可衡量。CI有助于减少开发者的假设。CI环境不应依赖环境变量,也不应依赖某个人机器上设置的特定配置文件。
CI构建应该干净地独立于每个开发者的本地机器,并且应该与任何本地环境解耦。如果一个开发者说构建在他/她的机器上可以运行,但其他开发者无法运行完全相同的代码,那么你就知道构建可能无法正常运行。CI构建可以帮助解决这些问题,因为CI构建与任何给定开发者的设置和环境变量解耦,并且独立运行。
CI可以帮助减轻软件构建中普遍存在的风险,比如“在我的机器上可以运行”的症状。CI还有助于统一集成失败点,比如数据库逻辑以及其他类型的问题。
开发者之间的一个共同问题是,一个开发者的机器上可以运行软件构建,但另一个开发者的机器上却无法运行。每个开发者的机器应尽可能地与软件集成相似。完成软件构建所需的一切都需要提交到版本控制系统中。开发者不应该有只存在于他们本地机器上的自定义构建脚本。
完成软件构建所需的任何数据库构件都应存储在版本控制中。如果你有一个关系型数据库,那么任何数据库创建脚本、数据操作脚本、SQL存储过程和数据库触发器都应存储在版本控制中。
应该使用部署工具自动化软件部署。你使用的部署工具可能因不同的软件架构而有所不同。
以下是一些部署工具的列表:
部署工具很有价值,因为它们往往是跨平台的,并且可以在许多不同的软件架构中使用。例如,如果开发人员编写了一个Bash脚本,那么其中有一个基本假设,即其他开发人员正在使用类Unix系统,而在Windows环境中工作的开发人员可能无法运行脚本,这取决于他们使用的Windows版本。现在Windows10提供了一个bash子系统,Windows开发人员可以在操作Windows操作系统时运行Unix命令和脚本。
CI构建可以帮助防止软件缺陷的晚发现。CI构建应该具有足够好的测试套件,覆盖代码库的大部分代码。一个健康的代码库可能的度量标准是代码库中70%或更多的代码覆盖率。稍后我们将讨论代码覆盖率,但任何软件测试都应该被检入源代码,并且应该在CI构建上运行测试。您拥有的任何软件测试都应该在CI系统上持续运行。
总的来说,高百分比的代码覆盖率表示一个经过充分测试的代码库,但并不一定保证代码库没有软件错误,只是测试套件在整个代码库中具有良好的测试覆盖率。尝试使用代码覆盖工具,以查看您的测试实际上覆盖了多少源代码。
以下是一些流行的代码覆盖工具:
确保您知道您的代码在多大程度上受到单元测试的覆盖。dotCover可以计算并报告针对.NETFramework、Silverlight和.NETCore应用程序的语句级代码覆盖率。
CI系统应该配置为以多种方式发送警报:
一些软件开发办公室还使用一些其他创造性的方式来发送软件构建的问题通知,比如某种环境光变化或甚至对讲系统。主要的观点是开发人员需要被通知CI构建已经中断,以便他们可以快速修复构建。CI构建不应该保持中断,因为这可能会干扰其他开发人员的工作。
软件构建应该在版本控制系统中的每次源代码检入时触发。这是部署流水线中的一个重要步骤,我们将在下一章中看到。
软件构建可以仅包括编译软件组件。构建可以包括编译和运行自动化测试,但总的来说,您在构建中添加的进程越多,反馈循环就会变得越慢。
建议使用专门用于构建软件的脚本工具,而不是个人脚本。自定义Shell脚本或批处理脚本往往不具备跨平台性,并且可能隐藏环境配置。脚本工具是开发一致、可重复的构建解决方案的最有效过程。
以下是一些脚本工具的列表:
努力实现单一命令构建,以便简化构建软件的过程,因为您使运行构建过程变得更容易,您将加快采用速度和开发人员参与度。如果构建软件是一个复杂的过程,那么最终只有少数开发人员会真正进行构建,这不是您想要的。
这很重要,有两个原因:
以下软件资产应该在集中式版本控制存储库上可用:
您必须决定应该放入版本控制的内容。
您必须为软件资产选择一致的目录结构,因为它可以帮助您从CI服务器执行脚本检索。
这是我为骨架React/Redux应用程序做的一个示例文件夹结构:
主要的一点是,你应该遵循一个标准的命名约,所有开发人员都遵循。这将有助于开发团队的工作,因为他们将熟悉代码中的特定事物。并不是每个人都会同意特定的目录布局,但拥有一个标准是最重要的部分。例如,任何在新服务上工作的人应该能够根据文件夹的命名约定设置项目结构,源文件放在哪里,测试文件放在哪里:
可以通过以下方式实现:
对于他们的构建,其他步骤是必要的,这取决于每家公司。
应该为不同的环境设置配置文件和环境变量,例如dev/prod/test。日志详细程度应该能够根据环境进行设置。开发人员可能需要增加日志以进行调试。应用服务器配置信息可以在构建文件中设置,以及数据库连接信息和框架配置。
这是一个可以使用的示例文本文件。需要注意的一点是,这些文件不应该提交到源代码控制,因为它们可能包含客户机密和API密钥:
通常,小型构建是可以由CI服务器快速运行的构建,通常包括编译步骤以及运行所有单元测试。小型构建可以通过运行分阶段构建来优化,这将在CI构建实践部分讨论。
CI构建实践就像是阶梯;它们相互累积。正如我们将在下一章中看到的,CI构建过程中的每一步都很重要,并提供了确保代码库处于健康状态的保证。
开发人员在提交代码到存储库之前应该运行私有构建。
这是一个使用Git的示例开发人员会话:
努力通过增加计算资源尽快运行软件构建。将运行较慢的测试,如系统级测试,转移到次要构建或每夜构建。将代码检查转移到第三方服务。例如,对于代码覆盖分析,可以使用以下第三方服务:
运行分阶段的构建以促进快速构建。第一次构建可以编译并运行所有单元测试。第二次构建可以运行所有集成测试和系统级测试。您可以有尽可能多的阶段来实现快速构建。可以说,第一次构建应该是最快的,因为这将是开发人员在向代码库签入代码时使用的主要构建。
本章介绍了CI概念的基础,并介绍了在开发团队中使用成功的CI服务器的技术。我们研究了脚本工具和构建工具。我们讨论了软件构建是什么,创建构建脚本时要遵循的良好实践,以及一些测试概念,如代码覆盖。下一章是关于持续交付(CD),这是对CI的自然延伸,我们将详细介绍部署流水线、配置管理、部署脚本和部署生态系统。
可以说,软件的最重要部分实际上是将其交付并准备供最终用户使用。持续交付(CD)是您将软件产品交付给最终用户的时刻,也是本章的基础。只有当您的预期用户实际上可以使用产品时,产品才有用。在本章中,我们将讨论部署流水线,并结合自动化和CD的概念。
本章假定您了解自动化和持续集成的概念。如果您对这些主题中的任何一个感到不确定,请先阅读第一章,“自动化测试的CI/CD”和第二章,“持续集成的基础知识”,然后再阅读本章。
在尝试将软件产品交付给最终用户时,可能会出现许多问题,我们将看看影响软件交付的几种情况。一个可能的情况是开发人员正在开发一个新功能,但新功能可能实际上无法通过CI构建阶段,或者可能不像产品所有者最初提出的那样运行。另一个可能的情况是,预期的受众没有得到适当的理解,这将影响用户对最终产品的使用。另一个可能的情况是,软件产品没有得到适当的解耦,并且是用泡泡糖和胶带拼凑在一起的,新功能请求会导致许多回归问题。
关于软件交付实际意味着什么可能会有很多争论。在本章中,所指的是实际的软件产品已交付给预期的用户,而不仅仅是软件产品被质量保证(QA)部门批准为有效。
存在一些常见的发布反模式,您应该避免,例如手动部署软件,手动配置管理以及每个环境的不同环境配置。
如果存在一份详细描述软件产品交付的长文档,这可能表明存在手动流程。这进一步使产品的交付变得更加复杂,因为在整个过程中的任何错误都可能导致更多问题。如果交付变得不可预测,这也可能指向这种反模式。
正如我们在第一章中讨论的那样,自动化测试的CI/CD,自动化是一个行为以可重复和自动化的方式完成的过程。软件交付应该是一个自动化的过程,因为这将有助于确保软件交付的一致实践和行为。我们将在本章后面看到一些工具,这些工具将帮助您自动化软件交付流程。
这种反模式可能会让运维人员感到沮丧,因为他们将是最后一个了解产品新行为的人。如果软件交付的当天是运维团队第一次看到新功能,那么他们可能会对软件行为感到惊讶。辛迪是运维团队的成员,她被委托交付软件,并注意到安装脚本完全失效,因为它无法与识别(ID)服务器通信。辛迪向开发团队发送日志消息,发现ID服务器的一个客户端密钥已更改,并且安装脚本需要使用这个新值才能正确连接。
如果辛迪意识到ID服务器的这个新变化,这种问题可能会得到缓解,但开发人员正在另一个环境中工作,QA部门得到了这个信息来测试新功能,但直到交付当天他们才遇到这个问题,没有人想到将这个信息传递给运维部门。
我们将讨论可以帮助解决配置管理问题的工具,比如之前遇到的问题。使用适当的工具,运维/DevOps人员可以快速获得每个环境的正确环境配置,包括生产环境。
这种反模式可能特别具有挑战性,因为在开发中测试了所有的更改,暂存环境在生产环境中可能表现不稳定。例如,特拉维斯在QA部门担任测试员,自新功能推出以来一直在测试暂存环境。比利是一名运维人员,由于暂存环境与生产环境完全不同,他无法看到新功能。比利还注意到生产环境中缺少在暂存环境中显示的关键信息。比利联系开发团队,发现必须运行数据库迁移脚本才能使新功能在生产环境中运行。
所有环境,包括测试、暂存和生产环境,都应该有必要的迁移脚本和任何其他软件资产,以防止生产环境的故障,并且开发团队应该确保将操作指向脚本文件的任何更改,或者在共享文档中清楚地标记这些更改。
在进行软件发布时,有一些重要的步骤需要考虑,比如频繁发布以避免一次引入太多的更改,并确保发布是自动化的。
手动发布存在问题,因为它们不可重复。每次进行手动发布时,由于配置更改、软件更改和环境更改,都会有所不同。手动发布步骤充满错误,因为每个步骤都是手动的,可能导致级联错误。手动更改的危险的一个很好的例子是,当最受欢迎的云提供商亚马逊网络服务(AWS)在美国东部地区遭受重大故障时,因为运维人员在手动流程的一系列步骤中输入了错误的命令。自动化是软件发布的关键,因为它确保了软件交付流程的可重复性和控制。在本章中,我们将进一步探讨部署脚本工具,以帮助自动化软件交付。
正如我们之前所阐述的,自动化在软件交付中非常重要,因为它确保了软件发布的可重复性、可靠性和可预测性。通过自动化软件交付流程,可以避免或减轻灾难性事件,而不是通过漫长的手动流程。
如果自动化已经实施,QA部门可以安全地选择旧版本的软件发布来测试回归。运维人员可以运行在暂存中使用的脚本,而不会因为环境级别的差异而遇到问题。通过自动化软件流程,运维人员可以在交付过程中出现灾难时安全地回滚发布。此外,正如我们在第二章中所讨论的,自动化可以帮助实现一键式发布。
自动化可以帮助减少手动流程可能造成的错误。正如我们之前所看到的,配置管理问题可能导致软件交付不佳。手动软件发布无法有效地确保可重复性,因此容易出错。
另一个好处是在软件交付期间减少所有人员的压力。手动流程往往会造成不必要的压力,因为执行手动流程的人必须要细心,不能在交付过程中犯任何错误。自动交付流程非常好,因为它确保每次运行都会以相同的方式执行。手动流程中的错误可能需要高级人员的支持来解决问题。
包含重要信息的配置文件,如客户端密钥和密码,必须得到妥善管理,并且必须在其他环境中保持同步。每个环境可能有不同的环境变量,必须被使用并传递到应用程序中。
版本控制是保持所有软件工件之间修订的手段。版本控制对于配置管理非常重要,因为任何包含环境文件的文件的更改都应该在版本控制下。
我已经添加了一个示例属性文件,其中包含客户端秘密信息和身份验证秘密信息。这对于给定的环境正常运行是必要的,但不应该检入源代码控制,这里仅用于演示目的。
这是一个版本控制管理工具的列表:
一个重要的做法是尽可能将所有东西都放在版本控制下,以避免在软件产品中丢失重要工作。网络文件、配置文件、部署脚本、数据库脚本、构建脚本以及任何其他对应用程序正常运行很重要的工件都应该在版本控制下,否则你会冒着丢失关键数据的风险。
经常检查主分支非常重要,否则你会冒着在代码库中引入破坏性更改的风险。此外,频繁的检查可以帮助开发人员随时注意带入小的更改。应避免对代码库进行大规模的更改,因为这样更难测试并且可能会引起回归。频繁的检查也是有益的,因为破坏性更改会更快地被注意到。
使用包括问题跟踪信息的描述性提交消息,比如Jira问题,清楚地描述提交的意图。避免编写模糊的提交消息,比如修复错误或完成,因为这些类型的提交消息是没有用的,对开发人员以后也没有帮助。
这是一个示例描述性提交消息[DEV-1003],添加了一个新的导航链接到零部件供应列表。还添加了一个新导航的测试用例。这显然更加描述性。此外,在Jira中,当你提供一个像DEV-1003这样的问题时,它会在Jira问题中创建一个引用此问题的链接。此外,如果你创建一个拉取请求并在gitcommit中放入Jira问题,它将把你的拉取请求与问题链接起来。
应用程序通常具有对软件产品至关重要的第三方依赖项。依赖管理是任何应用程序的重要部分,不同的编程语言以不同的方式处理依赖管理。
这是一个Gopkg.toml文件,其中包含存储库中每个依赖项的版本和包信息。
通常,软件项目将以单片构建开始,所有工作组件都在一个层中。随着应用程序的规模和成熟度的增长,应用程序的层将分解为服务或不同的层,这就是需要单独的构建流水线的地方。也许一个ID服务用于应用程序中的身份验证,也许一个管理服务在单独的构建流水线中运行用于管理门户。微服务架构是应用程序的服务级组件化的延续,其中每个微服务在应用程序中有一个清晰而专注的目的。
配置是任何应用程序的重要部分,应该像您在代码中使用的业务逻辑一样小心对待。因此,配置需要像源代码一样得到适当的管理和测试。
乍一想,似乎将配置尽可能灵活是合适的。为什么不尽可能地使系统灵活,并允许它适应任何类型的环境呢?这通常被称为终极可配置性的反模式,意味着配置可以像编程语言一样行为,并且可以被制作成任何方式。以这种方式进行的配置管理可能会使软件项目陷入困境,因为其用户将期望这种灵活性是必要的。为您的配置管理设置一些约束更有用。约束可以帮助控制配置环境中过度灵活性的影响。
以下是应用程序可以利用的可能类型的配置列表:
当您跨不同应用程序进行配置时,配置管理变得更加复杂。有一些工具可以帮助跨应用程序边界进行配置,以下是这样的工具列表:
应用程序依赖的硬件、软件、基础设施和任何外部系统可以被视为应用程序的环境。任何环境的创建都应该以完全自动化的方式进行,因为能够复制环境是重要的,我们将会说明。
手动设置基础设施可能会出现几个问题:
以下是所有环境都需要的重要配置信息列表:
我们在第二章中谈到了CI的重要性,持续集成的基础知识,虽然CI是一个重要的生产力增强器,但它主要对开发团队有用。在等待修复或更新文档时,软件生命周期中常见的瓶颈是QA和运维团队。QA可能需要等待开发团队的良好构建。开发团队可能在完成新功能后的几周内收到错误报告。所有这些情况都会导致无法部署的软件,最终导致无法交付给最终用户的软件。创建可以部署到测试、分级和生产环境的一键式部署构建可以帮助缓解这些问题,正如我们之前所指出的。
部署管道可以被认为是构建、部署、测试和发布过程的端到端自动化。部署管道也可以被认为是将开发人员编写的软件交到用户手中的过程。
在本节中,我们将讨论一些部署管道实践,比如只编译一次二进制文件,以相同的方式处理每个环境中的部署,并在部署管道中设置提交阶段。
多次编译的二进制文件可能会出现问题,原因有几个:
如果可以的话,最好在编译时只编译一次二进制文件。
考虑到每次源代码检入时都会运行CI构建,开发人员通常会经常部署他们的软件。QA/测试人员不会那么频繁地部署,运维人员更少。与开发环境相比,部署到生产环境的频率要低得多,这是有充分理由的。
应该创建一个部署脚本,可以在开发、分级和生产环境中运行。每个环境中需要的任何更改都可以通过在版本控制中管理的属性文件来管理。例如,您可以在部署脚本中使用环境变量来区分不同的环境。
部署流水线的第一个阶段是提交阶段,或者说是开发人员将代码提交到版本控制时。一旦代码提交到CI构建流水线,构建流水线应该在必要时编译任何代码,运行一套单元测试(希望有一些存在)和集成测试,如果需要为后续部署流水线创建任何二进制文件,则创建任何二进制文件,运行静态分析工具来检查代码库的健康状况,并准备后续部署流水线所需的任何构建工件。
提交阶段构建的其他重要指标包括代码覆盖率、代码库中的重复代码、圈复杂度(衡量代码库中的复杂性)、监控大量警告消息以及代码风格(通常由代码检查工具报告)。
如果提交构建阶段通过,那么我们可以将其视为通过的第一个关卡,尽管这是一个重要的关卡。
运行一套验收测试应该是部署流水线的第二个关卡。验收测试还充当回归测试套件,以验证新功能是否已引入系统。在此阶段,验收测试套件中发生的任何测试失败都需要逐案评估。失败可能是由于系统中的有意行为更改,因此需要更新验收测试套件,或者失败可能代表需要解决的回归。无论哪种情况,都必须尽快修复验收测试套件。验收测试充当了部署流水线继续前进的另一个关卡。
非功能测试的命名恰如其分,因为这些类型的测试不是系统的功能要求。相反,非功能测试测试系统中的容量和安全性等方面。在部署流水线的这一步中,失败可能不需要将构建标记为失败,而只是作为构建的决策指标。
进行发布时总会伴随一定的风险,因此最好在进行软件发布时建立相应的流程。发布过程中出现的问题可以通过建立流程来预防或减轻。
在发布过程中可能要遵循的一些步骤如下:
尽量自动化尽可能多的发布流程,因为自动化越多,您对发布流程的控制就越多。手动步骤往往容易出错,并可能导致意外结果。在生产环境中发生的任何更改都需要得到适当的锁定,这意味着更改是通过自动化流程完成的。
发布日往往会很紧张,因为在发布过程中发生的错误可能会导致难以检测的问题,或者正在发布的新系统可能存在缺陷。排练发布可以帮助减轻这些问题,并可以帮助人们快速解决可能遇到的问题。
在发布之前和发布之后,最佳策略是准备好软件系统的先前版本,以防需要将系统回滚到先前版本;这不包括任何必要的数据迁移或配置。作为另一种可行的选择,您可以重新部署已知的良好版本的应用程序。回滚应该能够通过点击按钮完成。
部署脚本是必要的,因为开发团队编写的软件不仅在他们的IDE或本地环境中运行,而是需要在部署流水线期间运行。部署脚本是指您用于编写部署流水线脚本的特定构建工具。
已经有许多构建工具,每个都有其优缺点。以下是一小部分构建工具的列表:
无论您使用什么构建工具,进行部署脚本时都需要遵循某些实践。
在部署流水线的提交阶段,您将需要部署脚本执行的操作。例如,您可能需要编译任何源文件,运行一套单元和集成测试,并运行一个检查代码风格和静态分析工具的代码检查工具。所有这些步骤可能需要使用不同的工具,因此编写一个执行所有这些操作的脚本是最好的。根据脚本的特定操作,您可能希望进一步将脚本分解为执行专注操作的子脚本。在验收测试阶段,您的脚本可能会运行整个验收测试套件,并额外生成一些关于测试的报告和指标。
你应该在所有环境中使用完全相同的脚本,这将确保在每个环境中都以相同的方式进行构建和部署过程。如果每个环境都有不同的脚本,那么就无法确保在不同环境中运行的特定脚本的行为是相同的。开发人员在其本地环境中运行的部署脚本应该与在其他环境中运行的脚本相同,否则就会存在环境泄漏的风险。我们在这里的意思是,开发人员的环境可能设置了特定的环境变量,而部署脚本或每个环境,如开发、暂存和生产,可能设置了不同的环境变量,这将使在出现问题时调试变得更加困难。
部署过程应该在每次运行时保持不变。在数学中,有一个术语叫做幂等,它基本上表示某个操作可以多次执行并产生相同的结果。如果你的部署过程在任何给定的运行中发生变化,那么你无法保证每次运行的行为,这反过来将使故障排除变得更加困难。
本节讨论了部署脚本的最佳实践,例如确保仅测试已知良好的基础、测试环境配置、使用相对路径和消除手动流程。
你不应该测试甚至无法编译的源代码,当单元测试和集成测试失败时也不应该运行任何验收测试。基本上,任何额外阶段的部署过程都必须存在已知良好的基线才能运行和继续。
最好使用相对路径而不是绝对路径。开发人员可能有一定的文件系统或文件夹结构,在部署流水线运行的环境中不存在,因此最好使用相对路径,以免造成意外的破坏。有时可能会很难做到这一点,但尽量遵循这一点是最好的。Docker容器可以为每个容器映射文件夹结构;例如,如果Docker容器在部署流水线的特定部分生成,它也可以映射到某个特定的相对文件夹结构。
避免制作包含必须完成部署特定部分的步骤列表的构建脚本。
以下是手动流程中可能的步骤列表:
任何必须手动完成的步骤在文档中很快就会过时,因此最容易遵循的指令是,如果你必须再次执行某个操作,就要制定自动化流程。
在本节中,我们将简要介绍一些可以帮助你的部署流水线并提供不同用途的工具。
我们在本章前面简要提到了Chef;Chef是一个很好的工具,可以可靠地自动化基础架构的搭建。如果没有适当的工具,很难确保每个新环境的设置都是以相同的方式进行的。潜在地,您可能会创建具有不同配置的新环境,这在故障排除时可能会带来很大问题。
您可以使用Jenkins、Travis和CircleCI/CD工具与所有主要的云服务提供商,尽管MicrosoftAzure和AWS也已经创建了自己的CI/CD工具供您使用。
考虑阅读PacktPublishing出版的《DevOps:ContinuousDelivery,Integration,andDeploymentwithDevOps》一书,以更深入地了解CD。
这是一个需求清单的示例。需求清单的目的是为特定功能列出所有必要的事实。
在敏捷工作环境中,需求的误解是一个常见问题。虽然不可能完全消除需求的误解,但通过确保在功能请求的最初阶段与最终用户或客户进行沟通,可以最大程度地减少这种风险。
重要的是,你正在实施的功能请求必须清晰陈述,并且每个功能都必须有明确的商业意图。这很重要,因为它有助于开发人员、DevOps人员和QA/测试人员在实施阶段做好充分准备。
在前期了解关键业务需求将有助于减少团队之间的需求误解,因为缺少需求可能会在开发过程中轻易造成瓶颈。任何关键需求信息都需要得到适当的文档记录。
文档需要在定义任何需求的同时编写,并在功能开发过程中持续更新。只有在一切都尽可能清晰地定义和陈述之后,你才能开始编写实施特定功能的计划。如果开发人员遇到问题并需要向客户澄清,那么答案需要直接放在需求中以供将来参考。
避免有多个包含需求信息的文档,而是使用一个文档包含所有需求信息,否则你就有可能出现信息过时的情况,甚至更糟糕的是,不同的需求分散在不同的地方并相互矛盾。
随着越来越多的团队变得分布式和全球化,时区差异可能会导致沟通瓶颈。在时区差异较大的开发人员需要确保良好的CI/CD实践。CI构建失败和配置管理问题可能会因时区差异而迅速恶化,因为一个时区的团队可能会无法有效地进行工作。在分布式团队中,沟通尤为重要,因为缺乏面对面的互动可能导致沟通失败,最坏的情况下甚至会导致团队之间的敌意。
我曾在一家初创公司工作,那里有3小时的时区差异,这本身并不是问题,但每天的站立会议是在我们的工作日结束时进行的,而另一个团队在我们的中午开始工作。自然而然地,这导致了其他团队的更改会阻塞我们,直到我们的中午才能解决。
以下是一张图,说明了信任和相互尊重是相辅相成的,团队需要这一点才能高效运作:
团队之间的信任至关重要,很容易失去,但很难获得。最好有一个优秀的项目经理,可以促进团队之间的沟通,并帮助澄清必然会发生的问题。健康的团队会在功能工作中出现问题时进行开放的沟通,定期进行回顾也有助于排解团队成员之间的挫折感,并建立信任。
如果可能的话,最好能组织团队外出活动,让多个团队之间可以互动并建立合作关系。一些公司会定期举行会议,让团队一起参加有趣的活动,比如体育运动或游戏。定期进行团队建设活动也可以保持团队成员的参与度,并建立合作精神。
随着敏捷工作环境的全球化,全球团队变得更加普遍。团队之间的文化差异使得沟通成为项目成功的更加重要的因素。幽默可能是一把双刃剑,因为如果幽默被误解,很容易导致分裂和敌意,所以最好能教导团队有关文化规范和习俗,以避免沟通问题。
语言障碍也可能会导致问题,因为对功能请求的需求可能会被误解。最好是由项目经理作为团队之间的联络人,确保所有需求在团队之间清晰地理解,并帮助澄清任何沟通瓶颈。
理想情况下,团队应尽快获得他们所需的信息,但这并不总是现实。适当的联络人或项目经理可以帮助缩短团队之间的反馈循环,团队需要适当记录任何流程,并确保这些文档对其他团队可见和已知,否则团队之间的流程可能会有所不同。
团队成员能够有效地传达阻碍进展的特定痛点或障碍是很重要的。在本节中,我们将讨论几个痛点,包括等待需求信息、部署流水线中未记录的步骤、王国的密钥持有者过多以及沟通渠道过多。
开发人员通常会开始处理特定的故事/功能,但并没有所有必要的需求来完成他们分配的工作。这对开发人员来说尤为棘手,因为他们所处理的任何代码可能需要根据需求与正确完成的距离有多远而被废弃并重新完成。开发人员需要在开始故事之前就提前获得所有的需求;每个功能必须存在抓取所有需求的流程,并且每个故事理想情况下都将有验收测试作为特性工作的行动项来考虑完成。在理想的世界中,开发人员将在开始特定的功能工作之前准备好所有必要的信息,并且在需求文档中指定的特性完成时,故事的验收测试将通过。
在第一章中,自动化测试的CI/CD,我们讨论了比利·鲍勃机械零件公司的例子。现在,想象一下,开发团队的汤姆已经开始了显示供应商名称的工作,并且汤姆发现这个工作的范围似乎很大,他可能无法及时完成。这种情况也因需求文档严重缺乏和开发过程中缺少关键细节而变得复杂。汤姆询问产品负责人是否可以就某些项目提供反馈,但必须等待数天才能获得这些必要信息。
部署流水线过程中的每个步骤都应适当记录和自动化。我们在第五章中谈到了在Jenkins的安装和基础知识中尽可能自动化部署流水线的重要性。重申一下,手动流程是有问题的,因为它们是可重复和可靠的。自动化很重要,因为它为部署流水线带来了可重复性和可靠性。每当有人必须执行手动步骤时,就无法保证流程是否会正确执行,并且在每次运行中都以相同的方式执行;只有通过自动化,您才能保证部署流水线阶段的可重复性。
作为DevOps团队的一部分,阿尔文正在为软件产品的最新版本进行发布,并在部署流水线中运行一个复杂的手动流程。阿尔文输入了错误的命令,结果清除了生产数据库。幸运的是,阿尔文有一个一天前的备份,他可以将生产数据库恢复到这个备份。如果当时有自动化流程,这种情况就不会发生。
以下图表代表一个关键点,关于王国的关键点要记住的主要事情是只有少数人可以访问/拥有生产环境的密钥:
重要的是要控制谁可以在生产环境中进行更改,许多软件公司通常会选出少数几个甚至一个人可以在生产中进行更改。如果这个特定的个人不可用或离开公司,这可能会成为问题,但一些公司已经实行了开发团队全权拥有特定功能的做法,负责修复部署管道中遇到的问题的是同一个开发人员。在我工作过的一家公司,我们亲切地说,“只有少数人拥有王国的钥匙”。
阿尔文是为数不多的DevOps人员之一,拥有王国的钥匙。客户支持代表向开发团队发出关于生产中断的通知,开发团队正在努力恢复生产环境以满足客户需求。阿尔文和另一名DevOps成员是唯一可以触及生产环境的人。这个问题变得更加严重,因为阿尔文或其他指定的DevOps人员都不可用。
在所有这些警报中,噪音太大,真正的信号几乎没有。
如果CI/CD管道中的某些部分让你感到痛苦,那么自动化这个过程可能是一个好主意。如果你有一个15步的过程,在部署管道中容易出错,并且由于执行错误而在发布过程中引起许多问题,那么这可能是其他人在某个时候也会感到痛苦的地方。这个想法是痛苦应该引导你找到更好的解决方案。如果你在处理过程中遇到问题,那么你可能需要自动化这个过程。并不总是为了自动化而自动化一个任务;你需要不断评估你的过程,而PDD可以是发现需要改进的过程的有效工具。
吉米在每次提交阶段都遇到了linting失败的问题。吉米忘记在将代码推送到代码库之前检查lint任务。这尤其麻烦,因为吉米确保运行所有单元测试来检查它们是否通过,但习惯性地忘记检查linting错误。吉米决定痛苦已经足够了,需要建立一个新的流程。吉米编写了一个预Git推送挂钩脚本,每次Git推送到主分支时都会运行linter。现在,每当有人推送到主分支时,脚本都会运行linter,以确保不会引入linting错误到代码库中。
如果可能的话,你应该轮换团队成员,尝试征求开发实践的反馈意见,并尝试创建跨职能团队。
布鲁斯在API开发团队工作,并被调入网络工程团队。轮岗期大约为3到6个月,布鲁斯已经学到了一些对API开发团队有帮助的实践。跨培训工程师的一些优势是,他们在其他开发团队学到的技能可以转移到其他团队。布鲁斯学到了一些缓存优化的方法,可以应用在网络层和OSI层,这将有助于API开发团队。开放系统互连(OSI)是一个将通过网络发送的信息分解成不同层次的概念模型。OSI模型有七层——应用层(第七层)、表示层(第六层)、会话层(第五层)、传输层(第四层)、网络层(第三层)、数据链路层(第二层)和物理层(第一层)。布鲁斯一直在应用层利用优化策略,但通过对网络层的新知识,他提出了新的优化策略。
团队成员之间的沟通对于团队的长期成功至关重要。开发人员不应该害怕询问为什么以某种特定方式进行事情的反馈,重要的是要创造一个健康的环境,建设性的批评是受欢迎的。团队成员可能会对团队流程变得自满,并可能会错过优化流程的机会。
让我们回到我们的例子公司,比利·鲍勃的机械零件公司。假设汤姆最近加入了团队,并注意到在API存储库中设置的步骤过于复杂,需要许多步骤才能使特定环境运行起来。汤姆询问是否有人考虑使用构建工具自动化一些步骤,并被告知可以自行自动化他认为有帮助的任何步骤。汤姆决定编写一个Makefile,可以通过简单运行make命令来封装所有开始特定环境的步骤。汤姆向API存储库创建了一个拉取请求,并引入了这个新功能,这有助于自动化创建特定环境的步骤。
回到我们的示例公司——想象一下以下跨职能团队的阵容。汤姆、史蒂文和鲍勃都是开发人员,瑞奇是安全团队成员,苏珊是DevOps团队成员,尼基是产品负责人。他们都在同一个空间里共同工作,并每天早上进行晨会。现在,团队成员能够全权拥有部署流程的各个阶段,因为他们可以共同合作,互相帮助自动化流程。汤姆和史蒂文使用新库编写了自动化测试套件,瑞奇能够添加第三个构建阶段,对对主分支进行的更改进行安全检查。苏珊在每个项目通过部署流程时添加了监控和报告指标。尼基迅速更新了鲍勃的需求文档,因为他注意到了新功能工作中的边缘情况。团队成员在他们的流程中每一步都进行了公开交流,并且能够优化流程,因为他们之间进行了公开的合作。
尽管产品负责人可能承担项目经理的角色,并且可以帮助促进ScrumMaster的职责,但最好由不同的人来担任这些角色。项目经理可以被视为适应动态工作环境的变革者。在一天结束时,项目经理希望能够将交付内容交付给最终用户,并且可以帮助打开不同团队之间的沟通渠道。开发人员能够公开沟通,并通知项目经理在他们的功能工作中遇到的任何问题是很重要的。
公司文化很大程度上会受到高管团队的影响,比如首席执行官(CEO)、首席信息官(CIO)、首席技术官(CTO)和首席运营官(COO)。除非在这些高管层面上运作,否则很难对公司产生广泛的影响。如果开发团队觉得决策是一种命令,并且他们对所做的决定没有发言权,他们可能无法防止本来可以避免的问题。许多公司声称他们有开放式政策,并欢迎建设性的反馈,但通常开发团队在与破碎的流程作斗争时无法发声。
最终,最终用户将使用您为产品添加的新功能。在这方面,他们可以帮助澄清开发人员的必要需求。通常情况下,最终用户在看到产品之前并不清楚他们在寻找什么。如果需要,产品所有者必须从客户那里提前获取所有必要的需求,有些软件组织甚至要求产品所有者/客户编写测试,以代码指定必须实现的需求。无论如何,在开发人员开始工作之前,产品所有者和最终用户必须就所请求的功能达成一致。
开发团队基本上与最终用户隔离,不会与任何最终用户接触。然而,对于开发团队来说,了解最终用户在使用软件系统时遇到的具体痛点是很重要的。在这个意义上,开发人员是最有能力对系统进行改变以使最终用户受益的人,但如果开发人员不了解这些痛点,他们将无法进行必要的改变以使最终用户受益。在适当的时候,让开发人员与客户成功团队合作可能会有所帮助,以了解最终用户如何使用软件系统。
CI/CD流水线的重要性不容小觑,开发人员需要通过提供指标、报告和一般教育领导层自动化的重要性来证明其重要性。
通常在公司的高管层,数字和PowerPoint幻灯片必须证明某事的重要性。开发人员应该能够用指标(图表、图表和任何其他可视形式)说明CI/CD如何改进现有流程。已经存在企业解决方案可以帮助生成这些信息,但开发团队可以将这些信息汇总到Excel电子表格中。
开发团队不能假设领导了解自动化的含义以及哪些领域适合自动化。最好是由技术代表,如首席技术官,作为自动化的倡导者,并帮助向高管团队解释。像首席技术官这样的人可以成为变革的代理人,代表开发人员发言,但无论是谁传达这些信息,高管团队必须了解自动化的含义以及哪些事情可以自动化。
术语“秘密项目”的起源有待讨论,但一般的想法是,这是一个由个别人或一组人秘密进行的项目,旨在为组织带来创新和变革。开发人员不一定能够获得对于某项任务的批准,他们可能需要采取替代策略来表达自己的观点。
想象一下,开发团队的鲍勃有一个想法,要编写一个CLI应用程序,帮助第三方开发人员利用公司的仪表板。鲍勃试图向高层管理层传达这个想法,但没有成功。鲍勃决定在接下来的几周内编写一个CLI应用程序,并决定使用一种名为Rust的新编程语言来编写CLI项目。鲍勃创建了一个直观的CLI应用程序,易于使用和可插拔。鲍勃能够向团队展示这个新应用程序,并说服高层管理层投入资源来开展CLI项目。
开发团队可能无法获得启动CI/CD流水线的财务批准。为了发现并说服他人自动化CI/CD流水线的重要性,开发人员可以在自己的机器上复制部署流水线,并向团队和高层管理层展示构建自动化流水线阶段的好处。
如今,像Azure、AWS和GoogleAppEngine这样的大型云服务提供商可以提供免费的账户计划来提供云服务。通过这种方式,开发人员可以轻松地设置更真实的部署流水线,通过展示一个小项目,并展示CI/CD流水线中的所有阶段,如提交阶段、自动化验收测试阶段和可选的安全和容量构建阶段。
在您的组织中,公司范围的演示可能是获得CI/CD批准的最有效方式。一些公司赞助黑客马拉松,您可以在赞助的黑客马拉松上为公司创建一个新的自动化流程。这样做的优势是您可以在公司演示期间将自动化信息传达到组织的最高层。
以下图表只是叉子和刀子的描绘,但主要观点是与他人共进晚餐是打开沟通渠道和团结人们的好方法。您可以在午餐时将自动化演示纳入公司会议中:
您可以邀请高层管理人员,并使用图表和带有指标的幻灯片来解释自动化是什么,并展示手动流程上的花费。通常,高层管理人员更关心活动的货币影响,如果您能向他们展示手动流程的成本,他们会更愿意倾听。
下一章将介绍如何在本地环境中设置JenkinsCI。这一章将介绍本书的第一个CI/CD工具。
考虑阅读PacktPublishing的ContinuousIntegration,Delivery,andDeployment,因为这本书讨论了软件组织的CI/CD的价值:
本章将帮助您在Windows、Linux和macOS中安装Jenkins。我们还将了解JenkinsUI的基础知识。
本章是关于使用Jenkins进行CI/CD流程。在本章中,我们不会讨论CI/CD概念,因为我们正在设置环境以使用Jenkins。
有一些初步步骤可以安装Jenkins。
您需要确保已安装Java,并且从Jenkins2.54开始。Jenkins现在需要Java8。
单击开始Windows图标,键入system在搜索框中,然后单击程序列表中的系统。
现在打开了系统小程序,标题为查看有关计算机的基本信息,在大的Windows徽标下找到位于系统区域下的系统区域。
系统类型将显示64位操作系统或32位操作系统。
确保点击接受许可协议单选按钮,然后点击Windows下载,并确保选择正确的架构;即32位或64位操作系统。
然后使用安装程序在Windows中安装Java。
如果您滚动到页面底部,您将看到根据当前版本可以在其上安装Jenkins的操作系统列表:
我已从Jenkins下载页面下载并解压了Jenkins文件,如下图所示:
以下屏幕截图显示了Windows中的Jenkins安装程序:
一旦您完成安装程序中的所有步骤,您将看到以下屏幕:
您可以使用以下命令在cmd.exe中安装Chocolatey:
单击开始按钮,键入cmd并按Enter。这将打开一个命令提示符会话。
接下来,您可以在命令提示符中输入以下命令:
cd'C:\ProgramFiles(x86)\Jenkins'然后您可以使用以下命令:
$C:\ProgramFiles(x86)\Jenkins>jenkins.exestart$C:\ProgramFiles(x86)\Jenkins>jenkins.exestop$C:\ProgramFiles(x86)\Jenkins>jenkins.exerestart您还可以使用curl并使用以下命令:
运行以下命令将存储库密钥添加到您的系统中:
接下来,我们将通过运行此命令将Debian软件包存储库地址追加到服务器的sources.list中:
sudoapt-getupdate确保安装Java,因为它是Jenkins运行的依赖项,因此运行以下命令:
sudoaptinstallopenjdk-9-jre接下来,我们在Ubuntu上安装Jenkins:
sudoapt-getinstalljenkins在Ubuntu中启动Jenkins服务最后,我们需要通过以下命令启动Jenkins服务:
sudosystemctlstartjenkins现在我们需要确认Jenkins已经启动并且没有问题:
sudosystemctlstatusjenkins您应该会得到以下输出:
默认情况下,Jenkins在HTTP端口8080上运行,因此我们需要确保该端口允许流量:
sudoufwallow8080您将得到以下输出:
RulesupdatedRulesupdated(v6)接下来,我们需要查看规则的状态:
sudoufwstatus您将看到以下输出:
在Ubuntu终端会话中运行以下命令:
接下来,您将看到一个屏幕,您可以在其中安装建议的插件或选择要安装的插件:
这个屏幕在一开始并不是100%必要运行,所以您可以单击屏幕右上角的X:
单击X并决定启动Jenkins后,您将看到此屏幕:
在macOS上安装Jenkins相对容易,您可以通过几种方式完成。
在本节中,我们将介绍如何使用Mac软件包安装程序(.pkg)文件安装Jenkins:
第一次在主机上本地运行Jenkins时,您将看到以下屏幕:
如果Jenkins在主用户帐户中运行,请在Mac终端中运行以下命令:
pbcopy
这个屏幕一开始并不是100%必要运行,所以您可以单击屏幕右上角的X。单击X并决定启动Jenkins后,您将看到这个屏幕:
您还可以通过macOS中的Homebrew软件包管理器安装Jenkins。
安装Homebrew相对容易。单击MacFinder按钮打开终端应用程序,按Ctrl+Shift+G,然后输入/applications,并单击“前往”按钮。确保双击Utilities文件夹,然后双击终端应用程序图标。
只需将Homebrew安装脚本粘贴到终端应用程序提示中:
安装Jenkins后,您可以通过在终端应用程序中输入以下命令来启动Jenkins服务:
以下是Jenkins主仪表板页面的屏幕截图。我们将详细介绍每个项目:
在接下来的步骤中,我们将创建一个自由风格项目作为新项目,但根据安装的插件,可能还可以添加更多项目:
以下是Jenkins中典型的控制台输出屏幕:
这是一个非常简单的屏幕,我们只是向屏幕打印了HelloWorld。
然后确保单击“管理插件”链接:
然后您将进入插件页面,看起来像这样:
确保单击“可用”选项卡,您将看到可以安装的可用插件列表。
我们将安装Go插件(您可以通过使用“筛选”输入框快速找到插件):
请注意,我们在过滤输入框中输入了golang。然后,您可以单击“无需重启安装”按钮或“立即下载并在重启后安装”按钮。我们将使用“无需重启安装”按钮。
单击按钮后,您将看到以下屏幕:
我们将点击“返回到顶部页面”按钮。
让我们回到Jenkins仪表板,点击“管理Jenkins”,然后点击“管理插件”。
确保在过滤输入框中输入git:
现在我们将点击“立即下载并在重启后安装”按钮。
接下来,确保点击“返回仪表板”链接:
现在我们将看看如何在Jenkins仪表板中添加环境变量。确保点击“管理Jenkins”,然后点击“配置系统”:
然后您需要向全局属性中滚动:
然后确保配置所有工具,比如添加GitHub和golang的路径。
确保单击“新建项”按钮,现在请注意我们已添加了一个附加项。
现在我们将创建另一个名为“Golang项目”的Jenkins构建作业:
您可以继续向下滚动或单击“源代码管理”选项卡:
现在,如果您向下滚动,您将进入“构建触发器”部分:
在这里,我们配置了Jenkins的轮询并指定了一个cron计划。cron作业显示如下:分钟,小时,日,月和工作日。
然后我们添加以下配置:
我们将创建另一个shell脚本,其中我们执行测试。
确保点击“立即构建”按钮:
然后确保点击构建编号,然后点击控制台输出链接:
请注意,控制台输出打印出Jenkins正在执行的每个步骤。
本章介绍了Jenkins的安装以及导航JenkinsUI的基础知识。下一章将更多地介绍Jenkins仪表板和UI。
本章将详细介绍如何添加新的构建项目、配置构建作业、全局添加环境变量和项目级环境变量。您还将学习如何调试自由样式作业的问题。
本章介绍了如何使用Jenkins创建简单的自由样式脚本。您应该对Unix、Bash有基本的了解,以及环境变量的含义。
我们在本章中在Jenkins中创建了一个简单的自由样式脚本,但我们将快速回顾一下设置自由样式脚本项目所需做的事情。
如果您按照第五章中的说明进行操作,Jenkins的安装和基础知识,那么您应该已经在本地计算机上安装和/或运行了Jenkins服务。如果Jenkins没有在本地运行,请重新查看第五章,Jenkins的安装和基础知识,并阅读与您的操作系统相对应的部分。
在Jenkins仪表板中,有一个名为“新项目”的链接;确保单击它以添加新项目:
单击新项目后,您将被带到以下屏幕:
现在,根据您安装的Jenkins插件数量,您将在构建项目方面在屏幕上看到更多或更少的内容。我们将在本章中为目的输入一个名为FreestyleScripting的名称,但您可以为构建作业选择任何名称。输入名称后,请确保点击FreestyleProject按钮,然后点击OK按钮。
每当在Jenkins下创建新项目时,您将看到以下屏幕:
根据您已安装的Jenkins插件,您可能会看到更多选项卡或构建配置中的项目。
请注意,构建作业配置有多个选项卡。您可以滚动到选项卡中的每个部分,也可以单击选项卡本身。每个选项卡都有不同的功能,您可以在Jenkins构建作业中配置它们。
一般选项卡包含有关您正在创建的Jenkins构建的基本信息,例如描述和其他一般构建信息。查看一般选项卡信息:
通常由您决定要切换哪些选项;您可以单击问号符号获取信息。让我们看看“安静期”选项的含义:
要删除详细信息,只需再次单击问号符号。
源代码管理选项卡是您指定正在使用的版本控制管理系统类型的地方,例如Git、SVN和Mercurial。为了构建作业的目的,我们将点击Git单选按钮并指定GitHub存储库URL:
请注意,分支指定器默认为*/master分支,但您可以通过点击“添加分支”按钮指定任意数量的分支。我们不会添加凭据,因为我们在本地工作,但如果您点击带有钥匙的“添加”按钮,您将看到以下叠加屏幕:
您可以通过点击“种类”输入框选择不同类型的凭据:
您还可以点击源代码管理选项卡底部的“添加”按钮,您将看到可以添加的其他行为:
还有许多高级配置选项,比如子模块,您可以进行配置。
稍后,我们将讨论如何使用GitHub和Bitbucket在您将代码推送到远程存储库时触发Jenkins作业,这比轮询更好。
此部分将根据您安装的Jenkins插件的多少而有更多或更少的环境选项可供使用。在我的情况下,我安装了Golang和Node.js插件,但您可以安装任意数量的环境,比如Clojure和Ruby:
由于我们正在构建一个Golang微库,我们在此配置部分中勾选了“设置Go编程语言工具”复选框。
构建部分是您指定如何构建项目的地方:
如果您点击“添加构建步骤”按钮,您将看到以下选项:
我们将点击“执行shell”选项,这将为我们提供一个Unixshell脚本环境来使用。
请注意,现在我们有一个文本区域可用,我们可以在其中添加Unix脚本命令:
我们将在此shell脚本中添加以下命令:gotest。
在此构建部分,您可以指定成功构建后要运行的任何操作,比如运行代码覆盖和生成JUnit报告:
如果您点击“添加后置构建操作”按钮,您将看到以下选项:
根据您安装的具体Jenkins插件,您将看到更多或更少的选项。
一旦您对构建配置满意,点击“应用”按钮,这将保存您当前的配置选项,或者点击“保存”按钮,这将保存您的选项并将您导航到一个新配置的构建项目:
后置构建操作部分非常有价值,因为您可以在成功构建时调用其他服务,比如报告和收集指标。
您可以以多种不同的方式在Jenkins中添加环境变量。
从Jenkins仪表板,点击“管理Jenkins”按钮:
一旦您点击“管理Jenkins”按钮,您将需要点击“配置系统”按钮:
然后您将被导航到“配置系统”部分,然后可以使用全局属性部分添加环境变量:
请注意,我在这里添加了一个名称为SAMPLE_VALUE的值为HelloBookReaders的内容。现在,这个全局属性在shell环境变量中作为一个环境变量可用。您可以在这个部分添加尽可能多的环境变量。请注意,这个全局属性现在将对每个作业都可用。
您还可以为每个特定的构建项选择更细粒度的环境变量设置。
请确保点击Jenkins链接,您将被路由到Jenkins仪表板。然后点击“管理Jenkins”按钮,就像您添加全局属性时所做的那样。
接下来点击“管理插件”按钮,它看起来像这样:
您现在将被带到以下屏幕:
请注意,我们点击了“可用”选项卡,然后将EnvInject放入筛选框中。确保点击您想要的Jenkins插件,然后点击“无需重启安装”或“立即下载并在重启后安装”按钮。
请注意,我们现在在构建配置区的“构建环境”部分有了一些新的构建选项:
如果您点击“将环境变量注入到构建过程中”,您可以添加您的新环境变量,就像这样:
确保保存您的更改。需要注意的一点是,这个环境变量只适用于这个特定的构建项;它不像我们之前设置的全局属性那样是一个全局属性。
每当您在Jenkins中为一个构建项运行构建时,您可以通过点击您想要查看的特定构建作业来查看构建的所有细节。
现在,如果您点击一个实际的构建,您将进入以下屏幕:
如果您点击“控制台输出”链接,您将看到一个详细的CI构建日志,显示CI服务器执行的所有步骤。请记住,我们编写了一个自由样式的shell脚本。我将添加shell脚本的内容供您查看:
echo"$SAMPLE_VALUE"echo"$ANOTHER_SAMPLE_NAME"gotest请注意,我在这里添加了我们之前定义的两个不同的环境变量,并且我只是将它们发送到标准输出。
现在,如果您查看构建作业的输出,您将看到以下输出:
最后一个操作是实际执行shell脚本。在前面的屏幕截图中要注意的一件事是,因为在Jenkins中启用了执行跟踪,所以shell脚本中的每个命令都会被打印到标准输出。请记住,执行跟踪只是意味着在shell脚本中运行的每个命令以及命令本身的输出都将显示出来。例如,echo"$ANOTHER_SAMPLE_NAME"命令的值为echo"HelloBookReaders",将被打印到标准输出,然后打印出消息HelloBookReaders。最后要注意的是,构建显示了文本PASS,并以SUCCESS完成。
请注意我们如何记录具有简单信息的环境变量。有时候值在CI环境中没有被设置,这是您所期望的,这就是将值记录到标准输出中非常有帮助的地方。使用EnvInject插件的一个好处是,它将掩盖您注入到构建作业中的密码,以便您不会意外记录机密或保密信息:
请注意,在前面的屏幕截图中,我们已经检查了将密码注入到构建作业中作为环境变量,并为环境变量指定了名称和密码。如果您在构建作业中意外执行echo$SecretName,它将掩盖$SecretName的值,以便您不会在构建中泄露机密信息。
在本章中,您学习了更多关于Jenkins仪表板的知识。您学习了如何添加构建作业项以及配置自由风格构建作业的所有部分,如何将环境变量添加到Jenkins作业中,以及如何调试自由风格作业中的问题。
下一章将介绍如何构建Jenkins插件,并具体介绍构建过程,包括编写Java代码和使用Maven构建工具。
本章将详细介绍Jenkins中的插件,我们将首先看如何在Windows、Linux和macOS中设置Maven。然后我们将通过为Jenkins创建一个HelloWorld插件来了解插件开发,然后我们将简要介绍Jenkins插件站点以及如何浏览和使用它来找到各种插件。
本章是关于在Jenkins中构建插件的,您需要对Java编程语言有基本的了解,并了解Maven等构建工具的用途。
JenkinsCI已经提供了某些功能,包括构建、部署和自动化软件项目。您通常可以通过Jenkins中大量的插件生态系统获得您想要的任何额外行为。
转到插件索引以找到您需要的任何插件,我们将在本章的后面部分讨论这个问题。如果您访问Jenkins维基中的插件教程,您将获得创建Jenkins插件的完整说明。还有一些Jenkins维基之外的教程可以使用。您可以转到Jenkins原型库查看HelloWorld插件示例。
您需要转到Jenkins仪表板中的“管理Jenkins”链接:
确保点击“接受许可协议”单选按钮,然后点击Windows。下载并确保选择正确的架构,即32位或64位操作系统。
安装Java后,只需使用以下命令验证安装:
java-version这应该返回已安装的Java的当前版本。
您可以在Windows上以几种不同的方式安装Maven,但请确保您至少安装了Windows7操作系统和Java软件开发工具包(SDK)1.7或更高版本。如果您在第五章中跟随了Jenkins的安装和基础知识,那么您应该已经安装了Java。
echo%JAVA_HOME%然后,您需要通过以下方式将此Maven二进制可执行文件添加到Windows路径中:
首先确保您已安装JavaSDK,您可以在命令提示符中确认:
C:\ProgramFiles\Apache\maven是一个可能的位置,你可以使用。
您需要使用系统属性将M2_HOME和MAVEN_HOME变量都添加到Windows环境中,并且您需要将环境变量指向您的Maven文件夹。
通过附加Mavenbin文件夹%M2_HOME%\bin来更新PATH变量,这样您就可以在系统中的任何位置运行Maven可执行文件。
要验证Maven是否正确运行,请在命令提示符中运行以下命令:
mvn--version该命令应显示当前的Maven版本、Java版本和操作系统信息。
您需要确保macOS操作系统中已安装JavaSDK。如果您在第五章中跟随了Jenkins的安装和基础知识,那么您应该已经安装了Java。
首先确保Java已安装,通过在Mac终端应用程序中运行以下命令:
java-versionjavaversion"1.8.0_162"Java(TM)SERuntimeEnvironment(build1.8.0_162-b12)JavaHotSpot(TM)64-BitServerVM(build25.162-b12,mixedmode)您需要在系统上安装Java1.7或更高版本。
brewinstallmaven确保在您的.bashrc或.zshrc文件中设置以下环境变量:
exportJAVA_HOME=`/usr/libexec/java_home-v1.8`确保Maven已经正确安装,通过在Mac终端中运行以下命令:
mvn--versionApacheMaven3.5.3(3383c37e1f9e9b3bc3df5050c29c8aff9f295297;2018-02-24T14:49:05-05:00)Mavenhome:/usr/local/Cellar/maven/3.5.3/libexecJavaversion:1.8.0_162,vendor:OracleCorporationJavahome:/Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home/jreDefaultlocale:en_US,platformencoding:UTF-8OSname:"macosx",version:"10.13.4",arch:"x86_64",family:"mac"请注意,mvn二进制可执行文件打印出了已安装的Maven版本、Java版本和特定于操作系统的信息。
我们将在Ubuntu16.04DigitalOceanDroplet上安装Maven,但您应该能够在其他Linux发行版上运行类似的命令。请按照说明在您特定的Linux发行版上安装Maven。
确保Java已安装在您的Linux发行版中,您可以通过在终端shell中运行以下命令来检查:
java-versionopenjdkversion"9-internal"OpenJDKRuntimeEnvironment(build9-internal+0-2016-04-14-195246.buildd.src)OpenJDK64-BitServerVM(build9-internal+0-2016-04-14-195246.buildd.src,mixedmode)如果尚未安装Java,则运行以下命令:
sudoapt-getupdate&&sudoaptinstallopenjdk-9-jre接下来,在终端应用程序中运行以下命令安装Maven:
sudoapt-getinstallmaven接下来,您需要确保您的JAVA_HOME环境变量已设置。由于我们在UbuntuLinux操作系统中安装了Java1.9,我们将运行以下命令:
exportJAVA_HOME=/usr/lib/jvm/java-1.9.0-openjdk-amd64/您使用的目录可能不同,但如果您未设置此环境变量,则Maven将报告此警告。
通过在终端应用程序中运行以下命令来检查Maven是否已正确安装:
mvn--versionApacheMaven3.3.9Mavenhome:/usr/share/mavenJavaversion:9-internal,vendor:OracleCorporationJavahome:/usr/lib/jvm/java-9-openjdk-amd64Defaultlocale:en_US,platformencoding:UTF-8OSname:"linux",version:"4.4.0-127-generic",arch:"amd64",family:"unix"请注意,Maven二进制可执行文件打印出当前安装的Maven版本、当前安装的Java版本和特定于操作系统的信息,就像Windows和Mac操作系统一样。
有几个步骤是必要的,以便设置、运行和安装Jenkins插件。
根据您当前的操作系统,您需要创建/编辑.m2/settings.xml文件。
通过在命令提示符中发出以下命令,Windows用户将找到settings.xml文件:
echo%USERPROFILE%\.m2\settings.xmlMac操作系统用户可以在~/.m2/settings.xml中编辑/创建settings.xml文件。
settings.xml文件中的settings元素包含用于以各种方式配置Maven执行的值的元素,例如pom.xml,但不应捆绑到任何特定项目或分发给受众。这些值包括本地存储库位置、替代远程存储库服务器和身份验证信息。
将以下内容放入settings.xml文件中:
强烈建议您将您的settings.xml文件设置为正确运行您的Jenkins插件!
我们将发出以下命令以生成Jenkins的HelloWorld插件:
mvnarchetype:generate-Dfilter=io.jenkins.archetypes:hello-world以下是我创建插件的示例运行会话:
请注意,我为原型输入了1,然后选择了插件版本4,并定义了jenkins-helloworld-example-plugin的值,然后按Enter获取默认值:
如果一切顺利,您应该在命令提示符中获得BUILDSUCCESS的输出。
您需要确保能够构建您的Jenkins插件,因此请确保在命令提示符中运行以下命令:
//Firstgointothenewlycreateddirectorycdjenkins-helloworld-example-plugin//Thenrunthemavenbuildcommandmvnpackagemvnpackage命令将创建一个target目录,并运行您在该目录中创建的任何测试:
请注意,Jenkins原型实际上为我们的HelloWorldJenkins插件示例创建了一些测试。
以下是新创建的jenkins-helloworld-example-plugin目录的屏幕截图:
src目录包含Jenkins插件的源文件以及插件的测试。
目标目录是通过mvn包生成的。还有一个pom.xml文件,当我们运行原型子命令时Maven创建了它。
项目对象模型(POM)是Maven中的基本工作单元。它是一个包含有关项目和Maven用于构建项目的配置详细信息的XML文件。它包含大多数项目的默认值。其中包括构建目录,即target,源目录,即src/main/java,以及测试源目录,即src/test/java。
正如我们之前提到的,src目录包含了Jenkins插件的源文件。为了在Jenkins中构建插件,您需要使用Java编程语言进行编写。教授Java编程语言超出了本书的范围,但我们将简要讨论Maven为我们创建的一些文件。
请注意,Maven创建了一个相当长的目录结构,这是常见的,因此helloworld插件的目录结构为./src/main/java/io/jenkins/plugins/sample/HelloWorldBuilder.java。测试文件本身位于./src/test/java/io/jenkins/plugins/sample/HelloWorldBuilderTest.java。
我在这里包含了HelloWorldBuild.java类的源代码:
对于HelloWorldBuilderTest.java中的测试用例,我们使用了Java编程语言的流行单元测试库JUnit。
为了构建Jenkins插件,您需要在插件目录中运行mvninstall命令。
mvninstall命令将构建和测试Jenkins插件,并且更重要的是,创建一个名为pluginname.hpi的文件;或者在我们的情况下,它将在target目录中创建一个名为jenkins-helloworld-example-plugin.hpi的文件,我们可以用它来部署到Jenkins。
我已经在以下屏幕截图中附上了一个示例安装运行:
请注意,此运行通过将我们的Jenkins插件安装到多个位置来完成,我们将使用这些位置来安装我们的Jenkins插件。
然后您需要转到上传插件部分:
点击选择文件,然后找到我们新创建的HelloworldJenkins插件,它应该在目录中:
jenkins-helloworld-example-plugin/target/jenkins-helloworld-example-plugin.hpi然后确保点击上传按钮。
以下是新安装的Helloworld示例插件的屏幕截图:
请注意,Jenkins插件网站除了默认视图之外,还有多个视图可供您使用:
在本章中,您了解了Java的Maven构建工具以及如何在Windows、Linux和macOS上安装它。您还学习了如何使用Maven构建工具创建Jenkins插件,我们简要讨论了一些Java语法以及如何使用Jenkins仪表板中的管理插件UI中的高级选项来安装Jenkins插件。我们还研究了Jenkins插件生态系统。
本章将详细介绍如何使用现有的Jenkins实例设置Jenkins蓝海以及如何使用Docker进行设置。我们将详细了解蓝海用户界面(UI),并讨论Jenkins经典视图和蓝海视图之间的区别。我们还将详细讨论PipelineSyntax,并简要讨论其用途,并解释两种不同类型的PipelineSyntax。
本章需要基本了解如何与UnixShell环境交互。我们还将简要讨论PipelineSyntax,因此如果您具有一些基本的编程技能,就能理解编程语言中关键字的用途。
Jenkins2.0的设计方法和流程与Jenkins1.0相比有所不同。不再使用自由风格的作业,而是使用了一种新的“领域特定语言”(DSL),这是Groovy编程语言的缩写形式。
管道视图在Jenkins1.0中的功能也有所不同。管道阶段视图还帮助我们可视化管道中的各个阶段。
那么,首先,为什么要转移到Jenkins2.0,而不是继续使用Jenkins1.0?Jenkins经典视图被认为是混乱的,并且没有考虑到易用性。Jenkins2.0在更直观的方式上使用Docker镜像上做出了很大的推动。此外,新的UI包括了Jenkins管道编辑器,并通过引入管道视图改变了您查找构建的方式。新UI的目标是减少混乱,增加对使用Jenkins的团队中每个成员的清晰度。新UI还具有GitHub和Bitbucket集成以及Git集成。Jenkins2.0UI本质上是您安装的一组插件,称为蓝海。
为了在Jenkins实例上安装插件,您必须具有通过基于矩阵的安全设置的管理员权限,任何Jenkins管理员也可以配置系统中其他用户的权限。
安装蓝海插件的步骤如下:
请阅读第七章,开发插件,特别是安装Jenkins插件部分,以获取更多信息。
您需要确保已安装Docker才能获取JenkinsCIDocker镜像。
由于Docker利用操作系统的虚拟化技术,因此Docker的安装要求是特定的。
OSX的要求是:
Windows的要求是:
通过使用Windows命令提示符或OSX/Linux终端应用程序检查Docker版本来确保Docker已安装。在命令行中运行以下命令:
注意这里我安装了Docker版本18。
在Windows命令提示符或终端中运行以下命令:
请注意,我已经拉取了jenkinsci/blueoceanDocker镜像,因此该命令没有从DockerHub中拉取,而是打印出了一个SHA哈希校验和。这表明我已经拥有了jenkinsci/blueocean的最新Docker镜像。
接下来,您需要让JenkinsDocker容器运行起来,并且您需要在终端或命令提示符中运行以下命令:
您可以通过简单地创建一个为您执行此操作的shell脚本或创建一个别名来简化此过程。
以下是我在文本编辑器中创建的一个shell脚本:
我有一个个人的bin目录,我在~/bin中存储所有个人脚本,然后我确保将其添加到PATH变量中。脚本文件名为run-jenkinsci-blueocean。我们需要确保该脚本是可执行的,通过发出以下命令:
chmod+xrun-jenkinsci-blueocean然后我只需要运行~/bin/run-jenkinsci-blueocean命令。
您也可以在Unix中创建类似的别名:
#inside~/.zshrcaliasrunJenkinsDockerImage='dockerrun-urootjenkins-blueocean--rm-d-p8080:8080-p50000:50000-vjenkins-data:/var/jenkins_home-v/var/run/docker.sock:/var/run/docker.sockjenkinsci/blueocean'请注意,我在我的.zshrc文件中添加了这个shell别名,但您也可以将其添加到.bashrc文件中。
Windows用户可以创建一个批处理文件或找到其他方法来使运行Docker命令更容易。
为了停止Docker容器,您可以运行以下命令:
dockerps-a此命令将显示系统中所有正在运行的容器;您需要查看ContainerID、NAMES列,并复制与Docker镜像jenkinsci/blueocean对应的ID。最后,要停止容器,您需要运行以下命令:
dockerstopjenkins-blueocean请注意,因为我们在shell脚本中的dockerrun命令中使用了--namejenkins-blueocean选项,Docker创建了一个名为jenkins-blueocean的容器;如果我们没有这样做,那么Docker将为我们创建一个容器的名称。当您在终端或命令提示符中发出dockerps-a命令时,您可以使用容器ID和名称来停止容器。
通过点击安装建议插件按钮,您将获得所有建议的插件和依赖插件,这将帮助您在新的Jenkins2.0流程中使用流水线等功能。
您需要确保点击打开BlueOcean按钮,看起来类似于这样:
这是您将看到的初始屏幕,因为我们还没有创建任何流水线:
我们将在接下来的部分中探索流水线语法以及如何在Jenkins2.0UI中进行导航。
我们将使用Jenkins2.0UI创建我们的第一个流水线,并且还将使用内置到新Jenkins2.0UI中的流水线编辑器创建一个Jenkinsfile。
我们要做的第一步是点击“创建新流水线”按钮。您将被重定向到以下屏幕:
如果您在GitHub上还没有个人访问令牌,您将需要创建一个。请注意,在以下截图中,有一个名为“在此处创建访问密钥”的链接:
点击“在此处创建访问密钥”链接后,您将被重定向到以下GitHub页面:
您可以保持默认选项勾选,然后点击标题为“生成令牌”的绿色按钮。确保将此个人访问令牌保存在安全的地方,因为它只会显示一次;复制它,因为我们将需要它。您需要将访问令牌粘贴到“连接到Github”输入框中,然后点击蓝色的“连接”按钮:
您需要选择您所属的GitHub组织。在接下来的截图中,我选择了GitHub用户名组织jbelmont:
您需要做的最后一步是实际选择要创建Jenkins流水线的GitHub存储库。在这里的截图中,我输入了cucumber-examples并选择了下拉菜单。然后蓝色的“创建流水线”按钮被启用:
在我们选择的GitHub存储库中,没有现有的Jenkinsfile,因此我们被重定向到流水线编辑器屏幕,我们可以在其中创建我们的第一个Jenkinsfile:
我们需要为Node.js和代理添加一个Docker镜像,看起来类似于这样:
请注意,我们使用-v选项为Docker挂载数据卷提供了一个图像和参数。
接下来,我们点击灰色的加号按钮,然后我们将看到以下更改:
接下来,在为阶段命名后,我们点击蓝色的“添加步骤”按钮。对于这个演示,我们将选择“构建”:
接下来,我们需要选择一个步骤选项。我们将选择标题为“Shell脚本”的选项,这将安装我们所有的Node.js依赖项:
接下来,我们输入一些要在我们的Shell脚本中运行的命令:
接下来,我们将再次点击灰色的加号按钮,以向我们的流水线添加一个阶段,现在看起来类似于这样:
接下来,我们将为此阶段输入一个名称,并且在本章中,我们将选择CucumberTests:
接下来,我们为此阶段添加一个步骤,并且我们将再次选择Shell脚本作为选项:
最后,我们将点击保存按钮并提供提交消息,以便将此更改推送到我们的GitHub存储库:
点击蓝色的“保存并运行”按钮后,Jenkinsfile将合并到主分支中,并且管道将运行。
在JenkinsBlueOcean中,您习惯使用的一些视图在JenkinsBlueOcean中不可用。JenkinsBlueOcean背后的主要概念是使Jenkins内部导航更加便捷,并通过更好的图标和页面导航改进JenkinsUI。新JenkinsUI的许多灵感来自强调世界已经从功能性开发人员工具转向开发人员体验的《蓝海战略》一书,并且新UI致力于改善Jenkins的开发人员体验。
此列表将根据您在Jenkins实例中添加的管道数量而有所不同。请注意,您可以标记一个管道,并且有标有名称、健康、分支和PR的列。
如果您点击实际管道,那么您将进入一个管道详细信息页面,其中包含有关特定管道中运行的所有阶段的所有详细信息。接下来的屏幕截图是base64管道:
您可以单击管道视图中的每个节点,并查看该阶段完成的所有工作。在第一个屏幕截图中,我们点击“构建信息”节点,以查看在该特定阶段运行的命令,其中包括拉取GitHub存储库的最新副本并运行goversion和gofmt命令:
请注意,第二个节点标记为“运行测试”,当我们点击该特定节点时,我们只看到gotest命令,该命令在Golang中运行我们的单元测试用例:
管道视图的一大优点是,您可以更清晰地查看持续集成构建中每个阶段的更好布局的可视化效果。
如果您点击管道中的实际阶段,由>符号表示,它将向您显示一个下拉视图,其中包含该特定阶段的详细信息:
请注意,这里我们点击了“运行测试”阶段,以查看报告,报告显示我们用Golang编写的单元测试用例已通过。
还有其他视图可供使用,例如拉取请求视图,它会显示所有打开的拉取请求以及分支视图:
JenkinsBlueOcean视图仍在进行中,因此任何管理任务,例如添加插件和添加安全信息,仍然在Jenkins经典视图中完成。
在cucumber-examples存储库中,我们使用管道编辑器创建了一个Jenkinsfile。您实际上可以在不使用管道编辑器的情况下编写Jenkinsfile,尽管我建议在调试管道脚本时使用它,因为编辑器具有一些很好的功能。
代理部分指定整个管道或特定阶段将在Jenkins环境中执行的位置,取决于代理部分的放置位置。该部分必须在管道块的顶层内定义,但阶段级别的使用是可选的。
stages关键字包含一个或多个阶段指令的序列;stages部分是管道描述的大部分工作将被放置的地方。
本章讨论了如何在现有的Jenkins实例中使用JenkinsBlueOcean视图以及如何使用Docker设置BlueOcean视图。我们看了许多不同的JenkinsBlueOcean视图,并讨论了它们与Jenkins经典视图之间的一些区别。我们还讨论了管道语法和Jenkinsfile。下一章将介绍TravisCI的安装和基本用法。
本章将需要一些基本的编程技能,并且我们在前几章讨论过的许多CI概念将在本章中得到应用。如果您尝试创建一个GitHub账户和TravisCI账户将会很有帮助。您可以按照TravisCI先决条件部分中的步骤进行操作。一些示例使用了Docker,这是一种容器技术,因此如果您对容器和Docker有一些了解将会很有帮助。您将在本章学习有关YAML语法的知识。本章中有一些命令使用了命令行应用程序,因此熟悉命令行应用程序或CLI将会很有帮助。
看一下截图——您所需要做的就是提供用户名、电子邮件和密码,然后点击“注册GitHub”按钮:
为了本章的目的,我们将创建一个名为packtci的GitHub用户名。一旦您点击了“注册GitHub”按钮,您将被带到以下页面:
请注意,您可以在GitHub中免费创建无限数量的公共存储库,而私有存储库需要支付月度订阅费。一旦单击“继续”按钮,您将被重定向到以下页面:
如果您愿意,您可以通过滚动到页面底部并单击“跳过此步骤”按钮来跳过所有这些选项。一旦单击“提交”按钮或“跳过此步骤”按钮,您将被带到这个页面:
您应该收到来自GitHub的一封电子邮件;请查看以下截图:
您需要单击该链接以验证您在GitHub中的帐户,然后您应该已经设置好了您的GitHub帐户。
请注意,我们在TravisCI中有一个API令牌,我们将在以后使用。在这个新帐户中我们没有任何GitHub项目,所以没有显示。在本章中,我将创建一个运行一些基本测试的GitHub项目。
为了创建一个新的GitHub存储库,您需要将您的SSH密钥添加到您的用户帐户中。如果您的系统中没有任何SSH密钥,可以使用以下命令创建:
请注意,我提供了一个电子邮件地址并指定了一种RSA类型的加密算法。一旦运行此命令,它将在您的系统上创建一个公钥和私钥。
一旦您创建了SSH密钥,您只需要将公钥上传到GitHub。您需要复制文件的内容;如果您使用macOS,您可以运行以下命令将其复制到系统剪贴板:
ssh-keygen-trsa-b4096-C"myemail@someemailaddress.com"#Thiscommandgeneratesafileinthepaththatyoupickintheinteractivepromptwhichinthiscaseis~/.ssh/id_rsa_example.pubpbcopy<~/.ssh/id_rsa_example.pub您需要进入GitHub的设置页面:
然后您需要在设置页面中单击以下按钮:
接下来,您需要单击“新SSH密钥”按钮,然后提供一个名称并粘贴您的SSH密钥的内容。在下面的截图中,我提供了一个名为示例SSH密钥的名称,然后粘贴了我的公钥的内容:
您只需要单击“添加SSH密钥”按钮,然后您就可以准备好向您在GitHub中拥有的任何存储库提交更改。
我们首先在存储库的根目录创建一个名为.travis.yml的文件,然后将以下内容复制到此文件中:
language:node_jsnode_js:-"6.14.1"install:-npminstallscript:npmtest我们将更详细地讨论YML脚本的每个条目,但基本上我们对TravisCI说的是这是一个Node.js项目,我们希望TravisCI在CI构建中使用node版本6.14.1,然后使用npm软件包管理器安装项目所需的所有依赖项,最后使用npmtest命令运行所有测试。我将提交此更改到functional-summer存储库,然后我们将看到如何为该项目切换TravisCI。
接下来,我添加了一张截图,详细说明了将新存储库添加到TravisCI的步骤:
请注意,在截图中,我添加了一个文本块,说第一步是点击“同步帐户”按钮,这是必要的,这样TravisCI才能看到您已添加到GitHub帐户中的任何新存储库。一旦TravisCI同步了您的帐户,您就应该能够在您的帐户中看到您的存储库。根据您已经拥有多少项目,您可能需要按存储库名称筛选以找到您的项目。下一步是切换带有您的存储库名称的滑块,就像截图中所示的那样。
在下面的截图中,我们已经在TravisUI上切换了functional-summer存储库,现在我们只需点击行即可进入这个新添加的TravisCI构建作业:
一旦您点击行,您将被重定向到TravisCI中的以下页面:
我们还没有触发任何构建,但TravisCI有一些默认设置。如果您将更改提交到任何推送的分支,或者在GitHub上打开拉取请求,TravisCI将启动构建。让我们对functional-summer存储库进行一些微小的更改,这将触发TravisCI中的构建。如果您点击“构建历史”选项卡,您会注意到已创建了一个带有Git提交更改的构建:
您可以单击TravisCI屏幕左侧的构建作业项目,看起来像这样:
或者,您可以单击“当前”选项卡,以查看已配置存储库的当前执行作业。为了查看作业日志,您需要滚动到“作业日志”选项卡,并查看TravisCI构建中的运行命令,对于functional-summer存储库,看起来像这样:
请记住,在我们添加到GitHub的.travis.yml脚本中,我们指定了四件事:
您可以在作业日志中确认这些步骤是否已运行。请注意,在上一张截图中,有右箭头链接,展开以获取CI构建中每个命令的更多详细信息。
现在我们已经了解了YAML语法,我们可以更详细地解释TravisCI脚本的各个部分。
language:go在.travis.yml脚本的这一部分中,我们添加了我们将在持续集成构建中使用的编程语言。这通常是您添加到.travis.yml脚本中的第一个条目。
TravisCI支持许多编程语言,例如:
您可以通过在YML脚本中使用sudo和dist字段来设置TravisCI中更自定义的环境。
您可以在TravisYML脚本中使用以下条目来使用UbuntuPrecise基础设施:
sudo:enableddist:precise默认基础设施您可以通过添加此条目来显式设置默认基础设施,即基于容器的Ubuntu14.04环境:
sudo:false这并非必须,因为您可以只设置语言,默认基础设施将为您完成。
您可以在TravisYML脚本中使用以下条目来使用UbuntuTrusty基础设施:
sudo:enableddist:trusty基于容器的基础设施您可以在TravisYML脚本中使用以下条目来使用基于容器的基础设施:
sudo:falsedist:trusty请注意,我们在此处明确将sudo权限设置为false,并使用UbuntuTrusty。
您可以在TravisYML脚本中使用以下条目来使用macOS基础设施:
os:osx构建自定义在TravisCI中,您可以以各种方式自定义构建,我们将从解释构建生命周期开始。
TravisCI中的构建由两个步骤组成:
这一步骤正式称为before_install步骤,您可以在此步骤中安装CI构建中的任何其他依赖项,并启动自定义服务。
我们已经在install步骤中看到了这一步骤,您可以在此步骤中安装CI构建所需的任何依赖项。
在after_script步骤中,您可以执行任何有用的命令,例如报告和分析。您可能需要发布代码覆盖率报告或在代码库中创建指标报告。
以下是TravisCI的完整生命周期:
如果在before_install、install或before_script生命周期事件中发生错误,则CI构建将立即出错,CI构建将停止。
如果在脚本生命周期事件中发生错误,则构建将失败,但CI构建将继续运行。
如果在after_success、after_failure、after_script和after_deploy生命周期事件中发生错误,则构建将不会标记为失败,但如果其中任何生命周期事件导致超时,则构建将标记为失败。
您可以通过在before_install生命周期事件中添加条目来轻松安装另一种编程语言。最好指定您的主要语言,然后是次要语言。
在TravisCIYML脚本中,我们将Go版本1.10指定为主要编程语言,然后将Node.js指定为次要语言。我们在before_install生命周期事件中安装Node.js依赖项,然后运行Golang测试,然后是Node.js测试:
Docker可以在TravisCI中使用,启用Docker的唯一步骤是将以下条目添加到您的TravisCIYML脚本中:
sudo:requiredservices:-docker请注意,我们在services块中添加了一个条目,并添加了Docker的列表条目。
在TravisYML脚本中,我们指定了sudo权限,Golang编程语言,然后指定了Docker服务,下载了自定义Docker镜像jbelmont/print-average:1.0,然后运行Docker容器并将其删除:
sudo:requiredlanguage:goservices:-dockerbefore_install:-dockerpulljbelmont/print-average:1.0script:-dockerrun--rmjbelmont/print-average:1.0我已经添加了TravisCI构建的截图供您参考:
在TravisYML脚本中,我们在TravisCI构建中设置了许多不同的操作。首先,我们将语言设置为node_js,然后将node_js的版本设置为8.11,然后设置了一个名为dist:trusty的新属性,该属性将TravisCI环境设置为Ubuntu14.04,称为Trusty。然后我们使用add-ons块添加了最新的稳定版本的Chrome。然后我们在CI构建上的端口9222上运行了稳定版本的GoogleChrome,然后我们使用cache块,以便在每次CI构建运行时缓存node_modules。然后我们安装我们的Node.js依赖项,最后我们使用Jest库运行Node.js测试:
在第二个截图中,我们使用GoogleChromePuppeteer库运行测试。请注意,构建以0的退出状态运行,并成功完成:
在本章中,我们已经讨论了TravisCI的许多方面,包括TravisCI和Jenkins之间的区别。我们介绍了一些先决条件操作,以便设置TravisCI并学会将SSH密钥添加到您的GitHub帐户。然后我们详细解释了TravisCI构建作业,并详细介绍了YAML语法。然后我们探讨了许多TravisYML脚本的真实示例,并解释了TravisCI中的构建生命周期,并介绍了不同方面,例如启动Docker等服务及其在TravisCI中的用途。
在下一章中,我们将介绍所有TravisCLI命令,解释如何在Linux、macOS和Windows上安装TravisCLI,并介绍如何使用TravisCLI命令自动化任务,如加密凭据。
在上一章中,我们向您展示了如何在软件项目中配置TravisCI,并解释了如何使用TravisCI的基础知识。本章将帮助您在操作系统上安装TravisCLI,并且我们将介绍TravisCI中所有不同类型的命令,例如一般API命令,存储库命令等。我们将介绍CLI命令可以使用的不同选项,并且我们还将详细介绍每个命令的含义。我们还将通过使用我们的访问令牌和curlREST客户端直接查看使用TravisAPI。我们将简要介绍TravisPro和Enterprise版本。
本章将需要一些基本的Unix编程技能和关于使用命令行终端应用程序的知识。如果您使用的是Windows操作系统,则考虑使用命令提示符或PowerShell应用程序。如果您使用的是macOS操作系统,则使用默认安装的终端应用程序。如果您使用的是Linux,则应该已经安装或可用终端。
您可以通过在命令行或终端中运行以下命令来检查是否已安装Ruby:
我们需要在RubyInstaller下载站点上选择RubyDevkit版本2.5.1,然后确保接受许可协议,然后选择适当的安装选项。确保安装开发工具链。
在这里,我们已经在系统上安装了Ruby版本2.5.1,正如我们所期望的那样:
在这一步中,我们在Windows命令提示符中安装TravisRubyGems:
在最后一步中,我们验证TravisCLIRubyGem是否已安装在我们的系统上;它报告版本1.8.8:
Linux操作系统有多个不同的软件包管理器,因此如何在系统上安装Ruby取决于您的特定Linux操作系统。我们将在DigitalOcean服务器上的Ubuntu14.04上安装Ruby和TravisCLI:
geminstalltravis-v1.8.8--no-rdoc--no-ritravisversion1.8.8macOS安装您需要安装Xcode命令行工具,可以使用以下命令来执行:
Ruby已预安装在当前的macOS操作系统上,因此您只需要运行以下命令来安装TravisCLI:
这里我使用了sudo,因为我需要提升的管理员权限来安装RubyGem。
如果您在终端中看到此消息,您将知道TravisCLI已安装:
这里我使用的是TravisCLI版本1.8.8,但您的特定版本可能不同。
非APITravisCLI命令包括help和version命令。这些命令不直接命中TravisAPI,而是打印有关TravisCLI的有用信息。
help命令将显示特定命令接受的参数和选项。
在下面的截图中,我们在命令行终端中运行travishelp命令:
如果您想获取特定命令的帮助,只需使用travishelpCOMMAND。
以下是有关Travis中whoami命令的更多信息的截图:
version命令显示安装在系统上的当前TravisCLI客户端。以下截图显示了TravisCLI的当前客户端版本为1.8.8:
API命令直接命中TravisAPI,有些需要您拥有适当的访问令牌,您可以使用travislogin命令获取。
login命令通常是您需要使用的第一个命令,以便与TravisAPI一起工作,因为它会对您进行身份验证。
login命令将要求您输入GitHub用户名和密码,但不会将这些凭据发送到TravisCI。相反,它使用您的用户名和密码创建一个GitHubAPI令牌,然后将令牌显示给TravisAPI,然后运行一系列检查以确保您是您所说的人。然后它会给您一个TravisAPI的访问令牌,并最终Travis客户端将再次删除GitHub令牌。只要您成功运行travislogin命令,所有这些步骤都会在幕后发生。
在下面的截图中,我们运行travislogin命令并提供GitHub用户名和密码:
token命令用于显示当前访问令牌。截图中的访问令牌已经被隐藏,以确保安全:
请记住,我们可以通过运行以下命令在Travis中找到特定命令的所有选项:
travishelp在下面的截图中,我们为accounts命令运行help命令:
有一个名为--debug的选项,我们将使用它来调试发送到TravisAPI的HTTP请求。在下面的截图中,我们获得了有关发送到Travis的请求的其他信息,例如命中的端点是GET"accounts/"{:all=>true}以及其他信息:
console命令将您放入一个交互式的Ruby会话中,其中所有实体都被导入到全局命名空间中,并确保您已经通过Travis进行了身份验证,如果您正在设置正确。在下面的截图中,我按下了Tab并在控制台会话中获得了自动完成:
endpoint命令打印出我们正在使用的API端点。请注意,在截图中,我们正在使用TravisAPI的免费开源版本:
现在,如果我们回到Travis监视器正在运行的终端会话中,我们将看到已启动构建,然后它被传递:
我们有一个2.1的构建作业;在.travis.yml文件中,我们没有指定任何其他构建作业,因此TravisCI将所有构建作业捆绑到一个构建作业中。
您可以通过使用travisrawRESOURCE命令直接对TravisAPI进行API调用。请记住,我们始终可以使用travishelpCOMMAND来查找如何在TravisCLI中使用特定命令。在以下截图中,我们对raw命令运行help命令:
现在我们知道如何运行raw命令,让我们向TravisAPI的此端点发出请求:
在不久的将来,TravisAPI计划废弃V2API,只有V3API将得到官方支持。您可以使用API资源浏览器对V3API进行REST调用:
GET/owner/{owner.login}在以下截图中,我们使用API资源浏览器对以下端点进行REST调用:
我们将发出travistoken命令,以便我们可以将访问令牌复制到系统剪贴板:
travistoken接下来,我们将运行travisendpoint命令并复制URL:
repos命令将列出存储库,无论它们是活动的还是不活动的,并且有各种可以使用的选项。在下面的截图中,我们使用了-m选项来匹配packtciGitHub用户的所有存储库:
sync命令可以替换我们在第九章中所采取的步骤,即单击同步帐户按钮以同步我们帐户中的所有存储库信息。
language:blahnode_js:8.11现在让我们运行lint命令来检查语法。在下面的截图中,Travis通知我们正在使用blah的非法值,并且它将默认为ruby作为语言:
让我们修复语言条目以使用Node.js,然后再次运行lint命令:
lint命令报告我们现在在.travis.yml脚本中有有效的语法。
whatsup命令让您查看最近在Travis中发生的活动。当我们运行这个whatsup命令时,它给了我们TravisCI中最近的活动:
在packtciTravis帐户中,只有一个用户,但是您可以在TravisCI帐户中拥有许多用户,因此只查看您的存储库可能更有用。记住,我们可以使用help命令查找特定命令的更多选项。作为练习,使用help命令查找仅显示您自己的存储库的选项。
whoami命令报告packtci,正如我们所期望的那样。
存储库命令具有API命令的所有选项,此外,您还可以使用--repoowner/name选项指定要使用的特定存储库。
branches命令显示版本控制中每个分支的最新构建信息:
当您运行此命令时,可能会显示更多的分支。
cache命令可以列出存储库中的所有缓存:
cache命令还可以删除存储库中的缓存,如果您使用-d、--delete选项:
我们收到了一条红色的警告消息,要求我们确认删除缓存。
enable命令将在您的GitHub存储库中激活TravisCI:
enable命令有助于替换我们在第九章中所采取的手动步骤,即激活TravisCI中的存储库,在那里我们在Travisweb客户端中点击滑块按钮以激活存储库。
disable命令将使您的GitHub存储库中的TravisCI处于非活动状态:
让我们使用以下命令启用functional-patterns存储库:
travisenable现在让我们使用以下命令向存储库推送提交:
gitcommit--amend--no-edit先前的git命令允许您重用之前使用的gitcommit命令,但您需要发出以下命令:
在上一张屏幕截图中,我们发出了whatsup命令来查看构建的当前状态,并注意到packtci/functional-patterns开始了作业编号1。然后我们发出了traviscancel命令,并提供了一个参数1。这并不完全必要,因为这是当前的构建,所以我们可以只发出traviscancel命令。当我们运行traviswhatsup命令时,构建被取消。
encrypt命令允许您加密存储在环境变量和/或部署密钥中的秘密值,这些值您不希望公开显示:
我在.travis.yml脚本中添加了一个示例片段:
如果您愿意,您可以使用--add选项自动添加条目,尽管您在.travis.yml脚本中的任何注释都将消失,间距也将消失,因此在运行--add选项时要注意这一点。
encrypt-file命令将使用对称(AES-256)加密加密整个文件,并将秘密存储在文件中。让我们创建一个名为secret.txt的文件,并将以下条目添加到其中:
SECRET_VALUE=ABCDE12345CLIENT_ID=rocky123CLIENT_SECRET=abc222222!现在让我们加密我们的秘密文件:
因此,现在我们将把这个条目添加到我们的.travis.yml脚本中:
before_install:-opensslaes-256-cbc-K$encrypted_74945c17fbe2_key-iv$encrypted_74945c17fbe2_iv-insecret.txt.enc-outsecret.txt-d然后它可以为我们解密秘密文本文件中的值。
env命令可以列出为存储库设置的所有环境变量:
我们没有为此存储库设置任何环境变量。
env命令还可以从存储库中设置环境变量:
我们设置了一个名为API_URL的环境变量,并且它现在显示为多语言存储库的环境变量。
env命令还可以从存储库中删除环境变量:
travisenv列表命令现在报告我们没有为多语言存储库设置任何环境变量,这是我们所期望的。
env命令可用于清除存储库中设置的所有环境变量:
history命令显示存储库的构建历史:
history命令默认只显示最后10个构建,但您可以使用--limit选项来限制或扩展构建的数量。
该过程的步骤如下:
logs命令将打印出存储库的TravisCI日志的内容,默认情况下它将打印出最新构建的第一个作业。在这里,我们在最近创建的存储库中运行logs命令;但是,它不会通过CI构建,因为存储库中还没有任何可构建的Go文件:
open命令将在TravisCIweb客户端中打开存储库:
您可以使用--print选项来打印出URL,而不是默认情况下打开到特定项目视图。运行travishelpopen命令以获取更多选项。
pubkey命令将打印出存储库的公共SSH密钥:
出于安全原因,我删除了公钥信息。您还可以以不同格式显示密钥。例如,如果您使用--pem选项,您的密钥将显示如下:
运行travishelppubkey命令以显示此命令的更多选项:
restart命令将重新启动最新的构建:
requests命令将列出TravisCI收到的任何构建请求。我们将在刚刚为travis-init-command存储库触发的构建上运行travisrequests命令:
由于其中还没有任何可构建的Go文件,构建仍然失败。
settings命令将显示存储库的设置:
请注意,减号(-)表示已禁用,而加号(+)表示已启用。
travissettings命令也可用于启用、禁用和设置设置:
setup命令可帮助您配置Travis附加功能:
show命令默认显示有关最近CI构建的一般信息:
第一个命令travisshow显示了最近的构建,在下一次运行中,我们提供了一个特定的构建编号。
sshkey命令将检查是否设置了自定义SSH密钥:
travissshkey此命令仅适用于Travis的Pro版本,如果没有SSH密钥,它将报告未安装自定义SSH密钥。
status命令将输出有关项目最后构建的一行消息:
请注意,主机是travis-ci.com,这是TravisPRO。
如果您已设置TravisEnterprise,则可以使用--enterprise选项,以便访问您的企业域所在的位置:
我们没有设置TravisEnterprise,但如果您设置了,则可以在此处输入您的域。
在本章中,我们已经介绍了如何在Windows操作系统、macOS操作系统和Linux操作系统上安装Ruby和TravisCLIRubyGem。我们详细介绍了每个TravisCLI命令,并讨论了使用每个命令的各种方式以及每个命令所接受的一些选项。我们还向您展示了如何使用curlREST客户端直接调用TravisAPI。最后,我们还介绍了TravisPro和Enterprise版本中的一些功能。
在下一章中,我们将介绍一些更高级的技术,以记录值并使用TravisCI进行调试。
本章将概述Travis作业日志和作业日志中的各个部分。本章还将解释如何以几种不同的方式调试Travis构建作业,包括使用Docker在本地构建,然后以调试模式运行构建。我们将介绍所有获取作业ID的不同方式,以及如何在公共存储库中启用调试模式,然后使用TravisAPI以调试模式启动构建。我们将解释如何使用tmate,这是一个终端复用器,然后我们将讨论在TravisWeb客户端中记录环境变量。最后,我们将介绍如何在TravisCI中使用Heroku进行部署以及如何调试部署失败。
这一章将需要一些基本的Unix编程技能以及一些bash脚本知识。对于如何进行RESTfulAPI调用的基本理解将会有所帮助,因为我们将使用curl作为REST客户端来调用TravisAPI。对于Docker和容器的基本理解也会有所帮助,因为我们将使用Docker来运行本地构建。
我们在第九章中简要介绍了TravisCI的Web仪表板,TravisCI的安装和基础知识,但让我们再次看看UI的不同部分。
TravisCIWeb客户端有几个必须理解的不同部分:
请注意,TravisCI为我们推送的名为add-test-case的新分支创建了一个新的构建:
此外,您打开的任何拉取请求也将触发TravisCI的新构建:
当您将拉取请求合并到另一个分支时,TravisCI会触发另一个CI构建。
TravisCI中的作业日志以构建系统配置信息开始:
请注意,构建语言设置为go,构建操作系统为UbuntuTrusty14.04:
TravisCI克隆了multiple-languages存储库的新副本,这是持续集成的重要方面。请记住,CI构建应该在每次构建时构建一个新副本,并且不应该有任何假设的环境变量:
请注意,TravisCI为我们设置了一些环境变量,包括GOPATH和PATH环境变量。TravisCI运行goversion命令来验证CI构建中是否安装了Go版本1.10:
请注意,这里没有脚本构建生命周期的构建标签,因为这是CI构建的主要部分。
dockerpulltravisci/ci-garnet:packer-1512502276-986baf0请注意,我们运行dockerpull命令来实际拉取Docker镜像
请注意,我们在分离模式下运行了一个交互式shell会话
dockerexec-ittravis-debugbash-l此命令使用Bashshell启动一个与正在运行的Docker容器的交互式shell会话
su-travis在此命令中,我们切换到Travis用户,而不是默认的root用户
运行gitlog命令并找到我们想要在本地检出的提交。很可能是我们将要检查的顶级Git提交。
gitloggitcheckout2a663fc233d3ae3986fd99efc510369ded92ba94在这一步中,我们要确保只测试与我们想要测试的更改相对应的更改。
NODE_VERSION="6"nvminstall$NODE_VERSIONnpminstall在这一步中,我们使用node版本管理器(nvm)安装Node.js作为第二编程语言,然后运行npminstall命令来安装所有库依赖项
在下面的截图中,我们在本地Docker容器中运行gotest和npmtest命令,以模拟脚本构建生命周期事件:
接下来,您需要使用REST客户端和API令牌来访问调试端点。
您可以使用TravisCLI运行以下命令来获取访问令牌:
travistoken从构建日志中获取作业ID您可以通过展开“构建系统信息”选项卡并查找“作业ID”标签来获取作业ID。在下面的截图中,有一个箭头指向“作业ID”:
如果您点击“查看配置”按钮,URL将会改变,您可以从URL中复制作业ID。在下面的截图中,我们点击了“查看配置”按钮,如下所示:
在此URL中,作业ID是401101740。
您还可以通过调用TravisAPI中的/builds端点来获取作业ID。您需要发起一个GET请求,并提供有效的访问令牌以进行REST调用。以下是使用curlREST客户端的示例请求:
只要您拥有有效的访问令牌,您可以使用任何REST客户端来调用TravisAPI。
以下是针对作业ID40110174的调试端点的示例REST调用:
请注意,在此截图中,我们添加了AuthorizationHTTP标头,并使用TravisCLI通过Bash字符串插值打印出我们的访问令牌:
如果您返回TraviswebUI并查看当前作业日志,您将看到以下内容:
现在,您只需转到命令提示符或终端会话,并输入ssh命令以启动与当前构建的交互式调试会话:
调试模式的SSH会话只会保持30分钟,然后您需要发起另一个API调用来开始另一个调试会话:
以下是可用的便利Bash函数列表:
在下面的截图中,我们运行travis_run_before_install函数:
请记住,这是在before_install生命周期事件中指定的内容,在multiple-languages存储库中:
before_install:-nvminstall$NODE_VERSION现在我们将运行travis_run_install便利Bash函数,该函数安装了在Travisinstall生命周期事件中指定的库依赖项:
在multiple-languages存储库的TravisYML脚本中,我们有以下条目:
install:-npminstall请注意,这正是在运行travis_run_install便利函数时运行的内容。
接下来,我们运行travis_run_script便利函数,该函数运行在Travisscript生命周期事件中定义的任何脚本:
在multiple-languages存储库的TravisYML脚本中,我们在script生命周期事件中有以下条目:
script:-gotest-npmtest如果我们指定了其他生命周期事件,我们可以使用剩余的便利Bash函数。
![
您当然可以在TravisCI中记录一些环境变量,但要小心,不要在日志中记录秘密信息。
TravisCI默认会隐藏诸如令牌和环境变量之类的任何变量,并简单地显示字符串[secure]。
请记住,我们在此存储库中添加了以下加密环境变量第十章中的TravisCICLI命令和自动化:
travisencryptSECRET_VALUE=SuperSecret12345--add请注意,此命令将以下条目添加到TravisYML脚本中:
env:global:secure:WLiuzi0CTx/ta5zuoU5K2LeZgzrAhWATUjngx++Azz7Tw4+XqbxeHZ/6ITymE1YLDRMxdIh8hItvkoNCbPmJ6q1To6bdirloWZq2rlZ5BPGYfVY3cuoUuxTAz1uhhfnngkqd76eJfB4lBUfOIVNAg2rpI7QFAQr1aiIKxjthiTms57fR4dusEi/efVO90I7yzFtyxEa0tLTgW9x+dPSt2ApmJ0EP9tftk7M7Uw/F2Gm1/AzWpM1Blklm/iEHF3ZY6Ij/V+ZG2SCpfrF88m50a8nJF1a+KttZz/TTbwqA58dXNokxcD30HB468/oaGMTJxYLFmG3QMfbXuP2wUkuinIEWQxGBEDh3uw11ZhypCGVNvE6vbRpdIIzywcVcX95G1px+Dgcil+c8AebO1wbWlDXMuWNQHC7JjdQspvLUtsLeyyei3LKshTY7LktvhJEG/+sgd5sejeqnzFmLmC9TdbCazLMFWzqhl+SBcmQtFNVuqAGBlMFlT1l54zFnZl7mixetVeBziuS7xGG3XXm0BsYIQnkcJYxNGv8JrFMSoqBTdQV4C20UyyXAw8s+5lu6dGziiMPSUK4KUSVPJ3hyeNiGhLTBsJn4bnTPiJ5ilVdyNM8RD8X2EJRImT3uvGvuFqHraCBrBuZVaW4RtbGX0JYYtMMMr/P84jKrNC3iFD8=请记住,Travis作业日志中只显示字符串[secure]代替此环境变量。
我们在第三章,持续交付的基础知识中讨论了软件部署,但是为了回顾一下,部署是开发人员创建的软件的最终产品,您的最终用户将使用它。部署通常在成功的CI/CD流水线结束时完成。请记住,CI/CD流水线可以包括提交阶段,在该阶段构建任何二进制文件并运行单元测试套件,然后是第二阶段,可能运行集成测试,然后可能是第三阶段,包括负载测试和/或安全测试,最后是第四阶段,包括一套验收测试。只有当所有CI/CD流水线的阶段都成功完成时,才应启动部署流水线。
在TravisCI中部署相对容易/请记住,您可以使用TravisCLI轻松设置一些部署工具。
以下是一些支持的提供商,您可以在TravisCI中用于部署:
请注意,我们使用了herokuauth:token命令来打印出我们的访问令牌。
现在我们只需要使用travissetup命令进行设置:
travissetup命令会自动更新我们的TravisYML脚本,添加Heroku提供商信息,现在我们的TravisTML脚本看起来是这样的:
我们只需要在Heroku中创建一个名为multiple-languages的应用:
现在让我们使用travisrestart命令在Travis中重新启动构建:
现在让我们再次查看构建8.1的作业日志:
现在,如果我们查看Heroku仪表板,我们可以确认我们的应用已成功部署到Heroku:
在本章中,我们介绍了Travis作业日志的概述,并解释了作业日志的不同部分。我们查看了如何使用Docker在本地运行构建,并学习了如何使用TravisAPI启用调试模式构建。然后,我们查看了TravisCI采取的步骤来保护作业日志中的秘密和密钥。最后,我们查看了如何使用TravisCLI部署应用程序,然后查看了如何调试构建失败并在TravisCI中获得成功部署。
在下一章中,我们将解释如何在软件项目中设置CircleCLI,然后介绍CircleCIUI的基础知识。
在上一章中,我们展示了如何在本地调试TravisCI项目,并更详细地解释了TravisCI的Web界面。我们还看了如何在TravisCI中进行日志记录。本章将帮助您设置CircleCI,并解释如何创建Bitbucket帐户,以及如何在新的CircleCI帐户上设置GitHub和Bitbucket。我们将在Bitbucket中创建一个简单的Java项目,并为其运行CircleCI构建。我们还将讨论如何浏览Bitbucket的用户界面。最后,我们将通过创建一个新的GitHub存储库来结束本章,并讨论一个CircleCIYML脚本,该脚本将通过Docker镜像安装Golang并运行我们的单元测试。
本章将需要一些基本的编程技能,并且我们将利用本章将讨论的一些持续集成/持续交付概念。如果您尝试自己创建Bitbucket帐户和CircleCI帐户,将会很有帮助。您可以按照CircleCI先决条件部分中的步骤进行操作。我们将使用Maven创建一个基本的Java应用程序,因此了解一些Java的基本编程概念将会很有帮助,但如果您了解任何编程语言,应该也能够跟上。基本的Git和Unix知识将非常有帮助。
我们在第九章中详细介绍了如何创建GitHub帐户,在创建GitHub帐户部分。
我们将创建一个Bitbucket帐户,并再次使用用户名packtci作为我们的用户名:
点击绿色的继续按钮后,您将被重定向到以下页面:
您需要输入您的全名和密码,您在上一页提供的电子邮件地址已经为您设置好。点击绿色的继续按钮后,您将收到一个新Bitbucket帐户的验证电子邮件,类似于以下内容:
点击验证我的电子邮件地址按钮后,您将被重定向到以下页面:
您必须为您的新Bitbucket帐户提供一个唯一的用户名,因为您不能使用任何现有的用户名。点击继续按钮后,您将被路由到以下页面:
您可以通过点击跳过按钮来跳过此部分,或者您可以输入您的信息,然后点击提交按钮,您将被路由到以下页面:
您需要点击注册按钮以创建一个新的CircleCI帐户,然后您将被重定向到以下页面:
您可以选择其中一个进行注册,但我们将选择*使用Bitbucket注册**。一旦您点击按钮,您将被重定向到以下页面:
我们将点击授予访问权限按钮,然后我们将被路由到以下页面:
请注意,我们在CircleCI中没有设置任何项目,稍后需要添加项目。
即使我们注册了新的Bitbucket帐户,我们仍然可以将我们的GitHub帐户连接到我们的新CircleCI帐户。您需要点击屏幕右上角的头像,然后点击用户设置按钮:
点击用户设置按钮后,您将被路由到显示帐户集成的页面。我们需要通过点击连接按钮将我们的GitHub帐户连接到CircleCI:
点击添加项目按钮后,您将被路由到以下页面:
我们将点击functional-summerGitHub存储库的设置项目按钮,并将被路由到一个类似这样的页面:
CircleCI自动选择了Node作为我们的语言,因为我们有一个package.json文件,并且因为我们在这个存储库中有JavaScript文件。不过,我们还没有完成。如果您在此页面向下滚动,您将注意到一些启动CircleCI在我们项目中的下一步:
一旦我们点击此按钮,我们将被重定向到GitHubUI中的一个类似这样的页面:
输入我们文件夹的名称为.circleci,然后输入/字符,然后命名我们的文件为config.yml。完成后,它将如下所示:
现在我们需要为我们的config.yml文件输入内容,.circleci为我们提供了一个样本config.yml文件,其中包含我们可以用于新的CircleCI项目的值:
我们需要做的最后一步是回到CircleCI的添加项目页面,点击开始构建按钮,启动我们新配置的CircleCI项目:
这也会在CircleCI中设置一个Webhook,以便CircleCI监听我们提交到GitHub的任何新代码更改。
一旦我们点击开始构建按钮,我们将被重定向到我们的第一个构建作业,使用CircleCI构建functional-summer存储库:
如果我们继续向下滚动,我们将在CircleCI应用程序中看到构建的每个步骤:
我们将在后面的章节中更详细地解释这一点,但每个步骤都可以展开以显示该步骤的详细信息。例如,如果我们点击yarntest步骤,我们将看到以下详细信息:
由于我们刚刚创建了一个新的Bitbucket账户,我们需要将我们的ssh密钥上传到Bitbucket,以便能够将更改推送到Bitbucket。我们在第九章中介绍了如何创建SSH密钥,在安装和TravisCI基础章节中,向新GitHub账户添加SSH密钥部分,因此如果您还没有设置任何SSH密钥,请阅读该章节。我们已经在第九章,安装和TravisCI基础的向新GitHub账户添加SSH密钥部分创建了一个SSH密钥。我们只需要将公共ssh密钥复制到我们的系统剪贴板中,通过运行以下命令:
pbcopy<~/.ssh/id_rsa_example.pub一旦我们将公共SSH密钥复制到系统剪贴板中,我们需要转到Bitbucket的以下页面:
我们需要点击添加密钥按钮。这将打开一个模态窗口,我们在其中输入一个标签和我们的公钥的内容,看起来像这样:
然后点击添加密钥按钮,现在我们已经准备好将更改推送到我们的Bitbucket账户。
我们将通过点击左侧导航窗格中的加号按钮在Bitbucket中创建一个名为java-summer的新Java项目:
接下来,我们将单击“存储库”按钮,它看起来像这样:
接下来,我们将通过提供存储库名称,将我们的版本控制系统设置为Git,然后单击“创建存储库”按钮来创建一个新存储库:
请注意,这里我们点击了可选的高级设置下拉菜单,并将我们的语言设置为Java编程语言。一旦我们点击创建存储库按钮,我们将被重定向到一个看起来像这样的页面:
要使用Maven创建我们的新Java项目,我们将发出以下命令:
mvnarchetype:generate-DgroupId=com.packci.app-DartifactId=java-summer-DarchetypeArtifactId=maven-archetype-quickstart-DinteractiveMode=false我们首先通过在shell会话中发出以下命令来克隆我们的存储库:
gitclonegit@bitbucket.org:packtci/java-summer.gitjava-summer-proj然后我们将复制克隆存储库中隐藏的.git目录的内容,并将其粘贴到我们用Maven构建工具创建的新java-summer文件夹中。假设我们有正确的路径结构,我们可以发出以下命令:
我们将提交我们的更改并使用以下命令将其推送到Bitbucket:
gitpush现在,如果您查看CircleCI应用程序,我们可以通过单击应用程序左上角的packtciBitbucket用户帐户来切换到该用户帐户,它看起来像这样:
接下来,我们需要在左侧导航窗格中单击“添加项目”按钮,它看起来像这样:
然后我们需要单击“设置项目”按钮,以便CircleCI知道我们在Bitbucket中的java-summer存储库,它看起来像这样:
然后我们将被路由到设置项目页面,在这里我们需要选择我们的操作系统,默认情况下在CircleCI中为Linux。然后我们选择我们的构建语言,在我们的情况下应该是Java。为了清晰起见,我们将在以下截图中再次显示此页面:
然后我们将把CircleCI为我们提供的示例配置文件复制到.circleci/config.yml文件中:
这将触发我们对java-summer项目的第一次构建,并使webhook为存储库工作。一旦我们点击“开始构建”按钮,我们需要点击“作业”按钮,以查看我们触发的新构建:
让我们在应用文件中添加一个静态函数,就像这样:
publicstaticintaverage(int[]numbers){intsum=0;for(inti=0;i publicvoidtestaverage(){AppmyApp=newApp();int[]numbers={1,2,3,4,5};assertEquals(15,myApp.average(numbers));}我们可以使用mvnpackage命令在本地测试更改,以确保没有出现问题,然后提交我们的更改并将这些更改推送到Bitbucket版本控制系统。我们现在应该注意到,由于我们对主分支的代码更改,CircleCI自动触发了一个构建。 如果我们回到CircleCIWeb应用程序,我们可以看到触发了一个新的构建,并且通过了: 请注意,在上面的屏幕截图中,CircleCI显示第二次构建已经触发。它还显示了提交SHA哈希和提交消息,并确认构建成功。 在新的存储库中,我们添加了一个名为template.go的文件,这是我们将要测试的函数: funcparseTemplate(soldierSoldier,tmplstring)*bytes.Buffer{varbuff=new(bytes.Buffer)t:=template.New("Atemplatefile")t,err:=t.Parse(tmpl)iferr!=nil{log.Fatal("Parse:",err)returnbuff}err=t.Execute(buff,soldier)iferr!=nil{log.Fatal("Execute:",err)returnbuff}returnbuff}我们在template_test.go文件中添加了以下单元测试用例来测试parseTemplate函数: funcTestParseTemplate(t*testing.T){newSoldier:=Soldier{Name:"LukeCage",Rank:"SGT",TimeInService:4,}txt:=parseTemplate(newSoldier,templateText)expectedTxt:=`NameisLukeCageRankisSGTTimeinserviceis4`iftxt.String()!=expectedTxt{t.Error("Thetextreturnedshouldmatch")}}然后我们将以下CircleCIYML脚本添加到存储库中: 然后我们有一个名为docker的字段,其中包含了Golang的语言镜像。我们还可以有一个服务镜像来运行特定的服务,这将在后面的章节中讨论。 以下截图显示CircleCI构建已通过: 以下是构建作业中的步骤: 请注意,这里有一个额外的步骤称为SpinupEnvironment。此步骤创建一个新的构建环境,特别是对于我们的构建,它创建一个GolangDocker镜像,然后设置一些特定于CircleCI的环境变量。 在本章中,我们介绍了CircleCI和TravisCI之间的区别,并介绍了CircleCI的先决条件。我们创建了一个新的Bitbucket帐户,并解释了BitbucketUI的基础知识以及在Bitbucket中上传SSH密钥以访问存储库的位置。然后我们在GitHub和Bitbucket中设置了CircleCI,并解释了CircleCIWeb应用程序的部分内容以及如何在其中导航。最后,我们简要概述了CircleCIYAML配置语法。在下一章中,我们将介绍CircleCI命令,并介绍CircleCI的一些更高级主题,例如工作流程。 在上一章中,我们介绍了如何在Bitbucket和GitHub中设置使用CircleCI,并向您展示了如何导航BitbucketUI,并介绍了CircleCIWebUI的基础知识。在本章中,我们将介绍如何在macOS/Linux上安装CircleCICLI,并向您展示如何从CLI获取夜间构建。我们将详细介绍每个CircleCICLI命令,并解释CircleCI中的工作流程。我们将向您展示如何使用顺序作业设置更复杂的工作流程。最后,我们将介绍CircleCIAPI,并向您展示如何在使用HTTP请求时使用jqJSON命令实用程序转换JSON。 在本章中,我们将涵盖以下主题: 本章将需要一些基本的Unix编程技能,并且我们将在前几章中讨论的持续集成(CI)和持续交付(CD)概念上进行一些构建。熟悉使用RESTfulAPI可能会有所帮助,因为我们将在本章末尾使用curl作为REST客户端。 通过在Windows命令提示符或macOS/Linux终端应用程序上运行类似以下命令的命令来确保已安装Docker版本: 这里我安装了Docker版本18。 您需要运行以下命令来安装CircleCI: 我们将选择circleci-cli_0.1.771_darwin_amd64.tar.gz资产,因为我们将在macOS操作系统上运行本地CLI。 在终端shell会话中运行以下命令: #GototheDownloadsFoldercd~/Downloads#Unpackthecompressedassettar-xvzfcircleci-cli_0.1.771_darwin_amd64.tar.gz#Gointotheuncompresseddirectorycdcircleci-cli_0.1.771_darwin_amd64#Movethecirclecibinaryintothefolder/usr/local/binmvcircleci/usr/local/bin/circleci-beta#Makesurethatthebinaryisexecutablechmod+x/usr/local/bin/circleci-beta#Checkthatthebinaryversiontomakesurethatitisworkingcircleci-betahelp我们现在有一个更新版本的CircleCICLI,并且可以验证: 我们将这个二进制可执行文件命名为circleci-beta。这样我们就可以运行稳定版和夜间版本的CircleCICLI。这不是您必须做的事情;我们只是为了举例说明而这样做。 在下面的屏幕截图中,我们运行了help命令,显示了可用的命令,并简要概述了每个命令的功能: version命令输出您在本地系统上安装的CLI的当前版本: 您还可以向CLI中的每个命令传递标志/选项,并且可以通过运行--help标志找到每个命令接受的选项: 我们可以向version命令传递的选项只有一个,即-h,--help,因为这是一个非常简单的命令。 help命令将显示所有CLI命令,就像我们在本节开头演示的那样,但它也可以用来解释每个命令的工作原理,并显示每个命令接受的任何标志/选项: 在这里,我们对help命令本身运行了帮助。 config命令验证并更新CircleCI配置YML脚本: 这里config命令还接受validate命令,用于验证您的配置YML脚本文件。 让我们再次查看配置脚本: version:2jobs:build:docker:#specifytheversionyoudesirehere-image:circleci/node:7.10working_directory:~/reposteps:-checkout-restore_cache:keys:-v1-dependencies-{{checksum"package.json"}}-v1-dependencies--run:yarninstall-save_cache:paths:-node_moduleskey:v1-dependencies-{{checksum"package.json"}}#runtests!-run:yarntest这实际上是配置YML脚本中的一个非常微妙的错误,我们只需要缩进build字段,因为CircleCI认为我们的脚本中没有任何作业。为了解决这个问题,我们只需要缩进build字段: 当我们运行validate命令时,它报告说配置YML脚本是有效的。 build命令帮助您在本地计算机上运行CircleCI构建,并且可以采用各种选项,如下面的屏幕截图所示: 在运行构建命令之前,确保进入存储库所在的目录,因为它需要读取.circleci文件夹中的config.yml文件: build命令将执行配置YML脚本中的步骤,首先会启动一个环境。如果您尚未拉取配置YML脚本中指定的语言映像,则circlecibuild命令将为您拉取Docker映像。 默认情况下,circlecibuild命令将运行在jobs部分的build字段中定义的步骤,因此如果您想运行其他作业,就需要传递--jobstring选项。 这是我们在go-template-example-with-circle-ciGitHub项目中的当前config.yml脚本: version:2jobs:build:docker:-image:circleci/golang:1.9working_directory:/go/src/github.com/packtci/go-template-example-with-circle-cisteps:-checkout-run:name:"Printgoversion"command:goversion-run:name:"RunUnitTests"command:gotest如果我们想使用另一个作业,可以使用--jobstring选项,假设有另一个作业: ...build:...integration:docker:-image:cypress/base:8environment:TERM:xtermsteps:-checkout-run:npminstall-run:name:"RunIntegrationTests"command:$(npmbin)/cypressrun现在让我们验证我们的configYML脚本,以确保它仍然有效: 现在我们知道我们的configYML脚本仍然有效,我们可以使用--jobstring标志运行新作业。 在这里,CLI正在下载Docker映像,因为我们尚未将此特定Docker映像拉入我们的本地计算机。 step命令将执行您定义的配置YML脚本中的特定步骤。目前,只有一个halt的子命令,它将停止当前执行。 这是step命令的一个示例运行: circlecistephalt配置命令configure命令仅在CircleCI的夜间构建版本中可用,它可以帮助您配置您的凭据和将要访问的API端点: 我们将以无标志运行configure命令,这将将其设置为交互模式,然后我们将设置我们的API令牌和我们希望访问的API端点。 您需要点击CircleCIWeb应用程序右上角的用户头像,它看起来像以下截图: 一旦您点击“用户设置”链接,您将被重定向到账户API页面,它看起来像这样: 接下来,您需要点击“创建新令牌”按钮,这将弹出一个类似于这样的模态框: 在这里,我们输入了一个名为PacktCI的令牌名称。然后我们只需点击“添加API令牌”按钮,这将为我们生成一个新的API令牌。您需要将API令牌复制到安全位置,因为您只能使用一次。 我们将在终端会话中运行circleci-betaconfigure命令,并设置我们的凭据和API端点: configure命令仅在夜间版本中可用,而不是稳定版本。 tests命令收集并拆分具有测试的文件: version:2jobs:build:...integration:....workflows:version:2build_and_integration:jobs:-build-integration在这个工作流程中,我们分别创建了两个并行作业,分别称为build和integration。它们彼此独立,这将有助于加快构建过程。 如果我们点击左侧导航窗格中的“工作流程”链接,我们可以在CircleCIWebUI中看到工作流程。然后您需要点击特定的项目,本例中是go-template-example-with-circle-ci,如下截图所示: 如果您点击“RUNNING”工作流程,您将看到以下页面: 我们之前展示的工作流程示例包含了两个独立运行的作业,但我们也可以有需要其他作业完成后才能运行的作业。假设我们有一个只有在构建运行时才运行的验收测试套件,然后我们的应用程序只有在验收测试套件通过后才会部署。 一旦您点击齿轮图标,您将被重定向到“项目设置”页面,您需要点击“环境变量”链接。然后您的页面将看起来像以下截图: 我们将通过点击“添加变量”按钮向我们的项目添加两个环境变量,这将弹出一个如下的模态框: 出于安全目的,我已经删除了项目的应用程序名称和API令牌的内容,但是一旦您点击“添加变量”按钮,项目中就会有一个环境变量可用。我们现在有两个可以使用的环境变量,即HEROKU_API_KEY和HEROKU_APP_NAME。这些环境变量将在我们的.circleci/config.yml脚本中可用。 我们的配置YML脚本现在有一个部署jobs部分,并且我们已经更新了我们的工作流程字段如下: 我们将使用curl命令和我们的API令牌来测试我们是否有一个良好的CircleCIAPI连接: 在这里,我们没有得到任何响应头或状态码。为了接收这些,您需要在curl命令中使用-i、--include选项。 在下面的截图中,我们获取了最近30次构建的构建摘要,然后只显示实际通过的构建: 在这里,我们使用jq实用程序进行了两个不同的查询。 在本章中,我们介绍了如何在macOS/Linux环境中安装CircleCICLI,并向您展示了如何安装CLI的夜间构建。我们向您展示了如何使用CircleCICLI中的每个命令,并向您展示了CircleCICLI夜间构建中可用的一些命令功能。我们解释了工作流程为什么有用以及如何在CircleCI中使用它们。最后,我们向您展示了如何使用CircleCIAPI以及如何使用jq命令实用程序收集有用的指标。 在上一章中,我们深入介绍了CircleCICLI命令,并向您展示了一些自动化任务的技术。在本章中,我们将深入介绍作业日志,并更详细地解释运行步骤。我们将解释工作流程的概念,并向您展示如何使用CircleCIAPI查找项目的最新构建。我们将介绍如何通过在构建中实现缓存来调试慢作业,并最后使用一些故障排除技术来运行具有本地配置YML脚本的构建。 在本章中,我们将介绍一些关于使用RESTfulAPI的概念,并将使用curl实用程序进行REST调用,因此了解API是什么以及如何使用REST客户端(如curl)将是有益的。了解Unix编程环境的基本概念也会有所帮助,了解脚本编写和Bash环境是有益的。 本章的代码文件可以在以下链接找到: 现在你可以打开浏览器并粘贴此URL,或者你可以使用操作系统上可用的命令行实用程序。我们将在macOS中使用open实用程序,就像这样: 当我们打开通过API触发的新工作的URL时,UI的第一部分看起来像这样: 在这里,顶部显示基本信息,例如提交SHA哈希、贡献者信息和其他背景信息。如果您在作业日志中进一步向下滚动,您将看到作业的每个部分中运行的步骤: 构建完成需要9秒,注意到每个构建步骤都有自己的部分,这些部分都很方便地折叠起来。您只需点击每个部分即可获取步骤的详细信息。每个步骤的名称对应于配置YML脚本中的name字段。 请注意,多行命令的名称使用完整命令的名称作为其名称。 这是多行命令的条目: ...-run:|echo"GenerateCodeCoverage"npmtestecho"Showthecoverage"npmruncoverage如果我们展开一个步骤,我们将看到每个步骤都有以下共同的条目: Bash选项-e表示如果语句返回非真值,则脚本应该退出。Bash选项-opipefail表示使用第一个失败的错误状态,而不是管道中最后一项的错误状态。您可以不在Shebang行中添加这些选项,而是可以这样做: #!/usr/bin/envbash#Exitscriptifyoutrytouseanuninitializedvariable.set-onounset#Exitscriptifastatementreturnsanon-truereturnvalue.set-oerrexit#Usetheerrorstatusofthefirstfailure,ratherthanthatofthelastiteminapipeline.set-opipefail如果我们查看工作中的另一个步骤,我们会看到相同的事情: CircleCI在作业的每个步骤中都这样做,因为它有助于我们解决编写shell脚本时出现的问题,并有助于促进编写shell脚本的最佳实践。 这是一个可能失败的命令的示例,当使用Unix管道时,它将在构建的错误位置报告错误: dockerps-a|grep-v"busybox:latest"|awk'{print$1}'-|grep-v"CONTAINER"在这个流水线中,我们列出了所有正在运行、退出或因某种原因终止的容器,然后将其传输到grep实用程序中,并排除任何具有文本busybox:latest的条目,然后将其传输到awk实用程序中,并仅打印第一列。最后,我们将其传输回grep并排除文本CONTAINER。这个流水线可能在任何一条流水线链中失败,但因为我们使用了选项set-opipefail,脚本将在返回非真选项的第一个命令上失败。这很有帮助,因为默认行为是报告管道中的最后一项。 这是一个示例来说明这一点: 您还可以在右上角看到一个有用的按钮,它将使您进一步滚动到您感兴趣的特定运行步骤中。 重要的是,不要在.circleci/configYML脚本文件中添加机密信息。如果这样做,可能会在作业日志中泄露机密信息,这些信息可能是公开可访问的。config.yml的完整文本对于在CircleCI上访问您的项目的开发人员是可见的,因此请将您的机密信息和/或密钥存储在CircleCI应用程序中的项目或上下文设置中。在配置中运行脚本可能会暴露机密环境变量,因此在运行步骤中使用set-oxtrace/set-x时要小心,因为它们可能会暴露环境变量。 我们将使用工作流来将我们的作业分成更合适的部分,然后利用一些脚本彼此独立并可以分开运行的事实。通过在CircleCI中使用工作流,我们可以加快构建过程。 现在让我们考虑作业的哪些部分可以在我们的构建过程中分解为单独的步骤。我们可以将依赖步骤分解为构建的一个单独部分,然后我们可以将为三个测试运行的各个步骤合并为一个名为测试的步骤。请记住,配置YML脚本中的步骤如下所示: ...-run:name:RuntheSortTesttosortbyfirstnamecommand:$(npmbin)/tapesort_test.js-run:name:ComputeStandardDeviationcommand:$(npmbin)/tapestandard_deviation_test.js-run:name:FindtheTextandReplaceItcommand:$(npmbin)/tapefind_text_test.js-run:|echo"GenerateCodeCoverage"npmtestecho"Showthecoverage"npmruncoverage...在最后一步,我们有命令npmtest,这个命令引用了package.json文件中指定的以下命令: "scripts":{"test":"nyctape*_test.js","coverage":"nycreport--reporter=cobertura"}请注意,此命令已经运行了所有测试,然后使用NYC代码覆盖率实用程序报告覆盖率。最后一个命令生成了一个CoberturaXML报告,我们将在本章后面使用。现在,我们将把一系列步骤重写为它们自己的字段,称为test,它将如下所示: test:docker:-image:circleci/node:8.11.3steps:-checkout-run:name:RunTestsandRunCodeCoveragewithNYCcommand:|echo"GenerateCodeCoverage"npmtestecho"Showthecoverage"npmruncoverage请注意,我给折叠命令一个更合适的名称,并且请注意,我们可以在command字段本身使用管道(|)运算符使用多行命令。 我们将在我们的配置YML脚本底部添加workflows部分,但我们也可以将其添加到配置YML脚本的开头。更新后的配置YML脚本如下: ...workflows:version:2build_test_and_deploy:jobs:-build-test:requires:-build-deploy:requires:-test完成更新配置YML脚本后,我们应该使用CircleCICLI确保我们的配置YML脚本仍然有效,就像这样: 看起来我们在第19行的配置YML脚本中有问题: ...-run:name:RunTestsandRunCodeCoveragewithNYCcommand:|echo"GenerateCodeCoverage"npmtestecho"Showthecoverage"npmruncoverage这实际上是我们配置YML脚本中的一个细微错误,因为我们没有正确缩进多行命令,所以CircleCI不知道我们的多行命令从哪里开始。现在更新的配置YML脚本部分如下: ...-run:name:RunTestsandRunCodeCoveragewithNYCcommand:|echo"GenerateCodeCoverage"npmtestecho"Showthecoverage"npmruncoverage现在让我们再次运行CircleCICLI验证: 我们的配置YML脚本是有效的,现在让我们通过发出以下命令将其提交到源代码控制中: 请注意,我们在这里给出了一个描述性的提交消息,在版本控制中这是一个很好的做法,如果你正在处理的任何东西有一个特定的标签,比如JIRA;你可以像这样添加它,例如: gitcommit-m'[PACKT-1005]Updateconfigymlscripttodifferentjobsanduseworkflows.'使用CircleCIAPI查找最近的构建URL我们当然可以使用CircleCIWeb应用程序,点击工作流部分,找到我们最近的构建,但让我们改用CircleCIAPI,使用jq来解析JSON响应有效负载,就像我们以前对其他API端点所做的那样。 现在让我们转到这个URL并查看最近的构建;我们会注意到构建失败了: 构建失败是因为我们没有设置配置YML脚本引用的必要的环境变量,即HEROKU_API_KEY和HEROKU_APP_NAME。我们在第十三章中介绍了如何设置项目级别的环境变量,CircleCICLI命令和自动化,但我们只需要复制项目环境级别的变量。如果环境变量相同,CircleCI有一种简单的方法可以做到这一点: 点击导入变量按钮,然后输入要复制的项目,就像这样: 请注意,npminstall花了1分钟3秒才完成。让我们打开运行步骤调用npminstall以获取更多细节: integration:docker:-image:cypress/base:8environment:##thisenablescolorsintheoutputTERM:xtermsteps:-checkout#specialsteptorestorethedependencycache-restore_cache:key:v2-{{checksum"package.json"}}-run:npminstall#specialsteptosavethedependencycache-save_cache:key:v2-{{checksum"package.json"}}paths:-~/.npm-~/.cache-run:name:"RunIntegrationTests"command:npmtest注意到我们在npminstall之前放置了restore_cache步骤,然后在npminstall步骤之后放置了save_cache步骤。我们还在两个字段中使用了一个关键字段。关键值是不可变的,我们使用v2作为缓存关键值的版本,并获取package.json文件的校验和。如果我们想要使任何更改无效缓存,我们可以简单地将缓存值增加一,例如v3。还要注意,我们有一个路径字段,并且我们指定路径为~/.npm和~/.cache目录。Cypress测试运行程序期望将二进制文件保存到这样的目录中,否则它将抛出错误。让我们将这个更改推送到源代码控制,并触发新的构建并查看作业日志。现在让我们使用对最近构建API端点的调用,并复制URL并查看构建的情况: 我们可以点击工作流标签下的build_integration_and_deploy链接来到工作流。现在我们在集成构建中有以下步骤: 如果我们展开恢复缓存下拉菜单,我们会看到以下内容: 注意到这里没有找到缓存,这是预期的,因为这是添加了这个步骤的构建的第一次运行。 如果我们展开保存缓存按钮,我们会看到以下内容: 注意到这里创建了一个缓存存档,并存储在配置YML脚本中指定的node_modules路径中。 让我们在README.md文件中进行一个简单的文本更改,并提交更改以触发新的构建。我们将使用API找到最新的构建,就像我们一直在做的那样。现在让我们看一下集成作业的新作业日志: 现在让我们看一下保存缓存的步骤: 注意到它跳过了缓存生成,因为它能够找到我们从上一次构建中保存的缓存。 我们可以使用CircleCIAPI来排除有问题的配置YML脚本,而无需进行Git提交。我们可以做的一个技巧是创建另一个文件夹,并将我们的配置YML脚本的副本放入其中,然后使用这个YML脚本作为我们的调试脚本。一旦我们可以验证YML脚本工作正常,我们可以更新原始的YML脚本。这很有用,因为我们不会用故障排除提交来堵塞Git历史,而是直接使用CircleCIAPI。 运行此命令将复制.circleci目录的内容并在shell中创建一个新目录: cp-r.circlecistore_and_cache_experiment现在我们将使用store_and_cache_experiment文件夹来运行我们的本地配置YML脚本实验。这是我们将要对store_and_cache_experiment文件夹中的配置YML脚本进行的更改: 现在我们有一个修订号,可以用于我们将要进行的API调用。这是我们将用于调试新配置YML脚本更改的命令: 让我们删除故障排除目录和配置YML脚本和shell脚本,并将配置YML脚本复制到.circleci目录中,就像这样: cpstore_and_cache_experiment/config.yml.circlecirm-rstore_and_cache_experimentgitadd.gitcommit-m'CacheandStoreartifacts.'gitpush现在,如果我们点击当前构建,然后转到工作流链接,我们会看到上传产物步骤已添加到任务中;看起来是这样的: 现在我们可以向上滚动并点击Artifacts选项卡,看到构建中已保存了一个产物,就像这样: 如果我们点击index.html,我们将被重定向到一个漂亮的覆盖率报告,看起来像这样: 在本章中,我们深入介绍了作业日志,并向您展示了如何使用CircleCIAPI添加项目。我们向您展示了如何分析作业日志,并更详细地解释了CircleCI中的工作流程。我们看了如何使用CircleCIAPI找到最近的构建。然后,我们看了如何在CircleCI中调试慢构建,并最后向您展示了如何使用本地配置YML脚本来尝试对CircleCIYML脚本进行新更改的实验。 在下一章中,我们将介绍一些持续集成/持续交付的最佳实践,并研究一些配置管理模式,特别是秘密管理,并在软件公司实施CI/CD时提供一些检查表。 在上一章,[第十四章](c8355d57-1eb8-4e45-93f5-a32513185de3.xhtml),CircleCIUI日志记录和调试中,我们使用CircleCI涵盖了更高级的调试和日志记录技术,并介绍了使用CircleCIAPI的更多选项。在本书的最后一章中,我们将介绍不同类型测试的最佳实践,如单元测试、集成测试、系统测试和验收测试。我们将介绍密码管理的最佳实践,并以Vault库为例。最后,我们将介绍CI/CD中部署的最佳实践,并编写一个自定义的Go脚本来创建GitHub发布。 本章将需要一些基本的编程技能,因为我们将在部署脚本和单元测试示例中讨论一些特定于编程语言的材料。熟悉Unix编程和Bashshell将非常有帮助。 在[第三章](e80cf8c3-7464-4c16-865b-78e3c264a98e.xhtml),持续交付的基础知识中,我们介绍了验收测试,并简要讨论了验收测试套件如何作为回归测试套件。在本节中,我们将讨论您可以进行的不同类型的软件测试,并制定每种测试的最佳实践。我们将介绍以下类型的测试: 烟雾测试是一种特殊的测试,有助于验证应用程序的基本功能。烟雾测试将假定一些基本实现和环境设置。烟雾测试通常在测试周期开始时运行,作为完整测试套件开始之前的理智检查。 烟雾测试的主要目的是在软件系统的新功能开发中捕捉明显的问题。烟雾测试不是为了详尽无遗,而是为了快速运行。假设一个软件公司遵循敏捷软件开发实践,每两周进行一次冲刺,向产品添加新功能。当新功能合并到发布中,即软件的主干时,烟雾测试失败,这应立即引起警觉,表明新功能可能破坏了现有功能。 您可以创建特定上下文的烟雾测试,用于测试系统中的新功能,这些测试将采用一些基本假设,并断言是否满足要求。您可以在进行任何集成测试之前以及在为暂存环境进行任何部署之前运行这些烟雾测试,并且这些烟雾测试将检查每个暂存环境的不同条件。 我们将使用JavaScript编写以下简单的冒烟测试: 这是Cypress库拍摄的屏幕截图: Cypress的另一个好功能是它可以录制测试的视频,显示测试所采取的所有步骤,这可以进一步验证应用程序是否满足基本要求。 单元测试在测试系统的组件行为正确方面是基础的。单元测试在这方面的限制意味着更容易隔离缺陷发生的位置。单元测试通常用于测试代码分支以及函数如何处理不同类型的输入。开发人员通常会在构建中首先运行单元测试,而QA工程师可能会首先运行冒烟测试,然后进行任何单元测试。 个别开发人员将在提交更改到版本控制项目(如GitHub)之前在他们的工作站上运行单元测试。话虽如此,持续集成服务器(如Jenkins、TravisCI和CircleCI)将在运行任何集成测试之前运行单元测试,正如我们在前几章中所看到的。 test('Testthesortfunction',t=>{t.plan(1);constnames=[{firstName:'Sam',lastName:'Cooke'},{firstName:'Barry',lastName:'White'},{firstName:'Jedi',lastName:'Knight'}];constactual=sort.sortListOfNames(names);constexpected=[{firstName:'Barry',lastName:'White'},{firstName:'Jedi',lastName:'Knight'},{firstName:'Sam',lastName:'Cooke'}];t.deepEqual(actual,expected,'Thenamesshouldbesortedbythefirstname.')});您可以在这里看到,单元测试能够隔离并测试sortListOfNames函数的行为,这非常有用,因为如果sortListOfNames函数出现任何问题,我们可以快速确定回归发生的位置。当然,虽然这个函数非常基本和简单,但您可以看到单元测试在持续集成构建中捕捉软件回归方面起着重要作用。 集成测试将测试软件组件组合在一起的方式。虽然单元测试可以帮助验证代码块在隔离状态下的功能,但集成测试可以帮助测试代码块在彼此交互时的情况。集成测试很有用,因为它们可以帮助捕捉软件组件交互时出现的不同类型的问题。 虽然单元测试可能在开发人员的工作站上运行,但集成测试通常在代码检入源代码控制时运行。CI服务器将检出代码,执行构建步骤,然后进行任何冒烟测试,然后运行单元测试,然后运行集成测试。 由于集成测试是更高级的抽象级别,并且测试软件组件相互交互,它们有助于保护代码库的健康。当开发人员向系统引入新功能时,集成测试可以帮助确保新代码与其他代码块按预期工作。集成测试可以帮助确保系统中的新功能可以安全地部署到环境中。集成测试通常是在开发人员的工作站之外进行的第一种测试类型,并有助于显示是否存在环境依赖性破坏以及新代码是否与外部库、外部服务和/或数据正常行为。 系统测试可以包括: 还有其他类型的系统测试,但我们只包括了一些常见的系统测试类型。 const{setWorldConstructor}=require('cucumber')classAddition{constructor(){this.summation=0}setTo(numbers){this.numbers=numbers}addBy(){this.summation=this.numbers.reduce((prev,curr)=>prev+curr,0);}}setWorldConstructor(Addition)这个文件是一个执行简单加法的JavaScript类,这里还有另一个类,其中列出了一系列将数字相加的场景: const{Given,When,Then}=require('cucumber')const{expect}=require('chai')Given('alistofnumberssetto[]',function(){this.setTo([1,2,3,4,5])});When('Iaddthenumberstogetherby[]',function(){this.addBy();});Then('Igetalargerresultthatisthesumofthenumbers',function(){expect(this.summation).to.eql(15)});这是一个非常简单的验收测试,但它旨在说明验收测试是对新功能行为的正式验证。 我们在第三章中描述了以下阶段,持续交付的基础: echo$PATH##Thisprintsoutthecurrentpathwherebinariescanbefoundmv~/Downloads/usr/local/bin最后一个命令将把名为vault的二进制文件移动到我的路径中的/usr/local/bin目录中,然后我现在应该能够运行vault命令并查看帮助菜单,如下所示: 请注意,vault命令有常用命令和其他命令可以运行。 我们需要运行vaultserver-dev命令来启动开发服务器: 请注意,我们得到了一系列指令,以设置我们的本地开发环境。 请记住,这仅用于演示目的,开发模式不适用于生产实例。 在以下截图中,我们检查了开发Vault服务器的状态: 我们首先在新的shell中导出VAULT_ADDR环境变量,因为我们将使用此命令,然后检查我们的开发Vault服务器的状态。 在以下截图中,我们设置了一个API密钥,然后使用Vault检索它: 我们还可以列出Vault中的所有秘密,如下所示: 请记住,我们正在运行开发Vault服务器实例,因此我们可以在本地机器上作为REST客户端运行curl到VaultAPI。让我们运行以下curl命令,检查我们的Vault实例是否已初始化,此时应该已经初始化: 请注意,我们得到了一个令牌,这是我们将需要使用以下HTTP标头进行RESTfulAPI请求的令牌:X-Vault-Token:3507d8cc-5ca2-28b5-62f9-a54378f3366d。 以下是用于端点的示例curlGET请求: 正如我们在整本书中所述,将原始密码和秘密提交到源代码控制中并不是一个好的做法,您需要有一种安全地检索密码的方法来运行CI/CD流水线。您可以使用CI服务器本身来存储密码和秘密,然后使用环境变量检索它们,或者您可以使用Vault等服务来安全地存储密码。请记住,在CI环境中使用shell脚本的执行跟踪可能是不安全的,因此在调试构建和在Bash中使用set-x标志时要谨慎。 在第三章中,持续交付的基础,我们讨论了部署是什么,解释了部署流水线,并谈到了部署流水线中的测试门。我们还谈到了部署脚本和部署生态系统。 让我们在部署时突出一些其他好的策略: 每家公司都会有独特的限制,因此不可能创建一个满足每家公司限制的部署清单,但是总的来说,以下是一些可能在所有部署中有所帮助的指南。 开发团队和运维之间应该进行沟通,以便正确协调部署。这很关键,因为误解是不可避免的,因此在部署过程中应该进行密切的沟通,以避免中断和数据丢失。 手动流程容易出错,因此应尽可能自动化部署,以避免人为错误。手动流程不可重复,也不可持续,因为部署变得更加复杂。最好有自动化脚本,可以排除人为错误。 我们将使用Golang发出HTTP请求,并为Go脚本提供一些命令行参数。这些参数将用于构建以下request主体,其形式如下: {"tag_name":"v1.0.0","target_commitish":"master","name":"v1.0.0","body":"Descriptionoftherelease","draft":false,"prerelease":false}以下是部署脚本的第一部分: 现在,在脚本的第二部分中,我们在main函数中,解析命令行参数,然后调用我们的checkArgs函数。接下来,我们创建一个匿名结构,用于创建我们的请求体,然后设置HTTP请求并设置HTTP头。在脚本的最后部分,我们发出请求并打印发布URL。 让我们在终端会话中展示这个部署脚本的运行: 请注意,我们在gorundeploy.go之后提供了四个命令行参数,脚本在最后打印出了发布URL。 在这最后一章中,我们涵盖了CI/CD流水线中不同类型测试的最佳实践,包括单元测试、集成测试、系统测试和验收测试。我们提供了代码示例,并展示了如何使用Node.js、Golang和shell脚本测试API端点的方法。我们介绍了密码管理的最佳实践,并展示了如何使用Vault库安全管理秘密以及如何使用VaultAPI。我们最后讨论了一些关于部署的最佳实践,包括部署清单、发布自动化以及使用Golang编写自定义发布脚本。 这是书的结尾,我希望您已经学到了很多关于CI/CD、测试和自动化以及使用JenkinsCI、CircleCI和TravisCI的知识。