每月处理15亿次登录,Auth0高可用架构实践

每月处理15亿次登录,Auth0高可用架构实践

我们设计的 Auth0 一开始就可以在任何地方运行:我们的云、你的云,甚至在你自己的私有基础设施上。

在本文中我们将详细探讨我们的公共 SaaS 部署,并简要介绍 auth0.com 背后的基础设施,以及用来确保它顺畅运行的高可用性策略。

我们曾在 2014 年撰文介绍了 Auth0 的架构,自那以来 Auth0 已发生了很大的变化,下面是几大要点:

  • 我们从每月处理数百万次登录升级至每月处理逾 15 亿次登录,为成千上万的客户提供服务,包括 FuboTV、Mozilla 和 JetPrivilege 等。
  • 我们实施了新功能,比如自定义域、扩展的 bcrypt 操作以及大大改进的用户搜索等。
  • 为了扩展我们组织的规模并处理流量的增加,我们产品的服务数量从不到 10 个增加到了 30 多个。
  • 云资源的数量也大幅增长,我们过去在一个环境(美国)中有几十个节点,现在我们在四个环境(美国、美国-2、欧盟和澳大利亚)有 1000 多个节点。
  • 我们决定为自己的每个环境使用单一云提供商,并将所有公共云基础设施迁移到了 AWS。

核心服务架构

每月处理15亿次登录,Auth0高可用架构实践

图 1:Auth0.com 的核心服务架构

我们的核心服务包括如下不同的层:

  • 核心应用程序:可自动扩展的服务器组,这些服务器运行我们堆栈的不同服务(身份验证、管理 API 和多因子身份验证 API 等)。
  • 数据存储:MongoDB、Elasticsearch、Redis 和 PostgreSQL 集群,为不同的应用程序和功能特性存储众多数据集。
  • 传输/队列:Kinesis 数据流以及 RabbitMQ、SNS 和 SQS 队列。
  • 基础服务:针对速率限制、bcrypt 集群和功能标志等其他的服务。
  • 路由:AWS 负载均衡系统(来自 AWS 的 ALB、NLB 和 ELB)以及一些运行 Nginx 充当代理的节点。

高可用性

2014 年,我们使用了多云架构(使用 Azure 和 AWS,还有谷歌云上的一些额外资源),多年来该架构为我们提供了良好的服务。随着使用量(和负载)迅速增加,我们发现自己越来越依赖 AWS 资源。

最初,我们将环境中的主区域切换到 AWS,Azure 留作故障切换区域。我们开始使用更多的 AWS 资源(比如 Kinesis 和 SQS)时,将同样的功能特性放在这两家提供商处开始遇到了麻烦。

随着移动(和扩展)的速度越来越快,我们选择继续支持 Azure,但功能同等性(feature parity)有限。

如果 AWS 上的一切出现故障,我们仍可以使用 Azure 集群来支持身份验证等核心功能,但是不支持我们一直在开发的许多新功能。

2016 年出现几次严重宕机后,我们决定最终集中到 AWS 上。我们停止了与确保服务和自动化平台无关的全部工作,而是专注于:

  • 在 AWS 内部提供更好的故障切换机制,使用多个区域,每个区域至少 3 个可用区。
  • 增加使用 AWS 特有的资源,比如自动扩展组(而不是使用固定的节点集群)和应用程序负载均衡系统(ALB)等。
  • 编写更好的自动化:我们改进了自动化,完全采用基础设施即代码,使用 TerraForm 和 SaltStack 来配置新的 Auth0 环境(以及替换现有环境)。

这让我们从每秒处理约 300 次登录的部分自动化环境升级到每秒处理逾 3400 次登录的全自动化环境;使用新工具更容易向上扩展(必要的话还可以向下扩展)。

我们实现的自动化水平并不完美,但让我们极其方便地扩大到新的区域、创建新的环境。

  • 编写更好的剧本(playbook):随着时间的推移,我们发现除了自动化外,还需要更好的剧本,以便了解、管理和响应与我们越来越庞大的服务网格相关的事件。

这极大地提高了可扩展性和可靠性,同时加快了新员工的入职。

比如说,不妨看看我们的美国环境架构。我们有这个总体结构,如下图:

每月处理15亿次登录,Auth0高可用架构实践

图 2:Auth0 美国环境架构

下图是单一可用区内部的结构:

每月处理15亿次登录,Auth0高可用架构实践

图 3:Auth0 单一可用区

在这种情况下,我们使用两个 AWS 区域:

  • us-west-2(我们的主区域)
  • us-west-1(故障切换区域)

正常情况下,所有请求都流向 us-west-2,由三个独立的可用区处理请求。

这就是我们实现高可用性的方式:所有服务(包括数据库)在每个可用区(AZ)上都有运行中的实例。

如果一个可用区因数据中心故障而宕机,我们仍有两个可用区来处理请求;如果整个区域宕机或出现错误,我们可以通知 Route53 故障切换到 us-west-1、恢复操作。

我们在服务故障切换方面有不同的成熟度级别:一些服务(比如在 Elasticsearch 上构建缓存的用户搜索 v2)可正常运行,但数据稍显陈旧,不过核心功能按预期运行。

在数据层中,我们使用:

  • 面向 MongoDB 的跨区域集群。
  • 面向 PostgreSQL 的 RDS 复制。
  • 面向 Elasticsearch 的每个区域的集群,自动快照和恢复定期运行,以解决缺少跨区域集群的问题。

我们每年至少进行一次故障切换演练,我们有剧本和自动化,帮助新的基础设施工程师尽快了解如何演练以及由此带来的影响。

我们的部署通常由 Jenkins 节点触发;视服务而定,我们使用 Puppet、SaltStack 及/或 Ansible 来更新单个节点或一组节点,或者我们更新 AMI,为不可变的部署创建新的自动扩展组。

我们为新旧服务部署了不同类型的环境;由于我们需要为应该统一的系统维护自动化、文档和监控,结果证明这基本上很低效。

我们目前在为一些核心服务推出蓝/绿部署(blue/green deployment),我们打算为每个核心的支持服务实施同样的一套。

自动化测试

除了每个项目的单元测试覆盖外,我们还有在每个环境中运行的多个功能测试套件。

我们在部署到生产环境之前先在试运行环境上运行,完成部署后再在生产环境中运行,以确保一切正常。

我们的自动化测试要点:

  • 在不同的项目中有数千个单元测试。
  • 使用每分钟运行的 Pingdom 探针(probe)来检查核心功能。
  • 在每次部署前后结合使用基于 Selenium 的功能测试和基于 CodeceptJS 的功能测试。功能测试套件测试不同的 API 端点、身份验证流程和身份提供者等。

CDN

2017 年之前我们运行自己专门定制的 CDN,在多个区域运行 Nginx、Varnish 和 EC2 节点。

2017 年以后,我们改用 CloudFront,它为我们带来了几个好处,包括:

  • 更多的边缘位置,这意味着为我们的客户缩短了延迟。
  • 降低维护成本。
  • 配置起来更轻松。

但同时也有几个缺点,比如我们需要运行 Lambdas 来执行一些配置(比如将自定义标头添加到 PDF 文件等等)。不过,好处绝对压倒缺点。

Extend

我们提供的功能之一是能够通过身份验证规则或自定义数据库连接,运行自定义代码,作为登录事务的一部分。

这些功能由 Extend(https://goextend.io/)提供支持,Extend 是一个可扩展性平台,由 Auth0 发展而来,现在还被其他公司所使用。

有了 Extend,我们的客户就可以用那些脚本和规则编写所需的任何服务,扩展配置文件、规范属性和发送通知等。

我们有专门针对 Auth0 的 Extend 集群,它们结合使用 EC2 自动扩展组、Docker 容器和自定义代理,以处理来自我们用户的请求,每秒处理数千个请求,并快速响应负载变化。

想了解这如何构建和运行的更多信息,请参阅这篇介绍如何构建自己的无服务器平台的文章(https://tomasz.janczuk.org/2018/03/how-to-build-your-own-serverless-platform.html)。

监控

我们结合使用不同的工具来监控和调试问题:

  • CloudWatch
  • DataDog
  • Pingdom
  • SENTINL

我们的绝大多数警报来自 CloudWatch 和 DataDog。

我们往往通过 TerraForm 来配置 CloudWatch 警报,用 CloudWatch 来监控的主要问题有:

  • 来自主负载均衡系统的 HTTP 错误。
  • 目标组中不健康的实例。
  • SQS 处理延迟。

CloudWatch 是基于 AWS 生成的指标(比如来自负载均衡系统或自动扩展组的指标)来监控警报的最佳工具。

CloudWatch 警报通常发送给 PagerDuty,再从 PagerDuty 发送给 Slack/手机。

DataDog 是我们用来存储时间序列指标并采取相应操作的服务。我们发送来自 Linux 系统、AWS 资源和现成服务(比如 Nginx 或 MongoDB)的指标,还发送来自我们构建的自定义服务(比如 Management API)的指标。

我们有许多 DataDog 监控指标,举几个例子:

  • $environment 上的 $service 响应时间增加。
  • $instance 中的 $volume($ ip_address)空间不足。
  • $environment / $ host 上的 $process($ ip_address)出现问题。
  • $environment 上的 $service 处理时间增加。
  • $host($ip_address)上出现 NTP 漂移/时钟问题。
  • $environment 上的 MongoDB 副本集变更。

从上面例子中可以看出,我们监控低级指标(如磁盘空间)和高级指标(如 MongoDB 副本集变更,这提醒我们主节点定义是否发生了变化)。我们做了大量的工作,设计了一些相当复杂的指标来监控一些服务。

DataDog 警报的输出非常灵活,我们通常将它们全部发送给 Slack,只把那些“引人注意”的警报发送给 PagerDuty,比如错误高峰,或者我们确信对客户产生影响的大多数事件。

至于日志记录方面,我们使用 SumoLogic 和 Kibana;我们使用 SumoLogic 来记录审计跟踪记录和 AWS 生成的许多日志,我们使用 Kibana 存储来自我们自己的服务和其他“现成”服务(如 Nginx 和 MongoDB)的应用程序日志。

未来设想

我们的平台经历了很大的变化,以处理额外负载和对客户来说很重要的众多使用场景,但我们仍有优化的空间。

不仅我们的平台在扩大,我们的工程部门规模也在扩大:我们有许多新团队构建自己的服务,而且需要自动化、工具和可扩展性方面的指导。

有鉴于此,我们落实了这些计划,不仅扩展平台,还夯实工程实践:

  • 构建类似 PaaS 的平台:如前所述,今天我们有不同的自动化和部署流程,这导致了混乱,给工程师设置了门槛,因为很难在不接触众多代码库的情况下进行试验和扩展。

我们正在为目前在 ECS 上运行的平台开发概念证明(PoC)代码,工程师们可以配置 YAML 文件,只需部署它,即可获取计算资源、监控、日志记录和备份等。

所有这一切都无需明确配置。这项工作还处于早期阶段,可能会发生很大变化。然而,鉴于我们不断扩大的规模和可扩展性方面的限制,我们认为我们的方向正确。

  • 针对每个新的合并请求实施冒烟测试(smoke test):除了单元测试(已经在每个新的 PR 上运行)外,我们希望尽可能在短暂环境上进行集成测试。
  • 将我们的日志记录解决方案集中到一家提供商。这可能意味着远离 Kibana,只使用 SumoLogic,但我们仍需要评估功能集和数据量等。
  • 自动衡量指标:现在我们的指标好多都是手动的DD部署时添加与指标有关的代码调用,以及使用 DataDog 接口来构建仪表板和监控器。

如果我们使用标准格式和命名,可以实现一些任务,比如自动构建仪表板/监控器,从日志提取指标而不是明确添加代码调用等。

  • 确保我们针对每个核心服务都有自动扩展和蓝/绿部署。这应该是我们新平台的默认功能,但在构建和测试时,我们需要为这方面仍然不足的核心服务改进扩展/部署/回滚机制。

作者:Dirceu Tiegs(沈建苗编译)

相关推荐