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

PipelineasCode

时间:2023-03-12 11:57:57 科技观察

November2016TechnologyRadar给出了一个简洁的定义:PipelineasCode(管道即代码)是以编码而不是配置的方式,通过持续集成/持续交付(CI/CD)运行工具的方式来定义一个部署管道。事实上,早在2015年11月,技术雷达中就有一个类似的概念:避免在CI/CD工具中编程的方法是将构建过程的复杂性从工具的核心中提取出来,并放入一个简单的脚本中可以通过单个命令调用。然后可以在任何开发人员工作站上执行此脚本,因此消除了构建环境的特权/单一状态。总体思路是将复杂的构建过程合并到一个简单的脚本文件中,然后使用单个命令传输。这样,任何开发人员都可以在自己的工作空间中执行脚本,重建一个完全相同的构建环境,从而消除分散的配置损坏导致的CI/CD环境的特殊性。这样做的原因很容易理解。CI/CD工具用于暴露产品代码中的问题。如果它们已经复杂到不稳定的地步,我们使用它们就是在自找麻烦。实现管道即代码有点不言自明。在CI/CD的实践中,凡是能编码的都编码了,比如:构建、测试、数据库迁移、部署、基础设施/环境配置(InfrastructureasCode)等,说白了就是流水线已经是CI/CD实践过程中的“第一英里”,让流水线成为软件开发中的“一等公民”(即代码)已是大势所趋、人心所向。但是,这种说法终究是没有说服力的。然后我们从实践的痛点出发,总结一下目前pipeline遇到的问题。实践中的痛点我为客户搭建和配置了很多CI/CD流水线(被同事戏称为“CI/CD搭建兽”),最大的痛点是每次都要从头开始。在某些情况下,使用的工具和配置是相似的。二是人工操作造成的配置漂移。以Jenkins为例,先不说1.0版本不能直接支持流水线。为了支持构建、测试和部署等,我们一般先手动安装需要的插件,在多个文本框中粘贴大量的shell/batch脚本。下载依赖、设置环境变量等。久而久之(其实用不了多久),这个Jenkins服务器就变成了一个不可替代的(专门的)“怪物”,因为没有人确切地知道它到底做了什么改变,他们也不知道这些改变带来了什么改变已对系统作出。受到影响,此时的Jenkins服务器腐败成了MartinFowler口中的雪花服务器(snowflakeserver)。雪花服务器有两个显着的特点:极难复现,几乎无法理解***关键是之前做的修改没有记录,所以做的操作都是零散的,没有办法重现相同的操作不能复制一个相同的系统。第二点,大部分情况下,零散的配置是没有文档记录的,哪部分重要,哪部分不重要无从知晓,修改的风险非常大。随着管道的发展,这些问题会越来越严重。通常,管道在不再使用之前不会保持静态。在具体实施过程中,考虑到项目当前的特点,尤其是遗留项目和团队成员的“能力”,我们会首先将构建和部署自动化;部署节奏稳定后,开始自动化单元测试和代码分析;然后我们可以引导测试人员自动化验收测试;然后尝试自动化发布。之后还没有结束,团队会继续优化流水线,包括CI的速度和稳定性。也就是说,流水线的演进阶段其实和项目当前的进度是密切相关的,有时候需要保证这样的对应关系,比如:在多分支版本控制下,需要流水线和主分支对于发布分支将存在不同。发布分支是在某个时间从主分支分支出来的,需要在那个时候在流水线上才能正常工作。由于上面提到的雪花服务器的特点,重建这样的管道并不是一件容易的事。如何解决其实管道就是代码本身已经回答了这个问题。目前实现这一理念的CI/CD工具一般遵循两种模式:版本控制DSL(DomainSpecificLanguage)针对特别难复现且无法保证对应的痛点,我们将pipeline写成代码,放到版本中控制工具管理它。这样一来,每一次改动都可以被记录下来,并且永远与此时的项目进度保持同步。对于几乎无法理解且没有文档支持的痛点,我们使用特定领域的语言来描述整个流水线。举个Jenkins2.0的例子,它允许我们在项目的特定目录下放置一个Jenkinsfile文件,内容如下:node('master'){stage('Checkout'){...}stage('CodeAnalysis'){...}stage('UnitTest'){...}stage('Packing'){...}stage('Archive'){...}stage('DEV'){...}}stage('SIT'){timeout(time:4,unit:'HOURS'){input"DeploytoSIT?"}node('master'){...}}stage('AcceptanceTest'){node('slave'){...}}Jenkins2.0使用GroovyPipelineDSL实现了一组描述,即使我们不懂Groovy语言,只要稍微熟悉pipeline,我们就可以根据文档中的例子编写符合要求的代码。类似的工具还有Concourse.ci、λCD(LambdaCD)等。Concourse.ci使用基于yaml的DSL独立抽象Resource(外部依赖,如:gitrepo)、Job(函数,对Resource的get或put操作)和Task(纯函数,必须明确定义Input/Output)模型。效果图如下:λCD使用Clojure语言实现DSL,抽象出Pipeline和Step模型,使用Lisp特有的宏扩展和自定义常用函数,写起来简单明了。如下:(defpipeline-def`((eithermanualtrigger/wait-for-manual-triggerwait-for-repo)(with-workspaceclone(in-parallelrun-some-testsrun-smokeing-tests)run-packagedeploy)))上面的pipeline-def是此管道的定义。优雅到它的代码和UI竟然构成了映射关系,简单得要命。值得一提的是,与其他同类工具不同,λCD本身是一个用Clojure编写的微服务。也就是说,其他工具可能需要使用基础设施即代码来完成自己的安装,但λCD不需要。它可以使用其他微服务部署方式,比如用λCD部署自己,类似于编译器的bootstrap。).这时候我们需要两套λCD服务,一套用于部署λCD本身,一套用于部署开发中的项目。总结Pipelineascode是一个新的概念,这意味着我们还需要花时间去探索相关的实践,比如调试和测试(既然是代码,就需要测试)。一旦我们有了这些实践,我们就可以把管道本身作为一个产品放入管道中,然后我们会看到一个很有趣的现象——旧管道会构建并部署新管道,而上面提到的bootstraping现象也说明了管道在不断发展。另外,流水线变成代码后,在最终的交付物中必然占有一席之地,其潜在价值还有待我们去发现。至少从精益的角度来看,流水线能做的事情还是很多的。【本文为专栏作者“ThoughtWorks”原创稿件,微信公众号:Thinkworker,转载请联系原作者】点此查看该作者更多好文