初始远程与分布式构建模式详细指南

368 阅读13分钟

对Gradle构建工具来说,一个经常被要求的功能是执行远程或分布式构建的能力。但这究竟是什么意思?这些请求背后的动机是什么?这篇文章将探讨远程与分布式构建之间的区别以及它们的变化。由于业界对这些概念没有一致的术语,本帖的目的是概述这些模式以及它们之间的关系。

除了两个针对JVM的参考之外,这些意见一般适用于使用任何语言或生态系统的软件项目。

为什么呢?

这些特性通常是在缩短本地开发者机器上的构建时间的背景下讨论的。延长的构建周转时间阻碍了本地和CI环境下的生产力,但本地构建经验对开发人员的情绪有不成比例的影响。

术语

业界对 "远程 "和 "分布式 "构建这两个术语的使用并不总是一致的,它们经常被交替使用。下面,我们将给每个术语一个明确的定义。首先,我们还将定义更基本的 "构建缓存 "优化。

什么是远程构建缓存?

我们要讨论的第一个包含远程组件的模式是构建缓存。与增量构建类似,构建缓存避免了执行CPU密集型操作,如编译源文件或执行测试。增量构建将最近一次本地操作的输出留在磁盘上,而构建缓存则是通过存储和重用之前执行的任何操作的结果来实现的--很像从备份中恢复文件。更重要的是,缓存可以是本地的,也可以在工程师之间共享(远程构建缓存)。CI环境通常被配置为写到云端的共享缓存中。然后每个工程师的机器从共享缓存中提取结果。这意味着同样的源码只需要在CI主机上构建一次,避免在开发者机器上进行昂贵的本地编译器调用。

简而言之,构建缓存在远程服务器上存储中间构建工件以加快构建速度。与远程和分布式构建不同的是,没有任务被实际远程执行。

Gradle构建工具构建缓存功能自2017年推出以来,在减少本地和CI构建时间方面都非常成功。

远程构建的变种

一般来说,"远程构建 "是指通过将整个过程委托给另一台计算机来减少构建时间的方法。通常,远程计算机在计算资源和内存方面比本地计算机更强大。它可以在一个统一的、精心策划的环境中和/或在隔离的情况下主持构建,不与其他本地进程争夺资源。

在实践中,远程构建可以采取下面定义的四种形式之一:

传统的解决方案:远程桌面/屏幕共享

最原始的远程构建模式是通过古老的VNC或RDP协议使用远程桌面。虽然这些都是技术含量不高的屏幕共享工具,而且会受到低网络带宽和高延迟的影响,但它们确实允许在远程机器上构建软件。我们只是为了完整地提及这一历史性的解决方案,因为现代的远程集成开发环境提供了一个反应更快的解决方案。

远程构建

在远程构建的情况下,构建命令是在本地机器上调用的,但实际计算是在远程机器上进行的。源文件和其他支持构建的输入最初存在于本地机器上并与远程机器同步。同样,在构建结束时,所产生的构建输出/工件也从远程同步到本地机器。

这开启了一些有趣的可能性,尽管并非没有挑战。一方面,远程构建可能会增加在不同硬件架构或操作系统上构建代码的能力(例如,Windows客户端在Linux主机上构建)。而远程主机的硬件可能会导致构建速度的显著提高。同时,保持源文件、项目依赖性、构建工件和构建软件的短暂、中间状态的同步开销可能很快就会超过原始速度的好处了。

Gradle今天没有提供自己的远程构建解决方案,但存在一些有趣的互补性开源解决方案。

  • Mainframer:"一个在远程机器上执行命令的工具,同时来回同步文件"。
  • Mirakle。"一个Gradle插件,允许你将构建过程从本地机器转移到远程机器。"

远程IDE

远程IDE与远程构建方案类似,但有两个关键区别。首先,IDE处理与远程主机的通信,而不是命令行或构建工具的调用。其次,源代码可以完全在远程主机上克隆。在这种模式下,IDE在本地机器上充当一个 "瘦客户端"。IDE代码的 "后端 "部分作为后台进程在远程主机上运行。这种方法的好处是,项目源代码不需要存在于本地,而且减轻了远程构建的同步开销。与本地开发一样,"后端 "IDE和构建过程之间有可能出现资源争夺。

现在有三个很好的远程IDE的例子,它们都能与Gradle构建工具无缝地工作:

  • Visual Studio Code。通过其 "远程 - SSH "扩展提供了一个远程IDE体验。
  • IntelliJ IDEA。JetBrains Client和Gateway一起工作,在远程构建的同时在本地运行一个瘦IDE。
  • Fleet。虽然处于封闭预览阶段,但JetBrains Fleet提供了一个类似于Visual Studio Code的轻量级远程IDE体验。

像上面的例子一样的远程IDE可以提供一个非常舒服的开发体验。如果远程主机或虚拟机被安置在靠近VCS和二进制工件存储系统的数据中心,那么克隆代码和解决外部依赖关系就会非常快。如果做得好,远程IDE与本地构建相比,可以有很大的改进。

远程构建环境

在远程IDE的概念上更进一步,远程构建环境的目的是自动配置远程主机,强调一致性和协作。远程构建环境通常是集中管理的,确保所有的工程师都有一个可靠的、统一的环境,不需要在他们的本地机器上构建。一些远程构建环境结合了其他开发工具,如IDE、错误/问题跟踪和源代码控制。与远程IDE相结合,将工程师可能需要的所有工具打包到一个单一的、精心策划的环境中,可以产生一个非常舒适的和高效开发体验。

与远程构建/IDE一样,额外的性能是以更快的CPU内核的形式出现的,并且通过每台机器相对于本地环境的额外内核来提高并行性。虽然远程构建环境的使用与构建工具的选择没有直接关系,但Gradle构建工具将在任何远程环境中透明地工作。

远程构建环境的三个突出例子是。

集中管理的远程构建环境可以提供几个好处。

  • 更快的启动时间。工程师不需要手动检查代码和设置本地机器:环境可以被配置为 "开箱即用"。
  • 更快的反馈时间。这假定远程机器具有更高的性能,并与其他关键资源(如你的二进制工件存储)同处一地。
  • 多平台支持。例如,从macOS笔记本上远程构建Windows环境,或者反之亦然。
  • 统一的环境。本地工程师机器上不一致的风险较小。
  • 安全和审计。这指的是在数据中心集中管理的环境,这可能是保护知识产权所需要的,也可能是合规要求。

远程总结

纵观上述可用的解决方案,我们看到最令人兴奋的创新发生在远程IDE和远程构建环境领域。远程构建也有一些有趣的方面,但对本地开发者体验的边际效益可能会被增加的复杂性所抵消。因此,我们鼓励绕过远程构建而使用远程IDE,同时关注远程构建环境的新兴功能。

分布式构建的变化

与在一台远程机器上执行所有工作的远程构建不同,分布式构建专注于将工作分成小块,并在多台机器上分配。远程执行器通常从一个池中分配,类似于CI分配的工作方式,尽管每个分布式工作项目的执行时间相对较短。

分布式构建是作为构建的一个或多或少透明的功能来实现的,所以开发者在本地触发它们的方式与触发本地构建的方式类似。执行一个工作项目所需的输入被传送到执行器,而生成的输出被同步回来。

不要忘记分布式构建的支持基础设施需求。如果没有足够的远程构建代理,一个构建实际上可能会更慢。复杂的构建分布代理池的管理增加了额外的维护,如监控/可观察性、扩展和故障转移/容错。

在我们讨论真正的分布式构建解决方案之前,我们先介绍一下在多台机器上分布构建工作的最基本技术。

手动优化:CI扇出

CI扇出是一种技术,通过将构建(通常是测试的子集)分割到多个CI作业,使工作在不同的代理上执行,从而减少端到端的构建时间。虽然它是对无并行或单机并行的改进,但它也有很大的缺点。CI工作的分区必须手动配置,而且对每个CI平台都是独一无二的。虽然这减少了CI上的整体构建时间,但对本地构建没有好处。这里描述了这种方法的其他挑战。

现代测试分布

根据我们的经验,运行测试,而不是编译源代码,通常是导致缓慢构建的主要原因,特别是在JVM生态系统中。JVM上的测试执行自然适合于分发,因为测试通常在一个单独的短暂的虚拟机中执行,其系统环境、classpath和内存使用已经被指定。这些参数可以很容易地传达给远程主机进行分布式执行。

在本地划分或并行化测试执行时,同样的问题也适用于以分布式方式运行测试。好的测试方法应该是原子性的,只依赖于测试夹具的明确环境设置/删除指令。一个拙劣的、非原子的测试依赖于另一个测试的副作用,当以分布式方式执行时,可能会以意外的方式失败。

Gradle Enterprise的测试分布商业功能在一个远程主机池上执行测试,其并行性比本地实现的更大。它还在同一台主机上执行一个测试类的所有方法,缓解了上面提到的非原子测试失败的最常见原因。

一般分布

顾名思义,一般分布是一种在远程主机上执行任何构建操作的方式。必须仔细考虑环境变量或其他系统属性,与本地构建相同的代码相比,分布式构建结果可能会产生意想不到的变化。另外,哪些构建操作可以证明分布式的开销是合理的,这个问题很难回答。

下面的工具对构建分布采取了一般的方法。

在决定采用一般的分布式解决方案之前,要知道可能需要进行重大的权衡。一个通用的分布式构建环境可能会给构建逻辑增加很大的复杂性。"拆分包 "编译(有时称为1:1:1规则)是一种将源代码划分为较小的编译单元以帮助分发的技术,但给已经很复杂的依赖性管理问题增加了更多痛苦。更多细节请参见构建文件的颗粒度

分布非测试工作(如编译)的好处取决于所使用的编程语言的编译速度。例如,与 "本地 "语言相比,Java的编译速度相对较快。使用上面提到的 "分割包 "编译,可能会有小的性能改进。但是为了相对较小的性能提升,维护复杂的构建逻辑所带来的额外痛苦可能是不值得的。考虑到Gradle构建工具的Java增量编译器已经在javac的基础上提供了显著的性能提升,这一点尤其正确。

参见《通用构建分布》一文,以了解更多关于通用构建分布的权衡细节。

远程和分布式构建的共同因素

在远程和分布式模式中,都可能产生非同小可的网络流量。将源代码序列化到远程主机或在代理之间同步构建工件会产生大量的开销。本地客户端和远程主机之间的网络距离,或者分发代理和工件存储之间的网络距离,可能是一个主要因素。网络连接应该是高带宽和低延迟的,以防止侵蚀通过远程和/或分布式工作实现的理论收益。

此外,管理远程主机或分布式代理的池子会产生更多的成本和开销。将需要额外的工程投资来提供标准化的环境。应注意通过对高峰使用周期和停机时间的反应,避免资源饥饿或过度分配。

总结

在这篇文章中,我们回顾了利用远程机器的构建模式,澄清了远程和分布式构建的定义,并讨论了其变化。

我们首先解释了远程构建缓存是利用远程机器的最基本构建优化。然后,我们阐述了远程构建模式,并指出了在远程IDE和远程构建环境领域发生的令人兴奋的创新。最后,我们解释了CI扇出技术和分布式构建的不同模式,包括测试分布和一般分布。

反馈意见

如果你有任何问题,请在我们的论坛Gradle社区Slack上告诉我们。