下面的内容是我在作为一名程序员入职之前阅读的由Gergely Orosz写的The Software Engineer’s Guidebook。我将将阅读时得到的重要的信息总结成中文以供大家分享。
您的组织很可能隐含或明确地期望员工+工程师领导使系统更可靠的工作。
在本章中,我们将介绍构建和维护可靠系统的常见方法,包括:
作为员工+工程师,您在可靠性方面扮演什么角色?在大型科技公司中,通常明确期望您在自己的影响范围内拥有可靠性,无论是在您自己的团队还是其他团队。这意味着您有责任确保可靠性被衡量,制定改进计划,并倡导额外的工程带宽以提高可靠性。
OKR通常是提高系统可靠性的有效方法。例如,您可以捕捉使系统更可靠、高效和性能更好的目标。然后,您可以定义可衡量的关键绩效指标(KPI),例如:
你几乎总是需要与工程经理合作,才能在可靠性方面取得实质性进展。 归根结底,工程经理对其团队的表现和系统的可靠性负有责任和义务。然而,作为一名资深工程师,你具备识别可靠性问题的能力,并能够采用多种方法来改进这一点。你可以——并且应该!——向工程经理提供数据,突出说明为什么投资于可靠性很重要,以及这种投资的回报是什么。
在我们深入讨论日志记录方法之前,先明确一下为什么它很重要。日志的目的是帮助工程团队调试生产问题,通过捕获丢失但必要的信息,为未来的故障排查提供参考。
哪种日志记录策略可以帮助你的团队调试生产问题?这取决于你的应用程序、平台以及业务环境。
以下是一个可以帮助决定如何记录日志以及记录什么内容的日志工具集:”
日志级别。大多数日志工具提供记录不同日志级别的方法,例如“调试(debug)”、“信息(info)”、“警告(warning)”和“错误(error)”。这些级别可用于过滤日志。如何使用这些级别取决于你的环境和团队实践。
日志结构。日志捕获了哪些细节?是否记录了本地变量?日志是否捕获了时间戳——精确到毫秒或纳秒——以便轻松判断两个日志事件中哪个先发生?这些时间戳是否包含时区信息?
自动化日志记录。系统的哪些部分是自动记录日志的,以免日志记录依赖于工程师的记忆?
日志保留。日志在客户端设备上保留多长时间?在后端保留多长时间?保留日志的时间越长可能越有用,但会占用空间,并可能导致数据存储成本增加。
切换日志级别。对于应用程序,通常的做法是使用“调试版本(debug builds)”输出所有日志级别,但在生产版本中只记录警告或错误级别的日志。具体细节取决于平台级实现和团队实践。”
如果你所合作的团队没有任何日志记录实践,可以考虑引入相关实践。日志记录是一个领域,工程师们常常希望能够就日志记录的内容和方式达成一致,尤其是在试图从日志中查找信息却失败时。
为团队制定一份简短的日志记录指南,只需与几位工程师沟通,并授权一名团队成员提出建议——或者由你自己完成。对于日志记录的基础部分,达成某种共识总比没有好,只要团队知道这份指南是由他们负责并且可以随时修改即可。
以下指南来自 2008 年,由当时担任 LogLogic 首席日志推广员的 Anton Chuvakin 提出。这份日志记录指南至今仍然适用,以下内容已获得 Anton 的同意:
优秀的日志应具备以下特点:
需要记录的事件
每个事件需要记录的内容
你的团队是如何进行日志记录的?是否每个人都按照自己的方式调用日志?这种方法对小型团队且有资深工程师的情况下可能适用,但在较大的团队中,往往会导致临时性的日志记录方式:开发人员将日志记录到控制台,使用第三方日志记录供应商,或者调用内部日志记录解决方案。
一个相对简单的方法来提高一致性是就日志记录方法达成一致——例如,使用哪种策略——然后通过引入一个轻量但有指导性的日志记录框架,让“错误”的日志记录方式变得非常困难。
但为什么仅仅为了日志记录就要引入另一个框架?创建一个简单的接口可以帮助抽象当前使用的供应商,这在供应商更换频繁的大公司中尤其重要,因为它可以使迁移变得更加容易。这也有助于未来分析日志记录的使用情况。当然,不要为了框架而构建框架;只有在它能够解决临时性、不一致的日志记录以及不清楚该使用哪些框架的问题时才去做。
如何判断一个系统是否健康?最可靠的方法是监控关键特性,并在某个指标看起来不健康时触发警报。”
百分位数是监控和服务水平协议(SLAs)中的一个关键概念。在监控加载时间或响应时间等内容时,仅查看平均值是不够的。为什么?平均值可能掩盖了影响许多客户的最糟糕情况。为避免这种情况,可以考虑监控以下百分位数:
那么应该监控什么?有很多显而易见的选择可以提供有关系统或应用程序健康状况的信息,包括:”
对于后端服务:
对于 Web 应用程序和移动应用程序,还值得监控以下指标:
对于移动应用程序,还值得监控以下指标:
业务指标讲述了应用程序或服务健康状况的“真实”故事。上述指标更通用且基础设施化;它们指示基本问题。然而,即使上述指标看起来不错,服务或应用程序仍可能不健康。”
为了全面了解系统健康状况,你需要监控高度针对产品的业务指标。例如,在 Uber,Rides 产品的核心业务指标是生命周期事件:
像乘车接受率骤降这样的指标变化可能表明系统故障。
在我负责的 Rides 产品支付团队中,我们监控的业务指标包括:
我们测量了信用卡、PayPal、Apple Pay 等支付方式的业务指标。业务指标是针对你的业务部门的,但有些指标是普遍存在的,例如:
支持票数量。总支持票的数量是多少?按类别划分的情况如何?按类别跟踪这些数据可能很有用,因为峰值可能表明存在错误或故障。
仅仅监控是不足以确保系统可靠的。当指标异常时需要触发警报,这些警报需要由值班工程师接收、调查并解决。”
决定哪些指标需要分配警报。有许多内容可以监控,但哪些具体指标需要设置警报,以便在它们趋势恶化时触发?
回答这个问题的一种方法是从业务和产品入手。提出以下问题:
仅通过明确说明什么是“健康”和“不健康”的状态,你就应该能够确定系统中需要监控和警报的区域。
并非所有警报都是一样的。系统对所有客户宕机听起来是一个非常重要的警报,而小功能对一小部分用户失效——例如票务系统的‘导入用户’功能——影响要小得多。因此,需要对警报的紧急程度进行分类。以下是一个简单但有效的系统:
跟踪警报的“噪声”并采取行动。噪声警报是那些没有可操作性的警报。半夜被警报吵醒是很有压力的,更糟糕的是,这种警报可能没有实际意义。同时,由于警报未发送而错过故障也不是理想的情况。那么该如何找到正确的平衡?测量精准度和召回率是两个有帮助的概念。
精准度。 这衡量警报指示实际问题的百分比。一个精准度为 30% 的系统意味着 10 个警报中只有 3 个是故障,其余都是噪声。精准度百分比越高,噪声越少。一个 100% 精准度的系统只会触发表明故障的警报。”
召回率。 这衡量触发警报的故障百分比。一个召回率为 30% 的系统意味着 10 个故障中只有 3 个触发了警报。一个 100% 召回率的系统意味着所有故障都会触发警报。
理想的值班系统具有 100% 的精准度,没有噪声警报,同时检测到 100% 的故障。但在现实世界中,往往需要权衡,例如:
测量警报的精准度和召回率,以确定需要更多关注的领域。一种常见方法是:
测量精准度和召回率需要工程师执行上述两个手动步骤。工程师需要标记警报,以确认警报是否与故障相关;事件回顾人员应标记故障是否有警报先前触发。现有的值班系统可能已经能够捕获这些信息。如果没有,可能需要构建此功能或扩展值班系统。
如何决定何时为某个指标触发警报?有两种常见方法:
异常检测的优点是它可以捕捉到比静态阈值更多的变化。在一个训练良好且配置良好的异常检测系统中,警报会在流量意外激增或下降时触发。假设有一个异常检测框架可用,异常检测在各种指标中部署的工作量也要小得多。
缺点是,如果未经过良好训练或配置,异常检测可能会过于噪声化,甚至为正常流量模式触发过多警报。我记得当我们第一次在 Uber 的支付系统中部署异常检测时,在系统训练和配置的最初几周内,我们收到了太多警报,以至于不得不关闭实时警报。
异常检测可能存在的另一个问题是对真实异常的检测过于不敏感。配置异常检测通常比看起来更复杂,你可能需要让系统考虑不同时间段的常规流量模式。它可能会触发对可预测低流量或高流量的警报——例如电子商务业务在黑色星期五期间的流量激增或下降。
根据实际情况判断使用哪种类型的警报以及何时使用。如果你还没有使用过这两种警报类型,可以在不同项目中尝试两者!
通常,最实用的方法是两者结合:对大多数指标使用异常检测,同时对预期的流量增加/下降使用静态阈值,并捕捉关键指标降至零的情况。”
直到2000年代,许多公司仍然采用运维模式,“运维”指的是操作。开发人员编写和测试代码,将其提交到“下一个版本”的分支中,经过数周或数月的时间,候选版本会被最终确定并测试。
运维团队随后接管并通过将代码部署到服务器上以及应用数据库架构更新来发布版本。对于可下载的应用程序,运维团队更新二进制文件和更新脚本。然后由运维团队监控应用程序。
如今,随着迭代周期的缩短,工程团队经常每天多次部署。监控代码已不再是运维团队的职责,而是由负责更改的工程团队承担,并定义值班轮换。”
在科技公司中,典型的值班设置如下:
大多数科技公司定义了由团队成员组成的主值班和次级值班轮换。三级值班通常由工程经理担任,然后是工程管理链——例如工程组织中的总监和副总裁。”
在大多数大型科技公司中,工程团队通常负责自己的值班轮换,并定义和安排值班。在较小的科技公司中,通常会有一个专门的值班团队处理所有高优先级警报。这通常是一个虚拟值班团队,工程师通常会因额外的时间和努力而获得补偿。
在较传统的公司或刚开始数字化转型的公司中,通常由DevOps团队处理警报,因为警报通常会附带运行手册。
理想的值班团队规模是多少?
无论值班团队如何配置,通常工程师会值班一周。这意味着如果每月不希望某位工程师值班超过一次,团队规模至少需要5人,因为一个月平均有4.5周。如果考虑到假期和病假,6人的团队规模是合理的;一个健康的值班轮换至少需要6人。
如果工程师在完成主值班职责的同时还需要承担次级值班职责,那么一个健康的轮换需要10-12人,以避免成员过于频繁地值班。
在只有一个值班团队的公司中,通常更容易确保团队规模合理。但在每个团队都参与值班的公司中,如果团队少于6人,那么成员每月值班超过一次是很常见的。在这种情况下,通常会将两个小团队合并为一个相关领域的值班团队,以创建更健康的值班节奏。”
当警报触发时,通常由值班工程师接收通知。然后他们采取行动以确定警报是否表明发生了故障。
警报运行手册用于调试警报并采取措施缓解故障。“值班运行手册”是警报运行手册或“主”警报运行手册的统称。值班运行手册也可以称为“事件响应运行手册”。
将警报运行手册附加到每个警报上可以大大提高值班效率。一个有用的警报运行手册应包含以下信息:
警报运行手册需要保持更新。不幸的是,不可能编写出一个永远不需要更新的“完美”警报运行手册!当新事件发生时,运行手册需要更新,包括如何诊断故障的细节,以及系统更改时的更新。”
健康的事件回顾流程应包括在每次值班事件中更新值班运行手册,或者至少审查是否需要更新运行手册。
编写代码文档和撰写警报运行手册之间有相似之处。两者都对未来参考非常有用——例如,当工程师想要了解系统状况时——但在当下很容易被优先级降低。这意味着那些因缺乏值班运行手册而受挫的工程师需要以身作则,主动撰写这些手册。
作为资深工程师,定义一个“主”值班运行手册是提高值班效率的简单方法。尝试与工程团队合作,为常见警报创建运行手册,并将审查和更新警报运行手册作为事件响应流程的一部分。
值班是否有报酬取决于以下几个因素:
在大型科技公司以及市场薪酬靠前的公司(根据本书第一部分“薪酬”章节中的分类为第3层或第2层薪酬包),值班通常是一种普遍做法,并且没有额外补偿。亚马逊、Meta、苹果和微软等公司都遵循这一模式。例外情况是那些强制要求值班补偿的国家。”
谷歌是唯一一家为值班提供补偿的大型科技公司,同时限制值班时间。
拥有更“集中化”值班轮换的公司,工程师可以自愿加入,几乎总是会提供值班补偿。有关提供补偿的公司及其金额的列表,请参阅我的文章《软件工程师的值班补偿》。
薪酬处于市场中位或以下的公司,通常需要为值班支付报酬,因为这需要额外的时间投入,并且在正常工作时间之外会带来压力。在薪酬较高的公司,工程师通常会将额外的责任视为体面薪酬包的一部分。然而,如果工程师觉得值班没有得到足够的补偿,他们可能会寻找薪酬更高的工作,或者薪酬相似但没有值班压力的工作。”
在某些团队中,值班工作就像一份全职工作,频繁的故障占用了人们的时间,用于清理和执行后续行动。但对于大多数团队来说,工作强度并不高,在“良好”的值班周期间几乎没有额外工作。那么,值班工程师是否应进行“正常”工作?
最终,这取决于团队经理的决定。以下是定义值班工程师每周工作的一些常见方法:
作为资深工程师,你可能会对如何规划值班产生影响。在决定最佳方法时,需要考虑值班负载和团队动态。”
有一句常见的格言:“人们辞职是因为经理,而不是公司。”我有一个相关的观察:人们不仅辞职是因为经理,也因为糟糕的值班轮换。
“值班倦怠”是真实存在的,我曾多次见过。它往往发生在以下两种或更多因素的组合下:
“人们对值班倦怠的反应各不相同。有些人意识到问题的存在,并采取措施通过换团队或离职来改变现状。另一些人则继续坚持,但他们的表现会受到负面影响,这可能连他们自己都没有意识到!压力大的值班带来的影响是显而易见的;它让人感到疲惫不堪。
作为资深工程师,你可能是少数能让管理层认真倾听意见的个人贡献者之一。因此,如果你观察到某个团队或个人接近倦怠状态,请提出改善值班动态的建议。经理对团队健康负责,但如果经理比较放手,这可能需要你对值班状态进行诊断并提出改进建议。
当警报触发且值班工程师确认发生故障时,事件管理过程就开始了。事件管理的目标是尽快恢复系统的正常运行,并防止类似问题再次发生。
关于事件管理有各种框架,你的工作场所可能已经在使用其中之一。典型的事件生命周期步骤包括:
监控和警报是快速检测事件的关键方法,理想情况下可以在几分钟内完成。一旦警报触发,值班工程师需要评估是否发生了故障。
声明事件是事件管理流程的第一步。这通常通过公司首选的事件管理工具创建一个新事件来完成。
事件的分类和优先级通常在声明时确定。小范围客户受到影响的故障与所有客户系统宕机之间有很大的区别。
大多数科技公司从早期阶段就已建立了事件分级和分类机制。一些公司选择通过不同级别定义事件。例如,亚马逊按严重性定义级别:SEV-0是影响最大、范围最广的级别,SEV-1、SEV-2、SEV-3依次降低优先级。在Uber,5级(L5)是最严重的,L4、L3、L2依次影响更小、受影响用户百分比更低。一些公司将事件分为两个部分:影响(高/中/低)和范围(高/中/低)。
应有明确的标准来对事件进行分类,这些标准基于易于测量的指标,例如服务水平指标(SLI)。如果你发现公司在分类严重性方面模糊不清,这可能是改进这一领域的机会!
从声明事件开始,事件管理的角色分工应明确。谁负责协调事件响应?谁负责更新相关方?“事件指挥官”通常是响应协调员。这可能不是检测到事件的人。大多数工程团队很快就会发现明确这一角色是非常有帮助的。
大多数事件管理工具在声明事件时需要指定一名事件指挥官。与事件的严重性一样,这一角色可以后续更改。当故障从小范围发展到更严重时,事件指挥官发生变化并不罕见。
在声明故障后,缓解是最紧迫的步骤。尽快修复事件有时很简单;例如,如果故障是由最近的代码更改引起的,那么回滚该更改可能是一个快速的解决方案。
高效缓解通常涉及以下步骤:
评估故障的根本原因并不是最大的优先事项。较少经验的工程师常犯的一个错误是试图先了解故障原因,然后再开始修复。虽然不在了解原因前修复问题看起来不合理,但这种做法可能会延缓尽快缓解故障的努力。
如果有明显的缓解步骤可以立即开始,例如回滚代码更改或执行回滚计划,那么先执行这些步骤。一旦故障得到缓解,将有充足的时间去了解其原因。”
一旦事件得到缓解,是时候喘口气了。如果缓解发生在工作时间之外,那么可以好好休息,等到下一个工作日再跟进。
事件分析/事后总结通常是事件处理生命周期的下一步。常见的问题包括:是什么导致了事件?事件的确切时间线是什么?如何避免未来的类似事件?”
“事件回顾会议是一个更大的团队审查高影响故障事件分析文档的会议。一些公司有专门的事件管理团队负责此事,另一些公司每周或每两周召开一次会议,有一些经理参与,而其他公司则是根据需要临时进行。
事件跟进行动是团队认为有必要采取的措施,以避免未来发生类似事件。但在缓解后,这些行动很容易被降级优先级,特别是如果它们需要大量的工程工作。每个团队和公司都有不同的方式来跟踪这些事项并确保它们完成。作为资深工程师,你可以——也应该!——帮助团队腾出时间完成这些跟进工作,有时甚至需要以牺牲其他任务为代价。
无责回顾是科技行业中常见的方法。在进行事件分析时,避免将其变成寻找责任人的“猎巫”过程。
大多数故障是由某人进行的配置或代码更改引起的,很容易找到具体是谁导致的。但与其直接或间接地将责任归咎于某人,不如更深入地探讨为什么系统允许这些更改在没有反馈的情况下发生。如果未解决允许事件发生的条件,它们很容易在未来让其他人踩到同样的坑。
一些人对无责事后总结的理念持抵触态度。他们会问:“这会导致缺乏问责吗?”但在我看来,问责和无责文化是可以并行的。问责意味着人们对自己的工作负责,当事情不可避免地出错时,他们会承担责任并修复问题。无责方法则认识到,责备某人做了一件他们不知道会导致故障的事情是无益的,尤其是在他们对解决问题的原因负起责任时。
考虑你的事件回顾流程是否优先从事件中学习。在文章《事件回顾和事后总结最佳实践》中,我与John Allspaw——前Etsy CTO和Adaptive Capacity Labs创始人——进行了交流。John帮助公司改进事件管理流程,并分享了一个有趣的观察:
我们一次又一次地发现,大多数事件报告是为了归档,而不是为了阅读或学习。团队经历了事件,他们提交了一份报告,为自己感到自豪,并认为他们已经从中学到了东西。但实际上,他们的学习只是一小部分。
当前的事件处理方法仅仅触及了我们可以做的事情的表面。从某些方面来说,科技行业在如何构建可靠系统方面落后于其他几个行业。
关于建立符合大多数科技公司处理事件方式的事件管理流程,有很多现成的手册和工具。但真正稀缺的是那些能够成功利用事件管理作为学习工具,以使团队和系统更具弹性的公司。
作为资深工程师,你可以影响工作场所的事件管理流程如何演变。在此过程中,请记住,从事件中学习并将经验教训应用于整个组织应是任何事件管理系统的最终目标。这是领先公司的通用方法。”
如何构建一个可靠运行的系统?设计和编写具有弹性的系统是必须的。但弹性不仅仅来自于思考未来的故障和用例。以下是设计、构建、测试和操作弹性系统的方法:
弹性系统显然是为了表现出弹性而设计的。在规划阶段,请注意以下事项:
在构建系统时,有几个领域值得以弹性为重点进行关注:
有几种方法可以模拟故障并确认系统能够按预期处理它们。以下是一些示例: