Cloud Build のワザ その2

2025/06/23 11:21:11

Cloud Build のワザ その2

English follows Japanese.

結論

ビルドプロセスの最初に、とにかく pull だけを実行しておくようにしておくと、ビルド時間が短縮できる。
ビルドステップに現れるイメージの数が多いほど、効果が大きい。
Dockerfile で利用するイメージにも効果がある。

はじめに

みなさん、Cloud Build を使っていますか?
Cloud Build は、Google Cloud の CI/CD サービスです。いろいろな使い方ができますが、ソースコードをビルドして、コンテナイメージを作成し、デプロイするための強力なツールです。

この記事は、Cloud Build のワザの2回目です。
前回は、google-cloud-clicloud-sdk の代わりに gcr.io/cloud-builders/gcloud を使うだけで1分のビルド時間が短縮できることを紹介しました。

今回も、ビルドの「中身は変えず」に、ビルド時間だけを短縮する方法を紹介します。

詳細

ビルドプロセス内では、ステップ毎に異なる Docker イメージを使うことが多いでしょう。また、ビルドステップには「順序」があることが多いです。特定のステップの結果に依存して、次のステップが実行されることが多いからです。

https://console.cloud.google.com/cloud-build/builds;region=global/604d39a5-8ec1-4017-8b5e-xxxxxxxxxxxxx のような形式の Cloud Build のビルドの詳細画面を見てみると、ビルドステップで何が実行されているかが分かります。

ステップ内で

Pulling image: wordpress:php8.2-apache
php8.2-apache: Pulling from library/wordpress
61320b01ae5e: Already exists
b4ce612dc732: Already exists
093338982d92: Pulling fs layer
0c2a0ba9eb0c: Pulling fs layer
442abaed7751: Pulling fs layer
aa4e51934eef: Pulling fs layer
924949db942b: Pulling fs layer
153d2fb08c64: Pulling fs layer
26f17ce45149: Pulling fs layer
64a857ca8a6a: Pulling fs layer

のような出力があることがあります。これは、Docker イメージのダウンロードがそのステップ内で行われていることを示しています。

この「イメージのダウンロード」が、後ろの方のステップで行われていないでしょうか。ここに、実は、無駄があるのです。ステップの「内容」自体は前のステップの結果に依存しているため、順番に実行される必要があります。一方で、イメージのダウンロードは「先に」行うことができます。

Cloud Build のビルドプロセスは、前後関係が定義(指定)されていないステップ同士を「並行して実行」してくれる機能があります。ビルドステップの順序の構成に詳しく説明されています。

各ビルドステップは id を持つことができます。また、waitFor というプロパティを使うことで、特定の id のステップが完了するまで待つように指定できます。
waitFor を指定されない場合、ビルドステップは、ビルド リクエスト内の 先行するすべてのビルドステップが正常に完了 するまで待機した後に実行されます。例えば、次のようなビルド構成ファイルがある場合、ステップ A, B, C は順番に実行されます。

steps:
- name: foo
  id: A
- name: bar
  id: B
- name: baz
  id: C

ここで、ステップ B, C で使用する Docker イメージを、ステップ A と並行して、先にダウンロードしておくようにすると、ステップ B, C の実行時間が、それぞれのステップで利用するイメージのダウンロード時間分短縮されるはずです。

steps:
- name: foo
  id: A
  waitFor: ['-'] # Download images と並行して実行される、書かなくても良い
- name: bar
  id: B
- name: baz
  id: C
- name: 'gcr.io/cloud-builders/docker'
  id: 'Download images'
  entrypoint: 'bash'
  args:
    - '-c'
    - |
      # foo はステップ A で使うイメージなので、書かなくても良い
      docker pull foo
      docker pull bar
      docker pull baz
  waitFor: ['-'] # A と並行して実行される

もし、'Download images' のステップを先に書きたい場合は、ステップ B, C 等に waitFor を指定する必要があります。

ビルドステップのログで、例えば以下のように表示される場合は、「イメージのダウンロードが済んでいる」ことを示しています。時短に成功しています。

Already have image (with digest): wordpress:php8.2-apache

'Download images' のステップを先に書いておくと、利用するイメージの数によって、ビルド時間が大幅に短縮される可能性があります。10ステップあり、各1分のダウンロードに時間がかかっている場合、最初のステップ以外の9分のビルド時間が短縮される可能性がある…ということです。

ステップが多い場合や、複雑な場合は、以下のことも検討してみてください。

  • イメージのダウンロードを行うステップそのものを複数に分ける
  • 必要に応じてイメージのダウンロードを行うステップ同士に waitFor を指定する
  • ダウンロードが並行して行われるように、スクリプトを工夫する

この手法は、name に指定するイメージだけではなく、DockerfileFROM に指定するイメージも対象になります。ビルドステップで使用するすべてのイメージを最初にプルしておくことで、後続のステップでのイメージダウンロード時間を節約できます。

補足:
gcr.io/cloud-builders/dockerCloud Build 提供のサポートされているビルダー イメージの1つで、このイメージのダウンロードには時間がかかりません。

まとめ

この記事では、Cloud Build でビルド時間を短縮するテクニックを紹介しました。ビルドプロセスの最初に、後続のステップで使用する Docker イメージを先にダウンロードしておくことで、各ステップでのイメージダウンロード時間を節約できます。特に以下のポイントが重要です:

  • waitFor: ['-'] を使って、イメージのダウンロードステップを他のステップと並行して実行する
  • ビルドステップで使用するすべてのイメージを最初にプルしておく
  • イメージの数が多いほど、時間短縮の効果が大きくなる
  • 複雑なビルドの場合は、イメージのダウンロードステップを複数に分けるなどの工夫も効果的
  • Dockerfile で利用するイメージにも効果がある

この方法を使うことで、ビルドの内容自体は変えずに、ビルド時間だけを短縮することができます。多数のステップを持つビルドプロセスでは、特に大きな効果を期待できるでしょう。

記事執筆後、検索したところGoogle Cloud Build の高速化 tipsという記事があり、「Docker イメージのダウンロード処理を並列化する」という内容が書かれていました。本記事の説明の方が若干詳しいですが、& で並列実行する手法を紹介していました。

参考


Cloud Build Tips Part 2

Summary

You can reduce build time by simply running pull for all necessary images at the very beginning of your build process. The more images that appear in your build steps, the greater the time-saving effect.
Images specified in Dockerfile can also benefit from this technique.

Introduction

Hello everyone, are you using Cloud Build? Cloud Build is Google Cloud's CI/CD service. While it has many uses, it's a powerful tool for building source code, creating container images, and deploying them.

This is the second article in our series of Cloud Build tips. Last time, we showed how you can save a minute of build time just by using gcr.io/cloud-builders/gcloud instead of google-cloud-cli or cloud-sdk.

This time again, we'll introduce a method to reduce build time without changing the actual content of the build.

Details

Within a build process, you often use different Docker images for each step. Also, build steps usually have an "order" because the execution of one step often depends on the result of a previous one.

If you look at the Cloud Build details screen, which has a URL format like https://console.cloud.google.com/cloud-build/builds;region=global/604d39a5-8ec1-4017-8b5e-xxxxxxxxxxxxx, you can see what's being executed in each build step.

Within a step, you might see output like this:

Pulling image: wordpress:php8.2-apache
php8.2-apache: Pulling from library/wordpress
61320b01ae5e: Already exists
b4ce612dc732: Already exists
093338982d92: Pulling fs layer
0c2a0ba9eb0c: Pulling fs layer
442abaed7751: Pulling fs layer
aa4e51934eef: Pulling fs layer
924949db942b: Pulling fs layer
153d2fb08c64: Pulling fs layer
26f17ce45149: Pulling fs layer
64a857ca8a6a: Pulling fs layer

This indicates that the Docker image is being downloaded within that step.

Is this "image download" happening in one of the later steps? This is where there's an inefficiency. While the "content" of the steps must be executed in order because they depend on the results of previous steps, the image downloads can be done "earlier."

Cloud Build has a feature that allows it to "run in parallel" any steps that do not have a defined dependency order. This is explained in detail in Configuring the order of build steps.

Each build step can have an id. You can also use the waitFor property to specify that a step should wait until a step with a specific id is completed. If waitFor is not specified, a build step will wait for all prior build steps in the build request to complete successfully before running. For example, in the following build configuration file, steps A, B, and C will run sequentially.

steps:
- name: foo
  id: A
- name: bar
  id: B
- name: baz
  id: C

Now, if you pre-download the Docker images used in steps B and C in parallel with step A, the execution time of steps B and C should be reduced by the time it would have taken to download their respective images.

steps:
- name: foo
  id: A
  waitFor: ['-'] # Runs in parallel with 'Download images', can be omitted
- name: bar
  id: B
- name: baz
  id: C
- name: 'gcr.io/cloud-builders/docker'
  id: 'Download images'
  entrypoint: 'bash'
  args:
    - '-c'
    - |
      # Pulling 'foo' here is optional, as step A will pull it anyway.
      docker pull foo
      docker pull bar
      docker pull baz
  waitFor: ['-'] # Runs in parallel with A

If you want to place the 'Download images' step earlier in the file, you'll need to specify waitFor for steps B, C, etc.

If you see the following in your build step log, it indicates that the image has already been downloaded. You've successfully saved time.

Already have image (with digest): wordpress:php8.2-apache

By adding a 'Download images' step at the beginning, you can potentially reduce your build time significantly, depending on the number of images used. For example, if you have 10 steps and each takes 1 minute to download its image, you could potentially save 9 minutes of build time (for all steps after the first).

For builds with many or complex steps, consider the following:

  • Splitting the image download step into multiple steps.
  • Using waitFor between the image download steps themselves as needed.
  • Crafting your script to ensure downloads happen in parallel.

This technique applies not only to the images specified in the name field, but also to those in a Dockerfile's FROM instruction. By pulling all the images used in the build steps at the beginning, you can save image download time in subsequent steps.

Note:
gcr.io/cloud-builders/docker is one of the Google Cloud Build official builder images, and downloading this image itself does not take any time.

Summary

In this article, we introduced a technique to reduce build time in Cloud Build. By pre-downloading the Docker images used in subsequent steps at the beginning of the build process, you can save the image download time for each step. The following points are particularly important:

  • Use waitFor: ['-'] to run the image download step in parallel with other steps.
  • Pull all the images used in the build steps at the beginning.
  • The more images you have, the greater the time-saving effect.
  • For complex builds, strategies like splitting the image download step into multiple parts can also be effective.
  • Images specified in Dockerfile can also benefit from this technique.

By using this method, you can reduce build time without changing the content of the build itself. You can expect significant improvements, especially in build processes with many steps.

References