生产环境中的质量保证

2017年4月4日


Photo of Rouan Wilsenach

Rouan 是一位软件工程师和技术负责人,致力于打造优秀的团队和高质量的软件。他曾在金融服务、教育、休闲和能源等多个行业的公司工作,使用过各种技术栈(包括担任 Thoughtworks 的顾问)。他关心保持简单性、建立包容性团队以及写下他所学到的知识。


收集有关系统的运行数据是一种常见做法,尤其是指示系统负载和性能的指标,例如 CPU 和内存使用情况。多年来,这些数据一直被用来帮助支持团队了解何时发生或即将发生故障。例如,当系统变慢时,可能会启用代码分析器来确定系统中哪一部分导致了瓶颈,例如运行缓慢的数据库查询。

我观察到最近出现了一种趋势,它将这种传统运行监控的严谨性与对系统质量的更广泛视角结合起来。虽然运行数据是支持系统的重要组成部分,但收集有助于全面了解系统行为是否符合预期的数据也很有价值。我将“生产环境中的质量保证”定义为一种方法,在这种方法中,团队更加关注其生产系统的行为,以便提高这些系统所服务功能的整体质量。

生产环境中总会出错,但这不一定是坏事。 这是一个了解您的系统及其交互的现实世界的机会。借助正确的生产监控工具和良好的 持续交付 流程,您可以构建一套反馈机制,帮助您在问题发生时及时发现并快速发布修复程序。采用生产质量保证 (QA) 实践可以帮助您更深入地了解系统面临的实际问题,并学习改进其质量的新方法。

收集生产数据

您可以收集有关系统的很多数据。我发现,考虑什么对系统的成功至关重要,并以此来指导我的工作非常有帮助。在这种情况下,当我谈论成功时,我指的是能够带来收益的事情。如果您的系统无法提供客户付费或依赖的服务,那么每秒处理数千个请求也无关紧要。

关键成功指标

Tes,我花了一些时间研究教师用来申请工作的系统。我们团队确定为关键指标之一是教师的求职申请是否 tatsächlich 到达了他们申请工作的学校。电子邮件是系统通知学校新工作申请的主要方式。我们使用第三方服务发送电子邮件,这意味着这超出了我们正在处理的系统的范围。正如我们逐渐了解到的,尝试发送电子邮件时可能会出现很多问题。即使电子邮件地址看起来有效,也可能是无效的,邮箱可能会满,人们可能会设置外出回复,这可能会让人觉得他们没有收到电子邮件,即使他们 tatsächlich 收到了。这类现实世界的复杂情况几乎无法预测,也很难测试。我们所做的只是从生产环境中发生的事情中学习。以下是我们对系统质量的理解和反应如何演变的摘要

  • 我们开始统计提交的求职申请数量和我们发送的电子邮件数量(通过向指标服务器发送简单的指标)。我们设置了一个警报,以便在这些数字不同步时收到通知。
  • 我们设置了另一个警报,让我们知道我们的电子邮件发送微服务是否无法处理发送电子邮件的请求。
  • 事实证明,这种情况经常发生。我们的警报正在触发,但原因尚不清楚。我们查看了日志,但没有找到任何有用的信息,因此我们开始着手改进日志记录。特别是,我们记录了从第三方电子邮件提供商那里收到的错误的更多详细信息。
  • 从日志中,我们了解了不同类型的响应 - 有些意味着尽管出现错误,但对方可能还是收到了电子邮件(例如,外出回复),有些(例如 DNS 问题)可能是暂时的,而有些错误意味着电子邮件永远不会被送达。有些文档和错误消息令人困惑,因此我们使用未发送电子邮件的示例与学校进行了交谈,以确定哪些电子邮件 tatsächlich 收到了,哪些没有收到。
  • 我们注意到,我们的电子邮件发送代码对于收到的错误过于严格,因此我们对其进行了一些放宽。(当我们知道错误响应意味着学校很可能已经收到邮件时,我们就停止记录错误。)
  • 我们注意到,相当多的问题是由于电子邮件地址捕获不正确造成的。在这些情况下,我们会查看日志并与我们的客户服务团队联系。“您好。我们尝试向 [email protected] 发送电子邮件,但无法发送。请您与学校确认一下电子邮件地址是否正确?”
  • 有一段时间,客户服务团队会告诉我们何时修复了电子邮件地址,然后我们会使用我们在 Web 服务上构建的端点手动触发电子邮件重发。过了一段时间,我们开始在电子邮件地址更新时自动重发电子邮件。(我们侦听消息队列上作业信息的更新。)
  • 我们仍然需要做很多工作来向客户服务团队发送有关无效电子邮件地址的电子邮件,因此我们开始自动发送这些电子邮件。
  • 随着这个自动修复系统的到位,我们停止了对每封失败电子邮件的警报,因为它们中的大多数都得到了修复。现在,只有当错误的电子邮件地址漏掉并且在一定时间内没有得到修复时,我们才会收到警报。

我们在 Tes 的求职申请系统的另一个关键使用指标是教师是否 tatsächlich 在申请工作。他们是否点击了求职申请表末尾的“提交”按钮,或者有什么东西阻止了他们这样做?可能是某个小的 CSS 或 JavaScript 库更改导致“提交”按钮在某个浏览器中无法正确显示或行为异常。教师提交申请的频率下降可能表明存在问题,因此如果发生这种情况,我们会收到通知。我们还会密切关注我们的服务返回的 HTTP 500 响应代码。我们真的希望教师能够申请工作,所以如果有什么事情阻止了他们,我们想知道。我们对此非常谨慎,以至于我们会尽一切努力始终向他们显示求职申请表。如果在获取显示表单所需的数据时出现问题,我们会确保记录错误,然后尽最大努力让教师在出现问题的情况下仍然可以申请。我们会查看这些日志(我们的警报系统会提醒我们),并处理出现的任何问题。我们认为,我们宁愿修复错误的数据或系统状态,也不愿让潜在的候选人失望。

在这些示例中,我提到了一些收集生产数据的不同技术。让我们更详细地了解一下它们。

日志记录

日志记录是一种非常强大的收集系统数据的方法,前提是您在记录数据的方式上多加注意。日志不再仅仅是系统管理员用来查找问题根源的长文本文件。如果您的日志记录堆栈在构建时就考虑到了可搜索性,那么它可以为您提供有关系统的宝贵实时数据。

请记住,日志并不局限于记录技术信息。您还可以记录有价值的使用数据。我花了一些时间研究一个在线银行网站,该网站拥有出色的日志记录堆栈,我们可以利用日志来确定我们发布的新功能的受欢迎程度。我们发布了允许人们调整其在线支付限额的功能,并且很快发现该功能得到了广泛采用。我们还可以收集一些有关人们调整限额频率和调整幅度的有趣数据。如果您要进行此类日志记录,请记住用户的隐私,并 仅记录您真正需要的数据

实现更易于搜索的日志的一种技术是使用日志转发。这是通过在您的服务器上运行一些软件来完成的,该软件会定期将您的日志数据发送到另一个服务,通常是针对全文搜索优化的数据库,例如 ElasticSearchApache Solr。通过在此数据库上构建查询,可以创建可视化效果,以显示聚合信息和随时间变化的趋势。对于我提到的银行网站,可以绘制图表来显示有趣的业务指标,例如正在转移的资金数量,以及更多技术信息,例如用户使用哪些浏览器和设备进行银行业务。还可以搜索问题并深入查看各个日志条目,这是调查问题的非常有效的方法。

另一种方法是采用所谓的结构化日志记录,这是我在一家保险公司工作时使用的方法。您可以记录以下内容,而不是记录如下内容

There was an invalidInputFormat error capturing data for user 54321

您可以记录

There was an error capturing data for userId={54321},
errorType={invalidInputFormat}

通过在各个日志条目中添加一些额外的结构,您可以更轻松地搜索信息。有一些强大的工具可用(例如 Splunk),它们可以根据这些类型的日志构建索引,并提供优化的搜索、聚合和可视化功能。如果您使用的是 ElasticSearch 之类的工具,则还可以以 JSON 格式记录结构化数据。

现在可以使用更复杂的方式使用日志,但不要忘记利用指定日志级别的良好实践,例如 ERROR、WARN 和 INFO。以这种方式标记每个日志条目的严重性对于支持系统非常有帮助,因为它可以提高日志的信噪比。支持系统的人员应该能够查看一天的日志,并能够过滤以仅查看错误(理想情况下,这些错误应该不会太多)。这样,他们就可以确保没有任何问题被忽视。

指标

除了在日志文件中记录信息外,还有一些工具(例如 statsd)可用于统计系统事件和聚合系统数据。日志对于收集非常具体的信息很有用,而这种方法可以通过提供一种收集聚合信息的方式来补充日志记录。在 Tes,我们一直在使用 statsd 将数据发送到 DataDog。例如,我们会定期发送每个 Docker 容器的当前 CPU 负载和内存使用情况,以便我们可以注意到性能问题。同样,我想强调的是,您应该考虑捕获业务指标以及技术指标。

对于我之前提到的求职申请系统,我们会发送常见错误的指标。为了发送电子邮件,我们通过消息队列向共享微服务发送消息。如果该服务无法处理该消息,则该消息最终会进入 死信队列,我们会发送一个指标来指示我们的一条消息已进入该队列。如果我们收到此指标的任何实例,则会向团队发出警报,表明出现问题,应进行调查。然后,我们可以在电子邮件服务的同一时间窗口内搜索日志中的错误,以找出哪些求职申请没有通过电子邮件发送到学校,以及错误消息是什么。

我们还收集常见的用例指标,例如应用程序何时启动、更新或提交。我们可以使用它来查看已提交应用程序的百分比,并让我们比较不同类型的应用程序表单。这些统计数据帮助我们识别趋势、了解系统并对潜在问题做出响应。值得指出的是,像这样的工具旨在收集足够的数据以提供有用的统计数据,因此通常不能依赖于精确性。

API

如果上述技术一开始看起来很繁琐,请不要却步。寻找有关系统数据的最简单的地方是您已经在使用的工具的 API。许多性能监控、网络分析、正常运行时间监控和 IaaS 工具都提供 API,可以通过查询这些 API 来获取有关系统性能和使用情况的数据。

从生产数据中学习

警报

如果没有人对您收集到的所有数据做出反应,那么这些数据就毫无用处,因此建立某种警报系统非常重要。当生产中出现问题时,您希望第一个知道,以便将对用户的影响降到最低。您需要能够监控日志数据或指标并能够向您发送警报的软件。这些警报可以通过电子邮件、短信或您团队使用的聊天室中的通知来传递。然后,您需要设置一些警报阈值 - 您希望何时收到通知的规则。在求职申请示例中,当我们未能向学校发送任何电子邮件时(即阈值为 1),我们会收到通知。这是因为这类错误应该很少发生,并且在发生时应该认真对待。要查看教师放弃申请的频率,我们的警报需要与其他数据相关。当一天中的求职申请数量低于某个数字,或与当前趋势明显不同时,我们会收到警报,通知我们可能有什么因素阻止了教师申请。

需要指出的是,警报不仅仅是让您知道出了什么问题。良好的警报会在问题发生之前让您知道。您对系统了解得越多,就越能更好地设置能够指示即将发生的问题的监控类型。例如,您可能知道特定服务器上的内存使用率超过 64% 通常表示存在内存泄漏,并且很快就会耗尽资源。如果您及时收到警报,则可以采取措施解决内存问题或使备用服务器联机。复杂的设置甚至可能包括一些自我修复机制。

在警报中保持良好的信噪比至关重要。如果您的团队收到太多警报,或者收到太多误报,警报就会变成噪音,被忽略,重要问题就会被遗漏。

仪表盘

仪表板是使数据更容易使用的常用方法。这些通常包括图形集合(例如,所有服务器的 CPU 使用情况)或大的单个数字(例如,今天的求职申请数量)。这些可视化的数据可能来自各种数据源,因此您使用的仪表板工具可能需要进行一些数据处理或聚合。否则,您可以使用许多工具现在提供的内置仪表板。将这些可视化内容显示在团队工作区域的大屏幕上非常有用,这样您就可以一目了然地查看趋势和状态。如果您的团队远程工作,请确保指向仪表板的链接易于找到。人类具有发现趋势和注意到异常情况的敏锐能力,因此,如果您的团队习惯于查看数据,您可能会发现问题,而无需投入维护复杂的数据处理算法。

组织以状态页面的形式向其用户提供一些基本系统统计数据正变得越来越普遍。这有助于让用户及时了解发生的问题,并向他们保证这些问题正在得到解决。有关示例,请参阅GitHub 的状态页面,或查看我们在 Tes 上提供的状态页面

基于现实的质量保证方法

当大多数人想到质量保证时,他们会想到测试,但要很好地掌握系统的质量,重要的是要记住质量保证的其他方面。了解系统并分析最大的质量问题是一个经常被忽视的方面。

测试只能帮助您解决您已经知道的情况。测试非常适合发现您预期会发生的缺陷,但许多生产缺陷都是意外的。用户、网络、浏览器和设备极其多样化且高度不可预测。测试根本无法涵盖所有情况。测试是确保系统按预期运行的好方法,但它们无法告诉您预期的行为是否正确。良好的生产监控可以提供有关您未预见到的情况的宝贵反馈,并帮助您相应地调整系统的行为。质量保证既要了解系统的正确行为,又要维护该行为 - 这一方面经常被忽视。

测试需要证明其价值

测试是一种有价值的做法,可以为许多组织节省大量时间和金钱,但值得考虑的是测试确实需要成本。有些测试易于维护和使用,而另一些测试则需要付出更多努力。例如,众所周知,UI 或基于浏览器的测试是不稳定的(它们会产生大量误报)、脆弱的(随着 UI 的发展,它们需要大量维护),并且通常依赖于不可靠的下游系统。对于某些组织,尤其是那些依赖专有硬件或软件的组织而言,性能测试非常昂贵。您可以设置监控以确保关键指标不低于特定阈值,而不是进行 UI 测试来检查某些功能是否正常工作。您可以拥有生产中的指标来指示系统的平均响应时间,而不是拥有一套测试来模拟负载下的功能。

然后,团队可以继续对网站进行有价值的改进,利用他们原本会花在这些测试上的时间,并且如果系统出现任何问题,他们会收到通知。如果确实出现问题,他们可以调查并纠正故障。

您准备好采用生产质量保证实践了吗?

简短的回答是肯定的。大多数团队将通过更多地关注生产中发生的事情来获得对其正在构建的系统的更多洞察力,并且应该很快看到建立一些基本仪表板和警报的价值。

简短回答的问题在于,生产中的质量保证包括您可以将一些预生产质量实践换成本文中描述的技术的概念。在实践中,如果没有一些支持性的持续交付实践,很难支持这种转变。

在替换一些传统的预生产质量保证技术之前,您应该建立一种测试文化和相关的测试技能。当您尝试修复在生产中发现的问题时,拥有一套可靠的自动化测试非常重要,以确保您没有破坏其他东西。如果没有适当的测试类型,您就有可能陷入无休止的打地鼠游戏

依靠生产数据来识别问题意味着解决这些问题非常紧迫。一旦您解决了问题或找到了解决方法,您就需要能够快速将更改投入生产。您需要部署自动化部署才能做到这一点,否则您就有可能出现人为错误,从而使情况变得更糟。

找到合适的平衡点

用监控代替昂贵的测试可以让组织更快地行动,但每个组织都需要在速度和确保(或尽可能确保)在发布到生产环境之前一切正常之间找到自己的平衡点。这种平衡将在很大程度上取决于所讨论的系统。例如,Facebook 开发团队[1]能够放弃一些测试,因为他们有良好的反馈循环,让他们知道是否破坏了生产环境中的某些东西,并且他们可以快速发布修复程序。如果出现问题,并且有人无法查看照片或错过了朋友的生日,那也不是世界末日。另一方面,一家为医疗设备编程的公司风险很高,反馈循环非常缓慢,因此他们需要尽可能确保这些设备在使用前一切正常。大多数系统介于两者之间。

对于任何系统,都会有一些难以恢复的问题。对于这些情况,您需要在预生产测试中投入更多资金,以防止生产中出现事故。生产中发生的每个问题都会产生相关的成本,因此确定您需要多么谨慎非常重要。等到生产中出现问题才进行修复的好处是,您知道它值得修复,因为它确实发生了。

一些组织已经在生产中进行所有测试,但完全依靠生产中的问题或测试来了解系统的质量是一种反模式。它忽略了这样一个观点,即适当的质量保证需要各种质量实践,而生产中的质量保证只是其中之一。如果您所寻找的只是流程中的一个点来说“是的,看起来不错”,那么您将不会学到太多关于如何改进系统的知识。

找到预生产和生产质量实践之间的适当平衡可以帮助您对系统的质量获得更现实和全面的了解。最重要的是要记住,我们可以通过密切关注生产中发生的事情来学到很多东西,并且我们可以利用我们学到的知识来构建质量更好的东西。


脚注

1: Facebook 曾经有过“快速行动,打破常规”的座右铭。有关为什么这对每个人来说都不是正确方法的想法,请参阅此xkcd 漫画

重大修订

2017 年 4 月 4 日:首次发布