用户 4 亿多,Instagram是如何做持续部署的?

【伯乐在线导读】:2016 年 6 月的时候,Facebook 旗下图片社交软件 Instagram 注册用户达到 5 亿人,日活 3 亿。2016 年 12 月中旬,Instagram 对外宣传注册用户数量达到 6 亿。本文是 Instagram 工程博客在 2016 年 4 月发布,伯乐在线编译如下。

在 Instagram,我们每天部署后端代码大约是 30~50 次。无论什么时候,只要工程师提交了修改到服务器,都会进行一次部署,而大多数情况下,这都无人参与。这听起来很疯狂,尤其是在像我们这种规模的情况之下,但这的确没有什么问题。本文将讨论我们是如何实现这个系统的,以及如何保证它稳定流畅地工作。

为什么要持续部署?

持续部署对我们有诸多好处:

能够提高工程师们的工作效率。工程师们无须被限制于每天在固定时间进行几次部署;相反,他们随时都可进行代码部署。这意味着他们可以节约时间并快速地对代码进行迭代更改。

工程师们识别错误的提交变得容易得多。持续部署使得工程师们在查找引起新错误的原因的范围缩小了,由之前需要在几十或数百个提交中进行排查缩小到只需在一个或最多两、三个提交中查找。当你稍后发现问题并返回调试时,这也非常有用。运用指示问题的指标或数据,我们可以精确地确认问题的开始时间,继而查找出那个时间点进行的部署都有哪些代码提交。

让错误提交被快速发现和处理。这意味着我们在服务器上不会出现无法部署的糟糕情况,而对其它无关的变更造成重大延迟。我们总是处于一个能够快速地从重大困境中恢复的状态。

实现

这种系统能够得以实现成功,很大程度上归功于它采用迭代方法进行构建。我们没有独立地构建系统,然后突然切换;相反,我们逐步更新当前机制,直到它们成为了持续部署。

持续部署之前

在持续部署实现之前,工程师们需要对变更进行特地部署。他们完成代码变更后,如果希望它们被尽快部署,他们将运行一次部署。否则,他们会等待另一位工程师来这样做。工程师们被认为是能够知道如何事先进行小规模测试的: 他们先在一台机器上运行部署,登录该机器并检查日志,然后针对整个机队进行部署。这些都使用 Fabric 脚本实现,我们有一个非常基础的数据库和用户界面软件“ Sauron ”来存储部署日志。

Canary 和测试

第一步是添加 canarying,这在最初只是用来简单地编写工程师们已经预期的工作。脚本用来部署到 canary 机而不是在一台机器上运行一个独立的部署,跟踪用户日志,并询问是否继续全面部署。接下来是对 canary 机的一些基本分析: 一个脚本收集每个请求的 HTTP 状态码,对它们进行分类,并应用硬编码率阈值(例如,小于0.5% 5xx,至少90% 2xx)。但是,如果阈值失败,则只会向用户发出警告。

我们有一个测试组件,但它只运行在工程师的开发机器上。如果开发人员说测试通过,代码评审人员也只能采信,并且我们也无法知道提交到服务器上的代码的测试结果。因此,我们安装 Jenkins 对服务器上的新的代码提交运行测试,并报告结果给 Sauron。Sauron 将跟踪通过了测试的最新提交,并将此提交作为部署建议,而不是最后一次的提交。

Facebook 使用 Phabricator(http://phabricator.org/ )进行代码评审,并拥有一个与 Phabricator 集成很好的持续集成系统 Sandcastle 。一旦创建或更新存在差异,我们使用 Sandcastle 运行测试,并提供报告。

自动化

为了实现自动化,首先得做一些基础工作。我们给部署添加了状态(运行、完成、错误),一旦之前的部署不处于“完成”状态,就让脚本发出警告。我们在界面上添加了一个停止按钮,可以将状态变更为“停止”,同时,也让脚本不定期地检查状态并作出相应的反应。我们还对提交增加了完整跟踪; Sauron 之前只知道最后通过了测试的提交,但现在每一次在服务器上的提交都会有记录,而且知道每次提交的具体测试状态。

然后,我们对其余的仍需要人参与决策的部分也实施了自动化。首先,需要确认哪些提交可以部署。最初的算法总是选择已通过测试的提交,并且越少越好——不会超过三个。如果每个提交都通过了测试,那么每次都会选择一个新的提交,并且最多只可能有两个未通过测试的连续提交。其次,如何确认部署是否成功。如果有超过 1% 的主机部署失败,这次上线会被确认为失败。

到这个时候,当事情正常的时候,进行代码部署变成了只是简单地回答几次“是”(接受建议的提交,启动 canary ,继续全面部署)。因此,我们允许自动回答这些问题,然后让 Jenkins 运行部署脚本。起初,工程师们启用 Jenkins 实施部署还需要部门监督,到最后,已完全实现了无人监管。

问题

虽然我们在这个阶段进行了持续部署,但还没有完全顺利。这里仍有几个问题需要解决。

测试失败

工程师们通常会写出破坏测试的代码,这将导致服务器上所有后续的提交都会测试失败,从而导致任何代码都无法部署。这时候,响应人员需要做的是,恢复错误提交,等待测试通过,然后在自动化可以继续之前手动部署所有积压的代码提交。这破坏了持续部署的主要优点之一,即每次部署少量提交。这里的问题在于测试执行得很慢而且不可靠。我们进行了各种优化,使得测试在5分钟内执行完毕而不再需要12 – 15分钟,并且修复了导致测试不可靠的基础架构问题。

积压任务

尽管有这些改进,我们仍经常会有一些需要部署的变更积压。最常见的原因是 canary 执行失败(包括真的失败和误报),但偶尔也会有其它的原因。当这些问题解决时,自动化将一次部署一个提交,因此需要一段时间才能清除积压,这将导致新的变更提交部署严重延期。响应人员通常会立即介入,并一次性部署所有的积压的提交,这将破坏持续部署的主要优点之一。

为了改善这一点,我们在提交选择逻辑中实现了积压处理,这使得自动化在有任务积压时部署多个提交。算法基于设定好每次部署的目标时间(30分钟)。对于队列中的每个提交,它计算出满足目标的剩余时间,在该时间内可以完成的部署次数(使用硬编码值),以及每次部署时需要的代码提交数。它使用每次部署时的最大提交数,但设定上限为 3。这样可以让我们在合理的时间内对每次的提交进行尽可能多的部署。

造成任务积压的另一个特殊原因是随着我们的基础设施规模的增加,部署进程变得缓慢。我们发现 SSH 代理与整个 SSH 连接授权的内核挂钩,同时 Fab 主进程也与一个管理所有任务的内核挂钩。该问题的解决方案是切换到 Facebook 的分布式 SSH 系统。

指导原则

那么,为了实现类似于我们所做的系统,你需要些什么呢? 我们系统运行良好,有一些关键原则在起作用,你可以借鉴。

  1. 测试:测试组件需要能够被快速执行。它需要有适当的覆盖率,但无需十全十美。测试需要经常执行:在代码审查期间,在代码提交前(理想情况下阻止测试失败的代码进行提交),以及提交后。
  2. Canary : 需要一个自动化的 canary 脚本,以防止真正的错误提交被部署到整个机队中。然而,它不需要是完美的–即使是非常简单的统计数据和阈值,也是非常不错的。
  3. 自动化正常场景: 不必对所有的场景都实现自动化;仅仅需要对已知的,正常的情况进行自动化。如果有任何的异常,停止自动化,人工处理。
  4. 让人感觉舒服:我认为这种自动化系统的一大障碍是让人感到不连贯和失控。为了解决这个问题,自动化系统需要提供可视化界面,显示它所做的,正在做的,以及(最好)即将做的。它还需要提供良好的停止机制。
  5. 预计糟糕的部署:会出现有问题的变更,但这没关系。你只需要快速检测出问题,并且能够快速回滚。

这是一些其它公司也同样可以实施的原则。持续部署系统并不需要太过复杂。关注以上原则,从一些简单的东西开始,并不断地进行改进。

下一步工作

目前,这个系统工作得很好, 但我们仍面临着更进一步的挑战,同时,我们也希望做更深入的优化。

  • 保持快速部署: Instagram 正在迅速增长,因此提交率将会持续升高。我们需要保持快速部署,以便在每次部署时保持很少的提交。有一种可能的解决方案,将上线划分为多个阶段,并实施流水线。
  • 增加 Canarying 脚本: 代码提交率的攀升,  canary 的执行失败和任务的积压,影响着越来越多的开发人员。为了防止更多错误提交进入服务器并阻塞部署,我们将 canarying 集成到 Landcastle 。测试通过后, Landcastle 将测试生产流量的变化,如果没有通过 canary 阈值,将失败。
  • 比较更多数据: 我们希望改善 Canary 的检测能力, 所以我们计划收集和检查更多的统计信息,如每个视图功能的响应状态码。我们还尝试从一组控制机器上收集统计信息,并将其与 Canary 中的数据统计信息进行比较,而不是当前的静态阈值。
  • 改善检测能力:减少因未被 canary 捕获的错误提交带来的影响,也是不错的。我们可以在(集群或区域)之间添加更多的阶段。在进入下一阶段之前检查该阶段的指标,而不是仅在一台机器上测试然后就部署到整个生产环境。
2 4 收藏 2 评论

关于作者:听风

简介还没来得及写 :) 个人主页 · 我的文章 · 20

相关文章

可能感兴趣的话题



直接登录
最新评论
跳到底部
返回顶部