リモート キャッシュ

問題を報告 ソースを表示

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

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

概要

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 の HTTP キャッシュ プロトコルは、リモート キャッシュをサポートしています。

キャッシュに保存された出力を保存するバックエンド サーバーの選択、設定、メンテナンスは、お客様の責任で行う必要があります。サーバーを選ぶ際には、以下の点を考慮してください。

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

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

nginx

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

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

以下は、nginx の構成例です。/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 に接続します。

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

その他のサーバー

PUT と GET をサポートする任意の HTTP/1.1 サーバーをキャッシュのバックエンドとして設定できます。ユーザーから、HazelcastApache httpdAWS S3 などのキャッシュ バックエンドが成功したという報告が寄せられています。

認証

バージョン 0.11.0 から、HTTP 基本認証のサポートが Bazel に追加されました。 リモート キャッシュの 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 コマンドにフラグを追加する必要があります。以下の構成とそのフラグのリストをご覧ください。

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

これらのフラグを .bazelrc ファイルに追加すると、Bazel を実行するたびに指定する必要がなくなります。プロジェクトとチームの原動力に応じて、次のようなフラグを .bazelrc ファイルに追加できます。

  • ローカルマシン
  • プロジェクトのワークスペース(チームと共有する)
  • CI システムの場合

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

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

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

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

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

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

build --remote_upload_local_results=false

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

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

java_library(
    name = "target",
    tags = ["no-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 フラグを使用します。既知の問題はなく、今後のリリースではデフォルトで有効になる予定です。更新については、[問題 #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 でビルドを再構築する必要があります。