《DevOps实践:驭DevOps之力强化技术栈并优化IT运行》

DevOps实践:驭DevOps之力强化技术栈并优化IT运行

主旨

这本书并非坐而论道,而是介绍了DevOps全流程中的许多实践,以及相应工具的运用。虽然随着时代的推移,工具将来可能会过时,但是这些实践的应用和相应的方法是不会过时的,所以对于其中各种实践必要性和相关方法的讲解,是特别值得注意的。作者认为一切皆代码,所以各个章节是围绕代码的生命周期展开的,提到了这些环节的实践:

  • 管理代码
  • 构建代码
  • 测试代码
  • 部署代码
  • 监控代码

DevOps和持续交付简介

DevOps的由来

  • Patrick Debois对于IT行业中开发和运维的相对对立感到十分不爽
  • Patrick于是在2009年在比利时根特市组织了第一场DevOps Days大会,大会延续至今,DevOps Days也被简称为DevOps
  • 敏捷宣言里说“个体和互动高于流程和工具”,DevOps想要强调的就是个体和互动,有助于拆除企业里的部门墙
  • DevOps的另一个核心目标是实现自动化和持续交付。将重复乏味的工作自动化掉,才能把人解放出来互相交流或者做更有价值的事情

DevOps的转变必须要快,多快才算快?

一种观点是,如果存在不必要的等待而导致注意力不集中,就说明你的流程或工具有问题。

一切皆代码

对于DevOps而言,几乎一切都可以用代码来表示

  • 开发者的源代码
  • 运维人员用于描述基础设施的代码或脚本
  • 质量保证人员编写的自动化测试
  • 产品文档
  • 甚至硬件也可以用软件的形式来表达

管理代码

源代码管理(SCM)系统里最主要流行的就是Git。

分支策略实际上是一个约定,包括一系列规则,描述何时创建分支、如何命名、分支如何使用等。

Git flow有点复杂,Github flow更为简单。

当生产环境的部署节奏比开发团队的交付节奏慢时,会出现需要在已发布的代码里修复缺陷,但主干上海包含不想发布的新功能的问题。有两种处理办法:

  • 创建一个缺陷修复分支并将其部署到生产环境。虽然简单一些,但可能需要多一份测试资源来测试补丁分支。
  • 功能开关。发布包括缺陷修复在内的最新开发版,但将不想发布的新功能暂时关闭。不占用多一份测试资源,但实现起来需要一些额外准备工作。

可以两手准备,在特定环境下使用特定方法。

构建代码

为什么要让构建足够快?

如果构建足够快,开发者就更愿意频繁提交代码,集成问题就可以及早得到暴露。

构建从机(即slave机器)的作用:

  • 增加并行构建效率
  • 用于在不同操作系统下构建软件

测试代码

为什么实践中自动化测试难以落地?

有很多因素,但非技术方面的重要原因之一是:

非DevOps的企业中,如果有人不得不处理你的烂代码,那么对于开发人员来说写点烂代码根本无关紧要

换句话说,开发不用为自己的代码是否能在生产环境上运行负责到底,也就不会关注如何做好自动化测试。

要解决这个问题没有灵丹妙药,必须调动人的积极性,制定实际的目标,一步一步来。

单元测试

  • 如果测试代码前需要复杂的设置和运行时依赖,那么就不再是单元测试了
  • 单元测试必须又快又容易写,才能被开发人员所使用

测试覆盖率

  • 100%的代码覆盖率并不是终极目标,实际过程中要权衡成本和收益比
  • 单元测试要去测试那些真正有价值的逻辑

自动化验收测试

  • Cucumber对于DevOps的意义之一在于尝试将不同的角色结合在一起,以对话的形式去定义功能验收测试,让没有任何编程技巧的人也可以去完成
  • 尽管是用自由文本写成的,但是仍然需要规范化和简化

醒醒吧少年,只用Cucumber不能帮助你BDD

引言

在Ruby社区中,测试和BDD一直是被热议的话题,不管是单元测试、集成测试还是功能测试,你总能找到能帮助你的工具,Cucumber就是被广泛使用的工具之一。许多团队选择Cucumber的原因是“团队要BDD”,也就是行为驱动开发(Behavior Driven Development),难道用了Cucumber之后团队就真的BDD了么?

事情当然没这么简单了,BDD作为一种软件开发方法论,一定要理解其含义并且遵循特定的流程,工具只不过是起辅助作用而已。会切菜的不一定都是厨子,会写代码的不一定都是程序员。Cucumber的作者Aslak也在博客中提到

在BDD出现的9年后,依然有不少团队在使用BDD时出现问题……BDD依然经常被人误解成单纯的测试,或者是一个可以被下载的工具。

同时,Aslak也吐槽了Cucumber目前的处境

就在最近,Cucumber已经被下载了超过500万次,我很高兴它如此受欢迎,同时也为它被广泛的误用而感到失望……Cucumber有时依然被错误的当成自动化测试工具,而不是我当时创建的东西。

那么问题来了,怎样在日常项目中使用Cucumber呢?真的能在日常项目中进行BDD开发么?要回答这个问题,我们需要重新认识一下BDD。

BDD的提出

2003年,开发人员Dan North偶然间发现把测试的标题经过简单的文字处理可以更好表达代码蕴含的业务逻辑,比如下面这段代码,

public class CustomerLookupTest extends TestCase {
    testFindsCustomerById() {
        ...
    }
    testFailsForDuplicateCustomers() {
        ...
    }
}

当我们把测试方法中的test去掉,给单词加上空格,然后把他们组合在一起时,就会出现:

CustomerLookup
 - finds customer by id
 - fails for duplicate customers
 - ...

在Dan看来,这无疑是对CustomerLookup类的描述,并且是用测试内容来描述代码中类的行为。Dan发现他似乎找到了一种方式,可以在TDD的基础上,通过测试来表达代码的行为。在尝到甜头后,Dan写了JBehave,用一个更关注代码行为的工具来代替JUnit进行软件开发。经过一番折腾后,Dan觉得只描述类行为不过瘾,便开始把关注点从类扩展到整个软件,他和当时项目组的业务人员一起把需求转化成Given/When/Then的三段式,然后用JBehave写成测试来描述软件的某种行为。当测试完成后,开发人员才开始编码,一旦测试通过,那软件就完成了测试中描述的某种行为。在他看来,他把TDD升级了,因为他不再只关注于局部类的方法,而开始关注整个软件的行为。

通过这种方式,Dan成功的把需求转换成了软件的功能测试,先写功能测试再驱动出产品代码,保证软件行为正确性。其次,Dan强调在测试中要尽可能的使用业务词汇,保证团队成员对业务理解一致。于是,BDD就此诞生

bdd-definition

BDD不只是自动化测试

在上面的故事中,“测试”这个词出现了很多次,你是不是已经认为BDD就是用功能测试驱动产品代码的开发流程呢?其实不然,功能测试只是一个结果而已,更重要的是和业务人员一起分析需求,沟通交流来产生测试的过程。用测试驱动出来的代码可以保证是正确的,但如何保证测试是正确的呢?答案就是人,通过业务,开发和测试一起参与生成的测试文档,不仅能保证软件功能上是正确的,还能保证团队成员对业务理解是一致的。在测试文档中,也应该尽量保证使用自然语言和业务词汇,减少非技术人员的学习成本。

在多年之后,Dan也终于给出了他对BDD的定义

BDD是第二代的、由外及内的、基于拉(Pull)的、多方利益相关者的(Stakeholder)、多种可扩展的、高自动化的敏捷方法。它描述了一个交互循环,可以具有带有良好定义的输出(即工作中交付的结果):已测试过的软件。

Cucumber的另一位作者Matt Wynne也给出了自己的定义

BDD的实践者们通过沟通交流,具体的示例和自动化测试帮助他们更好地探索、发现、定义并驱动出人们真正想用的软件。

从上述定义我们可以看出,BDD更强调流程和一系列实践,自动化测试只是其中一部分而已。

Cucumber到底怎么用

理解了BDD的精髓后,我们就不难找出正确的使用Cucumber的方式了。根据Cucumber的定义,它的核心就是Specification,其实就是文档化的需求。Specification是通过Requrement Workshop生成的,在Workshop中,业务、开发和测试一起分析需求,把需求用自然语言写成文档,然后再转换成Given/When/Then的Specification文件,这样便完成了BDD中最重要的一步--定义软件正确的行为。接着开发人员开始编码,完成相应需求,保证Specification文件运行通过,整个流程结束。

简单来说,Cucumber其实不是一个自动化测试工具,而是一个促进团队沟通合作的工具。但由于Cucumber无法确保上述流程真正的发生,有很多团队简化或者跳过了Workshop,直接开始写Specification文件,没有沟通就很难保证理解一致,Bug也许就在那时潜伏了下来。这样大家也就不难理解作者吐槽的“Cucumber被广泛的误用”,其实Cucumber只是一个沟通工具,它只是刚巧可以运行测试而已。

bdd-flow

理想很丰满,现实很骨感

任何工具和实践都有优缺点,Cucumber也不例外。团队在开始尝试新的实践或者工具时,多多少少都会碰到一些问题,下面我们就来看看一些使用Cucumber的问题。

没有业务人员参与的Specification

要么业务人员没时间写Specification,交给其他人写,写完之后业务人员也没时间去审核。在这种情况下,很难保证Specification的业务正确性,一旦Specification出现问题,团队可能出现理解不一致、甚至做错需求的现象。反过来看,Specification文件由自然语言而不是代码组成,也能反映出对非技术人员参与的重视程度。然而现实情况很难保证业务、测试、开发有充足的时间进行Specification的讨论和编写,这也是导致业务人员逐渐脱离Specification的主要原因。

Specification关注实现细节而不是业务逻辑

Cucumber使用自然语言描述业务需求,然而不少团队都陷入到了实现细节中。比如

Scenario: Detect agent type based on contract number

Given I am on the 'Find me' page
And I have entered a contract number
When I click 'Continue' button
And a contact number match is found
Then the "Back" button will be displayed

上面的描述满篇是点击了哪个按钮,输入了什么内容,看完之后反而让人有点困惑,用户到底为什么要做这些,做了之后有什么价值。这样的Specification既不能满足团队成员对业务需求的了解,也会由于界面的细微改动运行失败。

Step的嵌套调用

Specification文件由Step组成,在Step中我们可以通过Ruby进行自动化的页面操作。有时我们会发现某些Specification会重复进行一系列的操作,这时我们就可以把重复的Step进行组合,创建出新的Step。比如这样

Given there is student Harry
And there is professor Snape
And student Harry joins class of professor Snape

# use 1 new step instead of 3
Given student Harry in class of professor Snape

那么这个新的Step该怎么实现呢?Cucumber支持在Step中调用Step,比如这样

Given /^student (.*) in class of professor (.*)/ do |student, professor|
    step "there is student #{student}"
    step "there is professor #{professor}"
    step "student #{student} joins class of professor #{professor}"
end

乍一看好像没什么问题,其实不然。Step使用正则表达式进行匹配,问题恰恰出在正则上。

首先,正则灵活性很大,你确定上面例子中step “there is student #{student}”一定会调用到你想要调用的Step么?你无法确定在运行时,是否会出现另一个Step “there is student come from China”来截胡。

其次,正则逆推难度很大,也就是说当你看到“^(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]).(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0).(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0).(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[0-9])$”时,你很难看出这是在匹配IP地址。所以当我们需要修改step时,很难确定有多少个step在依赖它,这也加大了维护成本。

最后,嵌套次数过多的Step也会导致代码复杂,难以理解。

Specification Report可读性不高

Specification除了是自动化测试的描述文件之外,更重要的是它是软件的“活文档”。有时我们需要通过“活文档”进行知识传递。Cucumber虽然提供生成Report的功能,但效果未免有些差强人意。比如下面

bad-report

满篇绿色的Step,再加上Given/When/Then来捣乱,这样的Report只是运行结果而已,可读性很差,很难当成软件需求文档。究其原因,主要因为Cucumber Report的表现力差。

首先,它只支持纯文本,在这个“一图胜千言,无图无真相”的时代很难只通过文字来描述复杂业务,如果能在文档中加上图片,甚至一段视频,都会帮助我们更容易的理解复杂业务。比如像下面这样的。

nice-report

其次,Cucumber Report关注的更多是Step,而不是软件需求。当我们想到软件文档或者手册时,我们脑海中想到的更多是像教科书一样的文档,内容之间有层级和关联关系,每个功能有重点和概要内容,更偏向自然语言,而不是简单的把Specification堆在一起,满篇的Given/When/Then。在现实情况下,这样的Cucumber Report也难免没有人愿意阅读了。

改进措施

遇到上面的问题不要怕,我们只要理解问题本质,找到对策解决它,依然可以帮助我们更好地完成任务。下面我们就来尝试解决一下上面提到的三个问题。

让业务人员写/审查Specification

对于上面的关注细节的例子,如果我们换一个思路,不去考虑UI之类的东西,就会得出更精炼的Specification。比如下面

Scenario: Customer has a tied agent policy 
so last name is required

Given I have a "TiedAgent" policy
When I submit my policy number
Then I should be asked for my last name

这样的Specification不再关注按钮,而关注具体的业务需求,这样就把细节的UI操作推向了Cucumber Step的实现中。这样可以更直接的展现需求,避免细节内容的干扰。如果情况允许,我更支持让业务人员写Specification,或者最起码也要审查Specification文件。通常业务人员是团队中最不懂技术的,这反而是他们的优势,可以把Specification变得更加面向需求,更加通俗易懂。

Step实现代码的重用

我们可以通过重构Step实现代码来进行有效的重用,比如下面

Given /^there is student (.*)/ do |student|
    ModelFactory.create_user(student)
end

Given /^there is professor (.*)/ do |professor|
    ModelFactory.create_user(professor)
end

Given /^student (.*) joins class of professor (.*)/ do |student, professor|
    ModelFactory.join_class(student, professor)
end

通过重构,我们抽象出ModelFactory进行相关数据准备,然后就可以重用ModelFactory实现新的Step

Given /^student (.*) in class of professor (.*)/ do |student, professor|
    ModelFactory.create_user(student, professor)
    ModelFactory.join_class(student, professor)
end

重用代码而不是重用Step,这样不仅可以让Step的实现代码更加简洁,同时也避免了Step的嵌套调用。

扩展Cucumber生成高质量的文档

Cucumber虽然自带不少种格式的Report,但都不能称其为真正的文档。不过我们可以通过扩展Cucumber来生成高质量的文档。

首先,我们可以使用Capybara在对某个正在执行的Step进行截图。Capybara提供的截图功能可以保留当前Step的运行状态,通过图片更容易理解当时的上下文,这些图片拼在一起,其实就是一个完整的用户操作流程。

其次,我们可以通过给Step添加详细描述来解决Report不给力的问题。我们可以给每一个Specification文件创建一个相对应的描述文件,描述文件由两部分组成,一部分是Step的标题,另一部分是详细描述Step的内容。只要通过文本匹配就可以找到某个Step的详细描述,再加上之前对Step的截图,拼在一起就可以生成一个高质量的文档了。

举个例子,如果我们有这样一个Specification文件

Scenario: Customer has a tied agent policy 
so last name is required

Given I have a "TiedAgent" policy
When I submit my policy number
Then I should be asked for my last name

创建一个对应的描述文件,文件类型是Markdown。

=====================
Given I have a "TiedAgent" policy
=====================
##Detail Information**
**In this step, you are assigned a "TieAgent" policy.**
![screenshot-1](./i-have-a-tied-agent-policy.png)
You can click [here](http://example.com) for more information
=====================
....

在上面的文件中,第一部分是Step标题,用来匹配Specification文件中的Step,第二部分是Markdown类型的片段,里面有图片、超链接等富文本元素,可以更好地帮助我们理解业务。

最后,通过Cucumber中提供的AfterStep Hook完成文档的生成。比如这样

AfterStep('@active-doc') do |scenario|
    @step ||= 0
    @doc ||= ActiveDocument.new
    @doc.generate(scenario, scenario.steps[@step].name)
    @step += 1
end

代码中的ActiveDocument是自己实现的,它把丰富的HTML内容和截图整合在一起,然后把Specification中所有的Step拼接在一起,就生成了一个Specification的文档。这样的文档相比之前提到的Cucumber Report具有更高的可读性,同时也具有更强的灵活性,因为文档是通过HTML展现的,我们可以添加更多的内容,比如Specification文档之间的跳转链接,或者提前录制的一段视频放入文档中,甚至可以加上第三方css和js库让文档变得更加引人入胜。

active-doc

原来生活可以更美的

随着BDD的发展,越来越多的工具进入了我们的视野。我们应该认清团队的需求,结合团队的特点选择合适工具,不要盲目的随大流。下面我来列举一些具有代表性的工具,推荐给不同类型的团队。

Cucumber

简单来说,Cucumber实际上是一款有一定文档性、可以帮助团队沟通合作的、提供自动化测试功能的工具。特点是上手简单、社区活跃、文档表现力不足。所以如果团队刚开始尝试BDD,更看重自动化测试方面,而对需求文档化要求不高,Cucumber是一个不错的选择。同时Cucumber目前支持Ruby, C#, JVM, JS和C++,众多平台也是一个加分项。

Concordion

与Cucumber相比,Concordion提供了更好的文档支持。Concordion的Specification是HTML格式的,我们再也不用生搬硬套的使用Given/When/Then进行功能描述了。在HTML文件中,我们可以更加自由的描述业务需求,同时可以增加好看的样式,添加更友好的交互,放入更多的视频和图片等等。

总而言之一句话,HTML比纯文本更加灵活强大,适合阅读。同时我们也要清楚HTML的学习和维护成本相比纯文本更加昂贵,非技术的人可能很难单独完成。和技术人员结对完成,或者在技术人员完成后进行审查也是一个不错的选择。但由于Concordion目前只对C#和JAVA支持较好,所以如果团队刚好用到C#和Java,并且非常看重文档化需求,那么Concordion要比Cucumber更加适合你们。在下面的例子中,我们使用Concordion生成了“教学评估”相关的需求文档,并且使用了shower.js增强了用户交互,在保证软件功能的同时,带来了更好的阅读体验。

交互性更好地需求文档,内容组织合理,阅读体验好。

concordion-index

其中一个需求的详细描述,同时也是自动化测试。

concordion-one-example

Gauge

Gauge也在文档方面进行了改善,Gauge的Specification文件由Markdown组成,相对纯文本有了一定程度的提升,但还是不如Concordion灵活。Gauge使用Go编写,天然支持并发运行,相比之下性能要更加有优势。同时Gauge支持多语言实现,目前支持Java、C#和Ruby,相比Cucumber在跨平台式需要整个切换工具,Gauge更容易做跨平台。虽然目前Gauge处在开发阶段,但依然值得关注。

gauge

总结一下

  1. BDD不是工具,而是一套流程和一系列实践。它需要团队成员的通力合作,可以帮助整个团队更好的理解业务,理解软件。
  2. Cucumber作为支持BDD的一种工具,不单单是自动化测试工具。在解决了Cucumber的一些问题后,团队可以更加有效的使用。
  3. Cucumber、Concordion和Guage各有不同,选择一款适合团队自身需要的工具,也能保证团队顺利运作,少走弯路。

好了少年,我只能帮你到这里了,接下来BDD之路就看你自己的了。

点击这里浏览演讲视频。

 
   

 

自动化GUI测试

如果需要在测试环境安装浏览器,可以在桌面环境中包装一个浏览器,或者使用Selenium Grid为测试生成多个浏览器实例

JavaScript测试

  • Karma - JavaScript语言单元测试
  • Jasmine - 类似于Cucumber的行为测试框架
  • Protractor - AngularJS的测试框架

测试后端集成点

  • 即对于SOAP和REST endpoint的功能性自动化测试
  • 通常性价比较高
  • 后端endpoint相对较稳定,测试的维护成本比GUI测试要小一些
  • soapUI是一个可以用于编写和执行这类测试的工具

部署代码

配置基础操作系统

  • 如果是跨平台的通用技术栈(Java、Python或Ruby等),对操作系统的依赖不那么明显
  • 其它情况下则显然会依赖操作系统,特别是在电信行业里,使用底层混合的软硬件进行集成时

部署工具

  • Salt
  • Ansible
  • Puppet
  • Chef
  • PalletOps

各有异同和适用场景,具体如何使用还得看实际情况。

在客户端节点运行代码的方式上:

  • Puppet主要使用拉模式
  • Ansible使用推模式,通过SSH来推送
  • Salt使用推模式,通过消息队列来推送

用Docker来做部署

  • Docker最吸引人之处是可用来创建可重用的容器,应用在开发、测试与生产环境中
  • 基于Docker构建大型服务器集群的解决方案:
    • Docker Swarm
    • Kubernetes

监控代码

Nagios

Nagios有两种不同类型的检查方式:主动检查与被动检查。

其他监控与统计工具

  • Munin
  • Ganglia
  • Graphite

日志处理

  • Log4j
  • ELK

powered by Git

猜你喜欢

转载自www.cnblogs.com/cx2016/p/12078733.html