持续集成&持续交付

作者简介:YY哥,11年+软件研发行业经验,4年研发经验,7年项目管理与过程改进经验。 曾服务于保险、金融、电商、智慧旅游、数字医疗等行业。拥有 Atlassian 官方认证 ACP-610,ACP-620, ACP-420。

在写系列文章之前,我画了使用Atlassian套件做持续交付的业务流程图,用来展示上面Demo的大致流程,指引后续系列文章的写作。在上篇文章【Atlassian全家桶系列】产品需求管理中,我跟大家分享了在产品需求管理过程中Jira与Confluence工具如何双剑合璧、发挥集成带来的益处,覆盖了产品规划、需求文档与过程管理、迭代计划与跟踪管理和报表管理等。接下来,我们一起进入软件研发世界,瞧瞧Atlassian套件如何实现“从代码到客户,持续交付”的目标。

CI/CD与持续部署介绍

 

持续集成Continuous Integration或CI)是一种软件工程实践,团队开发成员经常地集成TA们的工作,通常每个成员每天至少集成一次,意味着每天可能会发生多次集成。每次集成都通过自动化的构建(包括编译、自动化测试等)来验证,从而尽早地发现集成问题。持续集成原则之一是“快速失败”,强调对“构建失败”能尽早地检测、通知和纠正。早期检测容许早期纠正,减少对项目的影响。另外,如果我们更早地检测到问题,我们也没必要执行其他构建过程,所以节省了时间和资源。

持续交付Continuous Delivery或CD)是一种软件团队端到端的交付能力,“部署流水线”确保代码和基础设施始终处于可部署状态,所有提交到主干的代码都可以安全地部署到生产环境给用户使用。将持续集成构建的制品(Artifacts)部署到不同环境中测试,如果通过测试,那么该制品会被部署到下个阶段的环境中。这样,开发人员从每个阶段环境的测试中获得新的反馈,如果出现错误,他们可以更容易地知道问题所在,并在代码发布到生产环境前解决。

持续部署Continuous Deployment)指在持续交付的基础上,把部署到生产环境的过程自动化。部署与交付是完全不同的概念,部署属于技术领域的操作,而交付是一个业务决策活动,通常也称为发布,即:将新构建的特性交付到用户手里使用。我们可以向环境多次部署,但只有当业务需要时才向用户发布。

持续交付工具链配置

Atlassian通过应用程序链接Application Links)将Confluence、Jira、Bitbucket和Bamboo等系统集成在一起,系统之间使用oAuth协议进行用户资源的授权与共享。

1.Jira配置

为了实现在研发过程中自动更新相应的Jira状态,比如:在创建新代码分支时将Jira用户故事的状态从To Do更新到In Progress,我们需要配置Jira问题类型“用户故事”对应的工作流。在工作流转换动作的触发器(Trigger)设置中,添加相应触发器,目前Jira系统提供的触发器有:

  • Branch created:当代码仓库中有新分支被创建时自动转换工作流;

  • Commit created:当有新的代码提交到代码仓库时自动转换工作流;

  • Pull request created:当有新的拉取请求创建时自动转换工作流;

  • Pull request declined:当拉取请求被拒绝时自动转换工作流;

  • Pull Request reopened:当拉取请求被重新打开时自动转换工作流;

  • Pull request merged:当拉取请求被合并到主干时自动转换工作流;

     

2.Bitbucket配置

软件开发人员主要通过代码进行沟通和协作,一款优秀的源代码版本控制系统至关重要。Bitbucket采用Git和Mercrurial作为分布式版本控制系统,团队借助Bitbucket可以集中规划项目、开展代码协作以及进行测试和部署。其特点有:

  • 提供无限个私有代码库(5人以下小型团队免费);

  • 与Jira和Trello无缝集成,可以直接从Jira问题或Trello卡片创建Bitbucket分支;

  • 通过代码评审构建高质量软件,通过拉取请求更高效地批准代码评审,通过内嵌的评论直接在源代码中讨论;

  • 通过内置的持续交付功能实现频繁部署Bitbucket Pipelines with Deployments使用集成式CI/CD构建、测试和部署,获得配置即代码和快速反馈回路带来的优势;

  • 保护工作流,通过IP白名单和双重验证确保云上代码的安全性;

当第一次使用Bitbucket时,我们先创建Bitbucket项目,在项目下创建所需数量的代码仓库。在仓库设置(Repository Settings)中对仓库和分支安全、工作流、拉取请求检查等进行设置,比如:设置拉取请求的代码合并到主干之前至少有2个人审批通过,否则将无法合并。Sourcetree是适用于Windows和Mac的免费Git客户端,可以简化与Git代码库的交互,让软件开发人员集中精力编写代码。功能完善的图形用户界面,开箱即用,可与Git和Mercurial搭配使用。使用Bitbucket左侧菜单栏中的Clone功能,将新建的服务端代码仓库克隆到开发人员的本地环境中。

3.Bamboo配置

Bamboo Server是一款持续集成和交付工具,在简单的工作流程中整合了自动化构建、测试和发布,可以完美配合Jira Software和Bitbucket的工作,提供完全可跟踪的部署流水线。Bamboo适用于任何语言,以及AWS CodeDeploy、Docker和Amazon S3等其他热门技术。借助Bamboo的专用代理功能可以立即运行修补程序和关键构建。另外,Bamboo最多支持100个远端构建代理,能够并行地运行批量测试,从而使队列保持流动,并快速为开发人员提供反馈。

Bamboo模型将构建项目部署项目独立开,但是可以通过触发器在构建项目执行成功后自动触发部署过程。我们先创建构建项目,然后在项目下创建所需数量的计划(Plans):

  • 每个Plan定义了整个构建过程的所有配置信息,比如:关联的Bitbucket代码仓库、计划分支(Plan Branch)创建规则、通知配置和构建任务编排等;

  • 每个Plan下可以配置多个串行执行的Stages,用来控制工作流的执行,每个Stage表示构建流程的一个步骤,比如:为编译创建一个Stage,紧跟着测试、部署Stages;

  • 每个Stage中可以配置多个并行执行的Jobs,每个Job代表构建代理服务器上运行的任务集合,需配置构建的输入物、所需的可执行程序、运行构建的代理服务器和构建过程的输出物等;

  • 每个Job中可以配置多个串行执行的Tasks,每个Task是进行自动化构建的基本块,比如:Maven、Ant和自定义脚本等;

在构建计划中,Bamboo提供了多种触发器触发构建过程,比如:由Bitbucket服务器代码仓库触发(有变更被提交时触发)、远程触发器、代码仓库轮询(如:每小时轮询一次,检查到代码变更时触发)、按计划运行(如:工作日工作时间段内每小时触发一次)、每日构建(如:每天凌晨4:00触发)。在我们的Demo中,配置成“由Bitbucket服务器代码仓库触发”。计划分支(Plan Branch)让我们可以使用同样的计划配置在不同的分支上运行构建,可以设置在什么情况下创建和删除计划分支,比如:手动创建、在新建拉取请求时自动创建、在创建分支时自动创建、在创建分支且匹配指定正则表达式时自动创建。在我们的Demo中,配置成“在创建分支时自动创建”。 

Bamboo还提供了自动合并和通知偏好等功能,带计划分支的特性分支模型为开发人员提供了灵活且精确的冲突规避工具,通过频繁且规律的代码合并,减少了“代码漂移”(Code Drift),减少在项目中实施带缺陷的代码。当开发人员有权限打开或关闭“自动合并”开关以适应各自的开发周期时,特性分支模型会运作得尤为突出,而Bamboo系统提供了一个理想的环境供开发人员自由掌控这个权限。关于计划分支的特性(如:自动合并门禁和分支更新器策略),请参考之前写的文章Atlassian持续集成系统之Plan Branches介绍

Maven Build Job中配置了4个Tasks,它们的作用如下:

  • Source Code Checkout:将源代码从仓库中检出到Bamboo工作目录中,需配置代码仓库;

  • Maven 3.x:Apache Maven是构建和管理Java项目的工具,需配置可执行的Maven程序及相关参数、JDK和工作目录等;

  • Sonar Maven 3:做Sonar静态代码扫描与分析,需配置Maven程序、JDK、SonarQube服务器、分支与拉取请求选项等;

  • JUnit Parser:解析构建后的测试结果,需配置JUnit测试报告目录,比如:**/surefire-reports/*.xml

我们Demo的目标是将构建的制品(Artifacts)自动部署到阿里云服务器上,并发布给终端用户使用。接下来,我们需要创建部署项目,关联以上配置好的构建计划,可以选择使用主干计划分支(基于master)或自定义计划分支(Plan Branch)。一般针对不同阶段配置不同的部署环境(如:测试环境、预生产环境和生产环境),部署通常运行在代理服务器上,也可以配置到Docker容器中。每个环境需定义将发布部署到该环境所涉及的步骤及部署的触发器等。为了简化流程,这个Demo仅配置了一个生产环境,持续部署的触发条件是:在主干计划分支构建成功后。部署到生产环境的Tasks配置如下:

  • Artifact download task:用于在不同的构建计划之间共享制品,或者在部署环境中共享构建计划的制品,而不需要每次重新构建。需将制品名称配置成构建计划中Job产出的制品;

  • SCP Task:用于将Bamboo中的文件直接上传到远程服务器,可以拷贝多个文件,并保留拷贝文件的完整目录结构。需配置远程服务器地址、身份认证、制品名称和远程目录等;

  • SSH Task:用于执行远程服务器上的SSH命令,比如:调用数据库迁移脚本、启停服务等。需配置远程服务器地址、身份认证、SSH命令等;

3.与SonarQube集成

SonarQube是一个代码质量管理平台,可以将各种代码质量和覆盖率检测工具的结果(如:CheckStyle和Jacoco)直接展示给用户,还可以通过不同的插件算法对结果进行再加工,并最终以量化的方式来衡量代码质量,从而方便地对不同规模和种类的工程进行相应的代码质量管理,以便进行有针对性的代码修复或重构。同时,SonarQube还对大量的持续集成工具提供了接口支持,可以很方便地在持续集成中使用。

下图是将SonarQube集成到Atlassian持续集成中的解决方案,需要在Bitbucket和Bamboo中分别安装Sonar for Bitbucket ServerSonar for Bamboo插件。另外,由于要实现基于Branch的静态代码分析,我们采用的解决方案是:SonarQube Community (V8.3) + Community Branch Plugin (V1.3)。Bamboo中持续集成会触发SonarQube分析,通过构建计划分支,Bamboo中的持续集成程序会自动触发SonarQube分析,将分析结果发布到SonarQube服务器上,同时将feature分支的构建结果通知开发人员。在开发人员为feature分支创建拉取请求时,Bitbucket会收集Sonar度量和代码问题,并显示在拉取请求中,供拉取请求的发起人和评审人参考。我们接下来会介绍下Sonar在Bitbucket和Bamboo中的具体配置。

在安装好Sonar for Bitbucket Server插件后,Bitbucket提供从项目和代码仓库两个层级配置Sonar。在Sonar项目设置中,我们可以设置代码分析模式、是否在拉取请求的代码比较中显示Sonar问题、是否在拉取请求中显示Sonar统计信息、在质量未达标时是否要阻止拉取请求的代码合并等。

在代码仓库设置中,需启用Sonar,配置SonarQube服务器、Sonar master项目、构建类型,以及是直接复用项目配置还是为该仓库自定义配置。

在Bamboo后台管理-->管理插件-->Sonar for Bamboo中添加SonarQube服务器,配置Sonar服务器名、主机URL、身份认证、质量门禁构建失败规则等。在构建计划的Sonar Maven 3 task中配置通用任务选项(Maven可执行程序、JDK和工作目录)、Sonar服务器配置、构建失败选项、分支策略与拉取请求选项、是否启用增量代码扫描模式等。

在分支策略与拉取请求选项中,必须配置Replacement for illegal branch characters为“_”,因为Sonar无法解析Git分支命名中常用符号“/”,比如:必须将feature/user-story-xyz替换成feature_user-story-xyz。另外,为了在Sonar分析报告中显示测试覆盖率,需要在构建计划的Maven 3.x task中增加额外参数:clean org.jacoco:jacoco-maven-plugin:prepare-agent install,同时配置Maven中的Sonar服务器信息(*\Atlassian\Bamboo\tools\apache-maven-3.5.0\conf\settings.xml)

<profile> <id>sonar</id> <activation> <activeByDefault>true</activeByDefault> </activation> <properties> <sonar.jdbc.url>jdbc:h2:tcp://localhost:9000/sonar</sonar.jdbc.url> <sonar.jdbc.username>sonar</sonar.jdbc.username> <sonar.jdbc.password>sonar</sonar.jdbc.password> <sonar.host.url>http://localhost:9000</sonar.host.url> <sonar.login>admin</sonar.login> <sonar.password>admin</sonar.password> </properties> </profile>

Java项目采用SpringBoot框架搭建,使用IntelliJ IDEA开发,项目目标是:根据身高和体重计算BMI值,并给出计算结论。针对核心业务代码逻辑,我编写了部分单测用例,旨在模拟自动化测试在持续集成中的作用。

持续集成与部署Demo

在Jira看板中,选择Jira用户故事卡片,右侧将展示详情信息。在开发Panel中点击“创建分支”,在弹窗中确认代码仓库、分支类型、代码分支来源、分支名称。分支类型前缀在Bitbucket的项目或仓库的分支模型中设置,比如:feature的默认分支类型前缀为/feature,Release为/release。确认无误后,点击Create branch进入Bitbucket页面。

此时,返回Jira看板将显示Jira用户故事自动被更新到了“处理中”,这是因为我们在Jira工作流的转换动作的触发器中设置了:当代码仓库中有新分支被创建时自动转换工作流。在开发Panel中将显示“1 branch”,点击可以查看具体的代码仓库和分支名称。在Bamboo持续集成系统中,计划分支(Plan Branch)被自动创建并成功完成第一次构建,这是因为我们在Bamboo构建计划中设置了:当代码仓库有新分支被创建时自动触发计划分支的创建。

打开Sourcetree,从Bitbucket服务器获取全部更新,并将BUDDHA-22分支代码检出到本地。打开IntelliJ IDEA开发代码,开发完成后,在Sourcetree中将修改的文件暂存所选,然后输入备注(注意:必须包含Jira编号),勾选“立即推送变更到xxx”,点击“提交”将本地代码变更推送到远程feature分支。

打开Bitbucket,确认最新代码变更已成功提交到远程分支,且自动触发了基于feature分支的计划构建。点击构建图标会弹出构建详情,点击链接后跳转到Bamboo构建页面。

在构建页面,我们可以实时看到构建进度和结果。构建结果包含了自动化测试用例执行结果(6个用例全部通过)、Sonar分析结果(质量门禁通过)、关联的Jira问题及代码提交信息、构建的输出物(共享制品)、制品部署信息、构建日志等。如果构建失败,比如:某个单元测试用例未通过,那么在测试(Tests)中将显示详细的失败用例代码。这时需要尽快修复阻碍构建成功的所有问题,否则无法将拉取请求合并到master分支。

在feature分支构建成功(通过了Sonar代码质量扫码和自动化测试)后,我们需要在Bitbucket中发起拉取请求(Pull Request,简称PR),将最新代码提交合并到master分支。选择来源和目标分支,输入描述和评审人(可以在Bitbucket后台设置默认评审人),点击“创建”完成PR的创建。这时,返回Jira看板查看Jira用户故事被自动更新到“评审中”。

使用评审人账号登录Bitbucket,将看到所有待评审的PR记录(包括评审人、Sonar检查结果和构建结果),选择BUDDHA-22拉取请求,通过Diff查看具体的代码改动,可以对每行改动添加评语,也可以针对整个PR添加评语,评审通过后点击右上角“Approve”。而代码的合并可以由PR的发起人或评审人操作,具体由团队规则决定。返回Jira看板将显示Jira用户故事自动被更新到了“完成”,这是因为我们在Jira工作流的转换动作的触发器中设置了:当拉取请求被合并到主干时自动转换工作流。

此时,切换到PR发起人账号登录Bitbucket,查看到PR状态依然是Open,点击PR右上角的Merge按钮执行代码合并,PR状态变成Merged。合并代码到master会自动触发master分支构建,如果构建成功,则会自动触发持续部署,从构建计划的产物共享制品(shared artifacts),创建发布版本(Release),部署到远程服务器上。

构建过程产出的制品可以在其他构建项目或部署项目中共享。Bamboo自身管理制品(Artifacts),所以在构建和部署阶段所需的任何制品,Bamboo会自动传输到所需远程服务器上。一个发布版本(Release)创建自单次构建结果,发布版本是用来准确跟踪部署到某个环境中的软件包,本质上是在部署过程使用的制品和相关元数据(如:Jira问题、代码提交和测试数据)的一个快照(Snapshot),元数据可以用在发布说明和质量控制中,也可以通过元数据比较两个发布版本之间的差异。

在发布版本的部署阶段,主要做的事情是:使用Bamboo SCP Task将构建制品(jar包)从Bamboo服务器传输到阿里云发布服务器上的指定目录,然后使用Bamboo SSH Task执行阿里云服务器上deploy.sh脚本,完成jar包部署和重启服务等具体部署过程。最后,在生产环境中进行BMI计算器的手工测试验证,验证通过后,本次发布完成。

而在持续交付实践中,部署到生产环境是通过手动触发的。在Bamboo部署项目中,我们手动创建新的发布版本,需要选择计划分支及其构建结果、定义发布版本(版本号+构建ID)、自上个发布版本的所有变更(Jira问题和代码提交信息),然后将这个发布版本手动部署到生产环境中。部署成功后,各环境部署状态列表中的触发器为Manual run by XXX

写在最后

 

Atlassian套件从提出功能请求一直到完成部署,提供了完整的可追溯性。无论是Confluence记录每次团队对需求达成共识后的版本变更记录,还是Jira对需求过程每个细节的全面跟踪,亦或在研发过程中分支创建、代码提交、拉取请求、持续集成、发布版本(Release)创建和部署,无一不是以Jira问题(用户故事)为主线,体现了需求作为唯一受信源的原则。

在研发过程中,以代码仓库为唯一受信源,保存所有源代码的变更历史(如:产品源代码、测试代码、自动化脚本等),无论是拉取请求评审,还是持续构建亦或发布版本部署,都是围绕着提交(Commits)。 制品库也作为唯一受信源,在构建计划的Job中产生制品(Artifacts),Bamboo提供的共享制品用于在不同的构建计划之间共享,或者在不同阶段的环境中共享构建计划的制品,满足后续环节的快速取用,而无需每次重新构建,比如:从构建计划的制品创建发布版本后,如果测试环境测试通过(无法任何代码改动),那么将该发布版本直接提送(Promote)到预生产环境中测试,测试通过后,直接提送到生产环境中。所有团队都以唯一受信的需求/缺陷、源代码和构建制品仓库中内容为基准,相互沟通与协作。

 

文章来源YY哥的小屋公众号:【Atlassian全家桶系列】持续集成&持续交付