写点什么

如何将代码部署时间减少 95%?

2019 年 9 月 04 日

如何将代码部署时间减少95%?

本文作者所在的公司 Plaid 是一家金融科技公司,该公司搭建了一个技术平台,使应用程序能够与用户的银行账户建立联系。随着公司的发展,基础设施规模在不断扩大。目前,这家公司运行着 20 多个内部服务,每天在核心服务上部署 50 多个代码提交。因此,最小化部署时间对于最大化迭代速度至关重要,一个快速的部署过程能够迅速进行 Bug 修复并运行平稳的连续部署系统



几个月前,我们注意到,银行集成服务部署缓慢正在影响团队发布代码的能力。工程师要花至少 30 分钟才能通过多个过渡环境和生产环境构建、部署和监视变更,这将消耗大量宝贵的工程时间。随着团队越来越大,我们每天发布的代码也越来越多,这一点变得越来越不可接受。


虽然我们计划实现长期改进,比如将基于 Amazon ECS 服务的基础设施迁移到 Kubernetes 上,但是,为了在短期内提高迭代速度,有必要快速解决下这个问题。因此,我们决定实践自定义的“快速部署”机制。


Amazon ECS 部署的高延迟

我们的银行集成服务由 4000 个 Node.js 进程组成,这些进程运行在专用的 Docker 容器上,这些容器托管并部署在 Amazon 的容器编排服务ECS上。在分析了我们的部署过程之后,我们将增加的部署延迟归结到三个不同的组件上:


  • 启动任务会导致延迟。除了应用程序启动时间之外,ECS 健康检查也会导致延迟,它决定容器何时准备好开始处理流量。控制这个过程的三个参数是 interval、retry 和 startPeriod。如果没有对健康检查进行仔细调优,容器可能会卡在“启动”状态,即使它们已经准备好为流量服务。

  • 关闭任务会导致延迟。当我们运行ECS服务更新时,一个 SIGTERM 信号被发送到所有正在运行的容器。为了处理这个问题,我们在应用程序代码中使用了一些逻辑,以便在完全关闭服务之前占用现有资源。

  • 我们启动任务的速度限制了部署的并行性。尽管我们将MaximumPercent参数设置为 200%,但是 ECS start-taskAPI 调用的硬限制是每个调用只能执行 10 个任务,而且速度有限。我们需要调用 400 次才能将所有容器投入生产。


方法探索

我们考虑并试验了一些不同的潜在解决方案,以逐步实现总体目标:


  • 减少生产中运行的容器总数。这当然是可行的,但它涉及到对服务架构进行重大修改,以使其能够处理相同的请求吞吐量,在进行这样的修改之前,还需要进行更多研究。

  • 通过修改健康检查参数来调整 ECS 配置。我们尝试通过减少 interval 和 startPeriod 的值来加强健康检查,但是 ECS 在启动时将健康的容器错误地标记为不健康,导致我们的服务永远无法完全稳定在 100%健康状态。由于根本问题(ECS 部署缓慢)依然存在,对这些参数进行迭代是一个缓慢而费力的过程。

  • 在 ECS 集群中启动更多实例,以便可以在部署期间同时启动更多任务。这样做可以减少部署时间,但不会减少太多。从长远来看,这也不划算。

  • 通过重构初始化和关机逻辑优化服务重启时间。只需要做一些小小的修改,我们就能够在每个容器中节省大约 5 秒的时间。


尽管这些更改将总体部署时间减少了几分钟,但是我们仍然需要将时间提高至少一个数量级,才能认为问题已解决。这将需要一个根本不同的解决方案。


初步解决方案:利用 Node Require Cache“热重载”应用程序代码

Node require cache是一个 JavaScript 对象,它根据需要缓存模块。这意味着多次执行 require(‘foo’)或 import * as foo from 'foo’只会在第一次时请求 foo 模块。神奇的是,删除 require cache 中的条目(我们可以使用全局 require.cache 对象访问)将迫使 Node 在下次导入模块时从磁盘重新读取该模块。


为了绕过 ECS 部署过程,我们尝试使用 Node 的 require cache 在运行时执行应用程序代码的“热重载”。一旦接收到外部触发(我们将其实现为银行集成服务上的gRPC端点),应用程序将下载新代码来替换现有的构建,清除 require cache,从而强制重新导入所有相关模块。通过这种方法,我们能够消除 ECS 部署中存在的大部分延迟,优化整个部署过程。


Plaiderdays(我们的内部黑客马拉松)期间,来自不同团队的一组工程师聚在一起,为我们所谓的“快速部署”实现了一个端到端的概念验证。当我们一起设法构建一个原型时,有一件事似乎出了问题:如果下载新构建的 Node 代码也试图使失效缓存,那么下载器代码本身将如何重新加载就不清楚了。(有一种方法可以解决这个问题,就是使用Node EventEmitter,但是会给代码增加相当大的复杂性)。更重要的是,还存在运行未同步代码版本的风险,这可能导致应用程序意外失败。


由于我们不愿意在银行集成服务的可靠性上妥协,这种复杂性需要重新考虑“热重载”方法。


最终解决方案:重新加载进程

在过去,为了在所有服务中运行一系列统一的初始化任务,我们编写了自己的进程封装器,它的名称非常贴切,叫做 Bootloader。Bootloader 的核心包含设置日志管道、转发信号和读取 ECS 元数据的逻辑。每个服务都是通过将应用程序可执行文件的路径以及一系列标志传递给 Bootloader 来启动的,这些文件在执行初始化步骤之后会作为子进程执行。


我们没有清除 Node 的 require cache,而是在下载预期的部署构建后,使用特殊的退出代码来调用 process.exit 实现服务更新。我们还在 Bootloader 中实现了自定义逻辑,以触发使用此代码退出的任何子进程的进程重载。与“热重载”方法类似,这使我们能够绕过 ECS 部署的成本并快速引导新代码,同时避免“热重载”的陷阱。此外,Bootloader 层的这种“快速部署”逻辑允许我们将其推广到在 Plaid 运行的任何其他服务。


下面是最终解决方案:


  • Jenkins 部署管道向银行集成服务的所有实例发送 RPC 请求,指示它们“快速部署”特定的提交散列。

  • 应用程序接收 gRPC 请求进行快速部署,并根据接收到的提交散列从Amazon S3下载构建好的压缩包。然后,它替换文件系统上的现有构建,并使用 Bootloader 识别的特殊退出代码退出。

  • Bootloader 看到应用程序使用这个特殊的“Reload”退出代码退出,然后重新启动应用程序。

  • 服务运行新的代码。


下面这张图简单说明了这个过程。



结果

我们能够在 3 周内交付这个“快速部署”项目,并将 90%生产容器的部署时间从 30 多分钟减少到 1.5 分钟。



上图显示了我们为银行集成服务部署的容器数量(按提交表示为不同的颜色)。如果注意下黄线,就可以看到它在 12:15 左右增长趋于平稳,这代表我们的容器长尾仍然在占用资源。


这个项目极大提高了 Plaid 集成工作的速度,允许我们更快地发布特性及进行 Bug 修复,并将浪费在上下文切换和监视仪表板上的工程时间最小化。这也证明了我们的工程文化,即通过黑客马拉松得来的想法实现具有实质性影响的项目。


原文链接:


How We Reduced Deployment Times by 95%


2019 年 9 月 04 日 10:117667
用户头像

发布了 363 篇内容, 共 158.5 次阅读, 收获喜欢 814 次。

关注

欲了解 AWS 的更多信息,请访问【AWS 技术专区】

评论

发布
暂无评论
发现更多内容

如何从ERP将Material的Batch信息下载到CRM

Jerry Wang

CRM SAP ERP abap

如何从ERP下载Sales BOM到CRM

Jerry Wang

CRM SAP ERP abap

一种不通过UI给C4C自定义BO创建测试数据的方式

Jerry Wang

CRM SAP C4C Cloud for Customer

中间件事务码R3AC1里Block Size的含义

Jerry Wang

中间件 CRM SAP abap

架构训练营-模块四作业

冬天的树

模块4作业

梦寐凯旋

架构实战营

如何快速的学习一门新的编程语言?

架构精进之路

学习推荐 5月日更

One Order行项目里Item Category是怎么计算出来的

Jerry Wang

CRM SAP abap ONE-ORDER

同时开左右两个SAPGUI编辑器显示同一段ABAP代码

Jerry Wang

SAP abap SAPGUI

SAP CRM中间件里的CSA队列有什么用

Jerry Wang

中间件 消息中间件 CRM SAP

利用CRM中间件Middleware从ERP下载Customer Material的常见错误

Jerry Wang

中间件 CRM SAP ERP

CRM中间件里的发布-订阅者模式

Jerry Wang

CRM SAP ERP abap

如何检查某个用户是否具有某个权限对象上定义的某种权限

Jerry Wang

CRM ERP abap Authorization Authentication

网络攻防学习笔记 Day23

穿过生命散发芬芳

5月日更 网络攻防

CRM数据库表COM_TA_R3_ID的数据来源

Jerry Wang

中间件 CRM SAP abap

SAP CRM 和 Cloud for Customer 的 Document flow API 介绍

Jerry Wang

CRM SAP abap C4C documentFlow

CRM和ERP的Sales Organization的映射关系

Jerry Wang

中间件 CRM SAP ERP

SAP S4CRM (SAP Customer Management for S/4HANA) 1.0 介绍

Jerry Wang

CRM SAP abap S/4HANA

如何用ABAP代码读取CDS view association的数据

Jerry Wang

CDS SAP abap CDS view

观察者模式在One Order回调函数中的应用

Jerry Wang

CRM SAP abap

高性能 JavaScriptの六 -- 老生常谈Ajax

空城机

JavaScript ajax 前端 5月日更

如何处理错误信息 Pricing procedure could not be determined

Jerry Wang

CRM SAP

CRM product UI里assignment block的显示隐藏逻辑

Jerry Wang

CRM SAP abap

Mybatis获取自增长id

风翱

mybatis 5月日更

数字货币兴起背后:全球货币超发 主权信用贬值

CECBC区块链专委会

ABAP OPEN SQL里OPEN CURSOR和SELECT的比较

Jerry Wang

CRM SAP abap ST05 OPENSQL

设计千万级学生管理系统的考试试卷存储方案

俞嘉彬

金融科技如何在产业互联网蓝海中扬帆远航?大数据、区块链与物联网应用被看好

CECBC区块链专委会

如何使用代码获得一个function module的Where Used List

Jerry Wang

CRM SAP abap SAPGUI

面试问题 - 只用位操作在ABAP里实现a+b

Jerry Wang

面试 SAP abap

为什么使用中间件下载时总是收到警告消息Object is in status Wait

Jerry Wang

中间件 CRM SAP ERP

如何将代码部署时间减少95%?-InfoQ