在 Monorepo 中使用许多包进行条件云构建
Conditional Cloud Builds with Many Packages in a Monorepo
动机
我想在 Google Cloud Build and Google Kubernetes Engine 的帮助下完全自动化部署许多 服务 。这些服务位于 monorepo 中,其中有一个名为 services
.
的文件夹
所以我为每个服务创建了一个 cloudbuild.yaml
并创建了一个构建触发器。 cloudbuild.yaml
做:
- 运行 测试
- 构建 Docker 图像的新版本
- 推送新的Docker图片
- 将更改应用到 Kubernetes 集群
问题
随着服务数量的增加,构建触发器的数量也会增加。也有越来越多的服务被构建,即使它们没有改变。
因此我想要一种机制,它只有一个构建触发器并自动确定哪些服务需要重建。
例子
假设我有一个具有以下文件结构的 monorepo:
├── packages
│ ├── enums
│ ├── components
└── services
├── backend
├── frontend
├── admin-dashboard
然后我在 frontend
服务中进行一些更改。由于 frontend
和 admin-dashboard
服务依赖于 components
包,因此需要重建多个服务:
- 前端
- 管理员仪表板
但是不是后端!
我试过的
(1) 多个构建触发器
为每个 服务设置多个构建触发器。但其中 80% 的构建是多余的,因为代码中的大多数更改仅与个人服务相关。管理许多看起来几乎相同的构建触发器也变得越来越复杂。单个 cloudbuild.yaml
文件如下所示:
steps:
- name: "gcr.io/cloud-builders/docker"
args:
[
"build",
"-f",
"./services/frontend/prod.Dockerfile",
"-t",
"gcr.io/$PROJECT_ID/frontend:$REVISION_ID",
"-t",
"gcr.io/$PROJECT_ID/frontend:latest",
".",
]
- name: "gcr.io/cloud-builders/docker"
args: ["push", "gcr.io/$PROJECT_ID/frontend"]
- name: "gcr.io/cloud-builders/kubectl"
args: ["apply", "-f", "kubernetes/gcp/frontend.yaml"]
env:
- "CLOUDSDK_COMPUTE_ZONE=europe-west3-a"
- "CLOUDSDK_CONTAINER_CLUSTER=cents-ideas"
(2) 遍历 cloudbuild 文件
问题是关于一个非常相似的问题。所以我尝试在项目的根目录中设置一个 "entry-point" cloudbuild.yaml
文件并遍历所有服务:
steps:
- name: 'gcr.io/cloud-builders/gcloud'
entrypoint: 'bash'
args:
- '-c'
- |
for d in ./services/*/; do
config="${d}cloudbuild.yaml"
if [[ ! -f "${config}" ]]; then
continue
fi
echo "Building $d ... "
(
gcloud builds submit $d --config=${config}
) &
done
wait
这将消除对多个构建触发器的需要。但是我也 运行 遇到了这个方法的问题:
每个服务都被发送到它自己的构建过程中,并具有该特定服务的文件范围。这意味着,我只能在构建期间访问 /services/specific-service
内的文件。这对我来说真是太糟糕了(我需要访问父目录中的文件,例如 packages
和根目录中的配置文件)。
(3) 仅构建更改的服务
因为我想要一种机制来只构建更改的服务,所以我尝试确定需要重建的服务。在 lerna 的帮助下似乎很容易做到这一点。 运行
lerna changed --all --parseable
将return列出更改包的文件路径,如下所示:
/home/username/Desktop/project/packages/components
/home/username/Desktop/project/services/frontend
/home/username/Desktop/project/services/admin-dashboard
但是,该列表还包括 packages
,我不知道如何在脚本中使用此列表来遍历受影响的服务。另外:当我触发构建时(例如通过标记提交),lerna 将无法在构建过程中识别更改的包,因为更改已经提交。
我知道这是一篇很长的文章。但我认为这是一个重要的话题,所以我非常感谢任何帮助!
P.S.: This 是我的实际项目的样子,如果你想仔细看看具体的用例。
要从 monorepo 构建,您确实希望增量构建(更改的内容以及依赖于更改的部分的部分)。为此,您的构建工具需要以某种方式处理依赖关系图。
您描述的 Lerna 是为 monorepos 设计的。但是 Bazel and it is available as an option in Google Cloud Builder, cloud-builders/bazel 与 docker 构建器结合使用的文档也是如此。
但是,为 monorepos 设计的构建工具通常设置起来更复杂。
动机
我想在 Google Cloud Build and Google Kubernetes Engine 的帮助下完全自动化部署许多 服务 。这些服务位于 monorepo 中,其中有一个名为 services
.
所以我为每个服务创建了一个 cloudbuild.yaml
并创建了一个构建触发器。 cloudbuild.yaml
做:
- 运行 测试
- 构建 Docker 图像的新版本
- 推送新的Docker图片
- 将更改应用到 Kubernetes 集群
问题
随着服务数量的增加,构建触发器的数量也会增加。也有越来越多的服务被构建,即使它们没有改变。
因此我想要一种机制,它只有一个构建触发器并自动确定哪些服务需要重建。
例子
假设我有一个具有以下文件结构的 monorepo:
├── packages
│ ├── enums
│ ├── components
└── services
├── backend
├── frontend
├── admin-dashboard
然后我在 frontend
服务中进行一些更改。由于 frontend
和 admin-dashboard
服务依赖于 components
包,因此需要重建多个服务:
- 前端
- 管理员仪表板
但是不是后端!
我试过的
(1) 多个构建触发器
为每个 服务设置多个构建触发器。但其中 80% 的构建是多余的,因为代码中的大多数更改仅与个人服务相关。管理许多看起来几乎相同的构建触发器也变得越来越复杂。单个 cloudbuild.yaml
文件如下所示:
steps:
- name: "gcr.io/cloud-builders/docker"
args:
[
"build",
"-f",
"./services/frontend/prod.Dockerfile",
"-t",
"gcr.io/$PROJECT_ID/frontend:$REVISION_ID",
"-t",
"gcr.io/$PROJECT_ID/frontend:latest",
".",
]
- name: "gcr.io/cloud-builders/docker"
args: ["push", "gcr.io/$PROJECT_ID/frontend"]
- name: "gcr.io/cloud-builders/kubectl"
args: ["apply", "-f", "kubernetes/gcp/frontend.yaml"]
env:
- "CLOUDSDK_COMPUTE_ZONE=europe-west3-a"
- "CLOUDSDK_CONTAINER_CLUSTER=cents-ideas"
(2) 遍历 cloudbuild 文件
cloudbuild.yaml
文件并遍历所有服务:
steps:
- name: 'gcr.io/cloud-builders/gcloud'
entrypoint: 'bash'
args:
- '-c'
- |
for d in ./services/*/; do
config="${d}cloudbuild.yaml"
if [[ ! -f "${config}" ]]; then
continue
fi
echo "Building $d ... "
(
gcloud builds submit $d --config=${config}
) &
done
wait
这将消除对多个构建触发器的需要。但是我也 运行 遇到了这个方法的问题:
每个服务都被发送到它自己的构建过程中,并具有该特定服务的文件范围。这意味着,我只能在构建期间访问 /services/specific-service
内的文件。这对我来说真是太糟糕了(我需要访问父目录中的文件,例如 packages
和根目录中的配置文件)。
(3) 仅构建更改的服务
因为我想要一种机制来只构建更改的服务,所以我尝试确定需要重建的服务。在 lerna 的帮助下似乎很容易做到这一点。 运行
lerna changed --all --parseable
将return列出更改包的文件路径,如下所示:
/home/username/Desktop/project/packages/components
/home/username/Desktop/project/services/frontend
/home/username/Desktop/project/services/admin-dashboard
但是,该列表还包括 packages
,我不知道如何在脚本中使用此列表来遍历受影响的服务。另外:当我触发构建时(例如通过标记提交),lerna 将无法在构建过程中识别更改的包,因为更改已经提交。
我知道这是一篇很长的文章。但我认为这是一个重要的话题,所以我非常感谢任何帮助!
P.S.: This 是我的实际项目的样子,如果你想仔细看看具体的用例。
要从 monorepo 构建,您确实希望增量构建(更改的内容以及依赖于更改的部分的部分)。为此,您的构建工具需要以某种方式处理依赖关系图。
您描述的 Lerna 是为 monorepos 设计的。但是 Bazel and it is available as an option in Google Cloud Builder, cloud-builders/bazel 与 docker 构建器结合使用的文档也是如此。
但是,为 monorepos 设计的构建工具通常设置起来更复杂。