1. 概念

1.1. CI Continuous Integration,持续集成

  • 团队成员频繁地集成各自的开发成果,从而尽早约束个人理解和技术实现的偏差。类似大部队行军时每隔半天检查人员,及时找回迷路的队员,而不是到了战壕才去找人,把后期修复成本降低最低。往往通过手动或自动根据时间、事件触发的方式,拉取代码仓库,实施代码分析、编译、执行测试和制作安装包等。

1.2. CD Continuous Delivery,持续交付

  • 结合CI成果,通过网络发布安装包或可运行软件包,如果条件允许,直接部署到测试或生产环境,完成安装、配置、执行环境中特有的测试脚本,最后投入运行。

2. 变化

早期软件用户偏特定领域,软件团队对于最终形态有较大发言权,并且一贯要求测试团队的深度参与来保障目标。随着软件逐渐走入工作和日常生活,尤其进入网络时代之后发生众多变化:

  1. 软件的范围、功能不断细分,变化快、结构复杂。
  2. 早期软件偏重基础设施如OS、驱动、编译器、数据库,以及业务领域如银行、财务、ERP等,当今则更多面向个人,人的每一天都是崭新,导致不断变更试错
  3. 软件在社会分工中越来越重要。
  4. 软件活动中角色分工精细化,沟通链条长。
  5. 用户为王的时代,全球IT水平稳步提高,重要场合下明确强调响应时间如MTBF(Mean Time Between Failure) 平均无故障时间,MTTR(Mean Time To Repair) 平均故障修复时间。
  6. 对软件活动中沟通、专业度、质、量、运维提出更高的品质要求。
  7. 快速响应多用户需求,多产品落地,至少同时打好2场战争。
  8. 大型企业人力资源充分,用高成本人力也可达到目标,创新性企业首要是生存,频繁试错的人力也不足,哪条是弯道?
  9. 交通发展后以前企业为跨地域用户服务的壁垒不复存在,守着线下一条街的生存方式成为过去,IT组织不提升专业度就会丧失竞争力。

传统软件开发活动难以支撑以上变化,同时成本巨大,一旦犯错可能会被逐出竞争,因为对手变化得更快、成本更低、更专业。

2.1. 需求与产品

在整个软件活动流程中,有头有尾,头尾迭代相连,花些成本走好第一步、第二步等前面的路,比起出问题后变更、修复、人工测试、再部署的成本要低得多,并且高品质给予相关人以良好印象和健康的心理暗示,对于持续的业务活动、团队建设和人才培养,起到润物细无声的朴素效果。所以前期做需求分析产品设计时开展精密的评审验证来确认,并留下对应的验收测试用例系统测试用例以及功能测试用例来保障最终交付的是面向需求和功能的,而不是开发人员在较长开发周期中不断脱离出发点的个人理解。

2.2. 开发

到开发阶段,由于沟通链条长导致各环节出现理解偏差的概率增大,代码质量除Review外还需要把测试工作提前,帮助开发人员在每日Coding之前、之中、之后都认清终点目标,同时避免因人的长期记忆有限,忽视掉的边界、范围、依赖、冲突,而不是等到上线才发现放错了辣椒、回炉重炒。涉及到多个开发人员协同时,即便设计之初有协定,执行时亦难免变化,人的天性包括宽以待己,严以律人,容易对伙伴的实现提出过于美丽的假想,从而导致整体软件建筑发生局部位移,由小聚多,临到后来仓皇上线,频繁修复,丧失了品质,又假象于下一版本好好架构,可惜一款软件的生命周期之长,往往超过我们的想象,理想中充裕时间的重构往往只在梦中,所以开发活动前期的失误到将来会付出很高的修复成本,实施提升品质活动的最佳时机,永远是当下

2.3. 测试与交付

无论开发人员的单元测试,还是测试团队验证验收测试用例系统测试用例等,实质是在不同层面的内部环境实施一次发布、部署等交付流程,如果这个流程缓慢、故障频频,直接后果是测试频率放缓,交付压力下只能脱离测试环节扔到现场,实现阶段性目标的同时往往引发一些本该避免的故障,最后开发者、测试者、实施者,无法关注测试和质量,忙得只在乎进度。万一再遇到开发人员在外,他们独立完成的软件就无法发布,这好比银行休假了就无法取款。

举个例子,伙伴们在进度压力下,甚少关注单元测试,所以测试脚本或测试软件的编译和运行,以及确认测试结果等过程繁琐,又因为这个繁琐,索性不做单元测试,其他活动也一样:

  • “制作软件安装包很麻烦,我正在Coding,干脆给你Copy一个目录吧”
  • “我现在无法远程某电脑安装软件,所以用上个有残缺的版本吧”

看上去就是有些事做起来痛苦,所以不做。那么这样自我问答一次:

  • 这些事为什么要做?
  • 因为有价值?
  • 不做岂不是丢失了价值?
  • 是的,但这些重要吗?我很忙呢,她催我上线,虽然我知道有问题,上线后再验证吧。

就会发现宝贵的时间浪费后没有显著产出,陷入低质量的重复,所以得出一个简单的结论:

痛苦的事要频繁做,工具化、自动化、一键执行,直到做得轻松,这时候打通了上下游的活动环节。

3. 价值

  1. 显著降低软件构建成本,建立软件包仓库,从而自动化、频繁地开展软件质量活动,提前发现问题、低成本解决问题,显著增强软件品质。
  2. 提升软件发布和部署速度,复杂系统的升级牵扯多个子系统。
    • 上线周期降低后,收集新版本反馈意见的周期也在降低,从而快速进入下一轮迭代。
    • 有利于实施金丝雀发布等渐进式部署方案,降低升级风险带来的MTTR(Mean Time To Repair) 平均故障修复时间,并有利于开展产品试验活动。
  3. 结合敏捷实践,快速迭代验证成果是否达到预期,使得用户、产品、技术及时审视当下的位置和前进的方向,若有差池,立即调整,同时获取关联人的反馈与灵感。
  4. 暗示无处不在的技术活动提升组织能力,触类旁通,IT治理的魅力。

4. 操作实践

以网红Jenkins为例,安装、登录后如下操作。

4.1. 配置邮件模板

  1. 点击Manage Jenkins->Editable Email Notification Templates->Add New Template
  2. 填写各项信息,比如模板名称Name和收件人Project Recipient List,大多参数都是和SMTP等邮件协议有关,可保持默认。
  3. 点击Advanced Settings,通过Remove Trigger删除默认触发条件。
  4. 通过Add Trigger选择Failure-Any触发事件,点击Delete删除默认收件人Developers
  5. 点击Add选择Recipient List,针对Project Recipient List

4.2. 创建项目-New Item

  1. 填写项目名称,选择Freestyle project,下次可以通过下方的Copy from来复制已有项目。

4.3. Git仓库-Source Code Management

  1. 选择Git,填好git仓库地址、凭证,并指定分支名通配符,支持同时处理多个分支。
    • Credentials中创建新凭证,对应gitlab的账户名和密码。

4.4. 每日构建-Build Triggers

  1. 选择Build periodicallySchedule中填入0 0 * * *,表示每天的0点触发一次。

4.5. 构建-Build

点击3次Add build step,选择Execute Windows batch command

  1. 编译:第一个Command中填写build.bat rebuild release x64,利用MSBuild.exe执行编译。
  2. 打包:第二个Command中填写python make_setup.py -s setup_x64.nsi -r "xxx(.*).exe" -f xxx/version.h,利用nsis制作安装包。
  3. 成功后FTP上传并邮件:第三个Command中填写python release.py -i "xxx_(.*).exe" -f ftp://foo:bar@192.168.1.2/products/xxx -d 15 -e smtp://release.robot@scm.im:release@192.168.1.3:465 -r "foobar@xxx.yyy",FTP上传到192.168.1.2,并群发邮件。

4.6. 发布-Post-build Actions

  1. 点击Add post-build action->Editable Email Notification Templates,点击Add Template选择4.1. 配置邮件模板中填写的模板名称。

最后点击Save保存。

4.7. CD-自动化部署

下面简单列出示例步骤:

4.7.1. 定义清楚哪些电脑,分别要被自动化部署哪些软件

  1. 如果是Linux/Mac系列,SSH服务自动安装了。
  2. Windows系列,可安装freeSSHd.exe,在Settings->Server status中启动SSH Server。Users中加入管理员帐户和密码,选择NT authentication,类似于ROOT狂奔,开始时可先这样避免权限问题。

4.7.2. Jenkins中绑定SSH服务电脑

  1. Jenksin->Manage Plugins,安装插件Publish Over SSH
  2. Jenkins->Configure System->Publish over SSH,点击新增,加入所有需要被部署的电脑SSH信息,比如有一台的名称为:Test-19

4.7.3. CD配置

  1. Jenkins中新建一个Item项目
  2. General中勾选This project is parameterized,加入Choice Parameter,命该填为AutoDeploy,选项至少2个填为Empty表示不用部署,Test-19表示和上述4.7.2中配置相同的SSH电脑名称。
  3. 其他源码管理、构建触发器、构建环境、构建等步骤,正常填写。
  4. 点击增加构建后操作步骤->Send build artifacts over SSH,示例:http://192.168.1.8:8080/view/Test/job/TestBusinessService/configure
  5. 想要构建时,进入该Item首页,点击Build with Parameters,不想部署选择Empty,想部署选择Test-19,然后点击开始构建

4.7.4. 注意事项

  1. 该代码仓库的安装包,支持命令行参数形式的静默安装,使得无需人工干预。
  2. 该软件支持杀死某个指定名称的父进程后,全部相关进程均被杀死。同时该功能目前的Windows版采用taskkill,极端情况下会有杀不死的异常。Linux版也不完善。
  3. 该软件如果被集群管理软件监控,需要首先从集群中关闭对应作业。

4.8. 不足

Jenkins插件非常丰富,上面实践只在编译、打包失败时才用到了Email Extension Template Plugin邮件插件,大多数都是自写的Python脚本来完成编译、打包、发布工作的主要原因如下:

  1. 编译动作要求在开发者本机也能完成,所以必须本机就能一键编译而不依赖插件,一旦有了本机一键编译功能,放到Jenkins也就不用MSBuild插件了。
  2. 上传到FTP后时间长了浪费存储空间,需要按某些规则清理,这种规则默认FTP插件是不支持的。
  3. 打包成功时最好能读取该项目Changelog.md中最新记录,附在邮件正文发出来,起码需要自定义正文内容,所以写了一个release.py,但还未加入读取版本变更功能。
  4. 时间仓促,要一个能用的工作流,而不是最舒服的Jenkins姿势。

所以编译使用build.bat,打包使用make_setup.py,FTP上传和邮件通知使用release.py

5. 思考

  1. 现在的实践很简单,后续应加入自动部署到测试环境、并进行自动化测试的流程。
  2. 除开编码之外的很多活动,很容易短期被高估,长期被低估,比如CI、CD,比如自动化测试,评审等,要正确认清CI、CD能做什么,不能做什么,不要含糊行动和目标。
  3. 改善活动永无止境,能给组织带来提升的实践,就是好实践,不限形式,手段。

6. 参考

  1. 《持续交付》,难得一本好书,字字珠玑。
  2. how-to-control-parametrized-publishing-in-jenkins-using-publish-over-ssh-plugin