リモート キャッシュ

このページでは、リモート キャッシュ、キャッシュをホストするサーバーの設定、および リモート キャッシュを使用したビルドの実行について説明します。

リモート キャッシュは、デベロッパーのチームや継続的インテグレーション (CI)システムがビルド出力を共有するために使用されます。ビルドが再現可能な場合、1 台のマシンからの出力を別のマシンで安全に再利用できるため、ビルドを大幅に高速化できます。

概要

Bazel はビルドをアクションと呼ばれる個別のステップに分割します。各アクション には、入力、出力名、コマンドライン、環境変数があります。必要な 入力と予想される出力は、アクションごとに明示的に宣言されます。

サーバーを、これらの アクション出力であるビルド出力のリモート キャッシュとして設定できます。これらの出力は、出力ファイル名のリストとその コンテンツのハッシュで構成されます。リモート キャッシュを使用すると、新しい出力をローカルで生成するのではなく、別のユーザーのビルドの出力を再利用できます。

リモート キャッシュを使用するには:

  • サーバーをキャッシュのバックエンドとして設定する
  • リモート キャッシュを使用するように Bazel ビルドを構成する
  • Bazel バージョン 0.10.0 以降を使用する

リモート キャッシュには次の 2 種類のデータが保存されます。

  • アクション ハッシュからアクション結果メタデータへのマッピングであるアクション キャッシュ。
  • 出力ファイルのコンテンツ アドレス指定可能ストア(CAS)。

リモート キャッシュには、すべてのアクションの stdout と stderr も保存されます。したがって、Bazel の stdout/stderr を検査しても、キャッシュ ヒットを推定するのに適したシグナルにはなりません

ビルドでリモート キャッシュを使用する方法

サーバーをリモート キャッシュとして設定したら、キャッシュを次の方法で使用します。

  • リモート キャッシュの読み取りと書き込みを行う
  • 特定ターゲットを除くリモート キャッシュの読み取りと書き込みを行う
  • リモート キャッシュからのみ読み取る
  • リモート キャッシュをまったく使用しない

リモート キャッシュの読み取りと書き込みが可能な Bazel ビルドを実行すると、 ビルドは次の手順で実行されます。

  1. Bazel は、ビルドする必要があるターゲットのグラフを作成し、必要なアクションのリストを作成します。これらのアクションにはそれぞれ、宣言された入力 と出力ファイル名があります。
  2. Bazel はローカルマシンで既存のビルド出力を確認し、見つかったものを再利用します 。
  3. Bazel はキャッシュで既存のビルド出力を確認します。出力が見つかった場合、 Bazel は出力を取得します。これがキャッシュ ヒットです。
  4. 出力が見つからなかった必要なアクションについては、Bazel は アクションをローカルで実行し、必要なビルド出力を生成します。
  5. 新しいビルド出力がリモート キャッシュにアップロードされます。

サーバーをキャッシュのバックエンドとして設定する

サーバーをキャッシュのバックエンドとして機能するように設定する必要があります。HTTP/1.1 サーバーは Bazel のデータを不透明なバイトとして扱うことができるため、多くの既存のサーバー をリモート キャッシュ バックエンドとして使用できます。リモート キャッシュをサポートしているのは、Bazel's HTTP Caching Protocolです。

キャッシュされた出力を保存するバックエンド サーバーの選択、設定、保守はお客様の責任で行ってください。サーバーを選択する際は、次の点を考慮してください。

  • ネットワーク速度。たとえば、チームが同じオフィスにいる場合は、独自のローカル サーバーを実行することをおすすめします。
  • セキュリティ。リモート キャッシュにはバイナリが含まれるため、安全である必要があります。
  • 管理のしやすさ。たとえば、Google Cloud Storage はフルマネージド サービスです。

リモート キャッシュに使用できるバックエンドは多数あります。次のようなオプション があります。

nginx

nginx はオープンソースのウェブサーバーです。[WebDAV モジュール]を使用すると、 Bazel のリモート キャッシュとして使用できます。Debian と Ubuntu では、 nginx-extras パッケージをインストールできます。macOS では、Homebrew から nginx を入手できます。

brew tap denji/nginx
brew install nginx-full --with-webdav

以下に、nginx の構成例を示します。 change /path/to/cache/dir は、nginx が書き込みと読み取りの権限を持つ有効なディレクトリに 変更する必要があります。出力ファイルが大きい場合は、client_max_body_size オプションをより 大きな値に変更する必要があります。サーバーには、認証などの他の 構成が必要です。

nginx.confserver セクションの構成例:

location /cache/ {
  # The path to the directory where nginx should store the cache contents.
  root /path/to/cache/dir;
  # Allow PUT
  dav_methods PUT;
  # Allow nginx to create the /ac and /cas subdirectories.
  create_full_put_path on;
  # The maximum size of a single file.
  client_max_body_size 1G;
  allow all;
}

bazel-remote

bazel-remote は、インフラストラクチャで使用できるオープンソースのリモート ビルド キャッシュです。 2018 年初頭から、 複数の企業で本番環境で使用されています。Bazel プロジェクトでは、bazel-remote のテクニカル サポートは提供されていません。

このキャッシュはコンテンツをディスクに保存し、ガベージ コレクション を提供してストレージの上限を適用し、未使用のアーティファクトをクリーンアップします。キャッシュは [Docker イメージ] として提供され、そのコードは GitHubで入手できます。 REST と gRPC のリモート キャッシュ API の両方がサポートされています。

使用方法については、GitHub ページをご覧ください。

Google Cloud Storage

[Google Cloud Storage] は、Bazel のリモート キャッシュ プロトコルと互換性のある HTTP API を提供するフルマネージド オブジェクト ストアです。課金が有効になっている Google Cloud アカウントが必要です。

Cloud Storage をキャッシュとして使用するには:

  1. Storage バケットを作成します。 リモート キャッシュではネットワーク帯域幅 が重要になるため、最も近いバケットのロケーションを選択してください。

  2. Bazel が Cloud Storage に対して認証を行うためのサービス アカウントを作成します。 サービス アカウントを作成するをご覧ください。

  3. シークレット JSON キーを生成し、認証のために Bazel に渡します。キーを安全に保存します。キーを持つユーザーは誰でも、GCS バケットとの間で任意のデータを読み書きできます。

  4. Bazel コマンドに次のフラグを追加して、Cloud Storage に接続します。

    • フラグを使用して、次の URL を Bazel に渡します: --remote_cache=https://storage.googleapis.com/bucket-name。ここで、bucket-name は Storage バケットの名前です。
    • フラグを使用して認証キーを渡します。--google_credentials=/path/to/your/secret-key.json または --google_default_credentials を使用してアプリケーション認証を使用します。
  5. 古いファイルを自動的に削除するように Cloud Storage を構成できます。これを行うには、 オブジェクトのライフサイクルを管理するをご覧ください。

その他のサーバー

PUT と GET をサポートする HTTP/1.1 サーバーをキャッシュの バックエンドとして設定できます。ユーザーは、HazelcastApache httpd、および AWS S3 などのキャッシュ バックエンドで成功しています。

認証

バージョン 0.11.0 以降、Bazel に HTTP 基本認証のサポートが追加されました。 リモート キャッシュ URL を介してユーザー名とパスワードを Bazel に渡すことができます。構文は https://username:password@hostname.com:port/path です。HTTP 基本認証では、ユーザー名とパスワードがネットワーク上で平文で送信されるため、常に HTTPS で使用することが重要です。

HTTP キャッシュ プロトコル

Bazel は HTTP/1.1 を介したリモート キャッシュをサポートしています。プロトコルは概念的にはシンプルです。 バイナリデータ(BLOB)は PUT リクエストでアップロードされ、GET リクエストでダウンロードされます。 アクション結果メタデータはパス /ac/ に保存され、出力ファイルは パス /cas/ に保存されます。

たとえば、http://localhost:8080/cache で実行されているリモート キャッシュについて考えてみましょう。 SHA256 ハッシュ 01ba4719... を持つアクションのアクション結果メタデータをダウンロードする Bazel リクエストは次のようになります。

GET /cache/ac/01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b HTTP/1.1
Host: localhost:8080
Accept: */*
Connection: Keep-Alive

SHA256 ハッシュ 15e2b0d3... を持つ出力ファイルを CAS にアップロードする Bazel リクエストは次のようになります。

PUT /cache/cas/15e2b0d3c33891ebb0f1ef609ec419420c20e320ce94c65fbc8c3312448eb225 HTTP/1.1
Host: localhost:8080
Accept: */*
Content-Length: 9
Connection: Keep-Alive

0x310x320x330x340x350x360x370x380x39

リモート キャッシュを使用して Bazel を実行する

サーバーをリモート キャッシュとして設定したら、リモート キャッシュを使用するには、Bazel コマンドにフラグを追加する必要があります。構成とその フラグのリストについては、以下をご覧ください。

選択した サーバーに固有の認証を構成する必要がある場合もあります。

Bazel を実行するたびにフラグを指定する必要がないように、これらのフラグを .bazelrc ファイルに追加することをおすすめします。プロジェクトと チームのダイナミクスに応じて、次の .bazelrc ファイルにフラグを追加できます。

  • ローカルマシン上
  • プロジェクトのワークスペースで、チームと共有
  • CI システム上

リモート キャッシュの読み取りと書き込みを行う

リモート キャッシュに書き込むことができるユーザーに注意してください。CI システムのみがリモート キャッシュに書き込めるようにすることをおすすめします。

リモート キャッシュの読み取りと書き込みを行うには、次のフラグを使用します。

build --remote_cache=http://your.host:port

HTTP の他に、HTTPSgrpcgrpcs のプロトコルもサポートされています。

リモート キャッシュからのみ読み取るには、上記のフラグに加えて次のフラグを使用します。

build --remote_upload_local_results=false

特定ターゲットをリモート キャッシュの使用から除外する

特定ターゲットをリモート キャッシュの使用から除外するには、ターゲットに no-remote-cache のタグを付けます。次に例を示します。

java_library(
    name = "target",
    tags = ["no-remote-cache"],
)

リモート キャッシュからコンテンツを削除する

リモート キャッシュからコンテンツを削除することは、サーバーの管理の一部です。 リモート キャッシュからコンテンツを削除する方法は、キャッシュとして設定したサーバーによって異なります。出力を削除する場合は、キャッシュ全体を削除するか、 古い出力を削除します。

キャッシュされた出力は、名前とハッシュのセットとして保存されます。コンテンツを削除する場合、特定のビルドに属する出力を区別する方法はありません。

キャッシュからコンテンツを削除することをおすすめします。

  • キャッシュが破損した後にクリーンなキャッシュを作成する
  • 古い出力を削除して、使用するストレージ容量を減らす

Unix ソケット

リモート HTTP キャッシュは、Unix ドメイン ソケット経由の接続をサポートしています。動作は curl の --unix-socket フラグに似ています。Unix ドメイン ソケットを構成するには、次のようにします。

   build --remote_cache=http://your.host:port
   build --remote_cache_proxy=unix:/path/to/socket

この機能は Windows ではサポートされていません。

ディスク キャッシュ

Bazel は、ファイル システム上のディレクトリをリモート キャッシュとして使用できます。これは、ブランチの切り替え時や、同じプロジェクトの複数のワークスペース(複数のチェックアウトなど)で作業する場合に、ビルド アーティファクトを共有するのに 便利です。Bazel はディレクトリのガベージ コレクションを行わないため、このディレクトリの定期的なクリーンアップを自動化することをおすすめします。ディスク キャッシュを有効にするには、次のようにします。

build --disk_cache=path/to/build/cache

~ エイリアス を使用して、ユーザー固有のパスを --disk_cache フラグに渡すことができます(Bazel は現在のユーザーのホーム ディレクトリに置き換えます)。これは、プロジェクトのチェックインされた .bazelrc ファイルを介して、プロジェクトのすべてのデベロッパーに対してディスク キャッシュを有効にする場合に便利です。

既知の問題

ビルド中の入力ファイルの変更

ビルド中に入力ファイルが変更されると、Bazel は無効な 結果をリモート キャッシュにアップロードする可能性があります。 --experimental_guard_against_concurrent_changes フラグを使用して、変更検出を有効にできます。既知の問題はなく、今後のリリースでデフォルトで有効になります。最新情報については、[issue #3360] をご覧ください。通常、 ビルド中にソースファイルを変更することは避けてください。

アクションへの環境変数のリーク

アクション定義には環境変数が含まれています。これは、 マシン間でリモート キャッシュ ヒットを共有する場合に問題となる可能性があります。たとえば、 異なる $PATH 変数が異なる環境では、キャッシュ ヒットは共有されません。アクション定義には、環境変数 --action_envを介して明示的にホワイトリストに登録された環境変数のみが含まれます。Bazel の Debian/Ubuntu パッケージは、/etc/bazel.bazelrc などの環境変数のホワイトリストを使用して $PATHをインストールしていました。キャッシュ ヒットが予想よりも少ない場合は、環境に古い /etc/bazel.bazelrc ファイルがないことを確認してください。

Bazel はワークスペース外のツールを追跡しない

Bazel は現在、ワークスペース外のツールを追跡していません。たとえば、アクションが /usr/bin/ のコンパイラを使用する場合に 問題が発生する可能性があります。コンパイラが異なる 2 人のユーザーが、出力は異なるがアクション ハッシュが同じであるため、誤ってキャッシュ ヒットを共有します。 最新情報については、 issue #4558 をご覧ください。

Docker コンテナ内でビルドを実行すると、インメモリの増分状態が失われる Bazel は、単一の Docker コンテナで実行する場合でも、サーバー/クライアント アーキテクチャを使用します。 サーバー側では、Bazel はビルドを高速化するインメモリ状態を維持します。 CI などの Docker コンテナ内でビルドを実行すると、インメモリ状態が失われ Bazel はリモート キャッシュを使用する前に再構築する必要があります。