从 3 分以上到 40 秒,Docker 容器 5 倍速度部署实战!

Posted CSDN资讯

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从 3 分以上到 40 秒,Docker 容器 5 倍速度部署实战!相关的知识,希望对你有一定的参考价值。

【CSDN 编者按】使用 pex 使我们能够在 Docker 之上建立一个可重复的、一致的环境,我们很高兴使用这个 pex-on-docker 组合来探索其他的可能性。

原文链接:https://dagster.io/blog/fast-deploys-with-pex-and-docker

未经允许,禁止转载

作者 | Shalabh Chaturvedi

译者 | 邓晓娟    责编 | 王子彧

出品 | CSDN(ID:CSDNnews)

无服务器开发和反馈循环

Dagster 是一个数据编排器。在无服务器 Dagster 云上,不需要建立本地开发环境或云基础设施,就可以开发和部署 Dagster 代码。当你向 GitHub 提交修改时,GitHub Action 会直接构建和部署你的代码到 Dagster 云。你可以在用户界面中查看和互动你的 Dagster 对象。借助 Dagster 云,远程环境通常用于让使用自动创建的暂存环境与合作者共享部署。个人本地开发和共享远程环境相结合,形成了一个强大的开发周期。

最初,我们在这上面使用了基于 Docker 的标准构建流程。然而我们很快发现,这让编辑-部署-运行的周期变得非常繁琐缓慢。为了加快速度,我们构建了一个系统,实现在 Docker 镜像之外运送代码。这篇文章描述了我们分析的问题、确定的解决方案,以及在这个过程中做出的各种权衡。

Shalabh Chaturvedi 分享了 Dagster Cloud 新的快速部署能力的高水平概述,详情请观看视频:https://youtu.be/mPT3FBFSw6g

Docker镜像的问题

当我们在 GitHub 上构建 Docker 镜像并将其部署到 Dagster 云时,每次提交都需要3到5分钟才能在 Dagster 用户界面上显示出来。无服务器开发人员通常会在每次迭代中对代码进行小的改动,但却每次都要等待3分钟以上才能看到改动的效果,这种无意义的等待很容易让人厌烦。我们分析了一个问题:“当你修改一行代码并提交后,会发生什么?”发现了以下的情况。

我们分析了 "当你改变一行代码并提交时会发生什么",发现了以下情况。

  • 20s > 提供 GitHub 运行器并下载动作 

  • 10s > 下载基于 Docker 的行动 

  • 60s > 建立并上传用户的 Docker 镜像*。

  • 90s > 在 AWS 中运行用户的 Docker 镜像 

  • 180s的运行时间 

* 在启用缓存的情况下需要60秒(如果没有改变依赖关系的话);如果依赖关系有变化,则需要90秒以上。

如你所见,花费时间最长的两件事是:

  • 构建一个 Docker 镜像(60-90多秒)

  • 部署 Docker 容器(90秒)

那就让我们来看看这两件事都做了些什么。

构建 Docker 镜像

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

  1. Docker 镜像是由堆栈中的多个层堆叠而成的,其中每一层都是由 Docker 文件中的一个命令子集构建的;

  2. 每一层都由一个哈希值来识别;

  3. 当上传镜像到注册表时,只有不存在于注册表中的层(由哈希值识别)被上传;

  4. 使用 GitHub Actions 缓存在 GitHub 构建机上重建镜像时,会将所有未受影响的层从缓存中拉到构建机上。请注意,如果你的项目中有大量的依赖关系没有改变,它们会在构建过程中从缓存中一起被复制到构建机器上;

  5. Docker 的构建不是确定性的。如果你用完全相同的内容构建一个镜像两次,每次都可能产生不同的哈希值。(虽然不直接相关,但我们想记录一下这个意外的观察结果。作为一个极端案例,考虑到一个新构建的大层与已经在注册表中的层相同,仍然可能作为一个新的层被上传)。

启动 Docker 容器

关于启动 Docker 容器需要注意的是,我们使用 AWS Fargate,它需要45到90秒的时间来配置和启动一个镜像。且不提供任何图像缓存。启动一个新的容器会从注册表中下载所有的层到配置的容器上。

其他限制

在 Docker 镜像建立和启动后,我们运行用户的代码来提取元数据,显示在用户界面上。这一步无法避免,可能需要几秒钟到30秒,甚至更久,这取决于元数据的计算方式(比如它可以连接到数据库来读取模式)。这个代码服务器保持活动状态,为元数据请求提供服务,直到推送新版本的代码,然后启动一个新的容器。

我们的一个关键要求是可重复性:我们需要能够多次重新部署完全相同的代码和环境。使用 Docker 镜像的哈希值作为代码和环境的标识符,可以很好地满足这一要求。

 

备选方案综述

除了上述的方案以外,我们还探索和讨论了一些替代方案。

  1. 从 Fargate 切换到 EC2,以加快容器的启动。这将增加我们的运营负担,要求我们预先提供、监控和扩展我们的集群。我们仍然会遇到 Docker 构建缓慢的问题;

  2. 换成不同的 Docker 构建系统,如 AWS CodeBuild。这将需要更多的部署工作,并与 GitHub 进行更深入的整合。目前还不清楚这样做的回报是否值得;

  3. 切换到 AWS Lambda,启动时间快得多。Lambda 环境有自己的基础镜像,对于自定义需求来说不太友好。而且它的执行时间还有15分钟的限制,这对运行时间较长的服务器来说,需要复杂的变通方法;

  4. 通过构建并只上传修改后的代码到同一服务器,重新使用长期运行的代码服务器。这里的挑战是实现打包和运行机制,以确保一个可靠和可重复的执行环境。我们研究了各种打包和分发 Python 环境的方法,包括 rsync、poetry、nix、shiv 和 pex。还考虑了使用 EFS卷来挂载Python环境,与这些工具相结合。

我们作出最终决定背后的有一个关键因素,是意识到虽然 Docker 镜像是行业标准,但如果我们只需要同步一个小的变化时,就去移动100多兆的镜像,是很不必要的繁重操作。考虑到 Git 只提供差异,但却能产生完整而一致的存储库。因此我们倾向于方案4,只需要能找到一个合适的工具来做大部分的工作。经过一些实验,我们发现 pex 的许多功能对我们的用例非常有效。

什么是 PEX?

pex 是 Python Executable 的缩写,它是一种将 Python 包捆绑到称为 pex 文件的工具。这些是可执行文件,其中包含 Python 包和一些引导代码。例如,我们可以把 dagster 包和它的依赖项捆绑成一个文件,然后运行它。

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

将整个环境放在一个文件中,便于运输和存储在 S3 中。pex 提供的不仅仅是一个 "文件中的虚拟环境",以下是我们使用的其他功能。

  1. 隔离

在运行时,pex 环境与其他网站范围内的包完全隔离。环境中唯一存在的包是那些捆绑在 pex 文件中的包。我们将多个 pex 文件运送到同一台机器上,而不必担心环境隔离问题。

  1. 确定性

使用相同的输入包会产生位对位的相同的 pex 文件。

$ pex dagster pandas -o out.pex | sha256sum
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855  -
$ pex dagster pandas -o out.pex | sha256sum
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855  -

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

  1. 组成

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

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

我们用它把代码分成两部分,在运行时合并:一个包含所有依赖关系的 deps.pex 文件和一个只包含用户代码的 source.pex 文件。

  1. 跨平台的构建

我们在无服务器云中使用 Linux python :*-slim 衍生的基础镜像。只要软件包的轮子可用, pex 工具可以在任何平台上为 Linux 构建 pex 文件。

 

快速部署

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

我们的系统是这样工作的:当你向 GitHub 提交代码时,GitHub Action 要么进行完全构建,要么进行快速构建,这取决于你的依赖关系自上次部署后是否有变化。我们跟踪 setup.py 和 requirements.txt 中指定的依赖项。

对于一个完整的构建,将项目依赖性构建到 deps.pex 文件,将代码构建到 source.pex 文件。两者都被上传到 Dagster 云端。对于快速构建,只构建和上传 source.pex 文件。

在 Dagster 云中,可以重新使用一个现有的容器或提供一个新的容器作为代码服务器。将 deps.pex 和 source.pex 文件下载到这个代码服务器上,并使用它们在一个隔离的环境中运行代码。我们从不在用户之间共享一个容器,一个容器上的所有环境都属于同一个用户。快速部署的最佳情况和最坏情况的时间线如下。

 其结果是,在快速构建(Fast Build)的路径中,当我们进行快速构建并重用现有容器时,整个过程只需40秒,而不像以前一样需要3分钟以上。

我们将这一功能称为【快速部署】,现在所有新注册的无服务器用户都默认开启这一功能。

权衡与问题

快速部署极大地提高了部署速度(4-5倍),但它伴随着一些需要权衡的问题和其他因素,我们已经进行了调整:

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

  2. Docker 可以在任何操作系统上为 Linux 构建 Python 包,因为目标 Linux 操作系统和 Python 解释器在构建过程中是可用的。pex 只能为 Linux 构建提供轮子的包的 pex 文件。作为退路,我们在构建过程中使用 Docker 容器来处理源码分发。这个步骤可以在未来被移到一个单独的共享服务中;

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

 

GitHub 工作流程和 pex

很多人可能已经注意到,原图中,过去基于 Docker 的下载操作需要10秒左右。那么我们是如何完全消除这个步骤的呢?

以前我们把 GitHub Action 代码打包成 Docker 镜像,然后使用 Docker 容器操作。而现在,我们把动作代码打包成一个 pex 文件,将其检入动作仓库,直接在 GitHub 运行器上运行。这就省去了下载和启动 Docker 动作镜像的时间,同时仍然允许我们打包所有的依赖项。

我们做的另一个小优化是,只使用一个 GitHub 工作流作业。在 GitHub 中的每一个工作启动都需要10秒钟来配置一个新的运行器。

结论

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

☞奇瑞汽车回应要求员工周六上班:本意不是压榨员工;Google发布史上最大通才模型PaLM-E;Chrome 111发布|极客头条
☞创业 5 年暂未发布任何新品,由苹果前高管打造的初创公司,拥抱 AI 后,再获 1 亿美元融资!
☞“我们聘请 ChatGPT,让它当了一天 ML 工程师”

以上是关于从 3 分以上到 40 秒,Docker 容器 5 倍速度部署实战!的主要内容,如果未能解决你的问题,请参考以下文章

docker aufs 到 devicemapper docker 容器和图像迁移

docker managed volume - 每天5分钟玩转 Docker 容器技术(40)

docker容器扫盲

我的Docker学习笔记:从入门到实战过程全记录

我的Docker学习笔记:从入门到实战过程全记录

Docker 入门 第一部分: 定位和设置