JS `Monorepo` `Remote Execution` (`Bazel`, `Pants`) `Distributed Build Cache`

各位朋友,大家好!我是今天的主讲人,咱们今天聊聊“JS Monorepo + Remote Execution + Distributed Build Cache”这个组合,听起来是不是有点像科幻电影的名字?别怕,我会尽量用大白话把它掰开了揉碎了讲清楚。

先来个热身:你有没有遇到过以下情况?

  • 项目越来越大,构建时间长到可以煮咖啡甚至煲剧?
  • 团队成员的代码风格不统一,一会儿空格缩进一会儿Tab缩进,简直逼死强迫症?
  • 改了一行代码,整个项目都要重新构建,感觉生命都在浪费?
  • 多人协作时,环境配置不一样,本地跑得好好的,一提交就挂了?

如果你点头如捣蒜,那么恭喜你,今天的内容绝对能帮到你!

第一幕:Monorepo 了解一下

啥是Monorepo?简单来说,就是把多个项目或者模块的代码都放在同一个代码仓库里管理。别以为这只是简单的“打包”,它背后可是有很多好处的。

特性 解释 优势
单一代码库 所有项目/模块的代码都在同一个Git仓库中。 简化依赖管理、方便代码复用、原子性变更(一次提交修改多个项目)、更容易进行大规模重构。
共享依赖 多个项目可以共享同一个依赖包,避免重复安装和版本冲突。 减少node_modules体积、节省磁盘空间、提升构建速度。
显式依赖关系 可以清晰地定义项目之间的依赖关系,方便理解和维护。 避免循环依赖、方便进行依赖分析、更容易进行代码审查。
代码复用 不同的项目可以方便地复用相同的代码,减少重复开发。 提高开发效率、减少代码冗余、保持代码一致性。
原子性变更 可以一次提交修改多个项目,保证代码的一致性。 避免出现中间状态、保证代码的正确性。
易于重构 由于所有代码都在同一个仓库中,可以更容易地进行大规模重构。 提高代码质量、降低维护成本。

举个例子,假设我们有个电商平台,包括前端(website)、后端API(api)和公共组件库(components)三个模块。在传统的Multi-repo模式下,它们会分别放在三个Git仓库里。

# Multi-repo 结构
website/
api/
components/

而在Monorepo模式下,它们会放在同一个Git仓库里:

# Monorepo 结构
my-ecommerce-platform/
  packages/
    website/
    api/
    components/

常用的Monorepo管理工具有很多,比如Lerna、Yarn Workspaces、Nx等等。我们这里用Yarn Workspaces来简单演示一下。

  1. 初始化项目:

    mkdir my-ecommerce-platform
    cd my-ecommerce-platform
    yarn init -y
  2. 启用Workspaces:package.json中添加workspaces字段。

    {
      "name": "my-ecommerce-platform",
      "private": true,
      "workspaces": [
        "packages/*"
      ]
    }
  3. 创建packages目录和子项目:

    mkdir packages
    mkdir packages/website
    mkdir packages/api
    mkdir packages/components
    cd packages/website
    yarn init -y
    cd ../api
    yarn init -y
    cd ../components
    yarn init -y
    cd ../..
  4. 定义依赖关系: 比如website依赖components,可以在packages/website/package.json中添加:

    {
      "name": "website",
      "version": "1.0.0",
      "dependencies": {
        "components": "workspace:*"
      }
    }

    注意 "components": "workspace:*" 这行代码,它告诉Yarn,website项目依赖于当前Monorepo中的components项目。

  5. 安装依赖: 在项目根目录下运行yarn install,Yarn会自动安装所有项目的依赖,并进行依赖提升(hoisting),尽量减少node_modules的体积。

    yarn install

    你可以看到,所有项目的依赖都安装到了根目录的node_modules下,而每个子项目下只有指向根目录依赖的符号链接。

第二幕:Remote Execution 加速你的构建

有了Monorepo,代码管理方便了,但是构建速度可能还是个问题。这时候,Remote Execution就派上用场了。

Remote Execution,顾名思义,就是把构建任务放到远程的服务器上执行。这有什么好处呢?

  • 更快的构建速度: 远程服务器通常拥有更强大的CPU、更大的内存,可以并行执行更多的构建任务。
  • 统一的构建环境: 所有构建任务都在相同的环境中执行,避免了本地环境差异导致的问题。
  • 释放本地资源: 构建任务不再占用本地资源,你可以专心写代码。

目前比较流行的Remote Execution工具有Bazel和Pants。它们都是非常强大的构建工具,但是配置起来也比较复杂。我们这里简单介绍一下Bazel。

  1. 安装Bazel: 具体安装方法可以参考Bazel官网

  2. 创建WORKSPACE文件: 在项目根目录下创建一个WORKSPACE文件,用于声明项目的工作空间。

    # WORKSPACE
    workspace(name = "my-ecommerce-platform")
  3. 创建BUILD文件: 在每个需要构建的目录下创建一个BUILD文件,用于定义构建规则。

    比如,packages/website/BUILD文件:

    # packages/website/BUILD
    load("@npm//:defs.bzl", "npm_package", "npm_run_script")
    
    npm_package(
        name = "website_package",
        package_name = "website",
        srcs = glob(["**/*"]),
    )
    
    npm_run_script(
        name = "build",
        package = ":website_package",
        script = "build",  # 假设website项目有build脚本
    )

    这个BUILD文件定义了一个npm_package规则,用于将website项目打包成一个npm包。还定义了一个npm_run_script规则,用于执行website项目的build脚本。

  4. 配置Remote Execution: 这部分比较复杂,需要配置Bazel的远程执行环境,比如使用Google Cloud Build、AWS CodeBuild等。具体可以参考Bazel Remote Execution 文档

  5. 运行Bazel构建命令:

    bazel build //packages/website:build

    这条命令会把website项目的构建任务发送到远程服务器执行,并将构建结果返回到本地。

Pants 也是一个非常优秀的构建工具,和 Bazel 类似,它也支持 Remote Execution 和 Distributed Build Cache。Pants 比 Bazel 更容易上手,配置起来也更简单。

第三幕:Distributed Build Cache 缓存加速再加速

即使有了Remote Execution,构建速度还是可以进一步提升的。秘诀就是Distributed Build Cache。

Distributed Build Cache,顾名思义,就是把构建结果缓存起来,下次构建的时候如果输入没有变化,就直接从缓存中读取结果,避免重复构建。

这就像你第一次做红烧肉要查菜谱、准备食材、炒糖色,费时费力。但是下次再做的时候,如果你把上次做好的红烧肉汤汁冻起来,下次直接拿出来用,就能省很多事了。

Bazel和Pants都内置了对Distributed Build Cache的支持。只需要简单配置一下,就可以启用缓存功能。

  • Bazel: 可以使用Google Cloud Storage、AWS S3等作为缓存后端。
  • Pants: 可以使用本地目录、HTTP服务器、AWS S3等作为缓存后端。

启用缓存后,第一次构建会比较慢,因为需要将构建结果上传到缓存。但是后续的构建会非常快,因为可以直接从缓存中读取结果。

第四幕:三剑合璧,天下无敌

把Monorepo、Remote Execution和Distributed Build Cache结合起来,就能打造一个高效、可靠的JS项目构建系统。

  • Monorepo: 统一代码管理,方便代码复用和依赖管理。
  • Remote Execution: 加速构建速度,统一构建环境。
  • Distributed Build Cache: 避免重复构建,进一步提升构建速度。

这三者之间的关系就像一个金字塔:Monorepo是基础,Remote Execution是加速器,Distributed Build Cache是润滑剂。它们相互配合,共同提升开发效率和代码质量。

第五幕:一些注意事项和最佳实践

  • 选择合适的Monorepo管理工具: Lerna、Yarn Workspaces、Nx各有优缺点,选择最适合你项目的工具。
  • 合理划分模块: Monorepo不是把所有代码都塞到一个仓库里,要合理划分模块,避免代码耦合。
  • 编写清晰的构建规则: Bazel和Pants的构建规则比较复杂,要认真学习和编写,确保构建的正确性和效率。
  • 监控构建性能: 定期监控构建速度和缓存命中率,及时发现和解决问题。
  • 拥抱自动化: 尽量使用CI/CD工具,自动化构建、测试和部署流程。

代码示例:Pants 配置 Remote Execution 和 Distributed Build Cache

假设我们已经配置好了一个 Pants 项目,现在需要配置 Remote Execution 和 Distributed Build Cache。

  1. 安装 Pants: 按照 Pants 官网的指引安装 Pants。

  2. 配置 pants.toml: 在项目根目录下创建一个 pants.toml 文件,用于配置 Pants。

    [GLOBAL]
    pants_version = "2.16.0"  # 替换为你的 Pants 版本
    
    # 配置 Remote Execution
    process_execution_remote_enabled = true
    process_execution_remote_address = "grpcs://your-remote-execution-server.com:443"  # 替换为你的 Remote Execution 服务器地址
    process_execution_remote_ca_certs_path = "path/to/your/ca_certs.pem"  # 如果你的 Remote Execution 服务器使用自签名证书,需要配置 CA 证书
    process_execution_remote_oauth_bearer_token_path = "path/to/your/oauth_bearer_token.txt"  # 如果你的 Remote Execution 服务器需要 OAuth 认证,需要配置 OAuth Token
    
    # 配置 Distributed Build Cache
    process_execution_cache_remote_enabled = true
    process_execution_cache_remote_read = true  # 允许从缓存读取
    process_execution_cache_remote_write = true  # 允许写入缓存
    process_execution_cache_remote_address = "grpc://your-build-cache-server.com:9090"  # 替换为你的 Build Cache 服务器地址
    process_execution_cache_remote_ca_certs_path = "path/to/your/ca_certs.pem"  # 如果你的 Build Cache 服务器使用自签名证书,需要配置 CA 证书
    process_execution_cache_remote_oauth_bearer_token_path = "path/to/your/oauth_bearer_token.txt"  # 如果你的 Build Cache 服务器需要 OAuth 认证,需要配置 OAuth Token

    这个 pants.toml 文件配置了 Remote Execution 和 Distributed Build Cache 的基本参数。你需要根据你的实际情况修改这些参数。

  3. 创建 BUILD 文件: 在每个需要构建的目录下创建一个 BUILD 文件,用于定义构建规则。

    比如,packages/website/BUILD 文件:

    # packages/website/BUILD
    python_sources(
        name="website",
        sources=["*.py"],  # 假设 website 项目包含 Python 代码
        dependencies=[],
    )

    这个 BUILD 文件定义了一个 python_sources 规则,用于构建 website 项目的 Python 代码。

  4. 运行 Pants 构建命令:

    ./pants package packages/website

    这条命令会把 website 项目的构建任务发送到 Remote Execution 服务器执行,并将构建结果缓存到 Distributed Build Cache 中。

总结

今天我们聊了JS Monorepo、Remote Execution和Distributed Build Cache,这三个技术可以帮助你构建高效、可靠的JS项目。虽然配置起来有些复杂,但是一旦配置好,就能带来巨大的收益。希望今天的分享能对你有所帮助!

最后,我想说,技术是不断发展的,我们要保持学习的热情,不断探索新的技术,才能更好地应对未来的挑战。谢谢大家!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注