
本文讨论了生产中的各种类型的测试以及每种测试最有用的条件,还介绍了如何组织生产中各种服务的安全测试。
值得注意的是,本文的内容仅适用于那些
服务 ,这些
服务的部署由开发人员控制。 此外,您应该立即警告说,使用此处描述的任何类型的测试都不是一件容易的事,这通常需要对系统的设计,开发和测试进行重大更改。 而且,尽管有文章的标题,我认为生产中的任何类型的测试都不是绝对可靠的。 唯一的意见是,这样的测试可以在将来显着降低风险水平,并且投资成本是合理的。
(注意:由于原始文章是Longrid,为方便读者,它分为两部分)。如果可以分阶段进行,为什么必须在生产中进行测试?
不同人对暂存群集(或暂存环境)的重要性有不同的认识。 对于许多公司而言,分阶段部署和测试产品是其最终发布之前不可或缺的阶段。
许多知名组织将工作阶段视为工作环境的缩影。 在这种情况下,需要确保它们的最大同步。 在这种情况下,通常必须确保有状态系统的不同实例(例如数据库)的运行,并定期将生产环境中的数据与登台进行同步。 唯一的例外是允许您建立用户身份的机密信息(这是符合
GDPR ,
PCI ,
HIPAA和其他法规的要求所
必需的 )。
这种方法的问题(以我的经验)是,不同之处不仅在于使用包含实际生产环境数据的数据库的单独实例。 通常,差异扩展到以下方面:
- 暂存群集的大小(如果可以将其称为“群集”,有时只是一台伪装成群集的服务器);
- 分段通常使用较小的群集这一事实也意味着几乎每个服务的配置设置都会有所不同。 这适用于负载均衡器,数据库和队列的配置,例如,打开文件描述符的数量,打开数据库连接的数量,线程池的大小等。如果配置存储在数据库或键值数据存储中(例如, Zookeeper或Consul),这些辅助系统也必须存在于暂存环境中。
- 无状态服务处理的联机连接数,或代理服务器重用TCP连接的方法(如果完全执行此过程);
- 在分期中缺乏监视。 但是即使进行监视,由于监视的不是工作环境,所以某些信号可能完全不准确。 例如,即使您监视MySQL查询的延迟或响应时间,也很难确定新代码是否包含可以在MySQL中发起全表扫描的查询,因为对测试中使用的小表执行全表扫描要快得多(有时甚至更好)。数据库而不是生产数据库,在该数据库中查询可以具有完全不同的性能配置文件。
尽管可以合理地假设上述所有差异并不是反对使用分期的严肃理由,但应避免使用反模式。 同时,要想正确地做每件事,往往需要工程师付出巨大的人工成本,以确保环境的一致性。 生产在不断变化,并受各种因素影响,因此尝试实现这种匹配就像无处可去。
此外,即使分阶段的条件与工作环境尽可能相似,也可以根据实际生产信息使用其他类型的测试来更好地进行测试。 浸泡测试就是一个很好的例子,在浸泡测试中,将在多任务和负载的实际水平下长时间测试服务的可靠性和稳定性。 它用于检测内存泄漏,确定GC暂停的持续时间,处理器负载级别以及其他指示器在特定时间段内的状态。
以上所有内容都没有表明分阶段是
完全没有用的(在测试服务时阅读有关数据影子重复的部分后,这一点将变得很明显)。 这仅表明,在很多情况下,他们往往更依赖于登台,而在许多组织中,它仍然是在产品完全发布之前执行的
唯一测试类型。
生产测试的艺术
历史上曾经发生过“生产测试”的概念与某些定型观念和负面含义相关联(“游击程序设计”,缺少或没有单元测试和集成测试,对最终用户的疏忽或不重视产品的看法)。
如果在生产中进行的测试粗心大意,则肯定会赢得这样的声誉。 它绝
不能替代试生产阶段的测试,在任何情况下都不是
简单的任务 。 此外,我认为,在生产中进行
成功且
安全的测试需要相当高的自动化水平,对既定做法的充分理解以及对此类测试的初衷的系统设计。
为了组织对生产中的服务进行有效测试的全面而安全的过程,重要的是不要将其视为表示一组不同工具和技术的概括性术语。 不幸的是,我也犯了这个错误-
在我的上一篇文章中,没有对测试方法进行十分科学的分类,在“生产中的测试”部分中,对各种方法和工具进行了分组。
从“测试微服务”一文中可以看出(“一种测试微服务的明智方法”)自从该说明于2017年12月底发布以来,我一直在与几个人讨论其内容以及通常的生产测试主题。
在这些讨论的过程中,以及在一系列单独的交谈之后,对我来说很清楚,生产中的测试主题不能减少到上面列出的几点。
“生产中测试”的概念包括
在三个不同阶段应用的全部技术。 哪个-让我们明白。

生产的三个阶段
通常,有关生产的讨论仅在生产,监控或紧急情况下将代码部署到环境中进行时进行。
到目前为止,我本人已经使用了诸如“部署”,“发布”,“交付”等术语作为同义词,很少考虑其含义。 几个月前,我将所有区分这些术语的尝试都视为微不足道的事情而被我拒绝。
在考虑了这一点之后,我想到
了真正需要区分生产的各个阶段的想法。
阶段1.部署
当测试(甚至在生产中)是对
可能的
最佳指标是否实现的检查时,只有在执行测试的方法与生产中实际使用服务的方式尽可能接近的情况下,才能确保测试(以及实际上是任何检查)的准确性。
换句话说,测试必须在
最能模拟工作环境的环境中运行 。
而对工作环境的
最佳模仿是……工作环境本身。 为了在生产环境中执行最大数量的测试,必须确保其中任何一个的不成功结果都不会影响最终用户。
反过来,这只有
在将服务部署到生产环境中而用户无法直接访问该服务时才有可能。
在本文中,我决定使用
Turbine Labs撰写的
Deploy!= Release文章中的术语。 它对“部署”一词的定义如下:
“部署是由工作组在生产基础架构中安装新版本的服务程序代码。 当我们说已经
部署了新版本的软件时,是指它在正常运行的基础架构的框架内运行。 这可以是AWS中的新EC2实例,也可以是在Kubernetes集群的炉膛中运行的Docker容器。 该服务已成功启动,已通过运行状况检查,并且已准备就绪(您希望如此!)来处理生产环境数据,但实际上可能未接收到数据。 我再次强调这一点很重要:
对于部署,用户不必访问新版本的服务 。 有了这个定义,部署就可以称为风险几乎为零的过程。”
“零风险过程”这个词只是部署失败的许多人的灵魂的慰藉。
在实际环境中安装软件而不允许用户使用的能力在测试方面具有许多优势。
首先,维护开发,测试和过渡的独立环境的必要性已被最小化(甚至可能完全消失),而这些环境不可避免地必须与生产同步。
另外,在设计服务的阶段,有必要将它们彼此隔离,以使未能在生产
中测试服务的特定实例
不会导致级联或影响用户其他服务的失败。 一种解决方案可以是设计数据模型和数据库模式,其中非幂等查询(主要是
写操作 )可以:
- 将在生产中服务的任何测试启动期间相对于生产环境数据库执行(我更喜欢这种方法);
- 在应用程序级别被安全拒绝,直到达到写入或保存级别;
- 以某种方式(例如,通过存储其他元数据)在记录或保存级别进行分配或隔离。
第二阶段。发布
注意
Deploy!= Release定义术语发布如下:
“当我们说已经
发布了服务版本时,我们的意思是说它在生产环境中提供了数据处理。 换句话说,
发布是将生产环境数据定向到新软件版本的过程。 牢记这一定义,我们与发送新数据流相关的所有风险(中断,客户不满,
寄存器中的有毒注释)与新软件的
发布有关 ,而不
与新软件的
发布有关 (在某些公司中,此阶段也称为
发布 。在本文中,我们将使用术语
release 。
在Google关于SRE的书中,“发布”一词
在组织软件发布的
章节中使用
来描述 。
“
问题是由一个或多个单独任务组成的工作的逻辑要素。 我们的目标是使部署过程与该服务的风险状况相协调 。
在开发或预生产环境中,我们可以每小时进行构建,并在通过所有测试后自动分发发行版。 对于大型面向用户的服务,我们可以从一个集群开始发布,然后增加其规模,直到我们更新所有集群。
对于重要的基础架构元素,我们可以将实施期限延长至几天,然后依次在不同的地理区域执行。”在此术语中,“发布”和“发布”一词是指通用词汇所指的“部署”,并且通常用于描述各种
部署策略(例如,蓝绿色部署或金丝雀部署)的术语是指新
版本的发布。软件。
此外,应用程序的不成功
发布会导致部分或严重的工作中断。 在此阶段,如果发现服务的已
发布新版本不稳定,则还将执行
回滚或
修复程序 。
当
发布过程是自动化的并且
逐步运行时,它的工作效果最佳。 同样,当错误率和请求频率自动与基线相关时,服务的
回滚或
修补程序将更有用。
阶段3.发布之后
如果发布
顺利进行 ,并且新版本的服务能够处理生产环境数据而没有明显问题,那么我们可以认为
它是成功的。 成功发布之后是一个可以称为“发布后”的阶段。
任何足够复杂的系统将
始终处于逐渐丧失性能的状态。 这并不意味着需要
回滚或
修补程序 。 相反,有必要监视这种恶化(出于各种操作和操作目的)并在必要时进行调试。 因此,发布后的测试不再像例程那样,而是
调试或收集分析数据。
总的来说,我认为系统的每个组件都应考虑到以下事实:没有一个大型系统可以完美地100%运行,并且应该在软件的设计,开发,测试,部署和监视阶段识别并考虑故障。提供。
现在我们已经确定了生产的三个阶段,让我们看一下每个阶段可用的各种测试机制。 不是每个人都有机会从事新项目或从头重写代码。 在本文中,我试图明确确定在开发新项目时最有效的方法,并讨论在不对工作项目进行重大更改的情况下我们还能采取哪些措施来利用建议的方法。
部署测试
我们将部署和发布阶段彼此分离,现在我们将考虑在生产环境中部署代码之后可以应用的某些类型的测试。
整合测试
通常,集成测试由连续的集成服务器在每个Git分支的隔离测试环境中执行。 部署了
整个服务拓扑的副本(包括数据库,队列,代理等),用于将一起工作的
所有服务的测试套件。
我认为这并不是特别有效,原因有几个。 首先,
即使测试在与生产环境相同的Docker容器中运行,也不能像过渡环境那样
以与实际生产环境
相同的方式部署测试环境。 当测试环境中
唯一运行的是测试本身时,尤其如此。
无论测试是作为Docker容器还是POSIX进程运行,它都很可能与上级服务,数据库或缓存建立
一个或多个连接,如果该服务处于可以同时进行的生产环境中,则很少处理多个并发连接,通常重用非活动的TCP连接(这称为重用HTTP连接)。
同样,该问题是由以下事实引起的:每次启动时大多数测试都会在执行此测试
的同一节点上创建一个新的数据库表或缓存键空间(因此,这些测试与网络故障是隔离的)。 这种类型的测试充其量可以表明该系统可以针对特定请求正常工作。 在模拟严重的,分布均匀的故障类型时,它很少有效,更不用说部分故障的不同类型了。
, ,
, , , .
,
. ,
, , , . - , , :
- .
- , .
,
Pact , RESTful JSON RPC, , ,
-, . , , gRPC GraphQL, .
. , , , RPC- . , , , .
,
, — ,
, ( , ).
:
?
. – , : - - ( C) MySQL ( D) memcache ( B).
, ( ), stateful- stateless- .
,
.
service discovery
( ),
.
.
,
C .
,
, , . , , . ,
, .
Google
Just Say No to More End-to-End Tests (« »), :
«
( ) . , ? , .
, , , »., :
. , A .
,
C MySQL, .
( , , «» ,
).
MySQL , , .
— -. , . -, .
, -
, /:
, (, ).
, ,
. IP- , , , , , , , , .
, , , , . . Facebook,
Kraken , :
«
— , . - , . , . - , , , »., , , , , .
- . service mesh . -. -, , , :
如果我们测试服务B,则可以将其传出代理服务器配置为向每个测试请求添加特殊的
X-ServiceB-Test
标头。 在这种情况下,高级服务C的传入代理服务器将能够:
- 检测该报头,并向服务B发送标准响应;
- 告诉服务C该请求是测试 。
集成测试服务B的已部署版本与服务C的已发布版本之间的交互,其中写操作从未到达数据库以这种方式执行集成测试还可以测试服务B与更高的服务
在处理正常的生产环境数据时的交互作用-这可能是对服务B
发布到生产中后的行为的更近的模仿。
如果此体系结构中的每个服务都以测试或模拟模式支持真实的API调用,这也很好,它允许您在不更改真实数据的情况下测试与下游服务的服务合同的执行情况。 这将相当于合同测试,但是在网络级别。
影子数据重复(测试暗数据流或镜像)
在许多情况下,影子复制(在Google博客上的文章中称为“
暗启动” ,在
Istio中使用“
镜像” 一词 )比集成测试更具优势。
混沌工程原理规定如下:
“
系统的行为取决于环境和数据传输方案。 由于使用模式可以随时更改 ,因此
对真实数据进行采样是修复请求路径的唯一可靠方法。”影子数据复制是一种捕获进入给定服务的生产环境数据流并在该服务的新
部署版本中进行复制的方法。 当传入的数据流被拆分并发送到服务的已
发布和已
部署版本时,可以实时执行此过程,也可以在已
部署的服务中播放先前捕获的数据的副本时异步执行。
当我在
imgix工作时(一家只有7名工程师的创业公司,其中只有4名是系统工程师),暗数据流被积极地用来测试图像可视化基础架构中的更改。 我们注册了所有传入请求的一定百分比,并将其发送到Kafka集群-我们将HAProxy访问日志传递给了
heka管道,
heka管道又将经过分析的请求流传
递给了Kafka集群。 在
发布阶段之前,我们在捕获的暗数据流上测试了我们图像处理应用程序
的新版本-从而可以验证请求是否已正确处理。 但是,我们的图像可视化系统大体上是无状态服务,特别适合此类测试。
一些公司宁愿不捕获数据流的一部分,而是将其
完整副本传输到应用程序的新版本。
Facebook的McRouter (内存缓存代理)支持这种类型的内存缓存数据流的影子复制。
“
在测试新安装的缓存时,我们发现能够从客户端重定向数据流的完整副本非常方便。 McRouter支持灵活的阴影复制设置。 可以对各种大小的池执行阴影复制(通过重新缓存键空间),仅复制键空间的一部分或在操作过程中动态更改参数 。”
生产环境中已
部署服务的整个数据流的影子复制的负面影响是,如果在最大数据传输强度时执行该复制,则可能需要两倍的电源。
诸如Envoy之类的代理支持即发即忘模式下数据流的影子复制到另一个群集。 它的
文档说:
路由器可以执行从一个群集到另一群集的数据流的影子复制。 当前,实现了即发即弃模式,其中Envoy代理服务器在从主集群返回响应之前不等待影子集群的响应。 对于影子群集,将收集所有常规统计信息,这对于测试目的很有用。 使用影子复制时, -shadow
选项将添加到host / authority -shadow
。 这对于日志记录很有用。 例如, cluster1
变成cluster1-shadow
。
但是,创建与生产同步的集群副本以进行测试通常是不切实际或不可能的(出于组织同步的登台集群存在问题的相同原因)。 如果使用影子复制来测试具有许多依赖性的新
部署服务,则它可能会引发相对于被测试服务而言高级服务状态的意外变化。 由于已将影子数据流视为重复尝试注册和拒绝的事实,在
部署的服务版本中每天记录的用户注册量与生产数据库中记录的影子重复会导致错误率提高到100%。
我的个人经验表明,影子复制最适合用于使用服务器端存根测试非幂等请求或无状态服务。 在这种情况下,通常会使用数据的影子重复来测试负载,稳定性和配置。 同时,借助集成测试或登台,您可以测试在处理非幂等请求时服务如何与有状态服务器交互。
TAP比较
在Twitter博客上的
一篇文章中唯一提及此术语,该
文章致力于推出具有高服务质量的服务。
“为了验证现有系统的新实施的正确性,我们使用了一种称为抽头比较的方法。 我们的抽头比较工具可在新系统中重现样品生产数据,并将收到的答案与旧系统的结果进行比较。 获得的结果帮助我们甚至在最终用户遇到错误之前就发现并修复了系统中的错误。”另一则 Twitter博客文章将抽头比较定义如下:
“将请求发送到生产环境和登台环境中的服务实例,并检查结果并评估性能特征。”抽头比较和阴影复制之间的区别在于,在第一种情况下,将
发布版本返回的响应与已
部署版本返回的响应进行比较,在第二种情况下,该请求以脱机模式(如即发即
弃 )复制到已
部署版本。
在该领域工作的另一个工具是
科学家库,可在GitHub上获得。 该工具是为测试Ruby代码而开发的,但随后移植到
了其他几种语言 。 它对某些类型的测试很有用,但存在许多未解决的问题。 以下是GitHub开发人员在一个专业的Slack社区中写的内容:
“该工具仅执行两个代码分支并比较结果。 您应该小心这些分支的代码。 如果这会导致问题,则必须确保不重复数据库查询。 我认为这不仅适用于科学家,而且适用于您先做两次然后比较结果的任何情况。 创建科学家工具是为了验证新的许可系统是否与旧的许可系统相同,并且在某些时候用于比较几乎每个Rails请求的典型数据。 我认为该过程将花费更多时间,因为该处理是顺序执行的,但这是一个不使用线程的Ruby问题。
在我所知的大多数情况下,科学家工具用于读取而不是写入操作,例如,确定新改进的查询和权限方案是否获得与旧查询相同的答案。 这两个选项都在生产环境中(在副本上)运行。 如果经过测试的资源有副作用,我认为测试必须在应用程序级别进行。”Diffy是Twitter在2015年推出的Scala书面开源工具。 Twitter博客上一篇名为“
无需编写测试的测试 ”
的文章可能是了解抽头比较在实际中如何工作的最佳资源。
“ Diffy通过同时启动新版本和旧版本的代码来检测服务中的潜在错误。 该工具充当代理服务器,并将所有接收到的请求发送到每个正在运行的实例。 然后,他比较实例的响应并报告比较期间发现的所有偏差。 Diffy基于以下思想: 如果服务的两个实现通过一组足够大且变化多样的请求返回相同的答案,则可以将这两个实现视为等效,并且将它们视为较新的实现而不会降低性能。 Diffy的创新降噪技术使其与其他比较回归分析工具区分开来。”当您需要检查两个版本是否给出相同结果时,分接头比较非常有用。 根据Mark McBride的说法,
“ Diffy工具通常用于重新设计系统。 在我们的案例中,我们将Rails源代码库划分为使用Scala创建的多个服务,并且大量API客户端使用的功能与预期不同。 日期格式之类的功能特别危险。”点击比较不是在最大负载下测试用户活动或两个版本的服务行为标识的最佳选择。 与卷影复制一样,副作用仍然是一个未解决的问题,尤其是当部署版本和生产版本将数据写入同一数据库时。 与集成测试一样,解决此问题的一种方法是仅对有限的一组帐户使用抽头比较测试。
负载测试
对于那些不熟悉压力测试的人,
本文可以作为一个很好的起点。 不缺乏用于开源负载测试的工具和平台。 其中最受欢迎的是Twitter上用Erlang,
Siege和
Iago编写的
Apache Bench ,
Gatling ,
wrk2 ,
Tsung ,Twitter用Scala编写的
Iago ,用Scala编写(在测试实例中复制HTTP服务器,代理服务器或网络数据包分析器的日志)。 一些专家认为,生成负载的最佳工具是
mzbench ,它支持多种协议,包括MySQL,Postgres,Cassandra,MongoDB,TCP等
。Netflix的
NDBench是另一个用于负载测试数据仓库的开源工具。 ,它支持大多数已知协议。
Iago官方Twitter博客更详细地描述了好的负载生成器应具有的功能:
“无阻塞请求是基于内部自定义统计分布以给定的频率生成的(默认情况下对Poisson过程进行建模)。 可以根据需要更改请求的频率,例如,在满负荷工作之前准备缓存。
通常,主要关注点是根据利特尔定律的请求频率,而不是并发用户数,并发用户数可能会因该服务固有的延迟量而异。 因此,出现了新的机会来比较多个测试的结果并防止服务质量下降,从而减慢了负载生成器的运行速度。
换句话说,Iago工具旨在模拟一个系统,在该系统中,无论您的服务处理这些请求的能力如何,都可以接收请求。 在这方面,它不同于模拟封闭系统的负载生成器,在封闭系统中,用户将耐心地使用现有延迟。 这种差异使我们可以相当准确地对生产中可能遇到的故障模式进行建模。”负载测试的另一种类型是通过重新分配数据流进行压力测试。 其本质如下:来自生产环境的整个数据流被定向到比为服务准备的集群小的集群。 如果存在问题,则将数据流传输回较大的群集。 如Facebook
官方博客中的
一篇文章所述,Facebook使用了此技术:
“我们专门将更大的数据流重定向到单个群集或节点,测量这些节点上的资源消耗并确定服务稳定性的边界。 这类测试对于确定支持Facebook Live同时广播的最大数量所需的CPU资源特别有用。”这是专业Slack社区中的一位前LinkedIn工程师写的内容:
“ LinkedIn在生产中也使用了红线测试-从负载均衡器中删除服务器,直到负载达到阈值或开始出现错误为止。”实际上,Google搜索提供了有关该主题的
完整白皮书和LinkedIn博客
文章的链接:
“用于测量的Redliner解决方案使用了生产环境的真实数据流,避免了会妨碍在实验室中准确测量性能的错误。
Redliner将部分数据流重定向到被测服务,并实时分析其性能。 该解决方案已在数百种内部LinkedIn服务中实现,并且每天用于各种性能分析。
Redliner支持针对Canary和生产实例的并行测试执行。 这允许工程师将相同数量的数据传输到两个不同的服务实例:1)包含创新的服务实例,例如新的配置,属性或新的代码; 2)当前工作版本的服务实例。“在制定决策时会考虑负载测试的结果,并有助于防止代码部署,这可能导致性能下降。”由于采用了Kraken系统,Facebook将使用真实数据流的负载测试提升到了一个全新的水平,其
描述也值得一读。
通过更改
Proxygen配置(Facebook负载平衡器)中的边界设备和群集的权重值(从分布式配置存储中读取)时重新分配数据流来实现测试。 这些值确定在给定存在点分别发送到每个群集和区域的真实数据量。
来自Kraken白皮书的数据监视系统(
Gorilla )显示各种服务的指示器(如上表所示)。 基于监视数据和阈值,决定是否根据权重值进一步发送数据,或者是否有必要减少甚至完全停止将数据传输到特定群集。
配置测试
一波新的开源基础结构工具使得不仅可以实现代码形式,而且相对
容易地捕获所有基础结构更改。 尽管在预生产阶段大多数基础架构即代码测试只能确认正确的规范和语法,但也可以不同程度地
测试这些更改。
而且,在代码
发布之前拒绝测试新配置成为了造成
大量中断的原因 。
对于配置更改的整体测试,重要的是要区分不同类型的配置。 弗雷德·赫伯特(Fred Hebert)曾经建议使用以下象限:
当然,该选项不是通用的,但是这种区别使您可以决定如何最好地测试每个配置以及在哪个阶段进行测试。 如果可以确保程序集的真正可重复性,则构建时间配置才有意义。 并非所有配置都是静态的,但是在现代平台上,动态配置更改是不可避免的(即使我们正在处理“永久基础结构”)。
, , blue-green , . (
Jamie Wilkinson ), Google ,
:
« , , , - . . - , — , , . ., . , , — ».Facebook :
« . — , . . , .
. Facebook , . , .
(, JSON). , . .
(, Facebook Thrift) . , .
, , - . . — A/B-, 1 % . A/B-, . A/B- . , , , , . , A/B- . , A/B-. Facebook .
, A/B- 1% , 1% , ( « »). , . , .
Facebook . , . , , . , , .- 简单方便地取消更改
在某些情况下,尽管采取了所有预防措施,但仍进行了无效配置的部署。快速发现并回滚更改对于解决此类问题至关重要。“我们的配置系统中提供了版本控制工具,可以使撤消更改变得更加容易。”
待续!UPD:在这里继续。