本文来自Rancher Labs
持续集成和持续交付(CI/CD)是DevOps背后的助推力之一。如果你的企业正在考虑使用DevOps,那么CI/CD绝对是需要考虑的其中一部分。但是CI/CD到底意味着什么?为什么它如此重要呢?为了对你的DevOps工具包和IT部署进行战略规划,深入理解CI/CD至关重要。本文中,我们将探讨CI/CD所需解决的难点、需要的工具以及预期的收益。
首先,我们从大局着手。DevOps旨在创建一个流畅的工作流程,并尽可能减少越区切换和建立快速反馈回路。这意味着什么呢?工作会从第一步开始一直向前推进,并且在理想状态中,无需倒退再进行修复,因为它们应该能够验证和修复问题。为此,开发人员需要快速的反馈回路。该反馈通过快速自动化测试提供,并且该测试将验证代码在进入下一阶段之前能否按照预期工作。
为了减少越区切换,成员较少的小组将使用较小的功能并且掌控整个流程:创建请求、提交、QA以及部署。其重点是快速推出小段代码,因为变更越小,诊断、修复和补救就越容易。
持续集成(CI)实现了从第一步到最后一步的快速流程,并通过持续交付(CD)将其扩展到实际生产部署。我们将其称为CI/CD。现在,我们开始深入了解它们。
深入探索持续集成
首先,我们关注CI/CD的CI(持续集成)部分。实际上,大部分公司仅执行了CI。而完成整个CI/CD,需要该企业已经是一个成熟的DevOps企业。
说到集成,我们指的是程序员在其本地计算机上开发的代码(包括更新或添加新特性)集成到代码库中。这一过程会面临以下3个挑战:
跟踪所有变更,以便在发生错误时仍然可以恢复到之前的状态,以最大程度地避免服务中断。
当多个开发人员同时在同一个项目中工作时需要管理冲突
将新代码添加到代码库之前需要捕捉到错误
接下来,我们将讨论可以解决以上几个痛点的3个工具。
1、 版本控制
随着代码从开发人员转移到运维人员,它会根据测试结果不断进行调整。所有的更改都会被版本控制系统捕捉。版本控制是一个软件工具可以帮助开发者管理源代码更改。在特殊类型的数据库中它会一直跟踪所有更改。
理想状态下,软件系统的所有部分都会被捕获到,包括:
源代码
资产
环境
软件开发文档
对系统中存储的文件的任何更改
2、master和开发分支
通常情况下,在同一个项目中会有多个开发人员一起工作,可能是几个人也可能是上百个程序员,因此这可能会导致混乱。为了不让稳定性遭受破坏或减轻在版本控制主分支中引入错误的风险,每个开发人员应该并行处理系统的不同部分。他们通过本地计算机上的“分支机构(branch)”执行这一操作。
但是在分支机构上工作本身并不是解决方案,每个开发人员正在处理的代码必须集成到不断扩充的代码库中。
开发人员在分支机构中工作而无需提交主分支的时间越长,与每个人在master中所做的更改进行集成和合并的难度就越大。所以,由于开发人员在不提交代码的情况下处理代码的时间越长,获得代码的难度就越大,因此从逻辑上来说就应该增加提交代码的频率。但是更好的方法是,使其持续集成。
下图描绘了如何可视化不同分支。蓝色是master分支,其他颜色都是在自己的分支上工作的单个开发人员,这些分支最终合并到master分支中。
不过,就算有分支机制也并非一帆风顺。即使开发人员每天提交代码,冲突仍然会发生。因为其他团队成员会继续做出更改,而没有考虑各方的诉求。实际上,集成问题经常需要返工,包括手动合并冲突的更改。但是比起开发团队整周或一个月都在埋头工作而不处理冲突,找出并解决一天工作中的冲突要简单很多。因此,尽管无法避免集成问题,但CI可以大大减少集成问题。
3、部署流水线和自动化测试
QA的部分工作是找出错误并确保代码是可部署的。传统流程中,在部署完成后会由一个单独的团队来负责QA。因为开发人员通常每年仅执行几次测试,因此在引入更改几个月后他们才了解到错误。到那时,因果之间的联系可能已经很难查证,导致诊断越来越困难。但是自动化测试解决了这个问题。
使用部署流水线之后,每次将代码添加到版本控制中都会触发一系列测试。流水线会自动构建和测试代码以确保它可以按预期工作,并且一旦集成到代码库中就可以继续工作。虽然在测试环境中代码可以完美执行,但它仍有可能在生产环境中不幸失败,因为生产中的环境和所有依赖项都会影响代码性能。依赖项并不属于app中的一部分,但仍需要运行它。例如数据库、数据/对象存储以及服务和应用程序可能需要调用的API。因此,开发和测试环境必须模仿生产环境。另外,必须对所有依赖项进行代码测试。
简而言之,部署代码时有3个测试阶段,每个阶段都会额外增加复杂性:
(1)验证代码本身是否按照预期工作;
(2)在代码库中继续进行验证;
(3)验证性能在具有所有依赖项的类生产环境中保持不变。
如果代码每天都被提交到版本控制中,则可以对其进行自动化测试,并且在引入代码之日会标记出任何构建、测试或集成错误,从而可以立即进行修复。这确保代码总是处于可部署和可运送的状态,称为绿色构建。
自动测试允许开发人员提高测试和集成的频率——从周期性执行到持续测试集成,并在约束最少的情况下发现问题。最糟糕的情况也不过是一天的工作都浪费了。
关于版本控制的争议
关于是否改在版本控制中存储敏感信息(如 access token、密钥和密码)进行了一些讨论。一方面,有人认为应该将一切(包括secret)都存储在这里,从而将这一方法推向极限。但是有人认为这是不良做法,并认为敏感信息应该单独存储。
版本控制允许开发人员比较、合并和还原以前的修订。通过允许他们在出现问题时将生产中的系统快速还原到以前的版本,从而将风险降到最低。为此,无论版本多小,所有更新和更改都必须在版本控制中进行跟踪。如果不是这样,生产中的代码将开发和测试环境中的代码不匹配,从而导致不一致。
简而言之,版本控制是事实的单一来源,包含了系统的预期状态以及所有以前的状态。通过将所有生产环境中的组件放置到版本控制中,开发人员可以重复可靠地重现工作软件系统中的所有组件。这是启用所谓的不可变基础架构的关键,我们将在稍后讨论。
持续交付:扩展CI以实现流畅的代码部署
即使使用了持续集成,将代码部署到生产中的过程依旧是手动、乏味且容易出错的。如果真是这样,那么显然不会频繁地将代码部署到生产中。IT部门会尽可能避免执行艰巨而危险的任务,这会导致要部署的代码与生产中运行的代码之间差异越来越大,进一步加具危险,然后形成一种恶性循环。那么解决这一恶性循环的答案是启用CI/CD中的CD部分。
CD扩展了CI,确保在将代码推广到整个用户群之前让代码在生产环境中能够平滑运行。最常见的CD方法是金丝雀和蓝绿部署。
进行蓝绿部署期间,IT会与当前版本一起部署一个新的组件或应用程序版本。新版本(绿)被部署到生产中并对其进行测试,与此同时当前版本(蓝)依旧可以使用。如果新版本的代码运行良好,那么所有用户将会切换到新版本中。
金丝雀部署也有两个版本:当前版本和更新版本。IT开始将一小部分用户请求路由到新版本。代码和用户的行为会被持续监控。如果错误率或用户投诉并没有增加,则路由到新版本的请求份额将逐渐增加(例如,1%、10%、50%最后到100%)。一旦所有请求都发送到新版本中,那么旧版本就会自动退休或删除。
通过按需环境创建自助服务
现在,我们已经研究了CI、CD及其各自的工具和方法,下面我们来讨论环境和基础架构。CI / CD需要一种创新的方法。
如我们所见,自动化测试使开发人员可以自己执行QA。为了确保一切都能在生产中正常运行,他们必须在整个开发和测试过程中使用类似于生产的环境。
传统上,开发人员必须向Ops团队请求(手动)设置的测试环境。此过程可能需要数周,有时甚至数月。此外,手动部署的测试环境通常会出现配置错误,或者与生产环境有很大差异,因此即使代码通过了所有预部署测试,仍然会导致生产问题。
CI / CD的关键部分是为开发人员提供按需类似于生产的环境,使其可以在自己的工作站上运行。为什么这很重要?开发人员只有在相同的条件下进行部署和测试时,才能知道代码在生产中的行为。如果他们在不同的环境中测试代码,则当最终将代码部署到生产环境中时,他们可能会发现代码不兼容,那么此时对客户造成了重大影响,再解决问题已经为时已晚。
不可变基础设施:牛与宠物
在讨论版本控制系统时,我们谈到了将环境与所有其他应用程序组件进行编码的需求,接下来让我们进一步讨论这些环境。
如果在版本控制中定义了环境规范并进行了编码,那么在容量增加(水平扩展)时复制环境就像按一个按钮一样简单(尽管之后它很有可能通过Kubernetes自动化了)。
扩展意味着在高峰时段增加计算能力。例如,Netflix的使用率在每个星期五晚上达到峰值,然后在午夜之后的某个时间再次恢复正常。为了确保享受无缓冲的视频,Netflix复制了其流控制组件(已在版本控制中进行了编码),以满足需求。然后,流量恢复后所有所谓的副本都被破坏,使流容量恢复正常。
为了实现这一点,至关重要的是,每当实施基础架构或应用程序更新时,这些基础架构或应用程序都会自动复制到其他地方并置于版本控制中。这将确保无论何时创建新环境,它都将与整个流水线(从dev到QA到生产)的环境匹配。例如,如果Netflix要更新其流服务而忘了捕获版本控制的更改,它将在高峰时段复制有故障或过时的组件,从而导致问题甚至服务中断。
由于掌握了版本控制中的环境编码,因此手动更改环境不是最佳实践,因为任何手动操作都容易出错。而应该将更改放入版本控制中,并从头开始重新创建环境(和代码)。这称为不可变基础架构。这些与CI / CD部分中讨论的应用于基础架构的原则相同。
你也许听过牛与宠物的比喻。这个比喻放在这里十分合适。以前,基础设施被视为宠物。如果有问题,你会尽力解决它,以便它可以生存。今天,基础设施被视为牛。如果它无法正常工作或需要更新,请杀死它并启动新环境。这非常强大,并且大大降低了问题潜入系统的风险。
发布与部署解耦
传统上,软件发布是由市场启动日期驱动的。因此,要发布的新功能会在宣布日期的前一天部署到生产中。但是,我们知道将特性或更新发布到生产中总是有风险的,尤其是如果你一次发布整个特性时。因此,将部署与发布捆绑在一起将使IT部门总是需要为失败胆战心惊。试想一下,如果在广泛推广的前一天发生了重大问题,IT团队就会感到恐慌,并且会在客户和媒体中引起巨大不良反响。
更好的方法是使部署与发布解耦。尽管这两个词经常互换使用,但它们是两个独立的过程。部署意味着将软件版本安装到任何环境(包括生产环境)中。它不一定必须与发布相关联。另一方面,发布意味着向客户群提供新功能。在整个功能开发过程中频繁进行生产部署的目的是降低服务中断的风险,该风险由IT部门承担。另一方面,何时向客户展示新功能应该是业务决策,而不是技术决策。
部署周期长会决定新功能发布的频率。如果IT可以按需部署,那么公开新功能的速度应该成为业务和市场营销的决定。
结 论
总而言之,CI要求将代码连续集成到代码库中,以在发生错误时捕获错误,从而最大程度地减少返工。要实现这种方法,需要三个工具:版本控制,以跟踪所有更改并使整个团队都可以使用最新的源代码版本;master,负责自己分支的开发人员每天合并更新;部署流水线将触发一系列测试,基本上是自动进行QA。
CD扩展了CI,以验证代码是否处于可部署状态,并自动将其释放到生产环境中。为此,需要一个成熟的DevOps组织,该组织必须先掌握CI,然后才能尝试使用CD。
如果实施得当,CI(/ CD)将大大提高你的IT团队的生产力。你的系统或应用程序在不断改进,同时将部署风险降至最低,从而增强了生产力和员工满意度的积极循环。此外,快速推出新功能和更新可推动创新,进而更快,更频繁地为用户带来价值。显然,随着越来越多的组织采用这些DevOps方法,由于传统方法无法与CI / CD竞争,因此对那些没有采用DevOps方法的企业,压力会越来越大。
附录:部署流水线测试套件
集成测试检查应用程序如何与其他应用程序和服务交互,以确保代码与这些依赖项正确交互。远程服务的虚拟或模拟版本可用于准确地重新创建生产环境。
验收测试会验证是否满足业务需求,以确保功能或应用程序为最终用户提供所需的价值。
性能测试可验证该应用程序在类似生产的负载下如何在整个堆栈(代码、数据库、存储、网络、虚拟化)中工作。由架构决策或网络、数据库、存储或其他系统的意外限制引起的问题应在此处解决。
非功能测试包括可用性、可伸缩性、性能、容量、安全性等。这些要求取决于环境的正确配置。测试将验证环境是否已正确构建和配置。
冒烟测试验证该应用程序可以连接到所有支持系统,例如数据库、服务或信息传递系统;冒烟测试通常是手动的。
也有自动安全性测试以及探索性和其他手动或资源密集型测试。我们的目标是尽早捕获更多错误,并使用这些更耗时的测试来验证高层次的需求,并将产品完全集成到尽可能接近生产的环境中。