远程和分布式构建模式

目录

引言

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:通过其“远程 - SSH”扩展提供远程 IDE 体验。
  • IntelliJ IDEA:JetBrains 客户端和网关协同工作,在本地运行轻量级 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 上的测试执行天生适合分发,因为测试通常在独立的临时 VM 中执行,其系统环境、类路径和内存使用情况已经指定。这些参数很容易传达给远程主机以进行分布式执行。

在分布式运行测试时,本地分区或并行化测试执行时所面临的担忧同样适用。良好的测试方法应该是原子性的,仅依赖于测试夹具的明确环境设置/拆卸指令。设计不佳、非原子性的测试,如果依赖于另一个测试的副作用,在分布式执行时可能会以意想不到的方式失败。

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

通用分发#

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

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

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

分发非测试工作(如编译)的好处取决于所用编程语言的编译速度。例如,Java 编译相对于“原生”语言而言相对较快。使用上面提到的“分包”编译可能会实现小的性能改进。但维护复杂构建逻辑的额外麻烦可能不值得相对较小的性能提升。考虑到 Gradle 构建工具的 Java 增量编译器已经在 javac 的基础上提供了显著的性能提升,这一点尤其如此。

有关通用构建分发的权衡的更多详细信息,请参阅通用构建分发:是颠覆者还是噱头?一文。

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

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

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

总结 #

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

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

反馈 #

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

讨论