关键要点
- 在过去的四年中,Lyft 已从单体架构转变为包含数百个微服务的架构。随着微服务数量的增加,级联故障或内部拒绝服务导致的中断次数也在增加。
- 现在,这些问题在 Lyft 基础设施上已得到解决。Lyft 使用 Envoy 代理自动保护服务的吞吐量和并发。
- Envoy 可以作为中间件,或者部署在请求入口,但最好的做法是将它部署在应用程序流量的入口和出口。将 Envoy 部署在请求的两端,让它充当服务器的智能客户端和反向代理。
- 在接下来的几个月,Lyft 将与 Netflix 的团队合作,将基于他们的并发库的系统集成到 Envoy L7 Filter 中。
级联故障是造成高吞吐量分布式系统不可用的主要原因之一。在过去的四年中,Lyft 已从单体架构转变为包含数百个微服务的架构。随着微服务数量的增加,由级联故障或内部拒绝服务导致的中断次数也在增加。现在,这些问题在 Lyft 基础设施上已得到解决。Lyft 使用 Envoy 代理自动保护服务的吞吐量和并发。通过对最关键的服务进行一些有针对性的配置更改,因负载过大造成的影响用户体验的事故减少了 95%。
在介绍我们的特定故障场景和相应的保户措施之前,先让我们来了解下 Lyft 是如何部署网络防御的。 Envoy 是一款由Lyft 开发的代理软件,后来开源出来,并捐赠给云原生计算基金会(CNCF)。Envoy 与其他负载均衡解决方案的区别在于它可以被部署成“网格”。Envoy 可以作为中间件,或者部署在请求入口,但最好的做法是将它部署在应用程序流量的入口和出口。将Envoy 部署在请求的两端,让它充当服务器的智能客户端和反向代理。我们可以选择使用速率限定和断路器来避免服务器过载。
核心概念
并发和速率限定
并发和速率限定之间存在关联,但它们的概念不一样的,就像同一枚硬币的两面。在考虑限制系统负载时,运维人员一般会使用每秒请求数。速率限定是指限制发送到系统的请求速率。我们通常通过压力测试来确定服务在达到过载临界点时的请求速率,然后将速率限制设置为低于该点的某个值。在某些情况下,业务逻辑决定了速率限定。
硬币的另一面是并发性,即同时有多少个单元在运行。这些单位可以是指请求、连接等。例如,我们可以考虑某个时间点的并发请求数,而不是请求速率。在考虑并发请求时,我们可以应用队列理论来确定在队列出现之前我们的服务可以处理的并发请求数、请求延迟增长,以及因资源耗尽而导致的服务失败。
全局和局部决策
Envoy 中的断路器是基于局部信息计算得出的。每个 Envoy 实例都会跟踪自己的统计数据,并制定自己的断路决策。与全局系统相比,这种模型具有一定的优势。首先,可以在内存中计算并作出决策,无需通过网络调用中央系统。其次,限制决策可以随着集群的大小而伸缩。第三,限制决策考虑到了机器之间的差异,比如它们可能收到的不一样的查询,或者在性能上存在差异。
常见的故障场景
在介绍防御机制之前,先来了解一些常见的故障模式。
重试堆积
当一个依赖项发生故障,如果一个服务为所有对该依赖项的请求执行一次重试,那么整体调用量将加倍。
资源饥饿
每个服务都受到某些因素的约束,通常是 CPU、网络或内存。并发请求通常与消耗的资源量有直接的关系。
从资源饥饿中恢复
即使造成资源消耗增加的问题得到解决,服务本身仍然可能因为资源争用而无法恢复。
后端减速
依赖项(数据库或其他服务)速度变慢,导致服务需要更长的时间才能完成请求。
突发性和欠采样
在进行高级容量规划或扩展服务时,通常的做法是考虑整个群集消耗的平均资源。但是,服务的调用者可能会同时发送大量请求,导致单个服务器过载。在收集指标时,基于分钟或其他时间单位的数据将会掩盖掉这个事实。
如今的 Lyft
我们如何进行速率限定?
lyft/ratelimit 是一个开源的 Go/gRPC 服务,旨在为各种应用程序提供速率限定方案。速率限定可以被应用在域上,例如基于 IP 的速率限定,或每秒数据库连接数。ratelimit 已经被用在 Lyft 的生产环境中,每秒处理数十万个速率限定请求。我们还将它用在边缘代理和内部服务网格中。
这个服务是 Envoy 速率限定 API 的参考实现。Envoy 提供了两种集成方式:
- 网络级别的速率限定过滤器:为每个新的网络连接调用速率限定服务,在配置中指定要进行速率限定的特定域和描述符。
- HTTP 级别的速率限定过滤器:为每个新的请求调用速率限定服务。
在 Lyft,我们主要使用速率限定来抵御基础设施边缘的负载。例如,我们限定了每个用户 ID 的请求率。这样可以避免 Lyft 的服务发生因外部客户端非预期或恶意负载造成的资源饥饿。
监控
Lyft 的网络团队还收集了所有速率限定的度量指标。当服务所有者为边缘、服务之间或数据库创建新的速率限定以时,可以立即收集与防御机制有关的数据。
上面是 ratelimit 服务仪表盘的一个截图,其中显示了三个面板:
- 每分钟总命中数:这个面板显示了速率限定的总命中数时间序列,服务所有者可以看到速率限定的变化趋势。
- 每分钟超限(over limit):这个面板显示了超出限定的指标。这个面板为服务所有者提供了可量化的数据,用于评估服务模式,并为高负载事件进行容量规划。
- 每分钟近限(near limit):这个面板显示了达到限定阈值 80%的指标。
我们如何管理并发?
Envoy 的主要优点之一是它通过网络级的断路系统来强制执行并发限定,而不必在每个应用程序中进行配置。Envoy 支持各种类型的全分布式断路器:
- 最大连接数:Envoy 为上游群集的所有主机建立的最大连接数。实际上,这通常用于保护 HTTP/1 集群,因为 HTTP/2 可以通过单个连接复用请求,因此可以限制连接数的增长。
- 最大挂起请求数:等待连接池可用连接的最大队列请求数。实际上,这仅适用于 HTTP/1 群集,因为 HTTP/2 连接池不会将请求放进排队。HTTP/2 请求使用了多路复用机制。
- 最大请求数:在任何给定时间,群集中所有主机未完成的最大请求数。实际上,这主要用于 HTTP/2,因为 HTTP/1 通常是一个连接对应一个请求。
- 最大活跃重试次数:在任何给定时间,群集中所有主机可以执行的最大重试次数。通常,我们建议主动进行断路重试,在允许的范围内重试故障,但整体重试量不会出现暴增并导致大规模级联故障。这个配置可防止出现重试堆积。
关于在服务网格中管理并发性,我们专注于以下两种机制:
- 限制入口层的并发连接数。Lyft 的每项服务都配备了一个 Envoy 边车,用来管理进入服务的传入请求,因此,我们可以限制边车发给应用程序的并发连接数,从而限制传入并发请求进入应用程序。我们提供了合理的默认值,但服务所有者应该分析自己的并发模式并根据实际情况调整设置。
- 限制出口层的并发请求数。通过边车来管理服务传出流量的另一个好处是,我们可以管理从一个服务发到其他服务的并发传出请求。这意味着“位置”服务的所有者可以选择性地配置他们想要支持其他服务的并发级别,例如,他们可以只允许“乘车”服务向“位置”服务发出 100 个并发请求,“用户”服务只能向“位置”服务发出 50 个并发请求。
对每个服务的出口和入口实施并发限定的一个有趣结果是,可以更容易地跟踪非预期的行为。突发性故障难以通过指标来诊断,但在进行出口和入口并发限定之后,我们可以通过查看请求路径中发生并发溢出的位置来诊断整个系统的突发行为。
正如我们所提到的,并发并不总是一个直观的概念。为了改善这一缺点,网络团队为服务所有者提供了不同的可视化,让他们可以配置并发限定,然后对其进行监控。
配置速率限定
上面是一个交互式仪表盘,服务所有者可以试验服务允许的最大并发请求数。在上面的“位置”服务示例中,除“视口”服务之外,其他大部分服务被限定为 60 个并发请求。通过这个仪表盘,服务所有者可以查看并发配置的选择性变更在当前网络拓扑中是什么样子的,并可以放心地做出这些变更。
监控速率限定
如上所述,让 Envoy 边车处理每个服务的传入和传出流量,起到保护服务免受传入并发和传出并发影响的作用。网络团队自动创建如下所示的仪表盘,帮助服务所有者可视化并发。
传入并发
通过上面的两个面板,服务所有者可以看到从边车 Envoy 到服务的并发连接数(使用左边的面板),以及是否达到了并发限制(使用右侧的面板)。
传出并发
使用上面的两个面板,服务所有者可以看到从其他服务到他们的服务的并发请求数(从左侧的面板)。此外,他们还可以看到超出配置限定的服务(使用右侧面板),然后使用具体数据来解决问题。
存在哪些不足?
然而,选择合适的限定值是很难的。为速率限定选择合适的值难,为并发限定选择合适的值更难。我们必须考虑到几个重要因素。并发限定是局部的,必须考虑到最大可能的并发性而不是平均值。工程师不习惯于进行局部思考,他们主要考虑的是请求速率而不是并发性。借助一些可视化工具和统计数据,服务所有者通常可以看到并发情况并选择合适的值。
除了难以选择合适的值之外,Lyft 的服务一直在发生变化。服务网络中每天发生数百次部署。对服务及其依赖项的任何更改都有可能涉及更改资源和负载配置文件。一个选好的值会因为这些变化而变得过时。例如,几乎所有服务都是 CPU 密集型的,如果一个 CPU 密集型服务的直接依赖项减慢 25%,那么这个服务就可以处理额外的并发性,因为之前使用 CPU 的请求现在在等待网络 I/O。因此,建议将值提高 25-50%。
路线图
短期
Lyft 的网络团队专注于为服务开发人员构建易于使用的系统,方便他们配置、运维和调试 Envoy 及相关系统。因此,我们不仅要设计、实现和部署上面所展示的系统,还要为我们的用户提供持续的支持。在 Lyft,基础设施团队的一个主要原则是为服务所有者提供的抽象应该是自助式的。这意味着我们需要在文档化用例、提供调试工具和提供支持渠道方面投入大量精力。
根据并发性的不同,在短期内,网络团队继续在额外的文档和工程教育上进行投入。在过去,我们已经看到了在以下两个方面所取得的成功:
- 常见问题解答:常见问题列表对于客户来说是非常有用的,它减少了直接回答问题给我们所带来的负担(例如通过 Slack、电子邮件,甚至是面对面沟通!)。你可以轻松地向提问的人提供文档的链接,这种方式比反复回答相同的问题要好得多。不足是这些问题列表可能会变得冗长而难以解析。这个问题可以通过对内容进行分类来解决。
- 选择适合自己的路线:服务所有者需要根据自己的具体情况作出选择。之前所描述的并发可能会出现几个问题,我们可以通过修改几个设置来解决这些问题。
文档和工程教育的短期投入缓解了并发问题的一个方面:系统的直观性问题。但是,它们并没有解决另一个问题:过时。
长期
并发限定很容易实现,因为 Envoy 存在于网络的每一个节点上。但是,正如我们所看到的,限定阈值是很难确定的,因为它要求服务所有者对系统约束有全面的了解。此外,在当今的互联网公司中,尤其是那些处于成长阶段的公司,由于网络拓扑结构的不断演化,静态限定阈值很快就会过时。
Netflix 在这个问题上投入了大量精力,他们最近开源了一个库,用于衡量或估算网络中每个节点的并发限定。更重要的是,随着系统规模的增长和触及限定阈值,[系统中] 的每个节点将调整并强制执行其局部的限定视图。他们借鉴了常见的 TCP 拥塞控制算法,将系统的并发约束与 TCP 拥塞窗口对应起来。
Envoy 的设计原则之一通过丰富且功能强大的过滤器栈来提供可扩展性。Envoy 具有 L3/L4(TCP 级别)和 L7(HTTP 级别)过滤器栈。开发人员可以开发 HTTP 过滤器来操作 HTTP 级别的消息。HTTP 过滤器可以停止或继续后续的过滤器。这种过滤器架构可以应对复杂的场景,例如运行健康状况检查、调用速率限定服务、缓冲、路由、生成应用程序流量统计数据等。
在接下来的几个月里,Lyft 将与来自 Netflix 的团队合作,将基于他们并发库的系统集成到 Envoy L7 过滤器中。这意味着 Lyft——以及使用 Envoy 的公司——将迁移到自动化系统,让工程师不必再静态配置并发限定。例如,如果意外情况导致服务变慢,自适应限定系统会自动抑制检测到的限定,从而避免由非预期减速导致的故障。一般而言,自适应系统消除了我们过去遇到的两个问题:难以确定适当的限定阈值,以及静态限定在弹性分布式系统中的快速增长。
最后的想法
要了解更多有关 Envoy 断路器的实现细节,请参阅 Envoy 文档中的断路器架构概述。作为一个开源项目,Envoy 对代码贡献持开放态度,也欢迎新的想法。即使不参与贡献代码也没关系,可以提供改进断路器的建议。例如,目前还没有实现的一个功能是基于系统资源的断路。我们可以在处理传入流量时直接断开使用CPU 的流量,而不是根据CPU 的使用情况得出并发请求阈值。
虽然断路器可以改善系统在负载下的行为,但也不要忘了对系统本身进行改进。断路器应被视为故障保护,而不是主要的约束手段。服务所有者应该使用断路器知识来改进自己的代码库。限制资源池的并发是解决并发问题最常用的方法。如果在同一上下文中生成大量请求,则调用者可以选择使用批处理 API。如果批处理 API 不存在,可以尝试实现一个。这些模式往往是教育过程的进一步延伸。在 Lyft,网络团队与其他团队合作,对所有服务进行教育和改进。通过教育、系统设计改进和网络级别的并发管理,服务所有者可以设计、实现和运营大型分布式系统,同时最大限度降低因负载而导致级联故障的风险。
关于作者
Jose Nino 是 Lyft 网络团队开发工具和配置方面的负责人。在近两年的时间里,他一直在 Lyft 工作。Jose 一直在创建系统来扩展 Lyft Envoy 生产环境的配置,以适应日益庞大的部署和工程组织。他曾经是开源项目 Envoy 的维护者,并一起建立起了 Envoy 社区。最近,Jose 在扩展 Lyft 的网络负载容错系统。Jose 发表了一些有关 Envoy 和其他相关话题的演讲,最近一次是在 Kubecon EU 2018 上。
Daniel Hochman 是 Lyft 的高级基础设施工程师。他热衷于扩展创新产品和流程,以提高公司内外的工作和生活质量。在 Lyft 工作期间,他成功带领平台走过了产品和组织的爆发式增长时期。他开发了吞吐量最高的微服务之一,并引入了几种关键的存储技术。Daniel 目前负责 Lyft 的流量网络,并负责在内部和边缘扩展 Lyft 的网络基础设施。
查看英文原文: Envoy Service Mesh Case Study: Mitigating Cascading Failure at Lyft
评论 1 条评论