远程和分布式构建模式

目录

介绍

Gradle 构建工具的一个常见需求是执行远程或分布式构建的能力。但这究竟意味着什么?这些请求背后的动机是什么?本文将探讨远程构建与分布式构建之间的区别及其变体。由于行业内对于这些概念的术语尚无统一共识,因此本文的目标是概述这些模式以及它们之间的关系。

除了两个特定于 JVM 的引用外,这些观察结果通常适用于使用任何语言或生态系统的软件项目。

但为什么? #

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

术语 #

“远程”和“分布式”构建这两个术语在行业内并非总是使用一致,并且经常互换使用。下面,我们将分别给出明确的定义。首先,我们还将定义更基本的“构建缓存”优化。

什么是远程构建缓存? #

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

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

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

远程构建的变体 #

通常,“远程构建”是指通过将整个过程委托给另一台计算机来缩短构建时间的方法。通常,远程计算机在计算资源和内存方面比本地计算机更强大。它可以在统一、精心管理的环境中和/或隔离地托管构建,免受与其他本地进程的资源争用。

在实践中,远程构建可以采用以下四种形式之一。

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

最原始的远程构建模式是使用通过古老的 VNC 或 RDP 协议的远程桌面。虽然这些工具确实是低技术的屏幕共享工具,并且可能受到低网络带宽和高延迟的严重影响,但它们确实允许在远程机器上构建软件。我们提到这个历史解决方案只是为了完整性,因为现代远程 IDE 提供了响应更迅速的解决方案。

什么是远程构建? #

在远程构建场景中,构建命令在本地计算机上调用,但实际计算发生在远程计算机上。源文件和其他支持构建的输入最初存在于本地计算机上,并与远程计算机同步。同样,在构建结束时,生成的构建输出/工件将从远程同步回本地计算机。

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

Gradle 目前不提供自己的远程构建解决方案,但存在一些有趣的互补开源解决方案

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

什么是远程 IDE? #

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

如今存在三个远程 IDE 的优秀示例,所有这些示例都与 Gradle 构建工具无缝协作

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

像上面示例这样的远程 IDE 可以提供非常愉快的开发体验。如果远程主机或 VM 与 VCS 和二进制工件存储系统一起位于数据中心附近,则克隆代码和解析外部依赖项可能非常快。如果做得好,远程 IDE 可以比本地构建有显着改进。

云开发环境 #

将远程 IDE 的概念更进一步,云开发环境旨在自动化远程主机的配置,重点是统一性和协作。云开发环境通常是集中管理的,确保所有工程师都拥有可靠、统一的环境,而无需在其本地机器上进行构建。一些云开发环境结合了其他开发工具,例如 IDE、错误/问题跟踪和源代码控制。与远程 IDE 结合使用,将工程师可能需要的所有工具包装到单个、精心管理的环境中,可以产生非常愉快和高效的开发体验。

与远程构建/IDE 一样,额外的性能来自于更快的 CPU 核心以及相对于本地环境,通过每台机器的更多核心实现的改进的并行性。虽然云开发环境的使用与构建工具的选择没有直接关系,但 Gradle 构建工具可以在任何远程环境中透明地工作。

云开发环境的三个突出例子是

集中管理的云开发环境可以提供多种好处

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

远程总结 #

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

分布式构建的变体 #

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

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

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

在我们深入探讨真正的分布式构建解决方案之前,我们将首先描述跨多台机器分发构建工作的最基本技术。

手动优化:CI 分发 #

CI 分发是一种通过将构建(通常是测试子集)拆分到多个 CI 作业来减少端到端构建时间的技术,以便在不同的代理上执行工作。虽然它比没有并行性或单机并行性有所改进,但它也存在重大缺陷。CI 作业的分区必须手动配置,并且对于每个 CI 平台都是唯一的。虽然这减少了 CI 的总体构建时间,但它对本地构建没有好处。此处描述了此方法的其他挑战here

现代测试分发 #

根据我们的经验,运行测试而不是编译源代码通常是构建缓慢的主要原因,尤其是在 JVM 生态系统中。JVM 上的测试执行自然适合分发,因为测试通常在单独的临时 VM 中执行,其系统环境、类路径和内存使用量已经指定。这些参数很容易传达给远程主机以进行分布式执行。

本地分区或并行化测试执行时的相同顾虑也适用于以分布式方式运行测试时。良好的测试方法应该是原子的,仅依赖于来自测试装置的显式环境设置/拆卸指令。当以分布式方式执行时,编写不佳、非原子性的测试依赖于另一个测试的副作用可能会以意想不到的方式失败。

Gradle Enterprise 的测试分发商业功能在远程主机池上执行测试,并行性高于本地可以实现的水平。它还在同一主机上执行测试类的所有方法,从而减轻了上述非原子性测试失败的最常见原因。

通用分发 #

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

以下工具采用通用方法进行构建分发

在决定通用分发解决方案之前,请了解可能需要进行重大权衡。通用分发构建环境可能会显着增加构建逻辑的复杂性。“拆分包”编译(有时称为1:1:1 规则)是一种将源代码划分为更小的编译单元以帮助分发的技术,但这会给已经复杂的依赖管理问题增加更多痛苦。有关更多详细信息,请参阅构建文件的粒度

分发非测试工作(如编译)的好处取决于所用编程语言的编译速度。例如,与“原生”语言相比,Java 编译相对较快。使用上面提到的“拆分包”编译可能会实现小的性能改进。但是,维护复杂构建逻辑的额外痛苦可能不足以证明相对较小的性能提升是合理的。考虑到 Gradle 构建工具的 Java 增量编译器已经在 javac 之上提供了显着的性能提升,这一点尤其如此。

有关通用构建分发的权衡的更多详细信息,请参阅文章《通用构建分发:游戏规则改变者还是噱头?》

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

在远程和分布式范例中,都可能导致大量的网络流量。将源代码序列化到远程主机或在代理之间同步构建工件可能会产生显着的开销。本地客户端和远程主机之间,或分发代理和工件存储之间的网络邻近性可能是一个主要因素。网络连接应具有高带宽和低延迟,以防止削弱通过远程和/或分布式工作实现的理论收益。

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

总结 #

在本文中,我们回顾了利用远程机器的构建模式,阐明了远程构建和分布式构建的定义,并讨论了它们的变体。

我们首先解释了远程构建缓存作为利用远程机器的最基本的构建优化。然后,我们详细阐述了远程构建模式,并指出了远程 IDE 和云开发环境领域正在发生的激动人心的创新。最后,我们解释了 CI 分发技术以及分布式构建中的不同模式,包括测试分发和通用分发。

反馈 #

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

讨论