写点什么

部署太慢,我们用 Warm Docker 容器将速度提高了 5 倍

  • 2023-03-29
    北京
  • 本文字数:4012 字

    阅读完需:约 13 分钟

部署太慢,我们用 Warm Docker 容器将速度提高了 5 倍

背景

 

我们使用 Serverless Dagster Cloud 来开发和部署 Dagster 代码,无需设置本地开发环境或任何云基础架构。当提交更改到 GitHub 时,GitHub Action 会直接构建和部署代码到 Dagster Cloud,然后可以在界面上查看并与 Dagster 对象进行交互。 Dagster Cloud 可以利用一个远程环境来共享部署,并且可以利用自动创建的临时环境与合作者协作,实现了个人本地开发和共享远程环境的结合。

 

最初,我们在 Dagster Cloud Serverless 中使用了标准的基于 Docker 的构建流程,但很快发现这个流程会使“编辑-部署-运行”的循环变得非常缓慢。为了将过程加速,我们实现了一个在 Docker 镜像之外运送代码的系统。本文将描述我们分析的问题、选择的解决方案以及在此过程中做出的各种权衡。


Docker 镜像存在的问题

 

当我们在 GitHub 上构建 Docker 镜像并将其部署到 Dagster Cloud 时,每次提交需要 3~5 分钟才会在 Dagster UI 中显示。在每次迭代中,无服务器开发人员通常会对代码进行微小更改,但是必须等待 3 分钟以上才能看到该更改的效果,这很快就会变得非常烦人。我们分析了“当你更改一行代码并提交时会发生什么”,并发现以下问题:


你可以看到,有两样东西花了最多的时间:

 

  • 构建 Docker 镜像(60 ~ 90 多秒)

  • 部署 Docker 容器(90 秒)

 

让我们分别看一下这两个问题。

 

构建 Docker 镜像

 

有一些需要注意的关于构建 Docker 镜像的事情:

 

  1. Docker 镜像由堆栈中的多个层构成,每层由 Dockerfile 中的一部分命令构建。

  2. 每个层有一个哈希标识。

  3. 上传镜像到注册表时,只上传注册表中不存在的层(由哈希标识确定)。

  4. 在 GitHub 构建机上使用 GitHub Actions 缓存重新构建镜像会将所有未受影响的层从缓存中拉取到构建机上。请注意,如果你的项目中有大量不会更改的依赖项,它们仍将在构建过程中从缓存中复制到构建机上。

  5. Docker 构建不是确定性的。如果使用完全相同的内容构建两个镜像,可能每次都会产生不同的哈希值。(虽然与本文直接相关性不强,但我们想观察这个意外的结果。作为一个极端情况,请考虑,一个刚构建的大层与已存在于注册表中的层完全相同,但仍然会作为一个新的层被上传。)

 

启动 Docker 容器

 

关于启动 Docker 容器,我们使用亚马逊云科技 Fargate,需要 45~90 秒的时间来提供和启动一个镜像。它不提供任何镜像缓存。启动一个新的容器会将所有层从注册表下载到已提供的容器中。

 

其他限制

 

在 Docker 镜像构建和启动后,我们运行用户的代码来提取元数据,并在 UI 中显示。这是不可避免的,并且可能需要几秒钟、30 秒或更多时间,具体取决于如何计算元数据(例如可能会连接数据库以读取模式)。此代码服务器保持活动状态并服务元数据请求,直到推送代码的新版本,然后启动一个新的容器。

 

我们有一个关键要求是可重复性:我们需要能够多次重新部署完全相同的代码和环境。使用 Docker 镜像哈希作为代码和环境的标识符非常适合这个要求。

 

我们的备选方案

 

下面是我们探索和讨论过的一些备选方案:

 

  1. 切换到 EC2 以加快容器启动速度。这会增加我们的运营负担,需要我们预先预配、监控和扩展集群。我们仍然会面临 Docker 构建速度慢的问题。

  2. 切换到其他的 Docker 构建系统,例如 AWS CodeBuild。这需要更多的实现工作,并与 GitHub 进行更深入的集成。不确定收益是否值得。

  3. 切换到 AWS Lambda,启动时间更快。Lambda 环境附带其自己的基础镜像,如果需要进行定制则更加困难。它还对执行时间施加了 15 分钟的限制,这将需要为运行时间更长的服务器实施复杂的解决方案。

  4. 通过仅构建和上传更改的代码到相同的服务器,来重复使用长时间运行的代码服务器。这里的挑战是实现打包和运行时机制,以确保可靠和可重复的执行环境。我们研究了各种打包和分发 Python 环境的方法,包括 rsync、poetrynixshivpex。我们还考虑使用 EFS 卷以及这些工具来挂载 Python 环境。

 

做出这个决定的关键因素是因为我们意识到,虽然 Docker 镜像是行业标准,但在只需要同步一个小改变时,传输数百兆字节的镜像似乎是不必要的。考虑 git——它只传输差异,但却产生了完整且一致的代码库。

 

这让我们倾向于选择方案 4……如果我们能找到一个适合我们大部分工作工具的话。经过一些实验,我们发现 PEX 具有许多特性,非常适合我们的使用情况——在下一节中会详细介绍。

 

什么是 PEX?

 

PEX 是 Python 可执行文件的缩写,是一个将 Python 包打包成名为 pex 文件的工具。这些可执行文件包含 Python 包和一些引导代码。例如,我们可以将 dagster 包和其依赖项打包成单个文件,然后运行它:

 

% pex dagster --python=python3.8 -o dagster.pex% ./dagster.pexPython 3.8.16 (default, Dec  7 2022, 01:24:57)[Clang 14.0.0 (clang-1400.0.29.202)] on darwinType "help", "copyright", "credits" or "license" for more information.(InteractiveConsole)>>> import dagster>>>
复制代码

 

将整个环境存储在单个文件中非常方便,可以轻松地将其传输到 S3 中进行存储。PEX 提供了更多功能,不仅仅是“文件中的虚拟环境” - 这里是我们使用的其他功能:

 

  • 隔离性

 

在运行时,pex 环境与其他全局包完全隔离。在环境中只有捆绑在 pex 文件中的包。我们将多个 pex 文件一起发送到同一台机器上,而不必担心环境隔离问题。

 

  • 确定性

 

使用相同的输入包会生成完全相同的 pex 文件:

$ pex dagster pandas -o out.pex | sha256sume3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855  -$ pex dagster pandas -o out.pex | sha256sume3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855  -
复制代码

 

这让我们可以使用内容寻址来识别这些 pex 文件,从而对实现可重复性更有信心。为了实现可重复性,除了使用 Docker 镜像哈希之外,我们还使用 pex 文件哈希。

 

  • 组合

 

多个 pex 文件可以在运行时合并,有效地将多个环境合并为一个环境。

 

% pex pandas -o pandas.pex% pex dagster -o dagster.pex% PEX_PATH=pandas.pex ./dagster.pexPython 3.8.16 (default, Dec  7 2022, 01:24:57)[Clang 14.0.0 (clang-1400.0.29.202)] on darwinType "help", "copyright", "credits" or "license" for more information.(InteractiveConsole)>>> import pandas>>> import dagster>>>
复制代码

 

我们使用这个功能将代码分成两个部分,在运行时合并起来:一个包含所有依赖项的 deps.pex 文件,和一个仅包含用户代码的 source.pex 文件。

 

  • 跨平台构建

 

我们在 Serverless Cloud 中使用 Linux python:* -slim 衍生的基础镜像。只要有包的 wheelpex 工具就可以在任何平台上为 Linux 构建 pex 文件。

 

快速部署

 

使用 pex 和 S3 存储 pex 文件,我们构建了一个系统,其中快速路径避免了构建和启动 Docker 镜像的开销。

 

我们的系统工作方式如下:当你将代码提交到 GitHub 时,GitHub 操作根据你的依赖关系是否与上一次部署不同,执行全量构建或快速构建。我们跟踪在 setup.pyrequirements.txt 中指定的依赖关系集。

 

对于全量构建,我们将你的项目依赖项构建为 deps.pex 文件,将你的代码构建为 source.pex 文件。这两个文件都会上传到 Dagster Cloud。对于快速构建,我们只构建并上传 source.pex 文件。

 

在 Dagster Cloud 中,我们可能会重复使用现有容器或为代码服务器提供新的容器。我们将 deps.pexsource.pex 文件下载到此代码服务器上,并在隔离环境中使用它们运行你的代码。我们从不跨用户共享容器,容器上的所有环境都属于同一用户。快速部署的最佳时间和最差时间如下所示:

 

这里的要点是,在快速路径中——当我们进行快速构建并重用现有容器时——整个过程只需要大约 40 秒,而不是之前的 3 分钟多。我们称这个功能为快速部署,现在已经默认开启了所有新的无服务器注册。


权衡和问题

 

这种方式可以显著提高部署速度(4~5 倍),但也带来了一些权衡和其他需要调整的因素:

 

  1. 虽然现在我们可以在一个代码服务器上运行多个环境,并且在代码方面是隔离的,但它们仍然共享相同的 RAM 和 CPU。如果我们将太多的环境放在一个容器中,而一个环境占用了太多的内存,就可能对在同一个容器中运行的其他环境产生不利影响。

  2. Docker 可以在任何操作系统上为 Linux 构建 Python 包,因为在构建过程中目标 Linux 操作系统和 Python 解释器都可用。pex 仅为提供 wheel 的包构建 Linux 的 pex 文件。如果出现问题,我们在构建过程中使用 Docker 容器来处理源分发。未来,这一步骤可以移动到单独的共享服务中。

  3. 在构建 Docker 镜像时可以进行深度定制,例如,你可以指定自定义基础镜像而不是默认的 python:*-slim 镜像之一。为了实现功能的平等,我们实现了一种方法,允许用户指定他们自己的基础 Docker 镜像,我们将其用于快速部署。

 

GitHub 工作流和 PEX

 

你可能已经注意到,在最初的图表中,Download Docker based action 的操作大约需要 10 秒钟。我们是如何完全消除这个步骤的呢?

 

我们曾经将 GitHub action 代码打包成 Docker 镜像,并使用 Docker container action。现在,我们将我们的 action 代码打包为 pex 文件,将其检入我们的 action 存储库并直接在 GitHub runner 上运行。这消除了下载和启动 Docker action 镜像所花费的时间,同时仍允许我们打包所有依赖项。

 

我们做出的另一个小优化是只使用一个 GitHub 工作流作业。在 GitHub 中,每次作业启动需要大约 10 秒钟来准备一个新的 runner。

 

总结

 

将部署时间从超过 3 分钟缩短到 40 秒是一个显著的加速,我们对这个结果非常满意,特别是在测试自己的服务时。使用 pex 使我们能够在 Docker 之上构建一个可重复、一致的环境,我们很高兴能够探索使用 pex-on-docker 组合的其他可能性。

 

如果有兴趣,可以看 PEX 团另一篇关于使用 Docker 进行部署的博客文章,它描述了如何使用 pex 文件作为中间目标来加速 Docker 镜像构建。

 

原文链接:

 

https://dagster.io/blog/fast-deploys-with-pex-and-docker

2023-03-29 12:086415

评论

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

差点跳起来了!阿里首推22w字Java面试复盘宝典成功助我入职美团

Java 程序员 后端

并发容器之BlockingQueue详解,深入linux内核架构中文pdf

Java 程序员 后端

微服务架构陷阱:过渡设计和设计不足,2021年Java开发前景如何

Java 程序员 后端

并发编程专题四-原子操作和显示锁,java面试刷题

Java 程序员 后端

干了这么多年的Java面试官,给大家分享我面试时最爱问的Java高频题

Java 程序员 后端

干货第一弹-教你如何利用阿里开源工具进行排查线上CPU居高问题

Java 程序员 后端

强势出击!21天肝完这份字节面试题后直接斩获字节offer

Java 程序员 后端

微服务你得知道这些!从核心组件到远程调用方式以及HTTP通信方法

Java 程序员 后端

必知必会JVM四-垃圾收集器介绍,linux驱动开发入门与实践

Java 程序员 后端

快速上手Apache,java递归求阶乘原理

Java 程序员 后端

已开源!阿里巴巴SpringCloud微服务原理与架构项目实战,请签收

Java 程序员 后端

干货!使用Docker构建RabbitMQ高可用负载均衡集群,java面试题汇总及答案

Java 程序员 后端

年薪80W,Java高薪架构师成长背后血泪史,膜拜大佬

Java 程序员 后端

微服务网关与用户身份识别,JWT+Spring Security进行网关安全认证

Java 程序员 后端

快人一步!阿里爆款Java性能调优手册,源码,如何做到操作系统和并发同步结合

Java 程序员 后端

快速鸟瞰并发编程,-呕心沥血整理的架构技术【2】,分层展示的架构图

Java 程序员 后端

工商银行分布式服务 C10K 场景解决方案,java基础实战项目飞机大战

Java 程序员 后端

应届生想要找到一份Java开发的工作,需要达到什么水平?

Java 程序员 后端

开发多年put、get、resize不知道,springcloud视频讲解

Java 程序员 后端

彻底理解coookie、session、token,mybatis的动态sql执行原理

Java 程序员 后端

已拿offer热乎乎的蚂蚁金服面经分享,建议收藏(Java岗、附答案)(2)

Java 程序员 后端

并发编程专题三-JAVA线程的并发工具类,面试要掌握这几个关键点

Java 程序员 后端

应“云”而生的 Java 框架,带你手撸红黑树

Java 程序员 后端

强哥说Java--Java 方法,kafka视频

Java 程序员 后端

层层递进!MySQL性能优化步骤演进,一顿饭的时间我就会了

Java 程序员 后端

已拿offer热乎乎的蚂蚁金服面经分享,建议收藏(Java岗、附答案)(1)

Java 程序员 后端

很全面!每天一分享互联网大厂Java面试真题,java菜鸟教程实例

Java 程序员 后端

已拿offer热乎乎的蚂蚁金服面经分享,建议收藏(Java岗、附答案)

Java 程序员 后端

年末三面快手Java后端岗,没有刁难轻轻松松就拿下了意向书,分享面经

Java 程序员 后端

开门见山的问MySQL:InnoDB一棵B+树可以存放多少行数据

Java 程序员 后端

强哥说Java--Java 变量,java面试逻辑思维题及答案

Java 程序员 后端

部署太慢,我们用 Warm Docker 容器将速度提高了 5 倍_文化 & 方法_Shalabh Chaturvedi_InfoQ精选文章