当前位置: 首页 > 科技观察

解决CI-CD中存储库阻抗不匹配的问题

时间:2023-03-20 11:45:31 科技观察

对齐部署图像和描述符很困难,但某些策略可以使整个过程更高效。在软件架构中,当两个组件之间存在某些概念或技术差异时,就会发生阻抗不匹配。该术语实际上是从电气工程中借用的,表示电路中输入和输出的电阻抗必须匹配。在软件开发中,存储在镜像存储库中的图像与其存储在源代码控制管理系统(LCTT:SCM,SourceCodeManagement)中的部署描述符之间存在阻抗不匹配。您如何确定存储在SCM中的部署描述符表示正确的映像?这两个存储库不会以相同的方式跟踪数据,因此将图像(独立存储在镜像存储库中的不可修改的二进制文件)与其部署描述符字符(作为文本文件存储在Git中的一系列修改记录)结合起来并非如此直觉的。注:本文假设读者已经熟悉以下概念:源码控制管理SourceControlManagement(SCM)系统和分支Docker或OCI兼容镜像和容器容器编排系统ContainerOrchestrationPlatforms(COP),例如Kubernetes连续集成/持续交付持续集成/持续交付(CI/CD)软件开发生命周期(SDLC)环境阻抗不匹配:SCM和镜像仓库为了更好地理解阻抗不匹配在哪些场景下会成为问题,考虑任何项目中的软件开发生命周期环境(SDLC),例如开发、测试或发布环境。测试环境不会出现阻抗失配。在现在使用CI/CD的最佳实践中,开发分支的最新提交将对应开发环境中的最新部署。因此,一个典型且成功的CI/CD开发流程如下:向SCM的开发分支提交新的变更新的提交触发镜像构建新生成的镜像推送到镜像仓库,标记为developing镜像部署到容器编排系统中的开发环境(COP)中,镜像的部署描述符也更新为从SCM拉取的最新描述符。也就是说,开发环境中最新的镜像总是匹配最新的部署描述符。回滚到以前的构建也不是问题,因为SCM会随之回滚。最终,随着开发过程的继续,需要进行更正式的测试,因此镜像——对应于SCM中的提交——被推送到测试环境。如果构建成功,那就没什么大问题了,因为从开发环境推送过来的镜像,应该对应的是开发分支的最新提交。允许开发环境最新部署入库,触发入库过程的最新部署镜像标记为正在测试。镜像拉取部署在测试环境,(镜像)对应的是目前为止从SCM拉取的最新部署描述符到目前为止,一切都ok对吧?如果出现以下情况,会出现什么问题?场景A:镜像推送到下游环境,比如用户验收测试(UAT),或者生产环境。场景B:测试环境发现破坏性BUG,需要将镜像回滚到某个版本。在这两种情况下,开发过程都没有停止,即开发分支上游有一个或多个新的提交,这意味着最新的部署描述符发生了变化,最新的图像与之前部署的图像不同测试环境。镜像不一致。对部署描述符的修改可能会或可能不会对以前版本的图像起作用,但它们不能被信任。如果它们已更改,则它们不得与您迄今为止已测试但要部署的映像的部署描述符不匹配。问题的症结在于:如果部署的镜像不是镜像库中的最新版本,如何判断部署的镜像对应的是SCM中的哪个部署描述符?一句话,无法确定。这两个银行直接有一个阻抗失配。如果要细说,也有办法解决,但需要做很多工作,这部分是本文其余部分的主题。请注意,下面的解决方案并不是问题的唯一解决方案,而是已经投入生产并为许多项目工作,并且已经构建和部署在生产中运行了一年多。二进制和部署描述符源代码通常内置在Docker镜像或OCI兼容镜像中,通常部署到容器编排平台(COP),例如Kubernetes。部署到COP需要部署描述符来定义图像如何作为容器部署和运行,例如Kubernetes部署或CronJobs。这是因为图像与其部署描述符之间存在根本差异,可以看到阻抗不匹配。在本次讨论中,我们将图像视为存储在注册表中的不可修改的二进制文件。对源代码的任何修改都不会修改图像,而是用新图像替换它。相反,部署描述符是文本文件,因此可以被视为源代码并且可以修改。如果遵循最佳实践,部署描述符将存储在SCM中,并且所有更改都已提交,可以轻松追溯。解决阻抗不匹配的建议解决方案的第一部分是提供一种方法,将注册表中的图像与提交给保存部署描述符的SCM的代码进行匹配。最直接的解决方案是使用源提交的哈希标记镜像。这种方法可以区分不同版本的镜像,易于区分,并提供足够的信息来找到正确的部署描述符,以便更好地将镜像部署到COP。再回顾一下上面的场景:场景A镜像被推送到下游环境:当镜像从测试环境被推送到UAT环境时,我们可以知道SCM提交的哪个源码应该从镜像标签中拉取部署描述符.场景B当镜像在某个阶段需要回滚时:无论我们选择回滚到哪个版本的镜像,我们都可以知道SCM的哪个源代码提交拉取了正确的部署描述符。在每一种情况下,无论镜像部署到测试环境后,开发分支被提交构建了多少次,对于每一个升级后的镜像,我们都可以找到其最初部署时对应的部署描述符。然而,这并不是阻抗失配的完整解决方案。考虑另外两个场景:场景C在负载测试环境中,将尝试对不同的部署描述符进行多次部署,以验证某个构建的性能。场景D镜像推送到部署描述符错误的下游环境。在上述所有场景中,我们都需要修改部署描述符,但到目前为止我们只有一个源提交哈希。请记住,最佳实践要求我们对源代码的所有更改首先提交给SCM。提交本身的哈希值无法更改,因此我们需要一个比仅跟踪原始源代码提交哈希值更好的解决方案。解决方案是基于原始源提交哈希创建一个新分支。我们称这个分支为部署分支。每当将图像推送到下游测试或发布环境时,您都应该根据先前SDLC环境的部署分支的最新提交创建一个新的部署分支。这样就可以将同一个镜像重复部署到不同的SDLC环境中,后续的每个环境都可以感知到之前发现的变化或者对镜像所做的修改。注意:在一个环境中所做的更改如何影响下一个环境,是使用可以共享数据的工具(例如HelmCharts)还是手动剪切和粘贴到其他目录,不在本文讨论范围之内。因此,当图像从一个SDLC环境推送到下一个SDLC环境时:如果图像是从开发环境推送的,则创建部署分支,然后根据构建图像的源提交哈希创建部署分支,否则,部署分支是基于当前部署分支的最新提交创建的。镜像部署到下一个SDLC环境。使用的部署描述符是本环境中新建的部署分支的部署描述符。图1:Deployment分支树下游环境Deployment分支Deployment分支,只有一个提交到下游环境第二个部署分支,只有一个提交有deployment分支的解决方案,再回顾上面的场景C和场景D:场景C修改已经部署到下游SDLC环境的镜像DeploymentDescriptor场景D:修复SDLC环境中的部署描述符错误两种场景的工作流程如下:将对部署描述符的修改提交到部署分支对应SLDC环境和镜像,通过最新的部署分支提交对应的部署分支部署描述符,将镜像重新部署到SLDC环境中。通过这种方式,部署分支完全解决了镜像存储库(存储一个单一的、不可修改的镜像,代表一个唯一的构建)和(存储一个或多个SDLC环境对应的可执行文件)之间的关系。修改后的部署描述符的SCM存储库之间的阻抗不匹配。实践中的思考这看起来是一个可行的方案,但是也给开发人员和运维人员带来了新的实际问题,比如:A.为了更好的管理部署分支,部署描述符作为资源应该存储在哪里,是否应该和构建镜像的源代码存放在同一个SCM仓库中?至此,我们已经避免讨论部署描述符应该放在哪个仓库。在没有太多细节需要处理的情况下,我们建议将所有SDLC环境的部署描述符和镜像源放在同一个SCM仓库中。创建部署分支时,可以通过镜像的源代码作为参考,轻松找到运行在已部署容器中的镜像。如上所述,您可以使用图像标签将图像与原始源代码提交相关联。在单独的存储库中查找对提交源代码的引用将使开发人员(即使使用工具)变得更加困难,这就是为什么没有必要单独存储所有资源的原因。B.构建镜像的源代码是否应该在部署分支上修改?简短回答:不。详细说明:不,因为图像永远不应该在部署分支上构建,它们是在开发分支上构建的。修改部署分支上定义镜像的源代码会破坏部署镜像的构建记录,这些修改不会对镜像的功能生效。在比较两个部署分支的版本时,这也可能是一个问题。这可能导致两个版本之间功能差异的错误测试结果(这是使用部署分支的一个小额外好处)。C.为什么要使用图片标签?是不是不允许标注标签?通过tag标签可以很方便的在仓库中找到镜像,可读性也很好。读取和查找一组图像中的标签标签的值需要拉取所有图像的清单文件,这增加了复杂性并降低了性能。而且考虑到历史记录的追踪和不同版本的查找,还需要为不同版本的图片添加标签,所以使用源码提交哈希是最简单的保证唯一性的方式,保存有用的信息可以生效立即解决。D.创建部署分支的最佳实践是什么?DevOps最重要的三个原则:自动化、自动化、自动化。依靠资源不断强制遵守最佳实践充其量只是运气,所以在实现镜像升级和回滚等CI/CD流水线时,将自动化部署分支编写到脚本中。E.你对部署分支的命名约定有什么建议吗?--DeploymentbranchID:在所有部署分支范围内唯一的字符串;如“deployment”或“deploy”Environment:部署分支所适用的SDLC环境;例如“qa”(测试环境)、“stg”(预生产环境)或“prod”(生产环境)SourceCommitHash:源代码提交哈希包含被部署的原始构建镜像的源代码image,开发者可以通过它轻松找到创建镜像的原始commit,同时保证分支名称的唯一性。例如deployment-qa-asdf78s代表推送到QA环境的部署分支,deployment-stg-asdf78s代表推送到STG环境的部署分支。F.如何识别环境中运行的镜像版本?我们的建议是将最新的部署分支提交哈希和源代码提交哈希添加到标记中。开发者和运营者可以通过这两个唯一标识符找到部署的一切及其来源。使用这些不同部署版本的选择器还会在执行回滚或前滚等操作时清理资源碎片。G.何时应将部署分支的更改合并回开发分支?这完全取决于开发团队。如果你的改动的目的是做负载测试,只是为了验证什么会导致程序崩溃,那么这些改动不应该合并回开发分支。另一方面,如果你发现并修复了一个bug,或者对下游环境的部署进行了调整,那么你应该将部署分支的更改合并回开发分支。H.有现成的部署分支例子让我们试水吗?el-CICD已经在生产中使用这一策略一年半到一百多个项目,涵盖所有SDLC环境,包括管理生产环境的部署。如果您有权访问OKD、红帽OpenShift实验室集群或红帽CodeReady容器,则可以下载最新版本的el-CICD并按照教程学习何时以及如何创建和使用部署分支。结语通过练习上面的例子,可以帮助您更好地理解开发过程中与阻抗失配相关的问题。对齐图像和部署描述符是成功管理部署的关键部分。