【ArchSummit架构师峰会】探讨数据与人工智能相互驱动的关系>>> 了解详情
写点什么

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

  • 2019-09-04
  • 本文字数:2664 字

    阅读完需:约 9 分钟

如何将代码部署时间减少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-09-04 10:118183
用户头像

发布了 687 篇内容, 共 397.4 次阅读, 收获喜欢 1498 次。

关注

评论

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

前端二面必会手写面试题汇总

helloworld1024fd

JavaScript

HMS Core 3D流体仿真技术,打造移动端PC级流体动效

最新动态

这波无感升级有点秀——天翼云QEMU组件热升级方案来了

天翼云开发者社区

云计算 云主机 虚拟化

在北京选择哪家大数据培训机构

小谷哥

Java程序员培训机构怎么选

小谷哥

社招前端一面必会react面试题集锦

beifeng1996

React

社招前端一面经典手写面试题(边面边更)

helloworld1024fd

JavaScript

玩转云端 | 数据管理深似海,运维如何变“路人”?

天翼云开发者社区

大数据 数据仓库 数据存储

【面经分享,附答案】字节系统架构,一面,后端开发

小小怪下士

Java 程序员 面试

RocketMQ Schema——让消息成为流动的结构化数据

Apache RocketMQ

RocketMQ

手写现代前端框架diff算法-前端面试进阶

helloworld1024fd

JavaScript

迁移速度与计算性能兼得!天翼云DirtyLimit技术大显身手

天翼云开发者社区

虚拟机 迁移 弹性计算

天翼云Serverless边缘容器,为云上创新开启加速度

天翼云开发者社区

云计算 边缘计算 边缘容器

如何优化大场景实时渲染?HMS Core 3D Engine这么做

最新动态

Clickhouse表引擎探究-ReplacingMergeTree

京东科技开发者

Clickhouse 数据分片 数据验证 存储数据 MergeTree

企业转型难?火山引擎数智平台提供数智升级新路径

字节跳动数据平台

大数据 数据中台 12 月 PK 榜

百度APP Android包体积优化实践(四)Dex注解优化

百度Geek说

Java android 前端 12 月 PK 榜

8个Spring事务失效的场景,你碰到过几种?

JAVA旭阳

Java spring

培训班出来前端程序员好找吗?

小谷哥

【经验总结】HDI与普通PCB的4点主要区别

华秋PCB

工艺 PCB PCB设计

Vue的computed和watch的区别是什么?

bb_xiaxia1998

Vue

海量监控数据处理如何做,看华为云SRE案例分享

华为云开发者联盟

数据库 后端 华为云 12 月 PK 榜

选择从零开发一款小游戏如何能实现变现

Onegun

小游戏 小游戏开发 H5小游戏

手写JavaScript常见5种设计模式

helloworld1024fd

JavaScript

ZBC成功上线PancakeSwap的糖浆池,并有望在不久上线Binance

西柚子

架构实战 - 模块4作业

mm

redis sentinel #架构实战营

架构实战营 模块3-1

西山薄凉

「架构实战营」

盘点那些日赚万金的爆款小游戏

FinFish

小游戏 小程序游戏 微信小游戏 爆款小游戏

学习掌握哪些前端技术才能找到好工作?

小谷哥

参加大数据培训可以找到工作吗

小谷哥

论文解读丨【CVPR 2022】不使用人工标注提升文字识别器性能

华为云开发者联盟

人工智能 华为云 文字识别 12 月 PK 榜

如何将代码部署时间减少95%?_文化 & 方法_Evan Limanto_InfoQ精选文章