写点什么

构建自动化驶向何方?

The Future of CI Best Practices

2009 年 12 月 01 日

时下大多数开发人员对持续集成(Continuous Integration,CI)的基本原理已经很熟悉,但是他们中只有一小部分人能够从优化 CI 设置中彻底受益。毫无疑问,一个有效的持续集成环境可以帮 助你的团队节省时间、金钱甚至减少存有的顾虑。通过持续集成,我们可以更早地发现 bug,更轻松地找出导致其发生的原因并最终有效地解决。持续集成可以更 好地管理源代码,更有效地使用自动化分析工具,鼓励编写好的测试,跟踪进度以及突破开发人员活动中的瓶颈。持续集成可以让部署过程更简单且发布过程更加平 稳和可靠。借助于 CI,管理者可以得到更多的图表,了解到更多的信息,而开发者可以专注于开发而不用过多交流,也就更加高兴。换句话说,如果不使用持续集 成,那么就好比用记事本编写代码来开发软件,虽然可行,但是太低效了。

在本文中,来自 Wakaleo 咨询公司的首席咨询师 John Smart,将给我们介绍如何把持续集成由一个名义上的定时作业,变为开发活动中一个有效、而且能提高生产力的“中枢”。

持续沟通流

一个良好的敏捷开发环境本质特征就是尽可能的最大化小组成员间的信息流。每一个开发人员都需要尽快地知道何时构建过程失败,或者何处改动可能会对应用程序 质量造成不良影响。如果构建过程失败了,那么首要工作就是要知道做了什么改动,以及为什么做这些改动:所有的这些信息都应当在开发人员敲击几下键盘后就能 通过最直接的方式获得。

即便是最基本的 CI 设置,它也会在构建失败后给开发人员发送电子邮件。其实我们只需要下一点点功夫,就能比现在做得更好。一个良好设置的持续集成服务器应 该像团队沟通中的中枢一样工作。除了简单地发送电子邮件进行提醒外,其实还有很多事情可以做,例如现代 CI 工具中的许多特性——这些特性可以更平稳、更有 效地向开发人员反映代码改动和构建失败的情况。

电子邮件大概是 CI 提醒方式中最古老也是最常使用的方式了,只是因为它非常普遍而且易于建立。尽管如此,事实上电子邮件是一个相对低效的提醒机制。当构建 过程失败时,你希望被尽快地通知到,越快越好。如果因为电子邮箱客户端的更新而等上 15 分钟的话,那么可能就浪费掉了开发时间。除此之外,电子邮件通常很 容易让开发人员分心。在企业里,每天的非紧急的或是不重要的电子邮件都会有很多。事实上许多优秀的开发人员都禁用电子邮件提示,只是在一天中定时地去检查 几次邮件。

即时消息相对而言则是一个更加合适的提醒方式,有好几个原因。最重要的原因是,即时消息是(几乎是)即时的。你不再需要因为电子邮件客户端在服务器中检查 新邮件而等上 10 分钟,使用即时消息,你能被立即通知到。它比电子邮件更加方便,而且阅读起来也更快。不仅如此,来自构建服务器的 IM 消息也会得到相关的 重视,而不会淹没在大量无用的其它信息中。

当事件发生后,使用快速即时信息和花上 10 到 15 分钟使用电子邮件也许只相差几分钟,但千万不要低估这几分钟的重要性。那几分钟足够让一个开发人员丢失重点,并转移到其它的一些事情上,结果就导致了开发人员很难回到上下文中去修复问题。

使用即时消息的另外一个好处在于它可以扩展到桌面以外的应用。像 BeeJive 这样的应用程序可以将即时消息用到移动设备诸如 Blackberries(黑莓)和 iPhones 上。这样的话,即使开发人员不在办公室,也能收到至关重要的构建失败提醒。

即时消息提醒比起电子邮件,可以更多的与 CI 服务器进行交互。作为一个沟通的中介,即时消息要比电子邮件更加的自然,它还可以进行比 Subversion 提交信息还要多的交互。通过利用即时消息,如今的一些 CI 工具不仅仅可以发送提醒,还可以让开发人员更轻松地与构建服务器以及其他组员进行交互与交流。

当然,即时消息不是仅有的另一种可用提醒方式。如果想让大家注意到构建信息的话,最好采用一种能够融入相应企业文化的提醒机制。例如,一些公司使用社交网 络的工具例如 Twitter,作为一个有效的内部交流渠道。对于这些组织而言,使用 Twitter 也可以算是一种有效的构建提示方法了。

保持构建过程高效率

持续集成环境的一个首要目标是要保证开发过程的顺利进行,并且避免由集成问题带来的障碍和开发延期。当集成问题突然出现时,开发人员应当有义务尽快的去提 交一些代码来解决这个问题,以避免影响到其他开发人员。如果没有持续集成环境,开发人员通常会在集成问题上卡住而找不到一个解决方案(“嘿,它在我的机器 上能运行啊!”)。

想让开发过程一直保持最好的状态,那么团队成员(尤其是团队领头人,流程专家等)需要能够监测构建过程,这样他们可以识别定位出日常生活里降低开发人员效率的问题。其中最好的方法就是去了解如何最好地使用构建感测。

构建感测是一个用在持续集成周期中,用以描述构建长期统计数据的术语。Bamboo,一个不错的 CI 工具,它就提供了很高级的构建特性。构建感测为你提供 了关于构建要花多久,是否成功,以及解决这个构建失败问题要花多久等等之类的信息。这些数据很重要,因为它可以让你知道构建过程长期以来是什么样的。正是 这种数据,而不是单个构建的结果,可以帮助你最终优化构建过程。

一定数量和频率的构建失败一直是一个不错的着手点。单独的构建失败通常不需要担心——你需要做的是去检查一系列反复出现的构建失败。当一个构建反复失败 时,也许是因为开发人员正纠结于一段非常麻烦的代码,或者是团队人员没注意到。虽然这两问题的原因不一样,并且解决的方法也不一样,但是它们都应当被进一 步地调查。

通过深入分析测试结果,你可以了解到更多关于为什么构建会失败的信息。许多现代的 CI 工具都可以让你研究长期的测试行为,例如,把一直经常失败的、或者要 花很长时间进行解决的测试跟其他测试隔离开来。如果同样的测试不断失败的话,这就意味着有什么地方过于复杂或者代码过于脆弱,这种情况下可以做一些重构。 除此之外,这些工具还能让你知道测试运行了多久,这是另一类问题发生的源头。

事实上,构建失败不是唯一减慢开发进程的原因。缓慢的构建过程是另一个罪魁祸首。

导致构建过程缓慢的最常见的原因是结构混乱的测试套件。经验丰富的 Java 开发人员常常会将单元测试与集成测试分开来。虽然两者区别大差不差,但是一般来 说,单元测试是相对孤立的、较小的、快速的、轻量级的测试类。它们主要是用来确保类能够独立地正常工作。而另一方面,集成测试则较为缓慢,需要更长的运行 时间,并且可能会访问外部的资源如测试数据库,或者加载复杂的配置文件。它们被用来测试应用程序中的不同模块和类如何共同工作。性能测试和集成测试差不 多,但是两者的目标有所不同。

轻量级且运行很快的单元测试可以被迅速的执行完,并且在测试失败时能够给出很快的反馈。而另一方面,如果缓慢运行的集成测试与单元测试混在一起,那么单元 测试将要花上更多的时间来执行,结果开发人员也要等更久才会得到关于单元测试失败的消息。解决之道就是为单元测试和集成 / 性能测试创建单独的构建计划。使 用这种方式后,如果单元测试构建计划失败了,由于其执行速度很快,开发人员不再需要等待很长时间才被通知到构建失败。如果单元测试成功后,集成测试和性能测试计划才会开始。

另外,还有一个补充方法——可以使用分布式构建。例如,如果你的 web 功能测试由于要在几个不同的浏览器上运行并因此消耗很长时间,那么可以为每一个浏览器设立一个构建作业,以让它们(可能在不同的机器上)并行运行。

另一个问题来自过于缓慢和效率低下的测试用例。有许多方法可以用来监测这些缓慢的测试。提高测试上的运行时间意味着有些测试会由于所需时间太长而无法运行。这也许是因为它们被设计的很糟糕,也许是因为性能问题(需要进步一深究),抑或是集成测试伪装成了单元测试。

密切关注代码质量

持续集成服务器不应当仅仅是一个自动构建的机器。它应当成为你团队中沟通的中枢,尤其在代码质量上。时刻关注编码规范和一些度量值如代码覆盖率以及代码复杂度,它们可以让应用程序更加可靠且更易维护。

有很多优秀的工具可以帮助维护应用程序中良好规范的代码。静态分析工具如 Checkstyle,PMD 和 FindBugs,它们根据代码标准、最佳实践或 者潜在的 bug 来对代码进行分析。你所有使用的工具如何配置要依赖于你想要达到的目标。例如,Checkstyle 更多关注于编码规范和最佳实践,而 Findbugs 则更倾向于找出错误的,破碎的或者危险的代码。所有的这些工具可以很轻松地集成到自动化构建过程中,并且可以和 Ant、Maven 一起很好地工作。

覆盖测试率是代码质量的另一个方面。它用来衡量测试执行时所访问的代码行数。Java 开发人员中对覆盖测试率统计的相对值仍然有着分歧。事实上,虽然它可 以告诉你应用程序中的哪些行被执行,但是没法知道那些测试是写得很彻底、写得很好,抑或仅仅是简单的走马观花。总而言之,测试覆盖率不能保证你的测试质量 很高——只有人工进行代码检查时才能确保如此。然而覆盖测试率的度量值可以很好地展示出哪些代码没有被测试过。在 Java 世界里,用得最多的代码覆盖率测试工具当属 Clover 和 Cobertura, 前者是一个非常强大的商业代码覆盖率测试工具,而后者是一个更加轻量级的开源工具。它们都可以很容易地集成到基于 Ant 和 Maven 的构建脚本中。

编码规范也可以作为非常有效的培训支持和指导活动,尤其对于那些经验不足的开发人员。CI 工具可以提供这些数据如何随着时间的推移而变化的高层次图片,还 可以关注开发人员在应用他们学到的技巧时做得有多好。例如,如果一个类只有很低的代码覆盖率,甚至没有,就意味着某个新的开发人员在消化吸收小组培训的测 试驱动开发和测试实践上出现了问题。这种方法还可以通过代码审查和定期的代码质量会议(讨论任何新问题或者动向的会议)完成。

一旦构建结束——自动化部署过程

构建应用程序只是开发生命周期中的一部分。一旦代码编译测试后,需要进行其他的活动,例如部署到阶段性(staging)环境、冒烟测试、功能测试和性能测试、准备发布说明和提醒 QA 人员最新的发布。

将最新的构建结果自动部署到集成服务器上是一件相对简单的事情。而将其部署到阶段性环境或者生产环境下,则需要涉及一些与常规构建作业不一样的工作。一般而言你需要一个更加严格,更加正规且有更多可跟踪性和问责的过程。它通常涉及到的任务如下:

  • 为阶段发布标记源代码
  • 编译测试应用程序
  • 发布构建产品
  • 将应用程序部署到阶段性环境中
  • 运行数据库更新脚本或者其他特定环境脚本
  • 运行冒烟,功能和性能测试
  • 准备并发布产品说明
  • 提醒关于最新阶段发布的相关利益人

这通常是一个手工任务,但是其中的大部分工作没有理由不能自动化。事实上,开发生命周期中的自动化包装,部署和发布具有很稳固的商业意义。一方面自动化能 够得到更加可靠的构建:计算机不会忘记部署过程中的某一步,也不会在发生测试失败后继续进行。它还能够节约开发人员的时间:阶段性发布由之前几小时的 shell 脚本编程变为了只要点击一下按钮。它比以前的速度要更快,并且可以在没有人的情况下完成工作(例如,通宵或者午休时间)。

像 Maven 2 这样的工具也能够帮助自动化一些步骤。Maven Release 插件使得 Maven 的用户能够自动化处理一些如“更新版本号”,“Subversion 中新增标签”,以及“向 Maven 存储库中发布构建 产品”的工作。它可以用来管理阶段构建,并决定在不同的环境部署不同的发布产品。尽管如此,一旦产品构建结束并且可以部署到阶段性环境者生产环境时,这个 过程就会变得更复杂。

千真万确,现实世界中的部署步骤数目经常要比简单的用一个 WAR 文件多。根据应用程序架构和产品平台,你可能需要在阶段性环境者生产环境下的数据库中运行 SQL 更新脚本、用一个专用的工具部署 web 服务、运行自动化的冒烟测试或者做一定量服务端的工作。

CI 可以帮助简化比这些还要复杂的步骤。例如通过分布式构建,你可以设立阶段性环境或生产环境上的构建代理,并在该机器上直接运行相应的任务。几乎所有的 现代 CI 工具都支持相当好的安全模型,目的是为了将应用和产品环境限制给一些特定的人,以及跟踪谁在什么时间运行了什么构建。

这是 CI 的一个相对较新的应用,不同的工具在处理应用程序部署时使用的方法也不一样。有一些,如 Hudson,允许在构建作业中定义多个步骤,只有当前一 个步骤成功后,才能执行后续步骤。其他的像 Cruise 和 Anthill Pro,都尝试将部署生命周期中的如阶段和生产环境直接集成到构建工具中,尽管有时候这样会带来额外的复杂度开销。

还有更多低层次的操作可以和 CI 服务器联合起来使用。一个选择是使用诸如 Ant 或者 Maven 的构建工具。Ant 对于这种类型的脚本特别灵活。另一个流行的选择是古老的 Makefile,或者 Unix 上的 shell 脚本。它们的缺点是操作系统相关的,并且对于那些不熟悉 shell 脚本精髓的 Java 开发人 员来说很难掌握。想要与 Java 更加友好,可以选择动态语言诸如 Groovy 或者 Gant(一个使用 Groovy 而不是 XML 来制作 Ant 脚本的工具)。 Groovy 在提供所有轻量级的动态脚本语言中所有的优点的同时,也保留了对 Java 开发人员的熟悉程度和可读性。

总结

这些只是使用现代持续集成环境完善构建过程和增强团队的几个方法。持续集成环境绝对不仅仅是一个构建计划表,它还可以用来帮助打开队伍内部的沟通渠道、保持构建过程平稳有效的运行、时刻监控代码质量以及自动化发布和部署过程。

查看英文原文: Where To Now with Build Automation?


译者介绍:曹如进,计算机软件与理论专业硕士研究生,主要研究方向包括算法设计与分析,语义网。目前对函数式编程语言,富客户端开发比较感兴趣。InfoQ 中文站内容团队,尤其是架构、SOA 和 Ruby 社区需要您的参与,有意者请邮件至 editors [AT] cn.infoq.com

感谢金明对本文的审校。

给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家加入到 InfoQ 中文站用户讨论组中与我们的编辑和其他读者朋友交流。

2009 年 12 月 01 日 00:103113
用户头像

发布了 125 篇内容, 共 28.6 次阅读, 收获喜欢 2 次。

关注

评论

发布
暂无评论
发现更多内容

推进AI融合 2020 LF AI & DATA DAY(AI开源日)即将召开

Geek_459987

开源技术够用了么?我的 NAS 选型与搭建过程

LeanCloud

开源 NAS

Linux高级编程常用的系统调用函数汇总

哒宰的自我修养

Linux 线程 网络编程 进程 MySQL数据库

网易云音乐基于 Flink + Kafka 的实时数仓建设实践

Apache Flink

flink

刷了LeetCode的链表专题,我发现了一个秘密!

Simon郎

Java 链表 面试数据结构与算法

23张图!万字详解「链表」,从小白到大佬!

王磊

Java 数据结构与算法

环球易购数据平台如何做到既提速又省钱?

苏锐

大数据 hdfs S3 CDH 成本优化

架构师训练营 W03 作业

Geek_f06ede

架构师训练

Linux-技术专题-Linux命令如何进行查看进程

李浩宇/Alex

redis的stream类型命令详解

LLLibra146

redis stream 消息队列

vivo 云服务海量数据存储架构演进与实践

vivo互联网技术

数据库 架构 云服务 数据存储

第一届“多模态自然语言处理研讨会”精彩回顾(免费获取PPT)

京东智联云开发者

人工智能 自然语言处理

深度解读智能推荐系统搭建之路 | 会展云技术揭秘

京东智联云开发者

人工智能 推荐系统

一场关于FLV是否要支持HEVC的争论

wangwei1237

技术文化

接口测试用例编写和测试关注点

测试人生路

接口测试 测试用例

Redis-缓存雪崩,缓存击穿,缓存穿透

topsion

redis

甲方日常 44

句子

工作 随笔杂谈 日常

5G时代的到来对直播的影响

anyRTC开发者

5G 音视频 WebRTC 直播 RTC

如何在面试中解释关键机器学习算法

计算机与AI

学习 数据科学

程序人急速变富指南(一)

陆陆通通

程序员 职业 财富 认知 眼界

JDK8中的新时间API:Duration Period和ChronoUnit介绍

程序那些事

java8 jdk8 新特性 程序那些事 时间API

区块链数字货币交易所开发方案,交易平台搭建app

WX13823153201

CloudQuery V1.2.0 版本发布

CloudQuery社区

数据库 sql 编辑器 工具软件

架构师训练营 W03 总结

Geek_f06ede

架构师训练

面经手册 · 第16篇《码农会锁,ReentrantLock之公平锁讲解和实现》

小傅哥

Java 面试 小傅哥 ReentrantLock 公平锁

给萌新HTML5 入门指南(二)

Geek_Willie

腾讯内容首发:分布式核心原理解析笔记+分布式消息中间件实践笔记PDF版

Java架构追梦

Java 架构 面试 分布式 消息中间件

「排序算法」图解双轴快排

bigsai

排序算法 快速排序 双轴快排

TensorFlow 篇 | TensorFlow 数据输入格式之 TFRecord

Alex

tensorflow keras dataset tfrecord

英特尔独显终于来了!锐炬®Xe MAX为非凡S3x带来设计师级创作体验

intel001

央视呼吁电商双十一少一些套路:应该严打网店套路营销

石头IT视角

InfoQ 极客传媒开发者生态共创计划线上发布会

InfoQ 极客传媒开发者生态共创计划线上发布会

构建自动化驶向何方?-InfoQ