作者 | Mathias Schwarz
译者 | 王强
策划 | Tina
本文要点
- 优步的基础设施平台让数千名工程师能够在不牺牲稳定性的前提下并行更改系统。
- 我们扩展了原有的系统,并随着业务增长逐渐将抽象级别从单个主机提升到跨多个区域的众多地区。
- 从日常运维工作中抽象出物理主机和区域,让我们大大减少了在优步运行一系列无状态服务时产生的摩擦。
- 部署流程不仅运作起来简单、轻松,而且其自动化特性是整个无状态基础设施实现大规模自动化的关键所在。
- 统一到一个单一的托管控制平面后,我们极大提升了优步跨多个可用地域高效管理无状态负载的能力。
在 QCon Plus 上,优步的软件工程师 Mathias Schwarz 展示了优步如何 在全球级规模上安全、快速地部署。优步是一家大型企业,拥有多种产品。在大多数情况下,它们会被部署到全球数十或数百个市场。我们最大的产品是 Uber Rides——优步叫车产品,只需点击一个按键,它就能带你从城里的这个地方到达那个去处。每天,优步会完成 1800 万个出行订单——这还只是 2020 年第一季度的数字。除了优步叫车平台上的出行服务外,优步还有用于送餐的 Uber Eats——优食。
为了运营所有这些产品,优步拥有大量后端服务。大约有 4000 个不同的微服务部署在优步多个数据中心的机器上。
在优步,我们每周要做 58,000 次构建,每周对生产环境进行 5,000 次更改。换句话说,优步的每一项后端服务平均每周要在生产环境中部署一次以上。
由于执行服务升级过程需要一段时间,这也意味着系统每时每刻都至少会对我们的一项后端服务进行一些升级。
区域和地域
对于优步的基础设施,我们会将它拆分成许多层来考虑。最底层是各个服务器。服务器是单独的机器,是运行每个进程的硬件。服务器的物理实体会放置在某个区域(zone)内。区域可以是我们自己拥有的,比如说优步的数据中心,也可以是云区域,其中机器属于 GCP 公有云或 AWS 的一部分。
一个区域永远不会跨越多个提供者——它始终只有一个提供者,且一组区域组成一个地区(region)。地区本质上是物理上彼此靠近的一些区域的集合,因此这些区域内的进程之间的调用延迟较低,这意味着你可以期望从一个区域到另一个区域的请求具有较低的延迟。这些地区共同构成了我们的全球基础设施。因此,当你要将新构建部署到生产环境时,基本上就是将这些新构建全局部署到优步基础设施所有区域中的所有相关服务器的过程。
早期:非结构化部署
当优步开始构建他们的部署策略和部署系统时,一开始的做法与其他大多数公司是类似的。优步的每个服务团队都有一组特定主机,他们会在其中部署新构建。然后,每当他们想要发布更改时,都会手动访问这些服务器,或者使用 Jenkins 脚本将构建部署到服务器,并确保他们已经升级了所有进程,然后再发布新构建。然而这种方法有几个缺点。例如,当服务器出现故障时,团队需要手动清理。更糟糕的是,如果正在推出的更改中存在错误,就意味着团队必须在将错误的更改从生产系统中移除后做一番清理,让系统恢复到良好状态。
重要的部署系统特性
2014 年,我们退后一步,开始思考如何创建一个部署系统来将所有这些操作自动化,让我们的工程师更容易保持高频率部署节奏,同时确保安全性。我们提出了一系列希望系统能够完成的要求。我们希望我们的构建保持一致,此外还有:
- 希望构建看起来都是一样的,无论它们使用的是什么语言、什么框架,以及构建服务的是哪个团队。构建应该与部署系统是一致的,这样管理起来更方便。
- 此外,我们希望所有部署都具有零停机时间,这意味着当你想要部署你的构建时,你希望系统能够自动管理服务器的部署顺序。我们希望系统在不干扰进入服务的流量的情况下,尽量不要停止更多的进程。
- 想要让预防停机措施成为这个系统的一等公民。从本质上讲,我们希望系统能够在我们将新版本部署到生产环境时及时发现并响应可能存在的问题。
- 最后,我们希望系统能够让我们的后端恢复到良好状态。总体而言,我们希望工程师能够轻松部署新的更改,并能信任系统,让系统来处理这些部署的安全性。
使用 uDeploy 进行结构化部署
基于这些需求,优步开始构建 Micro Deploy 系统。Micro Deploy 于 2014 年上线。在那一年,我们将所有后端服务转移到了这个新平台。在 Micro Deploy 中,我们将所有构建都设为 Docker 镜像。我们还搭配使用了内部构建的,称为 Makisu 的构建系统来做到这一点。从本质上讲,这两个系统结合在一起意味着我们所有的 Docker 镜像看起来都是一样的,并且在部署的系统中有着相同的表现,从而显著简化了部署管理。
部署到区域中的集群
在优步,我们还改变了工程师的抽象级别。我们告诉他们,他们无需担心具体要部署到哪些服务器上,而只要告诉我们他们需要哪些区域以及每个区域中他们想要的容量即可。也就是说,我们并不要求工程师找到特定的服务器,而是给这些区域提供容量。然后我们会部署到目标区域内。每当出现服务器故障时,我们都会更换它,并且服务将被转移到这些新服务器上,全过程无需任何人工干预。我们在 uDeploy 中做到了这一点,结合了名为 Mesos 的开源集群管理系统,以及我们在优步内部构建的,名为 Peloton 的无状态负载调度程序(后者已开源)。今天,你可以使用 Kubernetes 实现类似的目标。
安全 - 监控指标
我们还决定将安全机制直接构建到已部署的平台中,以尽可能提升部署的安全性。我们在已部署平台中内置了我们的监控系统,uMonitor。我们所有的服务都会发出由 uMonitor 摄取的指标。uMonitor 按时间序列持续监控这些指标,并确保指标不会超出某些预定义的阈值。如果我们看到数据库指标突破了这些预定义的阈值,就会开始回滚到安全状态,这一操作将在 Micro Deploy 系统中自动执行。Micro Deploy 会捕获系统之前的状态,然后在启动回滚时自动将服务恢复到其旧状态。
安全 - 白盒集成
此外,对于优步最重要的那些服务,我们还进行了 白盒集成测试。我们使用了内部开发的,称为 Hailstorm 的系统。当你将第一个实例部署到一个新区域时,它将在生产环境中对这些特定实例运行负载测试,并运行白盒集成和负载测试。这种测试是在发布代码之前运行的大量集成测试之外的一种补充。
这些集成测试会针对已部署服务的 API 端点,并确保 API 仍按我们预期的方式运行。在部署到某个区域的前几个实例上执行此操作后,我们就可以在生产环境中的问题影响多个主机之前发现它们。如果其中一些测试失败,我们还可以回滚到服务的先前已知的安全状态。
安全 - 连续黑盒
最后,我们构建了所谓的 黑盒测试。黑盒测试本质上是在优步产品所在的所有城市不断发生的虚拟出行订单。黑盒测试会执行这些虚拟出行订单,如果我们发现某个城市订单无法完成,就会向一位工程师弹出一个页面。然后,这位工程师必须手动决定是回滚还是继续部署。他们还必须找出是哪些服务可能导致平台上的出行订单突然开始出现问题。所以黑盒测试是我们问题检测机制的最后一道防线。
Micro Deploy 为我们提供了大规模的安全性保障。即便个别服务器出现故障,它也能为我们提供服务的可用性。几年前,我们发现我们花费了越来越多的工程时间来管理服务。工程师仍然必须弄清楚在他们应该在哪些区域放置服务。例如,他们是要在 AWS 还是在我们自己的数据中心上提供服务呢?他们需要多少个实例?等等。两年前,服务管理仍然是一项需要大量人工干预的任务。
提升规模效率
因此,我们又一次退后一步开始思考,如何构建一个能够为我们的工程师自动完成所有这些日常任务,并确保平台能够自我管理的系统?
我们提出了三个要构建到系统中的原则:
- 首先,我们希望它是真正的多云架构,这意味着无论服务运行我们自己的数据中心还是某个公有云的主机或服务器上,对工程师来说都应该是一样的,没有什么区别。我们应该能够毫不费力地在任何地方进行部署。
- 其次,我们还希望它是全托管的,这意味着我们希望工程师只需要作出更改,确保这些更改有效,并将它们推到生产环境中就完事。我们不再指望他们来处理在区域中定位、扩展服务之类的人工管理任务。同时,我们仍然希望部署系统行为是可预测的。
- 最后,我们仍然希望工程师能够了解和预测他们的服务会发生什么事情。因此,即使我们决定更改缩放比例或将它们移动到云区域,我们也想告诉工程师到底发生了什么事情,以及为什么会这样做。
Up 提升基础设施效率
基于这三个原则,我们开始在优步构建我们使用至今的部署平台,名为 Up。在 Up 中,工程师在管理他们的服务并部署更改时需要考虑的抽象级别又进了一步。例如,我们并不会要求他们关心具体的区域,而是询问他们要部署到哪个物理地区。这样,使用 Up 的工程师只需指明他希望服务部署到哪个地区,然后 Up 就会负责其余的工作。对于我们今天的工程师来说,这套流程是下面这个样子。
我们可以看到,这个服务被部署到一个金丝雀中,并被部署到了两个不同的地区,在本例中它们分别称为“DCA”和“PHX”。我们不会告诉工程师物理服务器是在云区域中运行还是在我们自己的数据中心中运行。我们只是告诉他们有这两个地区,在这两个地区中一共有多少实例。
当工程师对生产环境进行部署时,如果系统决定对服务进行更改,他们就会看到这样的计划。该计划列出了已经执行的步骤,因此你可以看到服务到目前为止具体发生了什么事情。其次,它显示了当前正在发生的事情。例如我们目前正在为该服务升级哪个区域,以及我们的具体升级进度。最后,在应用当前更改之后,会有一个稍后将应用的更改列表——这意味着工程师完全可以预测在整个部署过程中会发生什么事情。
让我们添加一个新区域
我们希望 Up 系统做到的一件事,是让我们的后端工程师不用再关心基础设施,特别是底层基础设施的拓扑。具体来说,我们希望添加或删除区域的操作不会影响工程师。如果这里有一个地区,并且我正在向现有地区添加一个新区域,如下图所示。
基础架构团队将设置物理服务器,设置存储容量,并将区域物理连接到现有基础架构。现在的挑战是让 4000 个服务所有者和 4000 个服务,或者至少其中的一部分转移到新区域,以使用我们在该区域所有的新容量。在 Up 面世之前,需要数十名工程师才能完成一个新的区域部署工作,整个过程需要耗费大量人力和时间,所以我们希望 Up 为我们自动化这一步骤。
声明式配置
假设我们有一个如前所述的新区域;然后,工程师将仅根据地区及其在地球上的物理位置配置他们的容量。他们会告诉我们,他们想要 DCA 地区中部署 250 个实例,在 PHX 地区中部署 250 个实例。此外,他们可以告诉部署系统一些关于它们对其他服务的依赖关系的基本信息,以及他们是否想为这些服务使用金丝雀。然后,Up 负责查看这一配置并不断评估服务的当前位置和当前配置是否适合各个服务。Up 将不断地将基础设施的当前拓扑与这些声明式服务配置进行比较,并找出如何以最佳方式放置此服务。
有了这个配置和持续的评估循环,当我们添加一个新区域时,系统会发生什么事情呢?首先,Up 系统会自动发现更好的部署位置;比如说对于某些特定服务,可能有一个新区域可用,其容量比现有区域大得多。然后在评估这些限制并确定有更好的位置之后,我们还有一个 Balancer,负责启动从该地区内的一个区域到另一个区域的迁移任务。工程师不再需要花时间手动移动这些服务,因为我们的 Balancer 会自动为他们完成这项工作。
总 结
本文介绍了我们从由工程师来管理各个服务器的小规模系统,到可以自动管理服务器的,基于区域抽象的 Micro Deploy 系统的历程。彼时,整体来说服务管理仍然是我们工程师日常需要维护的一项任务。最后,到我们的新 Up 系统面世后,我们在区域层面实现了完全自动化。你可以安全地每周向生产环境部署 5,000 次更改,并且你可以轻松管理像优步后端这么巨大规模的系统。让它在实践中发挥作用的关键是自动化。它的抽象级别让你可以自动执行很多原本需要工程师手动管理的任务。这意味着无论是部署位置、主机提供商选择以及服务的扩展方面,我们都可以将工作完全交给机器。
作者介绍
Mathias Schwarz 在优步担任基础设施工程师已超过 5 年。他和他的团队负责开发被整个优步工程团队所使用的无状态服务部署平台。Mathias 拥有奥胡斯大学编程语言组的计算机科学博士学位。
原文链接:
https://www.infoq.com/articles/uber-deployment-planet-scale/