持续集成和交付实用手册(一)

原文:zh.annas-archive.org/md5/D4B1782DB08166E400DEF5DF3D2E1241

译者:飞龙

协议:CC BY-NC-SA 4.0

前言

编写现代软件很困难,因为在软件交付中涉及许多团队,包括开发人员、质量保证、运维、产品所有者、客户支持和销售。需要有一个流程,通过该流程,软件的开发是以自动化的方式进行的。持续集成和持续交付的过程将有助于确保交付给最终用户的软件具有最高质量,并经过 CI/CD 流水线的一系列检查。在本书中,您将学习如何使用 Jenkins CI,以及如何编写自由风格脚本、插件,以及如何使用更新的 Jenkins 2.0 UI 和流水线。您将了解 Travis CI 的 UI、Travis CLI、高级日志记录和调试技术,以及 Travis CI 的最佳实践。您还将学习 Circle CI 的 UI、Circle CLI、高级日志记录和调试技术,以及 CircleCI 的最佳实践。在整本书中,我们将讨论诸如容器、安全性和部署等概念。

本书适合对象

本书适用于系统管理员、质量保证工程师、DevOps 和站点可靠性工程师。您应该了解 Unix 编程、基本编程概念和 Git 等版本控制系统。

本书涵盖内容

第一章,自动化测试的 CI/CD,介绍了自动化的概念,并解释了与手动流程相比自动化的重要性。

第二章,持续集成的基础知识,介绍了持续集成的概念,解释了软件构建是什么,并介绍了 CI 构建实践。

第三章,持续交付的基础知识,介绍了持续交付的概念,特别是解释了软件交付、配置管理、部署流水线和脚本编写的问题。

第四章,CI/CD 的商业价值,通过解释沟通问题来介绍 CI/CD 的商业价值,例如能够向团队成员传达痛点、在团队成员之间分享责任、了解您的利益相关者,并展示为什么 CI/CD 很重要。

第五章,Jenkins 的安装和基础知识,帮助您在 Windows、Linux 和 macOS 操作系统上安装 Jenkins CI。您还将学习如何在本地系统上运行 Jenkins 以及如何管理 Jenkins CI。

第六章,编写自由风格脚本,介绍了如何在 Jenkins 中编写自由风格脚本,以及如何配置 Jenkins 中的自由风格脚本,包括添加环境变量和调试自由风格脚本中的问题。

第七章,开发插件,解释了软件中插件的概念,如何使用 Java 和 Maven 创建 Jenkins 插件,并介绍了 Jenkins 插件生态系统。

第八章,使用 Jenkins 构建流水线,详细介绍了 Jenkins 2.0,并解释了如何在 Jenkins 2.0(Blue Ocean)中导航,还详细介绍了新的流水线语法。

第九章,Travis CI 的安装和基础知识,向您介绍了 Travis CI,并解释了 Travis CI 与 Jenkins CI 之间的区别。我们将介绍 Travis 生命周期事件和 Travis YML 语法。我们还将解释如何开始并在 GitHub 上设置。

第十章,Travis CI CLI 命令和自动化,向您展示如何安装 Travis CI CLI,详细解释 CLI 中的每个命令,展示如何在 Travis CI 中自动化任务,并解释如何使用 Travis API。

第十一章,“Travis CI UI 日志和调试”,详细解释了 Travis Web UI,并展示了 Travis CI 中日志和调试的高级技术。

第十二章,“CircleCI 的安装和基础知识”,帮助您在 Bitbucket 和 GitHub 上设置 CircleCI,并展示如何使用 CircleCI Web UI。我们还将解释 CircleCI YML 语法。

第十三章,“CircleCI CLI 命令和自动化”,帮助您安装 CircleCI CLI,并解释 CLI 中的每个命令。我们还将介绍 CircleCI 中的工作流程以及如何使用 CircleCI API。

第十四章,“CircleCI UI 日志和调试”,详细解释了作业日志,并展示了如何在 CircleCI 中调试缓慢的构建。我们还将介绍 CircleCI 中的日志记录和故障排除技术。

第十五章,“最佳实践”,涵盖了编写单元测试、集成测试、系统测试、CI/CD 中的验收测试的最佳实践,以及密码和秘密管理的最佳实践。我们还将介绍部署的最佳实践。

充分利用本书

为了充分利用本书,您需要熟悉 Unix 编程概念,比如使用 Bash shell、环境变量和 shell 脚本,并了解 Unix 的基本命令。您应该熟悉版本控制的概念,知道提交是什么意思,并且需要了解如何使用 Git。您应该了解基本的编程语言概念,因为我们将使用诸如 Golang、Node.js 和 Java 之类的语言,这些语言将作为我们在 CI/CD 流水线和示例中使用的构建语言。

本书不受操作系统限制,但是为了使用本书中的一些概念,您需要访问 Unix 环境和命令。因此,如果您使用 Windows,最好安装 Git Bash (git-scm.com/downloads)和/或 Ubuntu 子系统。您需要在系统中安装 Git (git-scm.com/downloads)、Docker (docs.docker.com/install/)、Node.js (nodejs.org/en/download/)、Golang (golang.org/dl/)和 Java (java.com/en/download/)。最好安装文本编辑器,如 Visual Studio Code (code.visualstudio.com/download)和终端控制台应用程序。

下载示例代码文件

您可以从www.packtpub.com的帐户中下载本书的示例代码文件。如果您在其他地方购买了本书,可以访问www.packtpub.com/support并注册,文件将直接通过电子邮件发送给您。

您可以按照以下步骤下载代码文件:

  1. www.packtpub.com上登录或注册。

  2. 选择“支持”选项卡。

  3. 单击“代码下载和勘误”。

  4. 在搜索框中输入书名,然后按照屏幕上的说明操作。

下载文件后,请确保使用以下最新版本的解压缩或提取文件夹:

  • Windows 上的 WinRAR/7-Zip

  • Mac 上的 Zipeg/iZip/UnRarX

  • Linux 上的 7-Zip/PeaZip

本书的代码包也托管在 GitHub 上,网址是github.com/PacktPublishing/Hands-On-Continuous-Integration-and-Delivery,在 README 部分,您可以找到按章节划分的所有代码文件的链接。如果代码有更新,链接将在现有的 GitHub 存储库中更新。

我们还有来自我们丰富的图书和视频目录的其他代码包,可在**github.com/PacktPublishing/**上找到。去看看吧!

使用的约定

本书中使用了许多文本约定。

CodeInText:表示文本中的代码词、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 句柄。这是一个例子:“Chocolatey 安装说明可以在chocolatey.org/install找到。”

代码块设置如下:

{
  "@type": "env_vars",
  "@href": "/repo/19721247/env_vars",
  "@representation": "standard",
  "env_vars": [

  ]
} 

任何命令行输入或输出都以以下方式编写:

 Rules updated
Rules updated (v6) 

粗体:表示一个新术语、一个重要词或屏幕上看到的词。例如,菜单或对话框中的单词会以这种方式出现在文本中。这是一个例子:“点击继续,确保点击同意按钮。”

警告或重要说明会显示为这样。提示和技巧会显示为这样。

第一章:自动化测试的 CI/CD

在本书中,我们将探讨持续集成CI)和持续交付CD)的概念,并使用 Jenkins、Travis CI 和 CircleCI 等工具应用这些概念。我们将编写许多实用脚本,并探索真实世界的 CI/CD 自动化脚本和场景。本章将通过解释一个名为比利·鲍勃机械零件的虚构公司的当前实践来帮助阐明自动化的概念。比利·鲍勃机械零件公司有许多手动流程,并且由于软件发布只由首席开发人员完成,质量保证(QA)和开发团队之间存在一些紧张关系。

本章将涵盖以下主题:

  • 手动流程-一个假设的场景

  • 员工的挫败感

  • 引入自动化

  • 开发人员的生产力

  • 打破沟通障碍

  • 创建协作环境

业务场景

本章将描述一个模拟的手动流程以及手动测试和手动流程中固有的缺陷,并将解释如何使用 CI/CD 可以大大提高开发人员的生产力。在这种情况下,每个成员都设置了一套手动流程,完成起来非常耗时。此外,如果 QA 在最新的发布版本中遇到问题,这些步骤必须重新执行。

我们将在我们虚构的公司的多个部门中看到不同的场景。一些场景将关注开发团队、QA 团队、客户成功团队和销售团队的痛点。我们将构建可能发生在这些团队中的场景,识别适合自动化的领域,并且通过这些团队之间的沟通揭示出可以通过自动化大大改进的领域。

以下图表显示了一些业务场景:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

手动流程-一个假设的场景

贝蒂·苏是比利·鲍勃机械零件公司的 QA 部门的一部分。在比利·鲍勃机械零件公司,有一个中等规模的开发团队。首席开发人员埃里克在季末的星期四早上开始手动发布流程。埃里克需要两天时间来完成发布周期。不幸的是,他是开发团队中唯一能够进行发布的人。埃里克在本地工作站上运行所有测试,并在必要时集成紧急补丁。埃里克完成后,他会将一个 ZIP 文件通过电子邮件发送给 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 文件。贝蒂第二天开始进行手动测试流程,不幸的是,她再次发现了一些小问题,并在下午通知维克多最新发布仍然存在一些问题。

维克多此刻承认感到沮丧,他把所有开发人员都召集到一个房间里,并说除非所有问题都得到解决,否则谁也不能离开。在度过了一个漫长的晚上后,所有最新的问题都得到了解决,维克多让每个人回家。第二天早上,维克多打包了最新的发布,并给贝蒂发送了一个新的 ZIP 文件。贝蒂在上一次测试周期后有些担心,但她很高兴所有的错误都得到了解决,并给予了 QA 的批准,并告诉维克多最新发布已经准备就绪。开发团队和 QA 团队一起庆祝了一周工作的结束,并享受了公司赞助的午餐,然后回家过周末。

在 QA 部门测试热修复时,一些 QA 团队成员的工作出现了重叠。Dillon 感到沮丧,因为他的一些工作与 Betty 的工作重叠。QA 部门没有自动化,因此每次发布都需要手动完成所有工作,无论是补丁还是常规发布,QA 都必须重新测试 UI 的所有部分。QA 团队的新成员 Nate 问 Dillon 是否有比手动测试更好的工作方式,但被告知这些做法已经在 QA 部门实施。

客户成功团队的 Tony 对新版本感到沮丧,因为他花了很多时间为客户X创建新视频,却被告知他的一些视频无法发布,需要放入储备库。QA 部门在最后一刻做出了停止功能Y的决定,但没有将这些信息传达给其他部门。

销售工程师领队之一的 Victor 正在进行公司演示,并向潜在客户展示导出 PDF 功能。在演示过程中,Victor 点击导出 PDF 功能,出现了一个显眼的错误消息。Victor 迅速转移到产品的另一个方面,称这是一个暂时的故障,并表示他将在另一个演示中展示这个功能。Victor 发现其中一个开发人员在后端服务中进行了一个本应该是简单更改的操作,却在生产环境中破坏了导出 PDF 功能。Victor 发现潜在客户已决定选择另一种软件解决方案,现在他显然很沮丧,因为他指望这位新客户来获得年终奖金。

UI/UX 团队的成员 Samantha 被告知她的一份模型缺少验证流程。Samantha 在功能Z的原型设计阶段寻求澄清,被告知页面不需要任何验证,但 David 认为需要验证流程。Samantha 显然很沮丧,决定休息一天,现在 David 在功能Z的工作上落后了进度。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

QA 的 Betty Sue 和开发团队的 John 之间有双向沟通。在寻找自动化帮助的领域时,沟通是至关重要的。随着各方之间的互动次数增加,参与方对手动流程的意识也在增加。手动流程一直隐藏着,直到更多的参与方,如营销、销售、客户成功和开发团队开始更频繁地合作。开发人员特别适合发现手动流程,因为非开发人员并不总能意识到一个流程是手动的,可以被自动化。

引入自动化

这是一个名为Johnny The Automation Bot的插图,用于描述公司的不同部门。Johnny 的每个肢体代表公司的一个不同部门:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Johnny The Automation Bot 是一个可以极大受益于自动化流程的领域的插图。自动化可以被视为一种程序或系统,其中机器完成了人类通常会做的工作。自动化需要了解正在进行的手动流程,与其他部门进行沟通,并找出哪些流程是手动进行的。CI 和 CD,正如我们将在本书中看到的,是极大增强公司生产力和流程的过程,因为它们消除了开发人员的假设和特定环境设置。

约翰尼自动化机器人的每个部分都有一个可以自动化的领域。销售部门目前正在向销售团队发送 Excel 工作表,并且很难跟上其他销售工程师所做的更改。约翰尼自动化机器人建议销售工程师有一个简单的方法将销售信息上传到公司内部网络,以更好地跟踪销售信息。约翰尼建议开发团队编写一个 Excel 集成,销售工程师可以轻松地将新的销售数据上传到公司内部网络。例如,可以添加一个菜单选项,连接到公司的 API 端点,自动将新的 Excel 更改上传到公司内部网络页面,该页面具有最新的销售信息。

质量保证部门正在手动测试产品,而手动测试是一项耗时且容易出错的活动。约翰尼建议质量保证部门开始使用Selenium WebDriver编写验收测试。Selenium 是一个浏览器自动化工具,质量保证部门可以使用诸如 Python 之类的语言来编写验收测试。约翰尼表示,使用 Selenium 编写自动化测试的优势在于可以编写一次并反复重用。这将带来额外的好处,即这些测试可以连接到 CI/CD 流水线中,我们将在本书的后面看到。

贝蒂从质量保证部门发现客户成功团队正在制作一系列视频,教授客户每个构建中的新功能。客户成功团队通过 FTP 上传视频,其中一些团队成员花费大部分时间上传文件。约翰尼自动化机器人建议通过脚本自动化这个过程。该脚本应该足够直观,以便客户成功团队的任何成员都可以运行它,并且应该能够在上传过程中出现任何网络延迟时进行重试。贝蒂分享了质量保证部门编写的一个可以自动化这个过程并作为后台进程运行的脚本。

托尼,客户成功团队的成员,现在在工作日中解放了几个小时的工作时间,可以专注于工作的更重要方面,比如通过创建出色的视频来帮助客户取得成功。托尼已经开始与质量保证团队合作,将开始发布视频并对产品的部分进行用户验收测试。由于手动测试已经委托给客户成功团队,质量保证现在能够更好地测试功能。质量保证正在专注于使用新的库自动化端到端测试套件,这将帮助他们更快地编写测试,并通知开发团队功能出现故障。

营销团队一直在 PowerPoint 幻灯片中嵌入注释,有时在演示或公司展示中注释会丢失或被覆盖。约翰尼建议开发团队创建一个可以将 PowerPoint 幻灯片转换为 markdown 的脚本,然后 markdown 文件可以进行版本控制,因为 markdown 只是文本文件。这将有额外的好处,即营销团队可以与销售团队分享信息,以创建更具说明性的图表。

维克多意识到手动流程正在破坏生产力,并且手动流程具有明显的劣势。维克多可以在发布周期中引入一个自动化系统,任何开发人员都可以通过一个单击部署按钮来运行。与维克多目前正在做的在本地工作站上运行所有测试不同,每个软件构建都可以推送到诸如 GitHub 之类的版本控制系统,并且所有测试都可以在 CI 环境(如 Jenkins)上运行,并且开发人员可以自动收到测试是否通过或失败的通知。例如,布鲁斯是团队中的一名新开发人员,他可以快速阅读开发人员文档,并在几乎没有指导的情况下开始进行下一个发布。约翰自动化机器人对这种做法表示赞许。

贝蒂也有机会自动化手动测试过程。使用诸如BrowserStack之类的工具,贝蒂可以编写一系列测试脚本,测试产品的每个部分。在一个小时内,贝蒂可以在测试环境中运行一套验收测试,并让维克多知道发布中的最新问题。维克多可以开始将问题分配给开发团队,并开始编写回归测试用例,以确保当前构建中没有回归。维克多对最新更改的工作原理感到自信,可以指导贝蒂到一个新的 URL,她可以下载最新的软件发布。Johnny 自动化机器人指出,创建 ZIP 文件并通过电子邮件发送的旧做法并不是一个好的做法,因为每次都需要额外的步骤,如果发送了错误的 ZIP 文件,可能会出现错误。约翰建议 QA 部门有一个专用的 URL,所有最新的发布都在那里,并且每个发布都有版本和特定信息,比如热修复。例如,最新的热修复可以是v5.1.0-hotfix 1,因此,对于每个热修复,QA 部门都会有一个压缩文件,其中包含最新的构建和一个说明符,比如热修复。如果这个构建是一个常规构建,那么它可以被命名为v5.1.0

维克多发现 QA 部门有一个 Browser Stack 账户。Browser Stack 提供对整套浏览器和移动客户端的访问,这可以帮助自动化 UI 的负载测试。负载测试是使用开发团队用于特殊场景(如负载测试)的自定义服务器进行的。约翰自动化机器人建议使用诸如 Browser Stack 之类的服务,或者拥有一个可以提供必要资源进行负载测试的自定义服务。

维克多发现 QA 团队在测试开发团队编写的电子邮件服务时遇到了问题。约翰自动化机器人建议开发团队确保 QA 拥有可以帮助使用电子邮件服务的脚本。维克多告诉贝蒂,新的电子邮件服务正在代理到SendGrid服务,并且开发团队已经编写了一系列 QA 可以使用的脚本。这些脚本有助于编写测试电子邮件,并可以帮助 QA 测试在失败条件下会发生什么。

UI/UX 团队正在将模型上传到Sketch - Sketch 是一个原型工具 - 并嵌入有关页面可能的验证状态和流程的注释。这些注释非常详细,对开发团队在公司冲刺中开始功能工作时非常有帮助。Johnny 自动化机器人建议开发团队编写一个插件,可以帮助 UI/UX 团队轻松共享这些信息。维克多决定创建一个 Sketch 插件,该插件可以创建一个带有嵌入式注释的 PDF,UI/UX 团队在原型制作完成后可以通过电子邮件发送给开发团队。这个插件对 UI/UX 团队来说很容易安装,因为他们只需双击文件,它就会自动安装插件。访问 PDF 和嵌入的注释将帮助开发人员了解新功能的用例和 UI 流程。

文森特,一位主要销售工程师,已经与开发团队沟通,他需要及时了解产品的流程变化,特别是在向潜在客户介绍公司路线图上的新功能时。约翰尼自动化机器人建议开发团队利用 Git 提交日志,其中包含有关最新功能更改的详细信息。维克多编写了一个脚本,用于抓取 Git 提交日志并编写一个包含所有最新功能工作的漂亮的 Markdown 文档。反过来,客户成功团队可以与开发团队合作,使用 Markdown 文件在公司博客上详细介绍所有最新功能。

这里有一个共同的主题。部门之间的沟通是发现手动流程并创建帮助自动化流程的合作伙伴关系的关键。除非了解手动流程,否则无法进行自动化,有时,唯一的自动化发生的方式是其他部门传达特定的痛点。

让我们重申一些通过开放协作自动化和增强的流程。维克多通过提供开发团队创建的脚本来帮助 QA 自动化了电子邮件测试服务问题。QA 通过分享一个上传视频并具有重试逻辑的脚本,帮助客户成功团队自动化了视频上传任务。销售部门表示需要更好地了解产品的新功能;这促使开发团队编写了一个从 Git 提交日志中获取信息并生成 Markdown 文件的脚本,客户成功团队使用该文件在公司博客上写了一篇漂亮的博客文章。UI/UX 团队现在在其 Sketch 应用程序中集成了一个插件,他们可以简单地点击一个按钮来生成在原型阶段记录的笔记的 PDF 文档,这反过来有助于开发团队开发新功能。开发团队发现 QA 正在使用一个名为 BrowserStack 的工具,并开始使用它来对产品进行负载测试。营销团队现在拥有营销 PowerPoint 幻灯片的版本副本,并将这些信息分享给销售团队,以创建公司演示的新图表。

UI/UX 团队决定创建一个样式指南,开发人员可以在软件产品中查找常见的 UI 模式。UI/UX 团队发现不同页面中使用了许多不同的样式,这导致许多客户感到困惑。例如,零部件供应页面在一个页面上有一个大蓝色保存按钮和一个红色取消按钮,但在供应商详细信息页面上有一个大红色保存按钮和一个蓝色取消按钮。客户因为 UI 没有统一使用颜色而点击错误的按钮。有时,页面使用确认模态框来添加和删除项目;其他时候,没有确认模态框。UI/UX 团队已经开始制作样式指南,并在公司内部网站上创建一个特殊的 URL,用于存放实时样式指南。其目的是明确创建和列出页面可用的十六进制颜色,设计产品中的所有按钮,并决定页面上表单的外观和行为。

此外,将有一个特殊的小部件页面,其中包含产品中所有专用小部件的 HTML 标记和样式嵌入:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这个样式指南具有十六进制颜色值,并嵌入了一些 HTML 元素和一个切换开关,这是一个专门的小部件,有关闭状态和开启状态。样式指南的目的是让开发人员能够简单地右键单击并复制 HTML 标记和 CSS,并建立统一的 UI 呈现。这是一种自动化形式,因为开发人员可以简单地重用现有的标记和样式,而不必手动创建 HTML 和自定义样式,其中统一性最好使用。每当产品的用户不得不猜测该做什么时,你肯定会招致灾难。

开发人员的生产力

由于维克多将 CI/CD 管道引入了构建中,许多耗时的活动现在都被自动化管道所取代。每当软件被推送到版本控制系统(如 Git)等上游时,Jenkins 会触发自动构建,运行所有单元测试和集成测试。开发人员可以迅速知道他们编写的代码是否引入了缺陷。记住,维克多不得不整合所有软件补丁,并在本地工作站上手动运行所有测试。这是乏味、耗时且不必要的。

由于所有软件都是向上游推送的,维克多已经为发布分支设定了代码截止日期,并开始对软件发布二进制文件进行版本控制,以便质量保证人员可以更清晰地划分每个构建版本。维克多立即变得更有效率,因为他可以开始将发布周期委托给团队中的其他开发人员。发布周期中遇到的任何问题都可以由任何开发人员在发布中记录下来。维克多现在有更多时间开始规划下一个软件周期,并在团队中指导初级开发人员。大卫现在很高兴,因为他可以将最新的更改推送到源代码控制,并在 CI 环境中运行所有测试,并更有信心地确保他的更改按预期工作。

贝蒂已经建立了一整套验收测试,检查软件产品的每个部分。产品中的任何回归问题都会立即在 CI 环境中显现出来,并且所有测试都可以每天运行。质量保证团队运行的测试比开发团队的测试更耗时和资源密集,因为它们是端到端的测试,但对于质量保证团队来说,优势在于所有测试都可以每天运行,并且质量保证团队每晚都会收到详细的测试失败报告。贝蒂编写了一组页面对象,帮助质量保证团队的其他成员重用其他测试脚本并减少测试周期。贝蒂现在在质量保证周期中有时间指导质量保证部门的新成员进行测试实践,并教他们如何正确地标记问题,以便开发团队知道最新发布中的问题所在:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这里的螺栓象征着已经就位的流程;在这种情况下,需要自动化的发布流程。

大卫现在可以开始帮助维克多指导团队中的初级开发人员,开发团队已经开始进行午餐学习系列,其他开发人员可以在团队中分享知识。开发团队很快意识到这些午餐学习会议也适用于质量保证部门。在其中一次午餐学习会议中,质量保证部门提出了一个关于协调质量保证和开发团队之间发布的变更。通过这种合作,发布周期从一周的过程缩短到 3 小时的过程。开发团队轮流进行发布工作,以便团队中的每个开发人员都可以学习如何进行发布。值班的开发人员确保质量保证部门有一个可供质量保证开始测试的构建,并且可以使用 CI 系统(如 Jenkins、Travis 或 CircleCI)自动触发此构建。在这些 CI 环境中,您可以设置在指定日期和时间运行的构建触发器。质量保证部门通知开发团队发布中的任何回归,每当开发团队准备推送热修复时,构建都清楚地用以下模式标明 - vMAJOR.MINOR.PATH-[hotfix]-[0-9]*。为了清晰起见,这里有一个例子 - v6.0.0-hotfix-1。这标明了主要版本6,次要版本0,补丁版本0和热修复编号1。这种命名方案有助于质量保证部门区分常规构建和hotfix构建。

客户成功团队已经向开发团队传达了一些客户在使用 Billy Bob’s Machine Parts 应用程序编程接口(API)服务时遇到问题。客户成功团队询问开发团队是否有任何方法可以帮助新的第三方 API 消费者上手。为了澄清,API 消费者是指正在使用现有 API 的人,而 API 提供者是维护实际 API 服务的人;因此,在这方面,Billy Bob’s Machine Part 是提供运行 API 供第三方开发人员使用的 API 提供者。开发团队告诉客户成功团队,他们一直想要创建一个开发者门户,这将帮助 API 消费者轻松地使用 API。然而,开发团队很难说服高层管理层开发者门户的价值,因为没有人要求这个特定的功能。客户成功团队迅速说服了高层管理层,开发者门户对 Billy Bob’s Machine Parts API 消费者来说将是一项巨大的资产,API 消费者可以开始使用 API 服务的数据构建漂亮的仪表板。

在其中一次开发者会议中,发现营销团队正在使用 Google Docs 共享文档,但很难找到上传的文档,因为你必须知道你在找什么。维克多很快意识到开发团队可以帮助建立一个公司内部网,销售和营销团队可以以更一致的方式共享数据。几个月后,公司内部网被公开,销售和营销团队兴奋地提到公司内部网已经帮助他们自动化了文档共享流程,过去销售和营销之间的许多会议浪费了无数时间来寻找某些文档。公司内部网暴露了一个过滤机制,可以使用标签系统快速找到文档。公司内部网还实现了一个功能,即销售和营销团队可以编辑共享文档。

销售团队现在有一个很好的工具可以与公司博客一起使用,展示新产品功能。维克多现在可以查看公司博客,了解产品中的最新功能。所有这些都是因为维克多编写的脚本而实现的,该脚本会从 Git 提交日志中提取提交消息,然后发布一个漂亮的 markdown 文件。该脚本在每个发布上都会使用,并列出所有已处理的项目,开发团队将新创建的 markdown 文件发送给客户成功团队,他们又根据这个 markdown 文件撰写了一篇详细介绍最新发布的博客文章。

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)的计划,客户成功团队可以利用它来自动化当前手动流程的许多部分。开发团队解释了 CLI 如何可以为客户成功团队节省大量时间,还可以帮助 API 消费者更好地使用 API 服务。CLI 可以快速为界面提供重要的页面数据。随着每个新版本可能会暴露出新的端点,开发团队制定了一个计划,为 CLI 添加与新 API 端点一起使用的额外命令。

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 的基础知识。

问题

  1. 什么是手动流程?

  2. 什么是自动化?

  3. 为什么打开部门之间的沟通很重要?

  4. CI/CD 代表什么?

  5. 自动化脚本为什么有用?

  6. 公司内部网的价值是什么?

  7. 为什么其他部门应该共享数据?

进一步阅读

要更多地了解 CI 并使用流行的 CI/CD 工具,可以考虑阅读

使用 Jenkins 学习持续集成-第二版 (www.amazon.com/dp/1788479351/),作者Packt Publishing

第二章:持续集成的基础知识

本章将帮助介绍持续集成CI)的概念,并为我们在后续章节中探讨的 CI/CD 概念奠定基础。了解 CI 构建的目的很重要,因为这些概念超越了您可能使用的任何特定的 CI/CD 工具。CI 很重要,因为它有助于保持代码库的健康,并帮助开发人员保持软件系统独立于任何特定的开发人员机器运行。CI 构建强制执行软件组件的独立性和本地环境配置。CI 构建应该与任何一个开发人员的配置解耦,并且应该能够重复和隔离状态。每次运行的构建本质上是独立的,这可以保证软件系统正常工作。

本章将涵盖以下主题:

  • 什么是 CI?

  • CI 的价值

  • 利用 CI 来减轻风险

  • 源代码检入时的软件构建

  • 小型构建和大型构建分解

  • CI 构建实践

技术要求

本章只假设对版本控制系统有一定的了解,但读者至少应该了解配置文件是什么,并且对编程有基本的了解。我们将简���查看一个示例 makefile,并在本章中提供一些代码片段。

在本章中,我们将查看几个代码示例,包括一个 API Workshop(github.com/jbelmont/api-workshop),在那里我们将解释一个 Makefile 和一个 Demo Application(github.com/jbelmont/advanced-tech-in-wilmington-react-app),它使用 React/Node.js/Express.js/RethinkDB,并且我们还将展示一个gulp.js脚本文件。

什么是 CI?

CI 本质上是一个软件工程任务,其中源代码同时合并和测试在主干上。CI 任务可以执行任何多种任务,包括测试软件组件和部署软件组件。CI 的行为本质上是规定的,可以由任何开发人员、系统管理员或运维人员执行。持续集成是持续的,因为开发人员可以在开发软件时持续集成软件组件。

软件构建到底是什么?

软件构建不仅仅是一个编译步骤。软件构建可以包括编译步骤、测试阶段、代码检查阶段和部署阶段。软件构建可以作为一种验证步骤,检查您的软件是否作为一个统一的单元工作。静态编译语言,如 Golang 和 C++,通常具有生成二进制文件的构建工具。例如,Golang 的构建命令,如go build,将生成一个静态编译的二进制文件,并对代码库运行 linting。其他语言,如 JavaScript,可以使用诸如gulp.js/grunt.js之类的工具来执行被认为是构建步骤的操作,例如缩小 - 将多个 JavaScript 源文件转换为一个文件 - 和丑化,它会剥离源文件的注释和任何空白,以及 linting 和运行测试运行器。

CI 流程步骤概述

开发人员可以向版本控制 项目VCP)系统提交代码,例如 GitHub 和 GitLab。CI 服务器可以轮询仓库以获取更改,或者可以配置 CI 服务器通过 WebHook 触发软件构建。我们稍后将在 Jenkins、Travis 和 Circle CI 中进行讨论。CI 服务器将从 VCP 系统获取最新的软件版本,然后可以执行集成软件系统的构建脚本。CI 服务器应该生成反馈,将构建结果通过电子邮件发送给指定的项目成员。CI 服务器将持续轮询更改,或者将从配置的 WebHook 响应。

CI 的价值

CI 有很多价值。首先,CI 构建有价值,因为它可以减少风险,软件的健康状况变得可衡量。CI 有助于减少开发者的假设。CI 环境不应依赖环境变量,也不应依赖某个人机器上设置的特定配置文件。

CI 构建应该干净地独立于每个开发者的本地机器,并且应该与任何本地环境解耦。如果一个开发者说构建在他/她的机器上可以运行,但其他开发者无法运行完全相同的代码,那么你就知道构建可能无法正常运行。CI 构建可以帮助解决这些问题,因为 CI 构建与任何给定开发者的设置和环境变量解耦,并且独立运行。

CI 构建应该减少重复的手动流程,构建过程应该在每次构建时都以相同的方式运行。CI 构建过程可能包括编译步骤、测试阶段和报告生成阶段。CI 构建过程应该在开发者向 Git、Subversion 和 Mercurial 等版本控制系统提交代码时运行。CI 构建应该释放开发者的时间,让他们可以从事更有价值的工作,并减少重复手动流程可能带来的错误。

一个良好的 CI 构建应该能够在任何时间、任何地点生成可部署的软件。CI 构建应该提供项目的可见性,并且应该在开发团队中建立对软件的信心。开发者可以放心,CI 构建会比在本地运行构建更容易捕捉到代码变更的问题。

利用 CI 来减轻风险

CI 可以帮助减轻软件构建中普遍存在的风险,比如“在我的机器上可以运行”的症状。CI 还有助于统一集成失败点,比如数据库逻辑以及其他类型的问题。

但在我的机器上可以运行!

开发者之间的一个共同问题是,一个开发者的机器上可以运行软件构建,但另一个开发者的机器上却无法运行。每个开发者的机器应尽可能地与软件集成相似。完成软件构建所需的一切都需要提交到版本控制系统中。开发者不应该有只存在于他们本地机器上的自定义构建脚本。

数据库同步

完成软件构建所需的任何数据库构件都应存储在版本控制中。如果你有一个关系型数据库,那么任何数据库创建脚本、数据操作脚本、SQL 存储过程和数据库触发器都应存储在版本控制中。

例如,如果你使用 NoSQL 数据库系统,比如 MongoDB (www.mongodb.com/),并且使用 RESTful API,那么一定要在文档中记录 API 端点。记住,开发者可能需要数据库特定的代码来实际运行软件构建。

缺少部署自动化阶段

应该使用部署工具自动化软件部署。你使用的部署工具可能因不同的软件架构而有所不同。

以下是一些部署工具的列表:

部署工具很有价值,因为它们往往是跨平台的,并且可以在许多不同的软件架构中使用。例如,如果开发人员编写了一个 Bash 脚本,那么其中有一个基本假设,即其他开发人员正在使用类 Unix 系统,而在 Windows 环境中工作的开发人员可能无法运行脚本,这取决于他们使用的 Windows 版本。现在 Windows 10 提供了一个 bash 子系统,Windows 开发人员可以在操作 Windows 操作系统时运行 Unix 命令和脚本。

缺陷的晚发现

CI 构建可以帮助防止软件缺陷的晚发现。CI 构建应该具有足够好的测试套件,覆盖代码库的大部分代码。一个健康的代码库可能的度量标准是代码库中 70%或更多的代码覆盖率。稍后我们将讨论代码覆盖率,但任何软件测试都应该被检入源代码,并且应该在 CI 构建上运行测试。您拥有的任何软件测试都应该在 CI 系统上持续运行。

测试覆盖率未知

总的来说,高百分比的代码覆盖率表示一个经过充分测试的代码库,但并不一定保证代码库没有软件错误,只是测试套件在整个代码库中具有良好的测试覆盖率。尝试使用代码覆盖工具,以查看您的测试实际上覆盖了多少源代码。

代码覆盖工具

以下是一些流行的代码覆盖工具:

  • Istanbul (https://istanbul.js.org/):又一个 JavaScript 代码覆盖工具,可以计算语句、行、函数和分支覆盖率,并通过模块加载器钩子在运行测试时透明地添加覆盖。支持所有 JS 覆盖使用情况,包括单元测试、服务器端功能测试和浏览器测试。专为规模而构建。

  • Goveralls (github.com/mattn/goveralls):用于coveralls.io/持续代码覆盖跟踪系统的 Go 集成。

  • dotCover (www.jetbrains.com/dotcover/):JetBrains dotCover 是一个.NET 单元测试运行器和代码覆盖工具,可以与 Visual Studio 集成。

确保您知道您的代码在多大程度上受到单元测试的覆盖。dotCover 可以计算并报告针对.NET Framework、Silverlight 和.NET Core 应用程序的语句级代码覆盖率。

项目可见性不足

CI 系统应该配置为以多种方式发送警报:

  • 电子邮件

  • 短信

  • 通过智能手机的推送通知警报

一些软件开发办公室还使用一些其他创造性的方式来发送软件构建的问题通知,比如某种环境光变化或甚至对讲系统。主要的观点是开发人员需要被通知 CI 构建已经中断,以便他们可以快速修复构建。CI 构建不应该保持中断,因为这可能会干扰其他开发人员的工作。

源代码检入时的软件构建

软件构建应该在版本控制系统中的每次源代码检入时触发。这是部署流水线中的一个重要步骤,我们将在下一章中看到。

软件构建是什么?

软件构建可以仅包括编译软件组件。构建可以包括编译和运行自动化测试,但总的来说,您在构建中添加的进程越多,反馈循环就会变得越慢。

脚本工具

建议使用专门用于构建软件的脚本工具,而不是个人脚本。自定义 Shell 脚本或批处理脚本往往不具备跨平台性,并且可能隐藏环境配置。脚本工具是开发一致、可重复的构建解决方案的最有效过程。

以下是一些脚本工具的列表:

执行单一命令构建

努力实现单一命令构建,以便简化构建软件的过程,因为您使运行构建过程变得更容易,您将加快采用速度和开发人员参与度。如果构建软件是一个复杂的过程,那么最终只有少数开发人员会真正进行构建,这不是您想要的。

简而言之构建您的软件

  1. 使用脚本工具(例如 Ant (ant.apache.org/), Make (www.gnu.org/software/make/), Maven (maven.apache.org/), 或 Rake (ruby.github.io/rake/))创建您的构建

  2. 从 CI 构建中开始一个简单的过程

  3. 将每个过程添加到构建脚本中以集成您的软件

  4. 从命令行或 IDE 运行您的脚本

这是一个示例 makefile,它从我的开源github.com/jbelmont/api-workshop运行一个 Golang API 服务:

BIN_DIR := "bin/apid"
 APID_MAIN := "cmd/apid/main.go"
all: ensure lint test-cover
ensure:
 go get -u github.com/mattn/goveralls
 go get -u github.com/philwinder/gocoverage
 go get -u github.com/alecthomas/gometalinter
 go get -u github.com/golang/dep/cmd/dep
 go get -u golang.org/x/tools/cmd/cover
 dep ensure
lint:
 gometalinter --install
 gometalinter ./cmd/... ./internal/...
compile: cmd/apid/main.go
 CGO_ENABLED=0 go build -i -o ${BIN_DIR} ${APID_MAIN}
test:
 go test ./... -v
test-cover:
 go test ./... -cover
## Travis automation scripts
travis-install:
 go get -u github.com/mattn/goveralls
 go get -u github.com/philwinder/gocoverage
 go get -u github.com/alecthomas/gometalinter
 go get -u github.com/golang/dep/cmd/dep
 go get -u golang.org/x/tools/cmd/cover
 dep ensure
travis-script:
 set -e
 CGO_ENABLED=0 go build -i -o ${BIN_DIR} ${APID_MAIN}
 gometalinter --install
 gometalinter ./cmd/... ./internal/...
 go test ./... -cover
 gocoverage
 goveralls -coverprofile=profile.cov -repotoken=${COVERALLS_TOKEN}

以下是使用gulp.js的示例构建脚本,该脚本从sass源文件生成 CSS 构建并运行 linter。第一块是初始化变量和准备配置对象以供使用:

'use strict';

const gulp = require('gulp');
const webpack = require('webpack');
const sourcemaps = require('gulp-sourcemaps');
const sass = require('gulp-sass');
const autoprefixer = require('gulp-autoprefixer');
const uglify = require('gulp-uglify');
const concat = require('gulp-concat');
const runSequence = require('run-sequence');
const gutil = require('gulp-util');
const merge = require('merge-stream');
const nodemon = require('gulp-nodemon');
const livereload = require('gulp-livereload');
const eslint = require('gulp-eslint');

// Load Environment constiables
require('dotenv').config();
const webpackConfig = process.env.NODE_ENV === 'development'
  ? require('./webpack.config.js')
  : require('./webpack.config.prod.js');

const jsPaths = [
  'src/js/components/*.js'
];
const sassPaths = [
  'static/scss/*.scss',
  './node_modules/bootstrap/dist/css/bootstrap.min.css'
];

const filesToCopy = [
  {
    src: './node_modules/react/dist/react.min.js',
    dest: './static/build'
  },
  {
    src: './node_modules/react-dom/dist/react-dom.min.js',
    dest: './static/build'
  },
  {
    src: './node_modules/react-bootstrap/dist/react-bootstrap.min.js',
    dest: './static/build'
  },
  {
    src: './images/favicon.ico',
    dest: './static/build'
  },

  {
    src: './icomoon/symbol-defs.svg',
    dest: './static/build'
  }
];

这段代码的第二块是我们设置 gulp 任务的地方:复制 React.js 文件,对 JavaScript 文件进行丑化,创建构建 JavaScript 文件,并从 Sass 文件创建 CSS 文件。

gulp.task('copy:react:files', () => {
  const streams = [];
  filesToCopy.forEach((file) => {
    streams.push(gulp.src(file.src).pipe(gulp.dest(file.dest)));
  });
  return merge.apply(this, streams);
});

gulp.task('uglify:js', () => gulp.src(jsPaths)
    .pipe(uglify())
    .pipe(gulp.dest('static/build')));

gulp.task('build:js', (callback) => {
  webpack(Object.create(webpackConfig), (err, stats) => {
    if (err) {
      throw new gutil.PluginError('build:js', err);
    }
    gutil.log('[build:js]', stats.toString({ colors: true, chunks: false }));
    callback();
  });
});

gulp.task('build:sass', () => gulp.src(sassPaths[0])
    .pipe(sourcemaps.init())
    .pipe(sass({
      outputStyle: 'compressed',
      includePaths: ['node_modules']
    }))
    .pipe(autoprefixer({ cascade: false }))
    .pipe(concat('advanced-tech.css'))
    .pipe(sourcemaps.write('.'))
    .pipe(gulp.dest('./static/build'))
    .pipe(livereload()));

gulp.task('build:vendor:sass', () => gulp.src([...sassPaths.slice(1)])
    .pipe(sourcemaps.init())
    .pipe(sass({
      outputStyle: 'compressed',
      includePaths: ['node_modules']
    }))
    .pipe(autoprefixer({ cascade: false }))
    .pipe(concat('vendor.css'))
    .pipe(sourcemaps.write('.'))
    .pipe(gulp.dest('./static/build')));

在这段最后的代码中,我们运行一些监视任务,将监视 JavaScript 文件和 Sass 文件中的任何更改,进行 linting,并创建一个 nodemon 进程,该进程将在任何文件更改时重新启动 Node 服务器:

gulp.task('watch:js', () => {
  const config = Object.create(webpackConfig);
  config.watch = true;
  webpack(config, (err, stats) => {
    if (err) {
      throw new gutil.PluginError('watch:js', err);
    }
    gutil.log('[watch:js]', stats.toString({ colors: true, chunks: false }));
  });
  gulp.watch('static/js/components/*.js', ['uglify:js', 'build:js']);
});

gulp.task('watch:sass', () => {
  gulp.watch('static/scss/*.scss', ['build:sass']);
});

gulp.task('watch-lint', () => {
  // Lint only files that change after this watch starts
  const lintAndPrint = eslint();
  // format results with each file, since this stream won't end.
  lintAndPrint.pipe(eslint.formatEach());

  return gulp.watch(['*.js', 'routes/*.js', 'models/*.js', 'db/*.js', 'config/*.js', 'bin/www', 'static/js/components/*.jsx', 'static/js/actions/index.js', 'static/js/constants/constants.js', 'static/js/data/data.js', 'static/js/reducers/*.js', 'static/js/store/*.js', 'static/js/utils/ajax.js', '__tests__/*.js'], event => {
    if (event.type !== 'deleted') {
      gulp.src(event.path).pipe(lintAndPrint, {end: false});
    }
  });
});

gulp.task('start', () => {
  nodemon({
    script: './bin/www',
    exec: 'node --harmony',
    ignore: ['static/*'],
    env: {
      PORT: '3000'
    }
  });
});

gulp.task('dev:debug', () => {
  nodemon({
    script: './bin/www',
    exec: 'node --inspect --harmony',
    ignore: ['static/*'],
    env: {
      PORT: '3000'
    }
  });
});

gulp.task('build', (cb) => {
  runSequence('copy:react:files', 'uglify:js', 'build:js', 'build:sass', 'build:vendor:sass', cb);
});

gulp.task('dev', (cb) => {
  livereload.listen();
  runSequence('copy:react:files', 'uglify:js', 'build:sass', 'build:vendor:sass', ['watch:js', 'watch:sass', 'watch-lint'], 'start', cb);
});

gulp.task('debug', (cb) => {
  livereload.listen();
  runSequence('copy:react:files', 'uglify:js', 'build:sass', 'build:vendor:sass', ['watch:js', 'watch:sass', 'watch-lint'], 'dev:debug', cb);
});

将构建脚本与 IDE 分开

尽量避免将构建脚本与任何特定的集成开发环境IDE)耦合。构建脚本不应依赖于任何 IDE。

这很重要,有两个原因:

  • 每个开发人员可能使用不同的 IDE /编辑器,并且可能具有不同的配置

  • CI 服务器必须在没有任何人为干预的情况下执行自动化构建

软件资产应该是集中的

以下软件资产应该在集中式版本控制存储库上可用:

  • 组件,例如源文件或库文件

  • 第三方组件,例如 DLL 和 JAR 文件

  • 配置文件

  • 需要初始化应用程序的数据文件

  • 构建脚本和构建环境设置

  • 需要一些组件的安装脚本

您必须决定应该放入版本控制的内容。

创建一致的目录结构

您必须为软件资产选择一致的目录结构,因为它可以帮助您从 CI 服务器执行脚本检索。

这是我为骨架 React/Redux 应用程序做的一个示例文件夹结构:

  • ca(证书颁发机构)

  • 配置(配置文件)

  • db(与数据库相关的内容)

  • 文档(文档)

  • 图像

  • 模型(数据文件)

  • 测试(所有我的测试文件)

  • 单元

  • 集成

  • e2e

  • 助手

  • 静态

  • 构建

  • js

  • 操作

  • 组件

  • 常量

  • 数据

  • reducers

  • 存储

  • 实用程序

  • scss

  • utils(实用文件)

这是我遵循的另一个目录结构,它是面向包的,并且是由Golang 社区的 Bill Kennedy推荐的(www.ardanlabs.com/blog/2017/02/package-oriented-design.html):

  • 工具包

  • 为现有的不同应用项目提供基础支持的软件包

  • 日志记录,配置或 Web 功能

  • cmd/

  • 为正在启动,关闭和配置的特定程序提供支持的软件包

  • 内部/

  • 为项目拥有的不同程序提供支持的软件包

  • CRUD、服务或业务逻辑

  • internal/platform/

  • 为项目提供内部基础支持的软件包

  • 数据库、身份验证或编组

主要的一点是,你应该遵循一个标准的命名约��,所有开发人员都遵循。这将有助于开发团队的工作,因为他们将熟悉代码中的特定事物。并不是每个人都会同意特定的目录布局,但拥有一个标准是最重要的部分。例如,任何在新服务上工作的人应该能够根据文件夹的命名约定设置项目结构,源文件放在哪里,测试文件放在哪里:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这是我在 GitHub 上为 API Workshop 创建的一个示例目录结构(github.com/jbelmont/api-workshop)。

软件构建应该快速失败。

可以通过以下方式实现:

  1. 集成软件组件。

  2. 运行真正的单元测试——不依赖于数据库但在隔离环境中运行的单元测试。

  3. 确保单元测试能够快速运行。如果一个单元测试需要几分钟的时间,那么这可能是一个问题的迹象。

  4. 运行其他自动化流程(重建数据库,检查和部署)。

对于他们的构建,其他步骤是必要的,这取决于每家公司。

为任何环境构建

应该为不同的环境设置配置文件和环境变量,例如 dev/prod/test。日志详细程度应该能够根据环境进行设置。开发人员可能需要增加日志以进行调试。应用服务器配置信息可以在构建文件中设置,以及数据库连接信息和框架配置。

这是一个可以使用的示例文本文件。需要注意的一点是,这些文件不应该提交到源代码控制,因为它们可能包含客户机密和 API 密钥:

API_URL=http://localhost:8080
PORT=8080
AUTH_ZERO_CLIENT_ID=fakeClientId
AUTH_ZERO_JWT_TOKEN=someFaketToken.FakedToken.Faked
AUTH_ZERO_URL=https://fake-api.com
REDIS_PORT=redis:6379
SEND_EMAILS=true
SMTP_SERVER=fakeamazoninstance.us-east-1.amazonaws.com
SMTP_USERNAME=fakeUsername
SMTP_PASSWORD=FakePassword
SMTP_PORT=587
TOKEN_SECRET="A fake Token Secret"

这样的配置文本文件可以帮助其他开发人员连接到第三方服务,并有助于组织客户端秘密信息的存储位置。

小型构建和大型构建的分解

通常,小型构建是可以由 CI 服务器快速运行的构建,通常包括编译步骤以及运行所有单元测试。小型构建可以通过运行分阶段构建来优化,这将在CI 构建实践部分讨论。

大型构建实际上是运行所有构建任务的一个大型构建。进行大型构建的缺点是会阻止开发人员运行它们。如果软件构建需要很长时间才能运行,那么许多开发人员将避免运行构建。快速运行的较小构建鼓励开发人员不断地在版本控制系统上检入他们的更改,并有助于保持代码库的健康。

CI 构建实践

CI 构建实践就像是阶梯;它们相互累积。正如我们将在下一章中看到的,CI 构建过程中的每一步都很重要,并提供了确保代码库处于健康状态的保证。

私有构建

开发人员在提交代码到存储库之前应该运行私有构建。

这是一个使用 Git 的示例开发人员会话:

  1. 从存储库检出要更改的代码:

  2. 进入版本控制的文件夹。

  3. git checkout -b new_branch

  4. 对代码进行更改:

  5. 编辑myFile.go

  6. 从存储库获取最新的系统更改:

  7. git pull

  8. 在本地机器上运行一个执行所有单元测试和可能的集成测试的构建。

  9. 将代码更改提交到存储库。

  10. CI 构建应该自动触发构建并运行存储库中的任何测试。

  11. CI 构建还应该执行其他任务,如报告和调用其他服务(如果需要)。

使用 CI 服务器

CI 服务器应该定期轮询版本控制存储库系统(如 GitHub)的更改,或者通过 WebHook 配置以触发软件构建。CI 构建应该按计划执行某些操作-每小时或每天,如果需要的话。您应该确定一个安静期,在此期间不执行项目的集成构建。CI 服务器应该支持不同的构建脚本工具,如 Rake、Make、NPM 或 Ant。CI 服务器应该向相关方发送电子邮件,并显示先前构建的历史记录。

CI 服务器应该显示一个 Web 访问的仪表板,以便所有相关方在必要时可以查看集成构建信息。Jenkins、Travis 和 Circle CI 都有 Web 访问的仪表板。CI 服务器应该支持不同的版本控制系统,如 svn、Git 和 mercurial。

手动集成构建

手动运行集成构建是减少集成构建错误的一种方法,如果有长时间运行的功能,将很难在 CI 服务器上运行,但要谨慎使用这种技术。例如,您可以指定一个未被使用的机器来执行手动集成任务;尽管使用云,现在只需按需启动服务器实例就更容易了。

运行快速构建

努力通过增加计算资源尽快运行软件构建。将运行较慢的测试,如系统级测试,转移到次要构建或每夜构建。将代码检查转移到第三方服务。例如,对于代码覆盖分析,可以使用以下第三方服务:

运行分阶段的构建以促进快速构建。第一次构建可以编译并运行所有单元测试。第二次构建可以运行所有集成测试和系统级测试。您可以有尽可能多的阶段来实现快速构建。可以说,第一次构建应该是最快的,因为这将是开发人员在向代码库签入代码时使用的主要构建。

摘要

本章介绍了 CI 概念的基础,并介绍了在开发团队中使用成功的 CI 服务器的技术。我们研究了脚本工具和构建工具。我们讨论了软件构建是什么,创建构建脚本时要遵循的良好实践,以及一些测试概念,如代码覆盖。下一章是关于持续交付CD),这是对 CI 的自然延伸,我们将详细介绍部署流水线、配置管理、部署脚本和部署生态系统。

问题

  1. 什么是软件构建?

  2. 什么是分阶段构建?

  3. 您能说出一些脚本工具的名称吗?

  4. 为什么要遵循命名约定和文件夹结构?

  5. CI 的价值是什么?

进一步阅读

阅读的一本好书,更多关于 CI 的内容是《使用 Jenkins 进行持续集成学习-第二版:使用 Jenkins 2 实施持续集成和持续交付的初学者指南》(www.amazon.com/dp/1788479351/),由 Packt Publishing 出版。

第三章:持续交付的基础知识

可以说,软件的最重要部分实际上是将其交付并准备供最终用户使用。持续交付(CD)是您将软件产品交付给最终用户的时刻,也是本章的基础。只有当您的预期用户实际上可以使用产品时,产品才有用。在本章中,我们将讨论部署流水线,并结合自动化和 CD 的概念。

本章将涵盖以下主题:

  • 交付软件的问题

  • 配置管理

  • 部署流水线

  • 部署脚本

  • 部署生态系统

技术要求

本章假定您了解自动化和持续集成的概念。如果您对这些主题中的任何一个感到不确定,请先阅读第一章,“自动化测试的 CI/CD”和第二章,“持续集成的基础知识”,然后再阅读本章。

本章的代码文件可以在github.com/jbelmont/api-workshop/blob/master/Gopkg.toml找到。

交付软件的问题

在尝试将软件产品交付给最终用户时,可能会出现许多问题,我们将看看影响软件交付的几种情况。一个可能的情况是开发人员正在开发一个新功能,但新功能可能实际上无法通过 CI 构建阶段,或者可能不像产品所有者最初提出的那样运行。另一个可能的情况是,预期的受众没有得到适当的理解,这将影响用户对最终产品的使用。另一个可能的情况是,软件产品没有得到适当的解耦,并且是用泡泡糖和胶带拼凑在一起的,新功能请求会导致许多回归问题。

我们所说的交付软件是什么意思?

关于软件交付实际意味着什么可能会有很多争论。在本章中,所指的是实际的软件产品已交付给预期的用户,而不仅仅是软件产品被质量保证(QA)部门批准为有效。

常见的发布反模式

存在一些常见的发布反模式,您应该避免,例如手动部署软件,手动配置管理以及每个环境的不同环境配置。

手动部署软件

这种类型的反模式很常见,可能会导致软件交付中的瓶颈。软件交付的那一天是充满压力和错误的。运维人员 Tom 的一天是从将软件构件从版本控制系统复制到生产环境开始的。Tom 通过文件传输协议(FTP)复制文件,但忘记添加一个新的配置文件,登录页面不再工作。Tom 不得不与开发团队交谈,询问是否添加了新的配置文件,并等待数小时才得到回复。

一旦 Tom 得到了新的配置文件,他将其上传到生产环境。现在登录页面可以工作了,但一些页面加载时出现了奇怪的图像放置和不规则性。Tom 联系了 UI/UX 团队,发现生产环境中缺少一个 CSS 文件。Tom 上传了 CSS 文件,现在页面可以正确加载了。Tom 询问客户成功团队是否可以进一步测试生产环境中的新更改,最终在晚上 7 点左右结束了一天的工作。

如果存在一份详细描述软件产品交付的长文档,这可能表明存在手动流程。这进一步使产品的交付变得更加复杂,因为在整个过程中的任何错误都可能导致更多问题。如果交付变得不可预测,这也可能指向这种反模式。

部署自动化来拯救

正如我们在第一章中讨论的那样,自动化测试的 CI/CD,自动化是一个行为以可重复和自动化的方式完成的过程。软件交付应该是一个自动化的过程,因为这将有助于确保软件交付的一致实践和行为。我们将在本章后面看到一些工具,这些工具将帮助您自动化软件交付流程。

手动配置管理

这种反模式可能会让运维人员感到沮丧,因为他们将是最后一个了解产品新行为的人。如果软件交付的当天是运维团队第一次看到新功能,那么他们可能会对软件行为感到惊讶。辛迪是运维团队的成员,她被委托交付软件,并注意到安装脚本完全失效,因为它无法与识别(ID)服务器通信。辛迪向开发团队发送日志消息,发现 ID 服务器的一个客户端密钥已更改,并且安装脚本需要使用这个新值才能正确连接。

如果辛迪意识到 ID 服务器的这个新变化,这种问题可能会得到缓解,但开发人员正在另一个环境中工作,QA 部门得到了这个信息来测试新功能,但直到交付当天他们才遇到这个问题,没有人想到将这个信息传递给运维部门。

配置管理自动化

我们将讨论可以帮助解决配置管理问题的工具,比如之前遇到的问题。使用适当的工具,运维/DevOps 人员可以快速获得每个环境的正确环境配置,包括生产环境。

生产环境与其他环境的不同之处

这种反模式可能特别具有挑战性,因为在开发中测试了所有的更改,暂存环境在生产环境中可能表现不稳定。例如,特拉维斯在 QA 部门担任测试员,自新功能推出以来一直在测试暂存环境。比利是一名运维人员,由于暂存环境与生产环境完全不同,他无法看到新功能。比利还注意到生产环境中缺少在暂存环境中显示的关键信息。比利联系开发团队,发现必须运行数据库迁移脚本才能使新功能在生产环境中运行。

生产环境应该与暂存环境相同

所有环境,包括测试、暂存和生产环境,都应该有必要的迁移脚本和任何其他软件资产,以防止生产环境的故障,并且开发团队应该确保将操作指向脚本文件的任何更改,或者在共享文档中清楚地标记这些更改。

如何进行软件发布

在进行软件发布时,有一些重要的步骤需要考虑,比如频繁发布以避免一次引入太多的更改,并确保发布是自动化的。

频繁发布

软件发布必须频繁。大型软件发布往往充斥着问题,因此最好使发布之间的增量(更改)较小。通过增加软件发布的频率,您还可以获得更快的反馈。大型软件发布往往需要更长的时间,关键反馈可能无法及时传达。

自动化发布

手动发布存在问题,因为它们不可重复。每次进行手动发布时,由于配置更改、软件更改和环境更改,都会有所不同。手动发布步骤充满错误,因为每个步骤都是手动的,可能导致级联错误。手动更改的危险的一个很好的例子是,当最受欢迎的云提供商亚马逊网络服务(AWS)在美国东部地区遭受重大故障时,因为运维人员在手动流程的一系列步骤中输入了错误的命令。自动化是软件发布的关键,因为它确保了软件交付流程的可重复性和控制。在本章中,我们将进一步探讨部署脚本工具,以帮助自动化软件交付。

自动化在交付软件方面的好处

正如我们之前所阐述的,自动化在软件交付中非常重要,因为它确保了软件发布的可重复性、可靠性和可预测性。通过自动化软件交付流程,可以避免或减轻灾难性事件,而不是通过漫长的手动流程。

团队赋能

如果自动化已经实施,QA 部门可以安全地选择旧版本的软件发布来测试回归。运维人员可以运行在暂存中使用的脚本,而不会因为环境级别的差异而遇到问题。通过自动化软件流程,运维人员可以在交付过程中出现灾难时安全地回滚发布。此外,正如我们在第二章中所讨论的,自动化可以帮助实现一键式发布。

减少错误

自动化可以帮助减少手动流程可能造成的错误。正如我们之前所看到的,配置管理问题可能导致软件交付不佳。手动软件发布无法有效地确保可重复性,因此容易出错。

减轻压力

另一个好处是在软件交付期间减少所有人员的压力。手动流程往往会造成不必要的压力,因为执行手动流程的人必须要细心,不能在交付过程中犯任何错误。自动交付流程非常好,因为它确保每次运行都会以相同的方式执行。手动流程中的错误可能需要高级人员的支持来解决问题。

配置管理

包含重要信息的配置文件,如客户端密钥和密码,必须得到妥善管理,并且必须在其他环境中保持同步。每个环境可能有不同的环境变量,必须被使用并传递到应用程序中。

配置管理到底意味着什么?

配置管理可以简要描述为检索、存储、识别和修改与每个给定项目相关的所有软件工件以及软件工件之间的任何关系的过程。

版本控制

版本控制是保持所有软件工件之间修订的手段。版本控制对于配置管理非常重要,因为任何包含环境文件的文件的更改都应该在版本控制下。

托尼是开发团队的一员,他一直在使用一个未纳入源代码控制的属性文件,并且一直在对产品中的单点登录(SSO)流程进行更改。托尼不小心删除了文件,并且丢失了在 SSO 流程中必要的所有客户端 ID 和密钥。现在,托尼必须去不同的 API 门户,并重新生成一些属性的客户端密钥,因为它们在创建时只显示一次,现在他必须通知团队的其他成员更新他们的属性文件。

示例属性文件

我已经添加了一个示例属性文件,其中包含客户端秘密信息和身份验证秘密信息。这对于给定的环境正常运行是必要的,但不应该检入源代码控制,这里仅用于演示目的。

API_URL=http://localhost:8080
PORT=8080
AUTH_ZERO_CLIENT_ID=fakeClientId
AUTH_ZERO_JWT_TOKEN=someFakeToken.FakedToken.Faked
AUTH_ZERO_URL=https://fake-api.com
REDIS_PORT=redis:6379
SEND_EMAILS=true
SMTP_SERVER=fakeamazoninstance.us-east-1.amazonaws.com
SMTP_USERNAME=fakeUsername
SMTP_PASSWORD=fakePassword
SMTP_PORT=587
TOKEN_SECRET="A fake token secret"

TOKEN_SECRET环境变量只能看到一次,所以如果丢失了,那么你必须在 API 门户中重新生成它。

版本控制管理工具

这是一个版本控制管理工具的列表:

版本控制实践

一个重要的做法是尽可能将所有东西都放在版本控制下,以避免在软件产品中丢失重要工作。网络文件、配置文件、部署脚本、数据库脚本、构建脚本以及任何其他对应用程序正常运行很重要的工件都应该在版本控制下,否则你会冒着丢失关键数据的风险。

经常进行软件检查

经常检查主分支非常重要,否则你会冒着在代码库中引入破坏性更改的风险。此外,频繁的检查可以帮助开发人员随时注意带入小的更改。应避免对代码库进行大规模的更改,因为这样更难测试并且可能会引起回归。频繁的检查也是有益的,因为破坏性更改会更快地被注意到。

编写描述性和有意义的提交消息

使用包括问题跟踪信息的描述性提交消息,比如 Jira 问题,清楚地描述提交的意图。避免编写模糊的提交消息,比如修复错误完成,因为这些类型的提交消息是没有用的,对开发人员以后也没有帮助。

这是一个示例描述性提交消息[DEV-1003],添加了一个新的导航链接到零部件供应列表。还添加了一个新导航的测试用例。这显然更加描述性。此外,在 Jira 中,当你提供一个像 DEV-1003 这样的问题时,它会在 Jira 问题中创建一个引用此问题的链接。此外,如果你创建一个拉取请求并在git commit中放入 Jira 问题,它将把你的拉取请求与问题链接起来。

依赖管理

应用程序通常具有对软件产品至关重要的第三方依赖项。依赖管理是任何应用程序的重要部分,不同的编程语言以不同的方式处理依赖管理。

示例 Node.js 依赖文件和 Gopkg.toml 依赖文件

这是一个Gopkg.toml文件,其中包含存储库中每个依赖项的版本和包信息。

# Gopkg.toml example
#
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
# for detailed Gopkg.toml documentation.
#
# required = ["github.com/user/thing/cmd/thing"]
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
#
# [[constraint]]
#   name = "github.com/user/project"
#   version = "1.0.0"
#
# [[constraint]]
#   name = "github.com/user/project2"
#   branch = "dev"
#   source = "github.com/myfork/project2"
#
# [[override]]
#   name = "github.com/x/y"
#   version = "2.4.0"
#
# [prune]
#   non-go = false
#   go-tests = true
#   unused-packages = true

[prune]
  go-tests = true
  unused-packages = true

[[constraint]]
  branch = "v2"
  name = "gopkg.in/mgo.v2"

[[constraint]]
  name = "github.com/dgrijalva/jwt-go"
  version = "3.1.0"

[[constraint]]
  name = "github.com/go-playground/locales"
  version = "0.11.2"

[[constraint]]
  name = "github.com/pkg/errors"
  version = "0.8.0"

[[constraint]]
  name = "github.com/pborman/uuid"
  version = "1.1.0"

[[constraint]]
  name = "gopkg.in/go-playground/validator.v9"
  version = "9.9.3"

像这样管理依赖是很重要的,因为第三方依赖项很容易给应用程序带来破坏性更改,第三方依赖项中的 API 更改可能会破坏任何正在运行的应用程序中的关键行为。

管理软件组件

通常,软件项目将以单片构建开始,所有工作组件都在一个层中。随着应用程序的规模和成熟度的增长,应用程序的层将分解为服务或不同的层,这就是需要单独的构建流水线的地方。也许一个 ID 服务用于应用程序中的身份验证,也许一个管理服务在单独的构建流水线中运行用于管理门户。微服务架构是应用程序的服务级组件化的延续,其中每个微服务在应用程序中有一个清晰而专注的目的。

软件配置管理

配置是任何应用程序的重要部分,应该像您在代码中使用的业务逻辑一样小心对待。因此,配置需要像源代码一样得到适当的管理和测试。

可配置性和灵活性概念

乍一想,似乎将配置尽可能灵活是合适的。为什么不尽可能地使系统灵活,并允许它适应任何类型的环境呢?这通常被称为终极可配置性的反模式,意味着配置可以像编程语言一样行为,并且可以被制作成任何方式。以这种方式进行的配置管理可能会使软件项目陷入困境,因为其用户将期望这种灵活性是必要的。为您的配置管理设置一些约束更有用。约束可以帮助控制配置环境中过度灵活性的影响。

特定类型的配置

以下是应用程序可以利用的可能类型的配置列表:

  • 配置可以在构建时间中被拉取并合并到应用程序二进制文件中:

  • 像 C/C++和 Rust 这样的语言可以进行构建时配置

  • 在创建组件或 gems 时,配置可以在打包时间注入:

  • 像 C#、Java 和 Ruby 这样的语言可以使用这样的配置选项

  • 配置可以在部署时间进行,意味着部署脚本或安装程序可以根据需要获取任何必要的信息,或者部署脚本可以要求用户传递这样的信息:

  • 我们将在本书的后面使用 Jenkins、Travis 和 CircleCI 工具进行讨论

  • 配置可以在启动时间或 运行时进行,意味着应用程序启动时:

  • 像 Node.js 这样的语言在 Node.js 服务器运行时通常会注入环境变量

跨应用程序的配置管理

当您跨不同应用程序进行配置时,配置管理变得更加复杂。有一些工具可以帮助跨应用程序边界进行配置,以下是这样的工具列表:

环境管理

应用程序依赖的硬件、软件、基础设施和任何外部系统可以被视为应用程序的环境。任何环境的创建都应该以完全自动化的方式进行,因为能够复制环境是重要的,我们将会说明。

手动环境设置

手动设置基础设施可能会出现几个问题:

  • 手动设置的服务器实例可能被配置以适应单个运维人员。这个运维人员可能已经离开组织,导致核心基础设施破损。

  • 修复手动设置的环境可能需要很长时间,而在这样的环境中解决问题是不可重现和可重复的。

  • 手动设置的环境可能无法复制以进行测试。

环境的重要配置信息

以下是所有环境都需要的重要配置信息列表:

  • 需要在每个环境上安装的第三方依赖和软件包

  • 网络拓扑信息

  • 应用程序运行所需的外部服务,如数据库服务

  • 应用程序数据或种子数据,以便设置和运行新环境

容器化环境

像 Docker 和 Kubernetes 这样的工具因其隔离环境级别信息和创建可重现/可重复环境的能力而变得越来越受欢迎。使用 Docker,您可以声明所有外部服务,如 Redis 和 MongoDB。

这是 API 研讨会存储库(github.com/jbelmont/api-workshop)的docker-compose YML 脚本示例:

version: '3'
services:
  mongo:
    image: mongo:3.4.5
    command: --smallfiles --quiet --logpath=/dev/null --dbpath=/data/db
    ports:
      - "27017:27017"
    volumes:
      - data:/data/db
  redis:
    image: redis:3.2-alpine
    ports:
      - "6379:6379"
 apid:
    build:
      context: .
      dockerfile: Dockerfile-go
    depends_on:
      - mongo
      - redis
    env_file:
      - ./common.env
    links:
      - mongo
      - redis
    ports:
      - "8080:8080"
volumes:
    data:

我们已经声明了一个数据库,以及一个缓存服务(Redis)和一个 API,它们都作为独立的容器运行,所有这些都可以具有环境级别的信息,比如环境变量,可以单独配置。

部署管道

我们在第二章中谈到了 CI 的重要性,持续集成的基础知识,虽然 CI 是一个重要的生产力增强器,但它主要对开发团队有用。在等待修复或更新文档时,软件生命周期中常见的瓶颈是 QA 和运维团队。QA 可能需要等待开发团队的良好构建。开发团队可能在完成新功能后的几周内收到错误报告。所有这些情况都会导致无法部署的软件,最终导致无法交付给最终用户的软件。创建可以部署到测试、分级和生产环境的一键式部署构建可以帮助缓解这些问题,正如我们之前所指出的。

部署管道是什么?

部署管道可以被认为是构建、部署、测试和发布过程的端到端自动化。部署管道也可以被认为是将开发人员编写的软件交到用户手中的过程。

部署管道实践

在本节中,我们将讨论一些部署管道实践,比如只编译一次二进制文件,以相同的方式处理每个环境中的部署,并在部署管道中设置提交阶段。

只编译一次二进制文件

多次编译的二进制文件可能会出现问题,原因有几个:

  • 二进制文件在每次运行时可能具有不同的上下文,这会给系统引入不可预测性

  • 静态编译语言,如 C/C++,每次运行可能具有不同的编译器版本

  • 第三方软件可能在不同的编译执行上下文中指定不同的版本

  • 多次编译二进制文件也会导致低效的部署管道

  • 重新编译二进制文件也可能很耗时

如果可以的话,最好在编译时只编译一次二进制文件。

在每个环境中应该以相同的方式进行部署

考虑到每次源代码检入时都会运行 CI 构建,开发人员通常会经常部署他们的软件。QA/测试人员不会那么频繁地部署,运维人员更少。与开发环境相比,部署到生产环境的频率要低得多,这是有充分理由的。

应该创建一个部署脚本,可以在开发、分级和生产环境中运行。每个环境中需要的任何更改都可以通过在版本控制中管理的属性文件来管理。例如,您可以在部署脚本中使用环境变量来区分不同的环境。

提交阶段-部署管道的第一步

部署流水线的第一个阶段是提交阶段,或者说是开发人员将代码提交到版本控制时。一旦代码提交到 CI 构建流水线,构建流水线应该在必要时编译任何代码,运行一套单元测试(希望有一些存在)和集成测试,如果需要为后续部署流水线创建任何二进制文件,则创建任何二进制文件,运行静态分析工具来检查代码库的健康状况,并准备后续部署流水线所需的任何构建工件。

提交阶段构建的其他重要指标包括代码覆盖率、代码库中的重复代码、圈复杂度(衡量代码库中的复杂性)、监控大量警告消息以及代码风格(通常由代码检查工具报告)。

如果提交构建阶段通过,那么我们可以将其视为通过的第一个关卡,尽管这是一个重要的关卡。

测试关卡

在极限编程中,开发人员创建验收测试,这些测试作为功能级别的测试,测试软件系统的某个方面。例如,用户登录系统和用户退出系统。另一个例子是用户访问其个人资料并更新信息。这些测试比单元测试和集成测试要广泛得多,因此如果存在系统级问题,它们会揭示出来。

验收测试构建阶段

运行一套验收测试应该是部署流水线的第二个关卡。验收测试还充当回归测试套件,以验证新功能是否已引入系统。在此阶段,验收测试套件中发生的任何测试失败都需要逐案评估。失败可能是由于系统中的有意行为更改,因此需要更新验收测试套件,或者失败可能代表需要解决的回归。无论哪种情况,都必须尽快修复验收测试套件。验收测试充当了部署流水线继续前进的另一个关卡。

手动测试

验收测试确实提供了系统行为应该如何的一定程度的保证,但只有人类才能发现系统中的异常。质量保证/测试人员可以对系统进行用户级测试,以确保系统的正确可用性。测试人员还可以对系统进行探索性测试。自动化验收测试套件有助于释放测试人员的时间,以进行这种更高价值的测试。

非功能测试

非功能测试的命名恰如其分,因为这些类型的测试不是系统的功能要求。相反,非功能测试测试系统中的容量和安全性等方面。在部署流水线的这一步中,失败可能不需要将构建标记为失败,而只是作为构建的决策指标。

发布准备

进行发布时总会伴随一定的风险,因此最好在进行软件发布时建立相应的流程。发布过程中出现的问题可以通过建立流程来预防或减轻。

在发布过程中可能要遵循的一些步骤如下:

  • 创建一个涉及并由所有参与产品交付的人员共同创建的发布计划

  • 尽可能自动化发布流程,以防止错误发生

  • 在生产环境中经常排练发布,以帮助调试可能出现的问题

  • 建立流程,以迁移正在使用的任何生产数据,并在回滚(将发布回滚到上一个版本)或升级系统时迁移配置信息

自动化发布流程

尽量自动化尽可能多的发布流程,因为自动化越多,您对发布流程的控制就越多。手动步骤往往容易出错,并可能导致意外结果。在生产环境中发生的任何更改都需要得到适当的锁定,这意味着更改是通过自动化流程完成的。

进行回滚

发布日往往会很紧张,因为在发布过程中发生的错误可能会导致难以检测的问题,或者正在发布的新系统可能存在缺陷。排练发布可以帮助减轻这些问题,并可以帮助人们快速解决可能遇到的问题。

在发布之前和发布之后,最佳策略是准备好软件系统的先前版本,以防需要将系统回滚到先前版本;这不包括任何必要的数据迁移或配置。作为另一种可行的选择,您可以重新部署已知的良好版本的应用程序。回滚应该能够通过点击按钮完成。

部署脚本

部署脚本是必要的,因为开发团队编写的软件不仅在他们的 IDE 或本地环境中运行,而是需要在部署流水线期间运行。部署脚本是指您用于编写部署流水线脚本的特定构建工具。

构建工具概述

已经有许多构建工具,每个都有其优缺点。以下是一小部分构建工具的列表:

部署脚本概念

无论您使用什么构建工具,进行部署脚本时都需要遵循某些实践。

为部署流水线的每个阶段编写脚本

在部署流水线的提交阶段,您将需要部署脚本执行的操作。例如,您可能需要编译任何源文件,运行一套单元和集成测试,并运行一个检查代码风格和静态分析工具的代码检查工具。所有这些步骤可能需要使用不同的工具,因此编写一个执行所有这些操作的脚本是最好的。根据脚本的特定操作,您可能希望进一步将脚本分解为执行专注操作的子脚本。在验收测试阶段,您的脚本可能会运行整个验收测试套件,并额外生成一些关于测试的报告和指标。

每个环境应该使用相同的脚本

你应该在所有环境中使用完全相同的脚本,这将确保在每个环境中都以相同的方式进行构建和部署过程。如果每个环境都有不同的脚本,那么就无法确保在不同环境中运行的特定脚本的行为是相同的。开发人员在其本地环境中运行的部署脚本应该与在其他环境中运行的脚本相同,否则就会存在环境泄漏的风险。我们在这里的意思是,开发人员的环境可能设置了特定的环境变量,而部署脚本或每个环境,如开发、暂存和生产,可能设置了不同的环境变量,这将使在出现问题时调试变得更加困难。

部署过程不应在每次运行时都发生变化

部署过程应该在每次运行时保持不变。在数学中,有一个术语叫做幂等,它基本上表示某个操作可以多次执行并产生相同的结果。如果你的部署过程在任何给定的运行中发生变化,那么你无法保证每次运行的行为,这反过来将使故障排除变得更加困难。

部署脚本最佳实践

本节讨论了部署脚本的最佳实践,例如确保仅测试已知良好的基础、测试环境配置、使用相对路径和消除手动流程。

仅测试已知良好的基础

你不应该测试甚至无法编译的源代码,当单元测试和集成测试失败时也不应该运行任何验收测试。基本上,任何额外阶段的部署过程都必须存在已知良好的基线才能运行和继续。

测试环境配置

随着部署流水线通过每个阶段并随后通过每个阶段,检查相应阶段是否正常运行是很重要的。你对相关阶段进行的测试可以被视为烟雾测试。例如,通过访问 URL 检查网站是否正常运行,并检查数据库中的记录是否仍然可以获取。

使用相对路径

最好使用相对路径而不是绝对路径。开发人员可能有一定的文件系统或文件夹结构,在部署流水线运行的环境中不存在,因此最好使用相对路径,以免造成意外的破坏。有时可能会很难做到这一点,但尽量遵循这一点是最好的。Docker 容器可以为每个容器映射文件夹结构;例如,如果 Docker 容器在部署流水线的特定部分生成,它也可以映射到某个特定的相对文件夹结构。

消除手动流程

避免制作包含必须完成部署特定部分的步骤列表的构建脚本。

以下是手动流程中可能的步骤列表:

  • 将项目根目录中的所有图像复制到static/build文件夹中

  • 在新的生产发布中运行数据的手动迁移

  • 如果有人必须 SSH 到一个盒子并运行一个脚本,这可能会有问题

任何必须手动完成的步骤在文档中很快就会过时,因此最容易遵循的指令是,如果你必须再次执行某个操作,就要制定自动化流程。

部署生态系统

在本节中,我们将简要介绍一些可以帮助你的部署流水线并提供不同用途的工具。

基础设施工具

我们在本章前面简要提到了 Chef;Chef 是一个很好的工具,可以可靠地自动化基础架构的搭建。如果没有适当的工具,很难确保每个新环境的设置都是以相同的方式进行的。潜在地,您可能会创建具有不同配置的新环境,这在故障排除时可能会带来很大问题。

云服务提供商和工具

三大主要云服务提供商都有各自相关的工具:

您可以使用 Jenkins、Travis 和 CircleCI/CD 工具与所有主要的云服务提供商,尽管 Microsoft Azure 和 AWS 也已经创建了自己的 CI/CD 工具供您使用。

总结

CD,正如我们所见,围绕自动化的概念展开。在本章中,我们学习了软件交付的含义。我们首先研究了在交付软件时出现的常见问题。我们还详细讨论了配置管理以及版本控制和依赖管理在任何配置中所起的作用。我们还研究了部署流水线,并深入了解了不同的构建阶段。在部署脚本部分,我们研究了一些已有的构建工具,并制定了一些最佳实践。最后,我们简要介绍了部署生态系统和一些云服务提供商。在下一章中,我们将讨论不同团队之间沟通的问题,如何向其他团队成员传达痛点,如何在不同团队之间分享责任,向利益相关者展示 CI/CD 的重要性,以及如何获得业务利益相关者对 CI/CD 的批准。

问题

  1. 我们所说的交付软件是什么意思?

  2. 列举一些常见的发布反模式。

  3. 在交付软件时,自动化的一些好处是什么?

  4. 配置管理到底是什么意思?

  5. 为什么应该编写描述性和有意义的提交消息?

  6. 什么是部署流水线?

  7. 为什么在每个环境中部署应该以相同的方式进行?

进一步阅读

考虑阅读 Packt Publishing 出版的《DevOps: Continuous Delivery, Integration, and Deployment with DevOps》一书,以更深入地了解 CD。

第四章:CI/CD 的商业价值

现在我们清楚了自动化、持续集成(CI)和持续交付(CD)的概念,我们需要向业务利益相关者传达这些实践的商业价值,否则我们就会在不考虑这些实践的情况下构建功能。本章将讨论如何说服利益相关者这些价值,以及我们将讨论沟通问题、如何向团队成员传达痛点、在不同团队之间分享责任、了解你的关键利益相关者、演示 CI/CD 的重要性以及从利益相关者那里获得 CI/CD 的批准。

本章将涵盖以下主题:

  • 沟通问题

  • 向团队成员传达痛点

  • 在不同团队之间分享责任

  • 了解你的利益相关者

  • CI/CD 的重要性演示

  • 从利益相关者那里获得 CI/CD 的批准

技术需求

本章假设你已经熟悉自动化和 CI/CD 的概念;如果你对这些主题感到不确定,请在阅读本章之前阅读第一章《自动化测试的 CI/CD》和第二章《持续集成的基础知识》。本章主要讨论如何向利益相关者传达这些实践的价值,因此不会有任何代码示例或需要安装的内容。

沟通问题

在任何工作环境中,沟通问题都是不可避免的,但特别是在敏捷工作环境中存在问题。一些沟通问题包括需求的误解、缺乏适当的文档、时区差异、缺乏信任和相互尊重、文化差异、语言障碍和长时间的反馈循环。

需求的误解

这是一个需求清单的示例。需求清单的目的是为特定功能列出所有必要的事实。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在敏捷工作环境中,需求的误解是一个常见问题。虽然不可能完全消除需求的误解,但通过确保在功能请求的最初阶段与最终用户或客户进行沟通,可以最大程度地减少这种风险。

重要的是,你正在实施的功能请求必须清晰陈述,并且每个功能都必须有明确的商业意图。这很重要,因为它有助于开发人员、DevOps 人员和 QA/测试人员在实施阶段做好充分准备。

在前期了解关键业务需求将有助于减少团队之间的需求误解,因为缺少需求可能会在开发过程中轻易造成瓶颈。任何关键需求信息都需要得到适当的文档记录。

缺乏适当的文档

文档需要在定义任何需求的同时编写,并在功能开发过程中持续更新。只有在一切都尽可能清晰地定义和陈述之后,你才能开始编写实施特定功能的计划。如果开发人员遇到问题并需要向客户澄清,那么答案需要直接放在需求中以供将来参考。

避免有多个包含需求信息的文档,而是使用一个文档包含所有需求信息,否则你就有可能出现信息过时的情况,甚至更糟糕的是,不同的需求分散在不同的地方并相互矛盾。

业务需求应该有一个统一的真相来源,并且所有相关方都应该理解这些需求。

时区差异

随着越来越多的团队变得分布式和全球化,时区差异可能会导致沟通瓶颈。在时区差异较大的开发人员需要确保良好的 CI/CD 实践。CI 构建失败和配置管理问题可能会因时区差异而迅速恶化,因为一个时区的团队可能会无法有效地进行工作。在分布式团队中,沟通尤为重要,因为缺乏面对面的互动可能导致沟通失败,最坏的情况下甚至会导致团队之间的敌意。

我曾在一家初创公司工作,那里有 3 小时的时区差异,这本身并不是问题,但每天的站立会议是在我们的工作日结束时进行的,而另一个团队在我们的中午开始工作。自然而然地,这导致了其他团队的更改会阻塞我们,直到我们的中午才能解决。

缺乏信任和相互尊重

以下是一张图,说明了信任和相互尊重是相辅相成的,团队需要这一点才能高效运作:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

团队之间的信任至关重要,很容易失去,但很难获得。最好有一个优秀的项目经理,可以促进团队之间的沟通,并帮助澄清必然会发生的问题。健康的团队会在功能工作中出现问题时进行开放的沟通,定期进行回顾也有助于排解团队成员之间的挫折感,并建立信任。

如果可能的话,最好能组织团队外出活动,让多个团队之间可以互动并建立合作关系。一些公司会定期举行会议,让团队一起参加有趣的活动,比如体育运动或游戏。定期进行团队建设活动也可以保持团队成员的参与度,并建立合作精神。

文化差异和语言障碍

随着敏捷工作环境的全球化,全球团队变得更加普遍。团队之间的文化差异使得沟通成为项目成功的更加重要的因素。幽默可能是一把双刃剑,因为如果幽默被误解,很容易导致分裂和敌意,所以最好能教导团队有关文化规范和习俗,以避免沟通问题。

语言障碍也可能会导致问题,因为对功能请求的需求可能会被误解。最好是由项目经理作为团队之间的联络人,确保所有需求在团队之间清晰地理解,并帮助澄清任何沟通瓶颈。

长反馈循环周期

以下是一个反馈循环周期的图示。反馈循环越长,做出改变所需的时间就越长。在部署流水线上,有一个短的反馈循环是非常重要的,这样在必要时可以及时做出改变。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们在第一章中谈到了长反馈循环,特别是长反馈循环的危险以及缩短反馈循环周期的重要性,以便在正确的时间将正确的信息传递给正确的人。同样,团队之间长时间的反馈循环周期可能会产生问题和自然瓶颈。

理想情况下,团队应尽快获得他们所需的信息,但这并不总是现实。适当的联络人或项目经理可以帮助缩短团队之间的反馈循环,团队需要适当记录任何流程,并确保这些文档对其他团队可见和已知,否则团队之间的流程可能会有所不同。

记住,短的反馈循环会导致更快的响应时间。

向团队成员传达痛点

团队成员能够有效地传达阻碍进展的特定痛点或障碍是很重要的。在本节中,我们将讨论几个痛点,包括等待需求信息、部署流水线中未记录的步骤、王国的密钥持有者过多以及沟通渠道过多。

等待需求信息

开发人员通常会开始处理特定的故事/功能,但并没有所有必要的需求来完成他们分配的工作。这对开发人员来说尤为棘手,因为他们所处理的任何代码可能需要根据需求与正确完成的距离有多远而被废弃并重新完成。开发人员需要在开始故事之前就提前获得所有的需求;每个功能必须存在抓取所有需求的流程,并且每个故事理想情况下都将有验收测试作为特性工作的行动项来考虑完成。在理想的世界中,开发人员将在开始特定的功能工作之前准备好所有必要的信息,并且在需求文档中指定的特性完成时,故事的验收测试将通过。

在第一章中,自动化测试的 CI/CD,我们讨论了比利·鲍勃机械零件公司的例子。现在,想象一下,开发团队的汤姆已经开始了显示供应商名称的工作,并且汤姆发现这个工作的范围似乎很大,他可能无法及时完成。这种情况也因需求文档严重缺乏和开发过程中缺少关键细节而变得复杂。汤姆询问产品负责人是否可以就某些项目提供反馈,但必须等待数天才能获得这些必要信息。

部署流水线中未记录的步骤

部署流水线过程中的每个步骤都应适当记录和自动化。我们在第五章中谈到了在 Jenkins 的安装和基础知识中尽可能自动化部署流水线的重要性。重申一下,手动流程是有问题的,因为它们是可重复和可靠的。自动化很重要,因为它为部署流水线带来了可重复性和可靠性。每当有人必须执行手动步骤时,就无法保证流程是否会正确执行,并且在每次运行中都以相同的方式执行;只有通过自动化,您才能保证部署流水线阶段的可重复性。

作为 DevOps 团队的一部分,阿尔文正在为软件产品的最新版本进行发布,并在部署流水线中运行一个复杂的手动流程。阿尔文输入了错误的命令,结果清除了生产数据库。幸运的是,阿尔文有一个一天前的备份,他可以将生产数据库恢复到这个备份。如果当时有自动化流程,这种情况就不会发生。

王国的密钥只交给少数人

以下图表代表一个关键点,关于王国的关键点要记住的主要事情是只有少数人可以访问/拥有生产环境的密钥:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

重要的是要控制谁可以在生产环境中进行更改,许多软件公司通常会选出少数几个甚至一个人可以在生产中进行更改。如果这个特定的个人不可用或离开公司,这可能会成为问题,但一些公司已经实行了开发团队全权拥有特定功能的做法,负责修复部署管道中遇到的问题的是同一个开发人员。在我工作过的一家公司,我们亲切地说,“只有少数人拥有王国的钥匙”。

阿尔文是为数不多的 DevOps 人员之一,拥有王国的钥匙。客户支持代表向开发团队发出关于生产中断的通知,开发团队正在努力恢复生产环境以满足客户需求。阿尔文和另一名 DevOps 成员是唯一可以触及生产环境的人。这个问题变得更加严重,因为阿尔文或其他指定的 DevOps 人员都不可用。

太多的沟通渠道

在沟通方面应该有低信号噪音比。如果开发人员通过电子邮件、短信、语音邮件和 Slack 消息收到关于问题的警报,他们可能很快就会忽略并不关注这些问题。重要的是要引起开发人员的注意,以便在遇到问题时及时解决,但你可能不应该像军事指挥中心那样被来自许多不同来源的通知轰炸。

想象一下,布鲁斯是团队中的一名新开发人员,他收到了一个关于他处理的低优先级工单的警报。布鲁斯收到了有关这个工单的电子邮件、短信警报、Slack 消息和电话。布鲁斯经常收到这样的消息,很快就决定忽略它们。在一个下午,布鲁斯因为觉得这是一个毫无意义的警报而忽略了一个高优先级的工单。布鲁斯已经对这些警报麻木不仁了。

在所有这些警报中,噪音太大,真正的信号几乎没有。

痛苦驱动开发(PDD)

如果 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 团队成员和一个产品负责人,他们都可以一起工作,并且能够开发出效率,否则如果他们独立工作则不会发生。

回到我们的示例公司——想象一下以下跨职能团队的阵容。汤姆、史蒂文和鲍勃都是开发人员,瑞奇是安全团队成员,苏珊是 DevOps 团队成员,尼基是产品负责人。他们都在同一个空间里共同工作,并每天早上进行晨会。现在,团队成员能够全权拥有部署流程的各个阶段,因为他们可以共同合作,互相帮助自动化流程。汤姆和史蒂文使用新库编写了自动化测试套件,瑞奇能够添加第三个构建阶段,对对主分支进行的更改进行安全检查。苏珊在每个项目通过部署流程时添加了监控和报告指标。尼基迅速更新了鲍勃的需求文档,因为他注意到了新功能工作中的边缘情况。团队成员在他们的流程中每一步都进行了公开交流,并且能够优化流程,因为他们之间进行了公开的合作。

了解你的利益相关者

对于开发团队来说,了解所有利益相关者是很重要的,因为利益相关者将持有关键信息,这些信息可以帮助团队成功或失败。开发团队应该能够在必要时与项目经理进行沟通,向高管团队成员公开沟通,并能够与最终用户交流。

项目经理

尽管产品负责人可能承担项目经理的角色,并且可以帮助促进 Scrum Master 的职责,但最好由不同的人来担任这些角色。项目经理可以被视为适应动态工作环境的变革者。在一天结束时,项目经理希望能够将交付内容交付给最终用户,并且可以帮助打开不同团队之间的沟通渠道。开发人员能够公开沟通,并通知项目经理在他们的功能工作中遇到的任何问题是很重要的。

一些公司还雇佣了负责敏捷工作环境中工作流程和方法的敏捷项目经理。敏捷项目经理将为冲刺计划制定路线图,并确保开发团队中的每个开发人员都得到了按计划分配的工作。这种类型的经理通常会更加了解团队的所有工作,并确保所有利益相关方都拥有完成其交付内容所需的工具和信息。

高管团队

公司文化很大程度上会受到高管团队的影响,比如首席执行官(CEO)、首席信息官(CIO)、首席技术官(CTO)和首席运营官(COO)。除非在这些高管层面上运作,否则很难对公司产生广泛的影响。如果开发团队觉得决策是一种命令,并且他们对所做的决定没有发言权,他们可能无法防止本来可以避免的问题。许多公司声称他们有开放式政策,并欢迎建设性的反馈,但通常开发团队在与破碎的流程作斗争时无法发声。

假设汤姆在周末读了一篇博客文章,发现了一种减少自动接受测试套件中反馈循环的方法。汤姆想引入这种变化需要大量工作。汤姆试图在周一早上的站立会议上提到这一点,但被团队拒绝了,因为有更有价值的工作要做。汤姆决定这对上层管理意识到是很重要的。汤姆继续使用开放式政策与首席技术官谈论这个问题,但第二天因未经过适当的领导渠道而受到口头斥责。这样,汤姆无法做出最有利于团队的决定,因为团队成员都没有被授权对工作流程进行更改。

最终用户

这是最终用户的描述;最终用户是最重要的利益相关者。您的最终用户的反馈意见最为重要:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

最终,最终用户将使用您为产品添加的新功能。在这方面,他们可以帮助澄清开发人员的必要需求。通常情况下,最终用户在看到产品之前并不清楚他们在寻找什么。如果需要,产品所有者必须从客户那里提前获取所有必要的需求,有些软件组织甚至要求产品所有者/客户编写测试,以代码指定必须实现的需求。无论如何,在开发人员开始工作之前,产品所有者和最终用户必须就所请求的功能达成一致。

开发团队基本上与最终用户隔离,不会与任何最终用户接触。然而,对于开发团队来说,了解最终用户在使用软件系统时遇到的具体痛点是很重要的。在这个意义上,开发人员是最有能力对系统进行改变以使最终用户受益的人,但如果开发人员不了解这些痛点,他们将无法进行必要的改变以使最终用户受益。在适当的时候,让开发人员与客户成功团队合作可能会有所帮助,以了解最终用户如何使用软件系统。

展示 CI/CD 的重要性

CI/CD 流水线的重要性不容小觑,开发人员需要通过提供指标、报告和一般教育领导层自动化的重要性来证明其重要性。

指标和报告

以下图表是图表和图形的描述,您可以用它向利益相关者展示 CI/CD 的重要性。开发图表和图表是一个很好的主意,因为视觉很有说服力。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

通常在公司的高管层,数字和 PowerPoint 幻灯片必须证明某事的重要性。开发人员应该能够用指标(图表、图表和任何其他可视形式)说明 CI/CD 如何改进现有流程。已经存在企业解决方案可以帮助生成这些信息,但开发团队可以将这些信息汇总到 Excel 电子表格中。

让我们假设开发团队的鲍勃已经决定,手动流程在发布日期间已经够了,迫切需要自动化。鲍勃汇总了过去 6 个月在紧急修复上花费的时间,以及在发布日出现问题时每个开发人员浪费的工时。鲍勃创建了一个漂亮的可视化图表,帮助说服管理层创建一个处理自动化部署流程的敏捷史诗。

教育领导层关于自动化

开发团队不能假设领导了解自动化的含义以及哪些领域适合自动化。最好是由技术代表,如首席技术官,作为自动化的倡导者,并帮助向高管团队解释。像首席技术官这样的人可以成为变革的代理人,代表开发人员发言,但无论是谁传达这些信息,高管团队必须了解自动化的含义以及哪些事情可以自动化。

领导团队往往与开发人员日常工作相距甚远。领导团队对公司有更全局性的关注,并且往往会与其他成员合作,如销售、营销、运营和项目经理。对于高管领导团队来说,了解自动化仍然很重要,以便开发人员有足够的时间来开发自动化部署流水线,并且他们在每个迭代期间有时间进行测试,并不断将自动化流程添加到 CI/CD 构建流水线和部署流水线中。组织的最高层需要对自动化有清晰的理解,以便开发人员、系统管理员和 DevOps 人员可以将自动化实践纳入公司路线图的关键交付成果中。

获得利益相关者对 CI/CD 的批准

即使强调自动化的重要性并向利益相关者解释其重要性,您可能需要在没有官方批准的情况下采取行动。许多软件项目都是作为一项未经官方批准的秘密项目开始的。开发人员也可以在本地机器或未使用的机器上工作部署流水线自动化任务。

启动秘密项目

术语“秘密项目”的起源有待讨论,但一般的想法是,这是一个由个别人或一组人秘密进行的项目,旨在为组织带来创新和变革。开发人员不一定能够获得对于某项任务的批准,他们可能需要采取替代策略来表达自己的观点。

想象一下,开发团队的鲍勃有一个想法,要编写一个 CLI 应用程序,帮助第三方开发人员利用公司的仪表板。鲍勃试图向高层管理层传达这个想法,但没有成功。鲍勃决定在接下来的几周内编写一个 CLI 应用程序,并决定使用一种名为Rust的新编程语言来编写 CLI 项目。鲍勃创建了一个直观的 CLI 应用程序,易于使用和可插拔。鲍勃能够向团队展示这个新应用程序,并说服高层管理层投入资源来开展 CLI 项目。

在本地机器上启动 CI/CD

开发团队可能无法获得启动 CI/CD 流水线的财务批准。为了发现并说服他人自动化 CI/CD 流水线的重要性,开发人员可以在自己的机器上复制部署流水线,并向团队和高层管理层展示构建自动化流水线阶段的好处。

如今,像 Azure、AWS 和 Google App Engine 这样的大型云服务提供商可以提供免费的账户计划来提供云服务。通过这种方式,开发人员可以轻松地设置更真实的部署流水线,通过展示一个小项目,并展示 CI/CD 流水线中的所有阶段,如提交阶段、自动化验收测试阶段和可选的安全和容量构建阶段。

公司演示

在您的组织中,公司范围的演示可能是获得 CI/CD 批准的最有效方式。一些公司赞助黑客马拉松,您可以在赞助的黑客马拉松上为公司创建一个新的自动化流程。这样做的优势是您可以在公司演示期间将自动化信息传达到组织的最高层。

假设开发团队的汤米正在尝试 Docker,并且有创建每个部署流水线的 Docker 镜像的想法。汤米表明 Docker 容器可以用作 QA 测试软件产品版本的隔离版本控制系统,这也具有环境隔离的优势。汤米构建了这个自动化流程,并在公司演示中展示了这可以节省 QA 部门 25 小时的回归测试工时。CEO 并不知道 QA 在部署过程中花费了很多时间来设置环境进行回归测试。汤米通过令人信服的演示向领导层展示了自动化的重要性。

午餐和学习

以下图表只是叉子和刀子的描绘,但主要观点是与他人共进晚餐是打开沟通渠道和团结人们的好方法。您可以在午餐时将自动化演示纳入公司会议中:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

您可以邀请高层管理人员,并使用图表和带有指标的幻灯片来解释自动化是什么,并展示手动流程上的花费。通常,高层管理人员更关心活动的货币影响,如果您能向他们展示手动流程的成本,他们会更愿意倾听。

总结

传达 CI/CD 的业务价值非常重要,正如本章所阐述的那样。我们从讨论传达问题开始,讨论了一些传达痛点给团队成员的策略。我们讨论了在不同团队成员之间分享责任,了解您的利益相关者,向利益相关者展示为什么 CI/CD 对他们重要,最终从您的利益相关者那里获得 CI/CD 的批准。

下一章将介绍如何在本地环境中设置 Jenkins CI。这一章将介绍本书的第一个 CI/CD 工具。

问题

  1. 为什么您应该在开始时拥有所有的需求信息?

  2. 什么是痛苦驱动开发?

  3. 为什么拥有许多沟通渠道是有问题的?

  4. 轮换团队成员的一些好处是什么?

  5. 要求对现有开发实践进行反馈的好处是什么?

  6. 使用指标和报告如何有助于向利益相关者展示 CI/CD 的价值?

  7. 为什么需要教育领导层关于自动化?

进一步阅读

考虑阅读Packt PublishingContinuous Integration, Delivery, and Deployment,因为这本书讨论了软件组织的 CI/CD 的价值:

第五章:Jenkins 的安装和基础知识

本章将帮助您在 Windows、Linux 和 macOS 中安装 Jenkins。我们还将了解 Jenkins UI 的基础知识。

本章将涵盖以下主题:

  • Windows 安装有我们的第一个构建

  • Linux 安装

  • macOS 安装

  • 在本地运行 Jenkins

  • 管理 Jenkins

技术要求

本章是关于使用 Jenkins 进行 CI/CD 流程。在本章中,我们不会讨论 CI/CD 概念,因为我们正在设置环境以使用 Jenkins。

Windows 安装

有一些初步步骤可以安装 Jenkins。

安装 Jenkins 的先决条件

您需要确保已安装 Java,并且从 Jenkins 2.54 开始。Jenkins 现在需要 Java 8。

查找您的 Windows 版本

单击开始 Windows 图标,键入system在搜索框中,然后单击程序列表中的系统。

现在打开了系统小程序,标题为查看有关计算机的基本信息,在大的 Windows 徽标下找到位于系统区域下的系统区域。

系统类型将显示 64 位操作系统或 32 位操作系统。

安装 Java

要安装 Java,请转到 Java 下载页面(www.oracle.com/technetwork/java/javase/downloads/index.html):

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

确保点击接受许可协议单选按钮,然后点击 Windows 下载,并确保选择正确的架构;即 32 位或 64 位操作系统。

然后使用安装程序在 Windows 中安装 Java。

Windows 安装程序

在 Windows 操作系统中安装 Jenkins 相对容易;只需转到 Jenkins 下载页面(jenkins.io/download/):

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

如果您滚动到页面底部,您将看到根据当前版本可以在其上安装 Jenkins 的操作系统列表:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在 Windows 中安装 Jenkins

我已从 Jenkins 下载页面下载并解压了 Jenkins 文件,如下图所示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在 Windows 中运行 Jenkins 安装程序

以下屏幕截图显示了 Windows 中的 Jenkins 安装程序:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

一旦您完成安装程序中的所有步骤,您将看到以下屏幕:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

单击完成后,您可以在 Web 浏览器中转到http://localhost:8080,您将看到以下屏幕截图:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

使用 Chocolatey 软件包管理器安装 Jenkins

Chocolatey 安装说明可以在chocolatey.org/install找到。

您可以使用以下命令在cmd.exe中安装 Chocolatey:

@"%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe" -NoProfile -InputFormat None -ExecutionPolicy Bypass -Command "iex ((New-Object
System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))" && SET "PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin"

安装了 Chocolatey 后,您只需运行choco install jenkins来通过 Chocolatey 安装 Jenkins。

在 Windows 中使用命令提示符启动和停止 Jenkins

单击开始按钮,键入cmd并按Enter。这将打开一个命令提示符会话。

接下来,您可以在命令提示符中输入以下命令:

cd 'C:\Program Files (x86)\Jenkins'

然后您可以使用以下命令:

$ C:\Program Files (x86)\Jenkins>jenkins.exe start
$ C:\Program Files (x86)\Jenkins>jenkins.exe stop
$ C:\Program Files (x86)\Jenkins>jenkins.exe restart

您还可以使用curl并使用以下命令:

$ curl -X POST -u <user>:<password> http://<jenkins.server>/restart
$ curl -X POST -u <user>:<password> http://<jenkins.server>/safeRestart
$ curl -X POST -u <user>:<password> http://<jenkins.server>/exit
$ curl -X POST -u <user>:<password> http://<jenkins.server>/safeExit
$ curl -X POST -u <user>:<password> http://<jenkins.server>/quietDown
$ curl -X POST -u <user>:<password> http://<jenkins.server>/cancelQuietDown

Linux 安装

我们将在 Ubuntu 16.04 Digital Ocean Droplet 上安装 Jenkins;请按照 Jenkins 下载页面上的按钮链接上的说明在您特定的 Linux 发行版上安装 Jenkins(jenkins.io/download/)。您可以单击 Jenkins 官方支持的 Linux 发行版之一安装 Jenkins,但是,出于本节的目的,我们将看看如何在 Digital Ocean Droplet 上的 Ubuntu 操作系统上安装 Jenkins。

在 Ubuntu 上安装 Jenkins

运行以下命令将存储库密钥添加到您的系统中:

wget -q -O - https://pkg.jenkins.io/debian/jenkins-ci.org.key | sudo apt-key add -

添加密钥后,系统将返回OK

接下来,我们将通过运行此命令将 Debian 软件包存储库地址追加到服务器的sources.list中:

echo deb https://pkg.jenkins.io/debian-stable binary/ | sudo tee /etc/apt/sources.list.d/jenkins.list

接下来,我们需要通过运行以下命令更新系统中的存储库:

sudo apt-get update

确保安装 Java,因为它是 Jenkins 运行的依赖项,因此运行以下命令:

sudo apt install openjdk-9-jre

接下来,我们在 Ubuntu 上安装 Jenkins:

sudo apt-get install jenkins

在 Ubuntu 中启动 Jenkins 服务

最后,我们需要通过以下命令启动 Jenkins 服务:

sudo systemctl start jenkins

现在我们需要确认 Jenkins 已经启动并且没有问题:

sudo systemctl status jenkins

您应该会得到以下输出:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

打开网络流量防火墙

默认情况下,Jenkins 在 HTTP 端口8080上运行,因此我们需要确保该端口允许流量:

sudo ufw allow 8080

您将得到以下输出:

Rules updated
 Rules updated (v6)

接下来,我们需要查看规则的状态:

sudo ufw status

您将看到以下输出:

Status: inactive

解锁 Jenkins 进行首次登录

第一次在 Digital Ocean Droplet 上运行 Jenkins 时,您将看到以下屏幕:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在 Ubuntu 终端会话中运行以下命令:

cat /var/lib/jenkins/secrets/initialAdminPassword

将打印到标准输出的密码复制到系统剪贴板中,然后将此密码粘贴到初始登录屏幕中,然后单击“继续”按钮。

接下来,您将看到一个屏幕,您可以在其中安装建议的插件或选择要安装的插件:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这个屏幕在一开始并不是 100%必要运行,所以您可以单击屏幕右上角的 X:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

单击 X 并决定启动 Jenkins 后,您将看到此屏幕:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

macOS 安装

在 macOS 上安装 Jenkins 相对容易,您可以通过几种方式完成。

Jenkins 下载软件包

在本节中,我们将介绍如何使用 Mac 软件包安装程序(.pkg)文件安装 Jenkins:

  1. 转到 Jenkins 下载 URL(jenkins.io/download/)。

  2. 滚动到页面底部,您应该会看到可以在其上安装 Jenkins 的操作系统列表。

  3. 单击 Mac OS X 按钮链接,您应该会看到类似这样的页面:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 单击浏览器窗口底部可以看到的.pkg文件,或者在下载文件夹中双击 Jenkins 的.pkg文件:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 请注意,右下角有两个按钮,分别命名为“返回”和“继续”。只需单击“继续”,您将进入下一个窗口,即许可协议。

  2. 单击“继续”,并确保单击“同意”按钮:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 通常,您只需单击“安装”按钮,但如果您想自定义,您可以选择不安装文档等:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 除非您担心磁盘空间,通常最容易的方法是只需单击“标准安装”,然后单击“安装”:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 安装脚本运行完成后,您将看到以下屏幕:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 单击“关闭”,Jenkins 应该在您的本地机器上运行。

解锁 Jenkins 进行首次登录

第一次在主机上本地运行 Jenkins 时,您将看到以下屏幕:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

如果 Jenkins 在主用户帐户中运行,请在 Mac 终端中运行以下命令:

pbcopy < /Users/jean-marcelbelmont/.jenkins/secrets/initialAdminPassword

这将把初始管理员密码复制到系统剪贴板上。如果您的初始密码运行在Users/Shared/Jenkins中,也可能会出现这种情况,请尝试以下命令:

pbcopy < /Users/Shared/Jenkins/Home/secrets/initialAdminPassword

然后,将此密码粘贴到初始登录屏幕,然后单击“继续”:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这个屏幕一开始并不是 100%必要运行,所以您可以单击屏幕右上角的 X。单击 X 并决定启动 Jenkins 后,您将看到这个屏幕:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

通过 Homebrew 安装 Jenkins

您还可以通过 macOS 中的 Homebrew 软件包管理器安装 Jenkins。

如果您尚未安装 Homebrew,请首先转到 Homebrew 页面(brew.sh/)。

安装 Homebrew 相对容易。单击 Mac Finder 按钮打开终端应用程序,按Ctrl + Shift + G,然后输入/applications,并单击“前往”按钮。确保双击Utilities文件夹,然后双击终端应用程序图标。

只需将 Homebrew 安装脚本粘贴到终端应用程序提示中:

/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

一旦 Homebrew 安装成功,只需在终端应用程序中运行以下命令:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

安装 Jenkins 后,您可以通过在终端应用程序中输入以下命令来启动 Jenkins 服务:

brew services start jenkins

运行此命令后,只需简单地访问localhost:8080,然后您可以按照我们在首次登录解锁 Jenkins部分中运行的相同步骤。

在本地运行 Jenkins

以下是 Jenkins 主仪表板页面的屏幕截图。我们将详细介绍每个项目:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

创建新项目

在接下来的步骤中,我们将创建一个自由风格项目作为新项目,但根据安装的插件,可能还可以添加更多项目:

  1. 如果单击“新建项目”链接,您将进入以下页面:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 我们还没有安装任何插件,因此我们可以使用的唯一类型的项目是自由风格项目。

  2. 让我们为自由风格项目输入一个名称,然后单击“确定”:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 您将看到以下屏幕以配置您的自由风格项目:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 让我们为 Jenkins 创建一个简单的构建,打印出Hello World

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 确保单击“添加构建步骤”按钮,然后选择“执行 shell”。

  2. 最后,单击“保存”,您将返回到项目仪表板屏幕。

  3. 接下来,确保单击“立即构建”按钮以触发构建,您将看到一个文本弹出窗口,上面写着“构建已计划”:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 请注意,在以下屏幕截图中,我们的第一个构建标记为#1,在“构建历史”部分:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 请注意,我们现在有一个“构建历史”部分,通常您会想要查看控制台输出,以查看构建的日志信息。

控制台输出

以下是 Jenkins 中典型的控制台输出屏幕:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这是一个非常简单的屏幕,我们只是向屏幕打印了Hello World

管理 Jenkins

登录到 Jenkins 后,只需单击“管理 Jenkins”链接:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

然后确保单击“管理插件”链接:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

然后您将进入插件页面,看起来像这样:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

确保单击“可用”选项卡,您将看到可以安装的可用插件列表。

我们将安装 Go 插件(您可以通过使用“筛选”输入框快速找到插件):

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

请注意,我们在过滤输入框中输入了golang。然后,您可以单击“无需重启安装”按钮或“立即下载并在重启后安装”按钮。我们将使用“无需重启安装”按钮。

单击按钮后,您将看到以下屏幕:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们将点击“返回到顶部页面”按钮。

让我们回到 Jenkins 仪表板,点击“管理 Jenkins”,然后点击“管理插件”。

确保在过滤输入框中输入git

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

现在我们将点击“立即下载并在重启后安装”按钮。

现在,如果您单击“重启 Jenkins”标志,Jenkins 将重新启动,然后您将被提示登录。

接下来,确保点击“返回仪表板”链接:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

配置环境变量和工具

现在我们将看看如何在 Jenkins 仪表板中添加环境变量。确保点击“管理 Jenkins”,然后点击“配置系统”:

然后您需要向全局属性中滚动:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

然后确保配置所有工具,比如添加 GitHub 和 golang 的路径。

配置作业以轮询 GitHub 版本控制存储库

确保单击“新建项”按钮,现在请注意我们已添加了一个附加项。

现在我们将创建另一个名为“Golang 项目”的 Jenkins 构建作业:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

您可以继续向下滚动或单击“源代码管理”选项卡:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

现在,如果您向下滚动,您将进入“构建触发器”部分:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在这里,我们配置了 Jenkins 的轮询并指定了一个 cron 计划。cron 作业显示如下:分钟,小时,日,月和工作日。

您可以在 Linux 手册页下阅读有关 Crontab 的更多信息(man7.org/linux/man-pages/man5/crontab.5.html)。

然后我们添加以下配置:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们将创建另一个 shell 脚本,其中我们执行测试。

确保点击“立即构建”按钮:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

然后确保点击构建编号,然后点击控制台输出链接:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

请注意,控制台输出打印出 Jenkins 正在执行的每个步骤。

总结

本章介绍了 Jenkins 的安装以及导航 Jenkins UI 的基础知识。下一章将更多地介绍 Jenkins 仪表板和 UI。

问题

  1. 在 Windows 中,我们用什么软件包管理器来安装 Jenkins?

  2. Jenkins 需要安装哪些先决条件?

  3. 在 Windows 操作系统中,重新启动 Jenkins 的一种方法是什么?

  4. 我们用什么命令来打开 Linux 中的网络流量防火墙?

  5. 我们在 macOS 中用来安装 Jenkins 的软件包管理器叫什么?

  6. 您在哪里安装 Jenkins 的插件?

  7. 您在哪里配置 Jenkins 的环境变量?

进一步阅读

请查看《使用 Jenkins 进行持续集成学习-第二版》(www.amazon.com/dp/1788479351),由 Packt Publishing 出版。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值