サンドボックス化

問題を報告 ソースを表示 Nightly · 8.3 · 8.2 · 8.1 · 8.0 · 7.6

この記事では、Bazel のサンドボックスとサンドボックス環境のデバッグについて説明します。

サンドボックス化は、プロセスを相互に、またはシステム内のリソースから分離する権限制限戦略です。Bazel の場合、これはファイル システムへのアクセスを制限することを意味します。

Bazel のファイル システム サンドボックスは、既知の入力のみを含む作業ディレクトリでプロセスを実行します。これにより、コンパイラなどのツールは、絶対パスを知らない限り、アクセスすべきでないソースファイルを参照できません。

サンドボックス化によってホスト環境が隠されることはありません。プロセスは、ファイル システム上のすべてのファイルに自由にアクセスできます。ただし、ユーザー名前空間をサポートするプラットフォームでは、プロセスは作業ディレクトリ外のファイルを変更できません。これにより、ビルドの再現性に影響する可能性のある隠れた依存関係がビルドグラフに存在しないことが保証されます。

具体的には、Bazel は各アクションの execroot/ ディレクトリを作成します。これは、実行時のアクションの作業ディレクトリとして機能します。execroot/ にはアクションのすべての入力ファイルが含まれており、生成された出力のコンテナとして機能します。Bazel は、オペレーティング システムが提供する手法(Linux のコンテナ、macOS の sandbox-exec)を使用して、execroot/ 内でアクションを制約します。

サンドボックス化する理由

  • アクション サンドボックスがない場合、Bazel はツールが未宣言の入力ファイル(アクションの依存関係に明示的にリストされていないファイル)を使用しているかどうかを認識できません。未宣言の入力ファイルのいずれかが変更されても、Bazel はビルドが最新であると認識し、アクションを再ビルドしません。これにより、増分ビルドが正しく行われない可能性があります。

  • キャッシュ エントリの再利用が正しくないと、リモート キャッシュで問題が発生します。共有キャッシュ内の無効なキャッシュ エントリは、プロジェクトのすべてのデベロッパーに影響します。リモート キャッシュ全体を消去することは、実現可能な解決策ではありません。

  • サンドボックスはリモート実行の動作を模倣します。サンドボックスでビルドが正常に動作する場合、リモート実行でも正常に動作する可能性が高くなります。リモート実行ですべての必要なファイル(ローカルツールを含む)をアップロードすることで、新しいコンパイラを試したり、既存のツールを変更したりするたびにクラスタ内のすべてのマシンにツールをインストールする必要がなくなり、コンパイル クラスタのメンテナンス コストを大幅に削減できます。

使用するサンドボックス戦略

戦略フラグを使用すると、使用するサンドボックスの種類を選択できます。sandboxed 戦略を使用すると、Bazel は以下のサンドボックス実装のいずれかを選択します。OS 固有のサンドボックスが、密閉性の低い汎用サンドボックスよりも優先されます。--worker_sandboxing フラグを渡すと、永続ワーカーは汎用サンドボックスで実行されます。

localstandalone)戦略では、サンドボックス化は行われません。これは、ワークスペースの execroot に設定された作業ディレクトリでアクションのコマンドラインを実行するだけです。

processwrapper-sandbox は「高度な」機能を必要としないサンドボックス戦略です。POSIX システムであれば、すぐに利用できます。元のソースファイルを指すシンボリック リンクで構成されるサンドボックス ディレクトリを構築し、execroot ではなくこのディレクトリに作業ディレクトリを設定してアクションのコマンドラインを実行し、既知の出力アーティファクトをサンドボックスから execroot に移動してサンドボックスを削除します。これにより、宣言されていない入力ファイルがアクションで誤って使用されたり、不明な出力ファイルで execroot が散らかったりするのを防ぐことができます。

linux-sandbox はさらに一歩進んで、processwrapper-sandbox の上に構築されます。Docker が内部で行う処理と同様に、Linux Namespace(ユーザー、マウント、PID、ネットワーク、IPC Namespace)を使用して、アクションをホストから分離します。つまり、サンドボックス ディレクトリを除くファイル システム全体を読み取り専用にするため、アクションがホスト ファイル システムのものを誤って変更することはありません。これにより、バグのあるテストが誤って $HOME ディレクトリを rm -rf するような状況を防ぐことができます。必要に応じて、アクションがネットワークにアクセスできないようにすることもできます。linux-sandbox は PID 名前空間を使用して、アクションが他のプロセスを参照しないようにし、最後にすべてプロセス(アクションによって生成されたデーモンも含む)を確実に終了させます。

darwin-sandbox は同様ですが、macOS 用です。Apple の sandbox-exec ツールを使用して、Linux サンドボックスとほぼ同じことを実現しています。

オペレーティング システムが提供するメカニズムの制限により、linux-sandboxdarwin-sandbox の両方が「ネストされた」シナリオでは機能しません。Docker もコンテナの処理に Linux 名前空間を使用しているため、docker run --privileged を使用しない限り、Docker コンテナ内で linux-sandbox を簡単に実行することはできません。macOS では、すでにサンドボックス化されているプロセス内で sandbox-exec を実行することはできません。そのため、このような場合、Bazel は自動的に processwrapper-sandbox の使用にフォールバックします。

ビルドエラーを発生させたい場合(厳密性の低い実行戦略で誤ってビルドしないようにするなど)は、Bazel が使用しようとする実行戦略のリストを明示的に変更します(bazel build --spawn_strategy=worker,linux-sandbox など)。

動的実行では通常、ローカル実行にサンドボックスが必要です。オプトアウトするには、--experimental_local_lockfree_output フラグを渡します。動的実行では、永続ワーカーがサイレントにサンドボックス化されます。

サンドボックス化のデメリット

  • サンドボックス化には、追加の設定と破棄の費用がかかります。このコストの大きさは、ビルドの形状やホスト OS のパフォーマンスなど、さまざまな要因によって異なります。Linux の場合、サンドボックス化されたビルドが数パーセント以上遅くなることはほとんどありません。--reuse_sandbox_directories を設定すると、設定と破棄のコストを軽減できます。

  • サンドボックス化により、ツールが持つ可能性のあるキャッシュが実質的に無効になります。これは、サンドボックスの保証が弱くなるという代償を伴いますが、永続ワーカーを使用することで軽減できます。

  • 多重化ワーカーをサンドボックス化するには、明示的なワーカー サポートが必要です。多重サンドボックスをサポートしていないワーカーは、動的実行で単一プレックス ワーカーとして実行されます。これにより、追加のメモリが必要になることがあります。

デバッグ

以下の戦略に沿って、サンドボックス化に関する問題をデバッグします。

無効化された Namespace

Google Kubernetes Engine クラスタノードや Debian などの一部のプラットフォームでは、セキュリティ上の懸念からユーザー Namespace がデフォルトで無効になっています。/proc/sys/kernel/unprivileged_userns_clone ファイルが存在し、0 が含まれている場合は、次のコマンドを実行してユーザー Namespace を有効にできます。

   sudo sysctl kernel.unprivileged_userns_clone=1

ルールの実行エラー

システム設定により、サンドボックスでルールを実行できない場合があります。namespace-sandbox.c:633: execvp(argv[0], argv): No such file or directory のようなメッセージが表示された場合は、genrules には --strategy=Genrule=local、その他のルールには --spawn_strategy=local を使用してサンドボックスを無効にしてみてください。

ビルドの失敗の詳細なデバッグ

ビルドが失敗した場合は、--verbose_failures--sandbox_debug を使用して、ビルドが失敗したときに実行された正確なコマンド(サンドボックスを設定する部分を含む)を Bazel に表示します。

エラー メッセージの例

ERROR: path/to/your/project/BUILD:1:1: compilation of rule
'//path/to/your/project:all' failed:

Sandboxed execution failed, which may be legitimate (such as a compiler error),
or due to missing dependencies. To enter the sandbox environment for easier
debugging, run the following command in parentheses. On command failure, a bash
shell running inside the sandbox will then automatically be spawned

namespace-sandbox failed: error executing command
  (cd /some/path && \
  exec env - \
    LANG=en_US \
    PATH=/some/path/bin:/bin:/usr/bin \
    PYTHONPATH=/usr/local/some/path \
  /some/path/namespace-sandbox @/sandbox/root/path/this-sandbox-name.params --
  /some/path/to/your/some-compiler --some-params some-target)

生成されたサンドボックス ディレクトリを検査して、Bazel が作成したファイルを確認し、コマンドを再度実行して動作を確認できるようになりました。

--sandbox_debug を使用しても、Bazel はサンドボックス ディレクトリを削除しません。アクティブにデバッグを行っていない場合は、--sandbox_debug を無効にしてください。有効にしていると、ディスクが徐々にいっぱいになります。