このページでは、リモート キャッシュ、キャッシュをホストするサーバーの設定、リモート キャッシュを使用したビルドの実行について説明します。
リモート キャッシュは、デベロッパーのチームや継続的インテグレーション(CI)システムがビルド出力を共有するために使用します。ビルドが再現可能であれば、1 台のマシンの出力を別のマシンで安全に再利用できるため、ビルドを大幅に高速化できます。
概要
Bazel はビルドを個別のステップ(アクション)に分割します。各アクションには、入力、出力名、コマンドライン、環境変数があります。各アクションに必要な入力と出力が明示的に宣言されます。
サーバーをビルド出力(アクション出力)のリモート キャッシュとして設定できます。これらの出力は、出力ファイル名のリストとその内容のハッシュで構成されます。リモート キャッシュを使用すると、新しい出力をローカルで生成するのではなく、別のユーザーのビルドのビルド出力を再利用できます。
リモート キャッシュを使用するには:
- サーバーをキャッシュのバックエンドとして設定する
- リモート キャッシュを使用するように Bazel ビルドを構成する
- Bazel バージョン 0.10.0 以降を使用する
リモート キャッシュには、次の 2 種類のデータが保存されます。
- アクション キャッシュ。アクション ハッシュとアクション結果メタデータのマップです。
- 出力ファイルのコンテンツ アドレス指定可能ストア(CAS)。
リモート キャッシュには、すべてのアクションの stdout と stderr も保存されます。したがって、Bazel の stdout/stderr を検査しても、キャッシュ ヒットを推定するための適切なシグナルにはなりません。
ビルドでリモート キャッシュを使用する方法
サーバーがリモート キャッシュとして設定されると、キャッシュは次の方法で使用されます。
- リモート キャッシュの読み取りと書き込み
- 特定ターゲットを除くリモート キャッシュの読み取り/書き込み
- リモート キャッシュからのみ読み取る
- リモート キャッシュをまったく使用しない
リモート キャッシュの読み取りと書き込みが可能な Bazel ビルドを実行すると、ビルドは次の手順に沿って処理されます。
- Bazel は、ビルドが必要なターゲットのグラフを作成し、必要なアクションのリストを作成します。これらの各アクションには、入力ファイル名と出力ファイル名が宣言されています。
- Bazel はローカルマシンで既存のビルド出力を確認し、見つかったものを再利用します。
- Bazel は、既存のビルド出力がないかキャッシュを確認します。出力が見つかった場合、Bazel は出力を取得します。これはキャッシュ ヒットです。
- 出力が見つからなかった必須アクションについては、Bazel がローカルでアクションを実行し、必要なビルド出力を生成します。
- 新しいビルド出力がリモート キャッシュにアップロードされます。
サーバーをキャッシュのバックエンドとして設定する
キャッシュのバックエンドとして機能するサーバーを設定する必要があります。HTTP/1.1 サーバーは Bazel のデータを不透明なバイトとして扱うことができるため、既存の多くのサーバーをリモート キャッシュ バックエンドとして使用できます。Bazel の HTTP キャッシュ プロトコルは、リモート キャッシュをサポートするものです。
キャッシュに保存された出力を保存するバックエンド サーバーの選択、設定、メンテナンスは、ユーザーの責任で行ってください。サーバーを選択する際は、次の点を考慮してください。
- ネットワーク速度。たとえば、チームが同じオフィスにいる場合は、独自のローカル サーバーを実行することをおすすめします。
- セキュリティはその中の 1 つでしょう。リモート キャッシュにはバイナリが含まれるため、安全である必要があります。
- 管理のしやすさ。たとえば、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 の構成例を示します。/path/to/cache/dir
は、nginx が書き込みと読み取りの権限を持つ有効なディレクトリに変更する必要があります。出力ファイルが大きい場合は、client_max_body_size
オプションの値を大きくする必要がある場合があります。サーバーには、認証などの他の構成が必要です。
nginx.conf
の server
セクションの構成例:
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 をキャッシュとして使用するには:
Storage バケットを作成します。ネットワーク帯域幅はリモート キャッシュにとって重要であるため、最も近いバケットのロケーションを選択してください。
Cloud Storage に対して認証を行うための Bazel のサービス アカウントを作成します。サービス アカウントの作成をご覧ください。
シークレット JSON キーを生成し、認証のために Bazel に渡します。鍵を持つユーザーは誰でも GCS バケットとの間で任意のデータを読み書きできるため、鍵は安全に保管してください。
次のフラグを Bazel コマンドに追加して、Cloud Storage に接続します。
- フラグ
--remote_cache=https://storage.googleapis.com/bucket-name
を使用して、次の URL を Bazel に渡します。ここで、bucket-name
はストレージ バケットの名前です。 - アプリケーション認証を使用するには、フラグ
--google_credentials=/path/to/your/secret-key.json
または--google_default_credentials
を使用して認証鍵を渡します。
- フラグ
古いファイルを自動的に削除するように Cloud Storage を構成できます。これを行うには、オブジェクトのライフサイクルを管理するをご覧ください。
その他のサーバー
PUT と GET をサポートする HTTP/1.1 サーバーをキャッシュのバックエンドとして設定できます。Hazelcast、Apache httpd、AWS S3 などのキャッシュ バックエンドで成功したという報告が寄せられています。
認証
バージョン 0.11.0 以降、Bazel に HTTP 基本認証のサポートが追加されました。リモート キャッシュ URL を介してユーザー名とパスワードを Bazel に渡すことができます。構文は https://username:password@hostname.com:port/path
です。HTTP Basic 認証では、ユーザー名とパスワードがネットワーク経由でプレーンテキストで送信されるため、常に 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 コマンドにフラグを追加する必要があります。構成とそのフラグのリストについては、以下をご覧ください。
選択したサーバーに固有の認証を構成する必要がある場合もあります。
これらのフラグを .bazelrc
ファイルに追加すると、Bazel を実行するたびに指定する必要がなくなります。プロジェクトとチームのダイナミクスに応じて、次の .bazelrc
ファイルにフラグを追加できます。
- ローカルマシンで
- チームと共有されているプロジェクトのワークスペース
- CI システム上
リモート キャッシュの読み取りと書き込み
リモート キャッシュへの書き込み権限を持つユーザーに注意してください。CI システムのみがリモート キャッシュに書き込みを行えるようにすることをおすすめします。
リモート キャッシュの読み取りと書き込みを行うには、次のフラグを使用します。
build --remote_cache=http://your.host:port
HTTP
の他に、HTTPS
、grpc
、grpcs
のプロトコルもサポートされています。
リモート キャッシュからのみ読み取るには、上記のフラグに加えて次のフラグを使用します。
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_proxy=unix:/path/to/socket
この機能は Windows ではサポートされていません。
ディスク キャッシュ
Bazel は、ファイル システム上のディレクトリをリモート キャッシュとして使用できます。これは、ブランチを切り替えるときや、同じプロジェクトの複数のワークスペース(複数のチェックアウトなど)で作業するときに、ビルド アーティファクトを共有する場合に便利です。ディスク キャッシュを有効にするには、次の手順を行います。
build --disk_cache=path/to/build/cache
~
エイリアスを使用して、ユーザー固有のパスを --disk_cache
フラグに渡すことができます(Bazel は現在のユーザーのホーム ディレクトリに置き換えます)。これは、プロジェクトのチェックインされた .bazelrc
ファイルを介して、プロジェクトのすべてのデベロッパーに対してディスク キャッシュを有効にする場合に便利です。
ガベージ コレクション
Bazel 7.4 以降では、--experimental_disk_cache_gc_max_size
と --experimental_disk_cache_gc_max_age
を使用して、ディスク キャッシュの最大サイズまたは個々のキャッシュ エントリの有効期間を設定できます。Bazel は、ビルド間のアイドル時にディスク キャッシュを自動的にガベージ コレクションします。アイドル タイマーは --experimental_disk_cache_gc_idle_delay
で設定できます(デフォルトは 5 分)。
自動ガベージ コレクションの代替として、オンデマンドでガベージ コレクションを実行するツールも提供しています。
既知の問題
ビルド中の入力ファイルの変更
ビルド中に入力ファイルが変更されると、Bazel が無効な結果をリモート キャッシュにアップロードする可能性があります。変更検出は、--experimental_guard_against_concurrent_changes
フラグで有効にできます。既知の問題はなく、今後のリリースではデフォルトで有効になる予定です。最新情報については、[問題 #3360] をご覧ください。一般的に、ビルド中にソースファイルを変更することは避けるべきです。
アクションに漏洩する環境変数
アクション定義に環境変数が含まれている。これは、マシン間でリモート キャッシュ ヒットを共有する際に問題になる可能性があります。たとえば、$PATH
変数が異なる環境では、キャッシュ ヒットは共有されません。--action_env
を介して明示的にホワイトリストに登録された環境変数のみが、アクション定義に含まれます。$PATH
を含む環境変数のホワイトリストを使用して /etc/bazel.bazelrc
をインストールするために使用される Bazel の Debian/Ubuntu パッケージ。キャッシュヒット数が予想よりも少ない場合は、環境に古い /etc/bazel.bazelrc
ファイルがないことを確認してください。
Bazel はワークスペースの外部ツールを追跡しない
Bazel では現在、ワークスペースの外部ツールを追跡していません。たとえば、アクションが /usr/bin/
のコンパイラを使用している場合、これは問題になる可能性があります。この場合、コンパイラが異なる 2 人のユーザーが、出力は異なるがアクション ハッシュが同じであるため、誤ってキャッシュ ヒットを共有することになります。最新情報については、問題 #4558 をご覧ください。
Docker コンテナ内でビルドを実行すると、増分インメモリ状態が失われる Bazel は、単一の Docker コンテナで実行されている場合でも、サーバー/クライアント アーキテクチャを使用します。サーバー側では、Bazel はメモリ内の状態を維持し、ビルドを高速化します。CI などの Docker コンテナ内でビルドを実行すると、インメモリ状態が失われ、Bazel はリモート キャッシュを使用する前に再構築する必要があります。