将数百万并发WebSocket迁移到特使

2021-03-18 03:41:08

Slack有一个全球客户群,在高峰时段的数百万同时连接用户。用户之间的大多数沟通涉及彼此发送大量微小消息。对于大部分休闲历史,我们将Haproxy用作所有传入流量的负载均衡器。今天,我们将谈论我们面临的问题,我们如何用特使代理解决它们,迁移所涉及的步骤以及结果是什么。让我们潜入!

要立即传送消息,我们使用WebSocket连接,一个双向通信链路负责您看到“几个人正在键入...”,然后将其键入的东西键入,几乎与光速度一样快。 WebSocket连接被摄取到名为“WSS”(WebSocket服务)的系统中,并使用WSSS-Primary.slack.com和WSS-Backup.slack.com(这不是网站,您只需获取HTTP 404,即可从Internet访问你去那里)。

WebSocket连接作为常规HTTPS连接启动,然后客户端发出协议交换请求以升级与WebSocket的连接。在Slack时,我们有不同的WebSocket服务专用于消息,存在(列出哪些联系人在线),以及其他服务。其中一个WebSocket端点专门用于需要与Slack交互的应用程序(因为应用程序也想要实时通信)。

在过去,我们在多个AWS区域中有一组专用于WebSockets的HAProxy实例,以终止靠近用户的WebSocket连接并将请求转发给相应的后端服务。

虽然我们一直在使用Haproxy以来,但在Slack开始并了解如何在规模上运行它,但有一些业务挑战使我们考虑替代品,如Evenoy代理。

在Slack时,它是后端服务端点列表更改的常见事件(由于添加或循环外部)。 HAProxy提供了两种方法来更新其配置以适应端点列表中的更改。一个是使用HAProxy运行时API。我们使用这种方法与我们的一个Haproxy实例之一,我们的经验描述于另一个博客文章中 - 一个可怕,可怕,没有好的,在松弛时非常糟糕的一天。我们用于WebSockets Load Balancer(LB)的其他方法是将后端渲染到HAProxy配置文件中并重新加载HAProxy。

使用每个HAProxy重新加载,创建一个新的进程来处理新的传入连接。我们会继续运行旧进程数小时才能允许长期的WebSocket连接耗尽并避免频繁断开用户。但是,我们不能拥有太多的haproxy进程,每个流程每个都运行它是自己的“当时”的配置 - 我们希望在新版本的配置上更快地收敛实例。我们不得不定期获得旧的HAProxy流程,并限制Haproxy可以重新加载的频率,以防底层后端遇到潮流。

无论我们使用哪种方法,它都需要一些额外的基础架构来管理HAProxy重新加载。

Envoy允许我们使用动态配置的群集和端点,这意味着如果端点列表更改,则不需要重新加载。如果代码或配置进行更改,则Envoy可以在不删除任何连接的情况下热重启。 Envoy手表具有Inotify的文件系统配置进行更新。 Emenoy还在热重启期间将父进程中的统计信息复制到子进程,因此仪表和计数器不会重置。

这一切都会增加了与特使的操作开销的显着减少,并且无法管理配置更改或重新启动所需的其他服务。

恐慌路由 - 特使通常将仅将流量路由到健康的后果,但如果健康主机的百分比下降到阈值以下,则可以配置为向所有后端,健康或不健康发送流量。这在2011年1月4日的中断期间非常有用,这是由我们基础设施的广泛网络问题引起的。

由于上述原因,在2019年,我们决定从HAProxy迁移到Envoys Proxy,从WebSockets堆栈开始。迁移的主要目标是改进的可操作性,获取特使提供的新功能以及更多标准化。通过从Haproxy移动到所有休闲的特使,我们将消除我们的团队需要了解两块软件的怪癖,维护两种不同的配置,管理两个构建和释放管道,等等。到那时,我们已经使用了Servoy代理作为我们服务网格中的数据平面。我们还有经验丰富的特使开发人员在内部,所以我们已经准备好获得了特使专业知识。

此迁移的第一步是审查我们现有的WebSocket层配置并生成等效的特使配置。管理特派团配置是迁移期间的最大挑战之一。特使有一个丰富的功能集,它的配置与Haproxy的配置完全不同。 SEVOY配置处理四个主要概念:

群集,代表我们发送请求的内部服务,如消息服务器和呈现服务器

Slack的配置管理主要通过厨师完成。当我们开始使用Envoy时,我们将特使配置部署为厨师模板文件,但它变得繁琐和容易管理。要解决此问题,我们构建了厨师库和自定义资源以生成特使配置。内部厨师,配置是一个单身级,建模每个主机只有一个特使配置。所有厨师资源都在该单例上运行,添加侦听器,路由或集群。在厨师续期的末尾,Evenoy.yaml生成,验证,然后安装了 - 我们从不编写中间配置,因为这些可能无效。

此示例显示我们如何创建一个HTTP侦听器,其中两个路由将流量路由到两个动态群集。

需要努力在特使中复制我们复杂的Haproxy配置。所需的大多数特征已经在特使中提供,所以这只是向厨师图书馆和瞧瞧它的支持!我们实施了一些失踪的特使特征(有些是上游贡献,有些是在内部维持作为扩展)。

测试新的Senfoy WebSockets Tier是一个迭代过程。我们经常用手工编码的特派团配置原型,并在具有一个侦听器,路线和群集的开发机上本地测试。一旦他们工作,手工编码的变化将被移入厨师图书馆。

当事情按照预期运行时,我们使用了在机器上本地登录的特使调试:调试日志记录清楚地解释了为什么特使选择将特定请求路由到特定群集。 Envoy调试日志非常有帮助,但也有冗长且昂贵(您真的不想在您的生产环境中启用它)。可以通过CURL启用调试日志记录,如下所示:

/ clusters - 显示所有已配置的群集,包括每个群集中的所有上游主机的信息以及每个主机统计信息。

/ cercts - 显示所有已加载的TLS证书,包括文件名,序列号,主题备用名称和JSON格式到期。

我们的厨师库运行Sengoy与`-mode validate`命令行选项作为验证步骤,以防止安装无效的配置。这也可以手动完成:

特使提供JSON格式化的侦听器日志。我们将这些日志摄入到我们的记录管道中(当然,在消毒PII的日志之后),这通常有助于调试。

一旦对我们的开发环境中的配置充满信心,我们就可以在生产中进行更多的测试!

为了最大限度地减少迁移过程中的风险,我们构建了一个新的Senfoy WebSocket Stack,其具有等效配置到现有的HAProxy层。这意味着我们可以为新的特使堆栈逐步,控制的流量转移,并且如果需要,我们可以快速切换回Haproxy。缺点是我们的AWS成本 - 我们在迁移过程中使用了双倍的资源,但我们愿意花费时间和资源来为客户透明地进行这种迁移。

我们通过NS1管理我们的DNS记录WSS-PRIMARY.SLACK.com和WSS-BACKUP.SLACK.com。我们使用加权路由将流量从HAProxy-WSS转移到Envoy-WSS NLB DNS名称。第一区域单独推出10%,25%,50%,75%和100%的步长。与在一周的前一个地区相比,最终地区的完成速度更快(25%,50%,75%,100%),因为我们对新的特使层和卷展处理过程中的信心有信心。

即使迁移是平滑和无法下降的,也有许多造成的小问题,例如超时值和标题的差异。我们在迁移期间再次恢复,修复,并在多次再次推出。

经过一个很长而令人兴奋的6个月后,迁移完成,整个HAProxy WebSocket堆栈被全局替换为零客户影响。

迁移本身相对平静,无聊。无聊是一件好事:令人兴奋的意思是事情破裂,无聊意味着事情继续工作。

我们发现旧的Haproxy Config随着时间的推移而有机地增长。它主要由Haproxy使用的模型 - 一个包含所有侦听器的大型配置。特使的配置模型使用比haproxy模型更具定义的范围。输入侦听器后,只有该侦听器中的规则仅适用于请求。输入路由后,仅适用该路由的规则。这使得将规则与相关请求相互易于联系起来。

从有效的技术债务中提取旧的Haproxy配置中很重要的时间需要很多时间。弄清楚为什么某个规则到位为什么,有意就是无意的,以及其他服务的行为依赖的行为。例如,某些服务被认为只属于两个虚拟主机(vhosts)中的一个,但实际上是在Haproxy中的vhost中获得的。我们必须复制这个错误,因为现有代码依赖于该行为。

我们在Haproxy堆栈中错过了一些微妙的东西。有时这些都很重要 - 我们为一点(哎呀!)打破了Slack的日常活跃的用户(DAU)度量。还有很多小问题来解决。负载均衡器行为很复杂,除了时间和调试之外,还没有真正的方法。

没有负载平衡器配置的测试框架,我们开始迁移。而不是拥有自动测试,该测试验证了路由到正确的端点和与请求和响应标题相关的正确终端点和行为的测试URL ...一个HAProxy Config。在迁移期间测试是有帮助的,因为它们可以提供大量关于预期行为的原因的背景。因为我们缺乏测试,我们经常不得不与服务主人一起办理登机手续,以了解他们依赖的行为。

我们构建的厨师资源仅支持一个小组功能的子集。这使我们的图书馆更简单 - 我们只需要考虑我们实际使用的功能。缺点是,每次想要使用新的特征功能时,我们都必须在厨师库中为他们添加支持。例如,SNI(HTTPS)侦听器通过开发写成兼职,当我们决定它比向现有侦听器添加支持更简单。然而,当它来到vhost支持时,我们已经开发了很多代码,并且已经在整个公司其他地方使用的重构资源已经需要很长时间。我们厨师图书馆的vhost支持是一个黑客(有一天我们会解决它)。

为了使Senfoy资源厨师库更安全 - 换句话说,以确保我们没有打破使用我们库的其他团队 - 我们介绍了一系列生成这些团队的整个配置的一系列测试。这使得究竟在更新特使厨师资源时,究竟可以究竟如何影响所有生成的特使配置(或不会)。

这种迁移中的一个关键东西(如任何其他)是通信的。我们努力让每个人都通知并与我们所做的更改保持一致。我们的客户体验(CE)团队是一个很棒的合作伙伴 - 他们能够监控任何可能表明用户因此迁移而受到影响的任何可能影响的传入门票。

尽管偶尔的次要挫折,但特使WebSocket迁移是一大堆成功。我们通过迁移另一个关键的休闲服务,我们的软件客户指标摄取管道 - 从我们的其他入口负载平衡器隔离 - 给Envoy Proxy。我们差点迁移了为我们的网络和API流量迁移到Emenoy的内部负载均衡器。此史诗迁移的最后一部分是移动我们的(常规,非WebSocket)HTTP堆栈,该HTTP堆栈终止我们边缘的传入流量,从Haproxy到Envoy;这也是如此。

我们目睹了我们对我们的入口负荷均衡器和服务网格数据平面的指标标准化的最终目标,这将显着降低团队的认知负荷和运营复杂性,以及在我们的整个方面提供特使的高级功能负载平衡基础架构。由于迁移到特使,我们已经超出了我们之前的峰值负荷,没有任何问题。

巨大的Shoutout到团队 - Laura Nolan,Stephan Zuercher,Matthew Girard,David Stern,Mark McBeride,John On,Cooper Bethea,V Brennan,Ann Paul,普拉马拉·辛格,Rafael Elvira和Stuart Williams为项目的所有支持和贡献。

想帮助松懈解决棘手的问题并加入我们的成长团队吗?查看我们所有的开放职位并立即申请。