マージ + 差分: DAG をより効率的かつエレガントに構築する

BuildKitメンテナによって書かれたゲスト投稿 エリック・シプスマ.

全体像

  • MergeOp と DiffOp は、 でリリースされた 2 つの新機能です。 ビルドキット v0.10.これらの操作により、ファイルシステムを構成し (MergeOp)、それらを分割 (DiffOp) することでコンテナー イメージをアセンブルしながら、ディスク上のローカルとリモート レジストリの両方で重複データの作成を最小限に抑えることができます。
  • データの重複を最小限に抑えると、キャッシュの再利用性が向上し、多くのワークフローのパフォーマンスが大幅に向上します。 アーリーアダプターの1つであるNetflixは、以前は1時間以上かかっていた非常に複雑なビルドが、MergeOpを使用するように内部ツールを更新してからわずか3分で完了すると報告しました その代わりに コピーの。
  • さらに、これらの新しい操作により、ソフトウェアの依存関係が複雑な DAG で構造化されているパッケージマネージャーに見られるワークフローが、BuildKit のビルド時にはるかにパフォーマンスが高く、簡単に表現できるようになります。
  • MergeOp と DiffOp は、他の BuildKit 機能と同様に、高レベルのツールとインターフェイスを対象とした低レベルのプリミティブです。 それらは非常に新しいものですが、それらのサポートはすでに次の場所に存在します。

この投稿では、MergeOpとDiffOpがソフトウェアの依存関係の知識をエンコードするのにどのように役立つかを紹介し、BuildKitがキャッシュの再利用性を最大化し、いくつかの興味深い新しいユースケースを解き放ちます。

ビルドキットクラッシュコース

新機能に飛び込む前に、BuildKitとLLBの簡単なクラッシュコースを受講しましょう。

ファイル システム DAG ソルバー

ビルドキットは基本的に 有向非巡回グラフ (DAG) DAG 内の各頂点が 1 つ以上のファイル システムのセットに対して実行される操作を表すソルバー。 各頂点操作は、他の頂点のファイル システムを入力として受け入れ、独自の 1 つ以上のファイル システムを出力できます。 これらの入力と出力によって、DAG のエッジが作成されます。

DAG を解析する場合、BuildKit はキャッシュ、並列化、およびその他の最適化を使用して、解析の "ターゲット" 頂点によって出力されるファイル システムを可能な限り効率的に計算します。 必要に応じて、ターゲット頂点の結果をさまざまな方法 (コンテナー イメージをレジストリにプッシュするなど) でエクスポートしたり、クライアントのコンピューター上にローカルに作成されたディレクトリとしてエクスポートしたりできます。

LLB DAG

DAG は LLB の形式で BuildKit に提供され、これは高レベルのビルド仕様を "コンパイル" できるものとして設計された低レベル言語です。 これは、アセンブリ言語のような単一のターゲットにコンパイル可能な異なるプログラミング言語の数を反映しています。 LLBにコンパイルされるツールと言語の例をいくつか示します。 ドッカーファイル, ティッカー, アースファイルそして 短刀 (変換する キュー LLBに)。

重要なのは、これにより、BuildKitはキャッシュやエクスポートなどの一般的な機能や最適化に集中できることです。 より高いレベルのツールは、車輪を再発明する代わりに、これらのメカニズムを活用できます。

例: Dockerfile to LLB DAG

まず、次の点を考慮してください ドッカーファイル:

ゴランから:1.17-高山3.15 ゴップルとして
実行は golang.org/x/tools/gopls@latest をインストールしますゴランから:1.17-高山3.15 ゴインポートとして
実行は golang.org/x/tools/cmd/goimports@latest をインストールします高山から:3.15APKを実行する追加--更新-キャッシュなしのネオビム
COPY --from=gopls /go/bin/gopls /usr/local/bin/gopls
COPY --from=goimports /go/bin/goimports /usr/local/bin/goimports

これを取得して、LLB DAGに簡単にコンパイルできます。

画像5 1

ここでは、いくつかの異なるタイプの頂点が表示され、各タイプはファイルシステムを作成または変更するための異なる種類の操作を表します。

  • ソースオプ 頂点は、コンテナイメージ、Gitリポジトリなどの外部ソースから新しいファイルシステムをインポートします。 それらは入力を持たず、通常はLLBグラフの「根」を形成します。
  • エクセコップ 頂点は、入力ファイルシステム上でコマンドを実行し、ファイルシステムと加えられた変更を出力します。 また、異なる場所にマウントされた複数の入力ファイルシステムを受け入れ、変更された各ファイルシステムを個別に出力することもできます。
  • CopyOp 頂点は、指定されたファイルとディレクトリのセットが、ある入力頂点 (ソース) から別の入力頂点 (ターゲット) の上にコピーされたファイルシステムを出力します。 技術的には、コピーは一般的な FileOp 頂点のサブタイプです。 さまざまな基本的なファイル操作をサポートしていますが、わかりやすくするためにここでは CopyOp と呼びます。

重要なポイント

これまでに学んだことは次のとおりです。

  • BuildKit は、ファイル システム操作の DAG を解決します
  • BuildKit は、これらの解析の結果をコンテナイメージなど、さまざまな形式でエクスポートすることをサポートしています
  • LLBを通じて、BuildKitは現在のさまざまな高レベルのビルド仕様言語の基盤となることができます

問題:線形コピーチェーン

多くのビルドシステムと言語がLLBにコンパイルされ、大きな成功を収めましたが、Merge+Diffまで、システムの1つのクラスは、必要なだけ効率的にサポートすることが困難でした。

驚くべきことに、このクラスのシステムでは、ソフトウェアの依存関係を表すために DAG を多用します。 これには、多くの重複する推移的な依存関係を持つソフトウェアとライブラリを構築しているユーザーが含まれます。 その他の例としては、Linuxディストリビューションやプログラミング言語で使用されるパッケージマネージャーが含まれます。

表面的には、BuildKit は DAG を使用してビルドを表すため、ソフトウェア依存関係の DAG のモデリングに問題があったのは奇妙です。 これらの問題が何であるかを確認するために、例を調べてみましょう。

この例では、次の依存関係 DAG を持つソースからソフトウェアを構築することを中心に展開します。

画像11

この DAG はまだ LLB ではないことに注意してください。 この依存関係 DAG は、各頂点のソフトウェアを構築するために必要なものを示すだけで、各エッジは構築時の依存関係です。 たとえば、 バール バイナリのみが必要 そして、 バール ビルドするライブラリですが、 バズ バイナリのニーズ , バールそして フー ライブラリ。

これを LLB DAG に変換して、 フー, バールそして バズ バイナリ — ビルドしたものをコンテナイメージとしてエクスポートします。 コンテナイメージにはこれらのバイナリのみを含め、静的ライブラリファイルのような時間のみの依存関係を構築することはできません。

わかりやすくするために、いくつかのショートカットを使用しています。 したがって、次のような複数の依存関係をバンドルしています リブシー、GCC、および内部のソースコード 静的ライブラリのグループを単一の頂点に結合します。 実際には、これらの依存関係を別々の頂点間で分割できますが、複雑さが増します。

コピー チェーンを使用した DAG の構築

まず、各ターゲット頂点 (フー, バールそして バズ)それらをすべて組み合わせる前に、個別にLLBに入れます。 ありがたいことに、 foo bar 単純な依存関係構造があります。

画像13

各グラフの最終的なコピー操作は分離します /bin/foo そして /ビン/バー掻くこれは単なる空のファイルシステムです。 逆に言えば バズ もう少し複雑です:

画像2 1

ここでは、 フー そして バール libs は依存関係を共有しないため、別々のステップに組み込まれます。 これにより、BuildKit はそれぞれのビルドステップを並列化し、結果を個別にキャッシュできます。

これらの ExecOp の結果は、CopyOp を介して単一のビルド依存関係クロージャに結合され、ファイルシステムが作成されます。 lib/ ディレクトリには両方が含まれています フー そして バール ライブラリ。

最後に、すべてを1つのLLBグラフに結合しましょう。

画像4 1

ここで、解析のためにLLBをBuildKitに送信し、結果をレジストリコンテナイメージとしてエクスポートしたいとします。 CopyOps の最後のチェーンが、書き出された画像のレイヤーになります。 これは、イメージ マニフェストと共にレジストリにプッシュされます。

同じ BuildKit インスタンスで解析を再度実行すると、BuildKit のキャッシュ機能のおかげで、実際の作業が不要であることがわかります。

  • 解析中および各頂点の実行前に、BuildKit は頂点入力が最近変更されたかどうかを確認します。 そうでない場合、BuildKit はキャッシュされた結果を使用し、再実行を回避します。
  • 結果をコンテナー イメージとしてエクスポートする場合、BuildKit は、まだ存在しないレイヤーをレジストリにプッシュするだけで済みます。 レジストリには以前のビルドのレイヤーが既に含まれているため、プッシュは必要ありません。

カスケードキャッシュの無効化

これはすべて良いニュースですが、問題があります。 それらはどこから来たのですか? ビルドを再実行した場合に何が起こるかを考えてみましょう。 バール ライブラリのビルド。 この場合、新しいフラグを使用してビルドしていると仮定します。

ここでも、BuildKit は、キャッシュされた結果を取得する前に、LLB頂点の依存関係が変更されたかどうかを確認します。 変更によってキャッシュが無効になり、再実行が強制され、子孫の頂点にカスケードされます。

画像10

ここでは、キャッシュ無効化された頂点を ❌ . ここでのいくつかの無効化は完全に論理的です:

  • エグゼクティブオプは、 バール 定義が変更されたため、libsをキャッシュできません。 ビルドフラグ=何か新しいもの 頂点定義に追加します。
  • ビルド bar する ExecOp は、依存関係ライブラリが無効であるため、無効である必要があります。

一方、LLBの予期しない無効化を!?️で❌マークしました。

  • のコピー フー のビルドの下で依存関係のクロージャを作成するために必要なライブラリ /ビン/バズ は無効になります。 これは、そのコピーの宛先が バール libs — キャッシュ依存関係を作成します。 これは、私たちが フー そして バール ライブラリは実際には依存関係を共有しません。 CopyOp は再実行する必要がありますが、最終的には以前と同じレイヤーが作成されます。
  • 最終的な画像レイヤーへのコピー /bin/foo も、同様の理由で無効です。 最終的なイメージは、依存関係を共有するコンポーネントがない場合でも、CopyOps の線形チェーンとして構築されます。 チェーンの後に来る /bin/bar ので /bin/foo 、それらのカスケード無効化はそれに影響を与えます。

これらのニッチな問題は、コピー順序を切り替えることで技術的に修正できます。 しかし、それはシフトするだけです バール あなたのためのlibの無効化 フー ライブラリ。 これらの不要な無効化にはコストがかかります。

  • BuildKit はコピーをローカルで再度実行する必要があるため、時間とディスク容量が消費されます。
  • LLBの結果をコンテナレイヤーとしてエクスポートする場合、BuildKitは各レイヤーの内容を決定し、それらをtarballに再圧縮するためにより懸命に働きます.これは、レイヤーを変更する必要がない場合でも発生し、通常は以前とまったく同じコンテンツになります。
  • BuildKit には、厳密に必要な場合を除いて、リモートレジストリレイヤーがローカルにプルされないようにする最適化があります。 これにより、データのダウンロードが最小限に抑えられます。 これは、SourceOpによって参照される画像レイヤーに加えて、BuildKitのリモートキャッシュインポート機能を介して参照されるレイヤーで動作します。 ただし、不要なレイヤーがカスケードキャッシュの無効化に巻き込まれると、その有用性が妨げられます。 このプロセスにより、それらを CopyOp の宛先として強制的にプルダウンできます。

上記の問題は多くのユースケースに影響しますが、パフォーマンスのボトルネックを引き起こさないため、多くの場合重要ではありません。 少量のデータのコピーは、多くの場合、目立ちません。 ただし、この例はかなり基本的なものです。 依存関係 DAG はこれよりもはるかに複雑になり、サイズと量の両方でより多くのファイルが含まれる可能性があります。 これは、キャッシュの無効化が真のボトルネックになる場所です。

重要なポイント

通常、次の方法を使用して、LLBを介して依存関係DAGの各頂点を構築します。

    1. 依存関係を組み合わせる – 構築時の依存関係グラフをトポロジー的に並べ替え、そのクロージャを含むファイルシステムを出力する LLB 頂点を作成します。 CopyOpsは、必要に応じて独立したファイルシステムを組み合わせるために使用でき、コピー中に確認できます フー 一番上のライブラリ バール libs — for バズ 上記の例でビルドします。
    2. ビルドの実行 – 手順 1 の頂点の頂点の上に頂点ビルドを ExecOp として実行します。
    3. パッケージアーティファクト – 必要に応じて、手順 2 で作成したビルド成果物を、依存関係クロージャのファイルとディレクトリから分離します。 これにより、明確に定義された分離されたパッケージが作成されます。 分離は CopyOp によっても可能であり、これは前の例で各バイナリを最終的なイメージにコピーするときに明らかになります。

これらの手順については、後で参照します。

このアプローチの問題は、ソフトウェア依存関係の DAG がコピー チェーンに線形化されるときに、手順 1 で失われる情報が多すぎることです。 1 つの入力を変更すると、次の入力の宛先ディレクトリが無効になります。 後続の各入力は、そのカスケード効果の犠牲になります。 これは、各 DAG 入力が独立している必要があるにもかかわらず発生します。 これにより、過度に脆弱なキャッシュが作成され、ビルド中とコンテナレイヤーとしてエクスポートしている間の両方で、ファイルシステムの再利用を最大化するBuildKitの機能が損なわれます。

LLBには、ファイルシステムを独立させながら構成できる新しいタイプの操作が必要です。 ここで、マージ+差分が役立ちます。

解決策:マージ+差分を使用したコンポーザブルファイルシステム

マージオプ

MergeOp は、相互依存関係を作成せずにファイルシステムを効率的にマージできる新しいタイプの LLB 頂点です。 各頂点入力はファイルシステムであり、出力はファイルシステムであり、各入力は別の入力の上に適用されます。 基本的な例を次に示します。

画像6 1

ここでは、MergeOp 出力にその内容の注釈が付けられています。 これらは、MergeOp への各入力の結合された内容にすぎません。 重複するディレクトリの内容はマージされます。 2つのファイルが同じパス上にある場合は、後者の入力のファイルが優先されます(したがって、入力の順序が重要です)。 この例は、SourceOp 頂点と CopyOp 頂点のマージを示していますが、MergeOp は任意の LLB 頂点の結果を受け入れます。

ソフトウェア依存関係 DAG を構築する場合、MergeOp は前述の "依存関係の結合" 手順で役立ちます。 これはCopyOpを置き換え、このユースケースでの一般的な落とし穴を回避します。

MergeOp は、いくつかの重要な方法で動作します。

  • 入力レイヤーの再利用 MergeOp の結果がコンテナー イメージに含まれる場合、各入力のレイヤーは再作成または押しつぶされるのではなく、結合されます。 これにより、以前にエクスポートしたレイヤーを最大限に再利用できます。
  • 怠惰 – MergeOpは遅延して実装されているため、マージされたファイルシステムのディスク上の表現は、厳密に必要な場合にのみ作成されます。 ローカル作成は、この表現が ExecOp 入力である場合に必要です。 それは じゃない MergeOp 結果をコンテナー イメージとして直接エクスポートするときにローカルに作成するために必要です。 この機能の完全な影響については、後の「遅延パッケージ マージとしてのコンテナー イメージ」で説明します。
  • ハードリンクの最適化 – MergeOp 結果をローカルで作成する必要がある場合は、ディスク上のファイルシステムは、ファイルをコピーするのではなく、ハードリンクしてマージされたツリーを作成します。 これにより、特にreflinkの最適化をサポートしていないext4のようなファイルシステムを使用している場合に、マージ中に大きなファイルがボトルネックになるのを防ぎます。

ハードリンクは、オーバーレイとネイティブの2つの主要なスナップショットバックエンドを使用している場合にのみ使用できます。 現在、他のバックエンドはファイルをコピーしてマージされたファイルシステムを作成し、利用可能な場合はsyscallを使用すること copy_file_range を好みます。 ただし、他のスナップショットの種類 estargz と同等の最適化は、将来登場する可能性があります。

代わりに組み合わせることができるのに、なぜオーバーレイスナップショットにハードリンクが必要なのか疑問に思っている場合 下層ディレクトリ オーバーレイマウントに、あなたの好奇心は有効です。 残念ながら、このアプローチはディレクトリが不透明なため、常に機能するとは限りません。 この接線方向に関連するGithub問題のコーナーケース1と2で説明されているように.より多くの労力で機能させることは可能かもしれませんが、ハードリンクアプローチは今のところより簡単なままです。

ディフオップ

DiffOp は、ファイルシステムを依存関係ベースから効率的に分離できる新しいタイプの LLB 頂点です。 「下位」ファイルシステムと「上位」ファイルシステムの2つの入力を受け入れます。 次に、上位と下位を分離するファイルシステムを出力します。

また、DiffOp をファイルシステムを「アンマージ」する方法と見なすこともできます — MergeOp とは逆のものです。 基本的な例を次に示します。

画像1 1

差分が SourceOp ベースと、このベースの上に作成された ExecOp の間にあることがわかります。 これにより、基本的に ExecOp が依存関係から切り離されます。 DiffOp の結果には、ExecOp 中に行われた変更のみが含まれます。

ソフトウェア依存関係の DAG を構築する場合、DiffOp は前述の "パッケージ成果物" の手順で役立ちます。 ファイルを分離し、コピーを作成せずに、元の依存関係とは無関係に独自の「パッケージ」を使用できるようにします。 ただし、DiffOp のユースケースは、MergeOp のユースケースよりもあいまいです。 BuildKit のユーザーガイドでは、 これらのシナリオについて詳しく説明しています。

DiffOp は MergeOp と動作特性を共有しています。

  • 入力レイヤーの再利用 下位入力から上位入力への "既知のパス" がある場合、DiffOp 結果を含むコンテナー イメージのエクスポートでは、下位と上位を区切るレイヤーが再利用されます。 パスが不明な場合でも、DiffOp は一貫した結果を出力しますが、入力レイヤーを再利用することはできません。
  • 怠惰 – DiffOpも遅延して実装されています。ディスク上のファイルシステム表現は、必要な場合にのみ作成されます。 DiffOp の結果をコンテナー イメージとしてエクスポートする場合、その入力を "遅延解除" する必要はありません (前述のように、下位と上位の間に既知のパスがない場合を除く)。
  • ハードリンクの最適化 – BuildKit は、MergeOp と同じ最適化 (および現在の注意事項) を使用して DiffOp 結果をローカルに作成します。

例:2 つの新しい LLB 操作

これら2つの新しいLLB操作が前のセクションの問題をどのように解決できるかを見てみましょう。 LLB を使用して依存関係 DAG に頂点を構築するための新しく改善されたアプローチを次に示します。

    1. 依存関係を組み合わせる – 依存関係を MergeOp に接続します (必要に応じて、トポロジ的に並べ替えられた順序で)。
    2. ビルドの実行 – ステップ 1 でマージされたファイルシステムの上で ExecOp としてビルドを実行します。
    3. パッケージアーティファクト – DiffOp(またはユーザーガイドの同等の手法)を使用して、ビルドアーティファクトを独自の独立したファイルシステムに抽出します。 その後、DiffOp の出力を他の MergeOps にプラグインしたり、直接エクスポートしたりできます。

これらのルールを使用して、更新された LLB を次に示します。

画像9 1

ここで、前と同じシナリオで、上記のLLBの初期ビルドを実行し、その後のビルドを変更することを考えてみましょう。 バール ライブラリ:

画像3 1

この場合、予期される無効化のみが発生します。 BuildKit は、重要な依存関係のみを使用して各ビルドステップを実行します。 キャッシュの無効化は、必要な頂点にのみカスケードされます。 のコピーはもうあります フー ライブラリまたは フー それは不必要に無効になります。

これは、1 つの MergeOp 入力を無効にしても他の入力が無効にならないため、線形化されたコピー チェーンの問題が回避されるためです。 1 つの入力が無効になると MergeOp 全体が無効になりますが、マージされたファイルシステムは厳密に必要な場合にのみ作成されます。

それぞれのマージオプス バール そして バズ 依存関係のクロージャは、ExecOps がその上で実行されているため、ディスク上に作成する必要があります。 幸いなことに、ハードリンクは、サポートされているスナップショットバックエンドが使用されていれば、このプロセスを効率的にします。

最終的な MergeOp はディスク上に作成されません。 これは、DiffOpの遅延実装と組み合わせることで、エクスポートされた画像は、によって作成されたレイヤーのみで構成されることを意味します。 インストールする エクセオプス。 中間データやレイヤーを作成する必要はありません。

重要なポイント

Merge と Diff を使用すると、ソフトウェア依存関係 DAG で表される関係を反映した方法でファイル システム操作を構成および分解できます。 ファイルシステムレイヤーの再利用を許可することで、BuildKit はそれらのレイヤーのキャッシュ再利用を最大化できます。 これにより、パフォーマンスと再現性が向上します。

その他のアプリケーション

依存関係 DAG をより効率的に構築することに加えて、Merge と Diff は他にも多くの興味深い可能性を解き放ちます。 ここでは、いくつかの架空のものを探ります。

パッケージ DAG のインポート

多くの既存のコンテナイメージビルドは、LinuxディストリビューションパッケージマネージャーからExecOpを介してパッケージをインストールします(例: 実行 アプト, .apk, ティッカーなど)。 このための LLB は次のようになります。

画像8

同様のパターンは、次のような言語パッケージマネージャーに共通しています。 エヌピーエム, 、および Go モジュール。

このアプローチは機能しますが、前に説明したのと同じキャッシュの問題があります。 この場合、1 つのインストールパッケージが変更されると、そのインストールステップが無効になります。 パッケージ DAG が部分的にしか変更されていない場合でも、プロセス全体を再度実行する必要があります。 これは、LLBが実際の依存関係DAGに関する知識をエンコードしない別のケースであり、BuildKitがデータを効果的にキャッシュする機能を妨害します。

MergeOpはここで新しい可能性を解き放ちます。 次のことができます。

  • 必要なパッケージの一覧を取得し、すべての推移的な依存関係を含む完全な依存関係 DAG に展開します。
  • ExecOp を使用してパッケージの内容をダウンロードして、各パッケージを LLB 状態に変換します。 DiffOp は、アーティファクトを分離するのに役立つ場合があります。 ただし、ExecOp の実行中に別のマウントを使用するだけで十分な場合があります。
  • MergeOp を使用して、これらの個々のパッケージをトポロジ順に結合します。

これを実装する LLB は次のようになります。

画像7 1

ユーザーガイドで説明されているように、DiffOpの代わりに個別のExecOpマウントを使用していることに注意してください。DiffOpは、必要に応じて実行可能なソリューションです。

これにより、古いアプローチに共通するキャッシュの問題が解決されます。 パッケージは、データや作業を複製することなくコンテナー イメージにインポートできます。 わずかに異なるパッケージセットで別のビルドを実行した場合、BuildKit は以前のビルドのパッケージごとにキャッシュを再利用できます。

さらに、パッケージ DAG が LLB にコンパイルされたので、任意に拡張できます。 インポートしたパッケージの上に独自のソフトウェアを構築する場合は、上記の手順からLLBを取得して独自の操作を追加するのと同じくらい簡単です。

遅延パッケージマージとしてのコンテナイメージ

誰かが上記の手法を使用して大きなパッケージ DAG をビルドまたはインポートし、DAG 内の個々のパッケージを単一レイヤー イメージとしてコンテナー レジストリにプッシュしたとします。

次に、誰かがコンテナイメージに変換したいパッケージのリストを持っているとしましょう。 BuildKit を使用した場合の "ビルド" は、これらのパッケージの依存関係 DAG をトポロジ的に並べ替え、それらを MergeOp と組み合わせることで構成されます。

 

画像12

"ビルド" は、実際の作業がほとんど必要ないため、引用符で囲まれています。 画像の頂点は MergeOp や DiffOp と同様に遅延であるため、LLB DAG 全体が遅延します。 実際には、このビルドは コンテナー イメージのマニフェスト、そしてそれをレジストリにプッシュします。 入力パッケージイメージは、これらの遅延実装のおかげでローカルにプルダウンする必要はありません。 マニフェストをプッシュした後、誰でもレジストリからそのイメージを取得し、他のイメージの場合と同様に、選択したコンテナー ランタイムを使用して実行できます。

このシナリオでは、コンテナー イメージ レジストリはパッケージ キャッシュになり、"ビルド" は、マニフェストとしてレジストリにプッシュされる、特定のイメージに存在する必要があるパッケージの宣言にすぎません。

いくつかのレンチ

これはすべてかなりきれいです! ただし、いくつかの注意点があります。

  • パッケージマネージャーは、多くの場合、パッケージのインストール中にスクリプトを実行するフックをサポートします。 多くの場合、コンテナー イメージのビルドに対してこれらのスクリプトを無効にすることができます。 厳密に必要な場合は、MergeOps 上で ExecOps を使用していつでも表現できます。 ただし、これにより、マージされたファイルシステムをローカルに作成する必要があるため、パフォーマンスの最適化が軽減されます。
  • イメージのエクスポート後にランタイムパッケージマネージャーが必要な場合、LLBにはパッケージデータベースを含むレイヤーをマージする方法も必要です。 たとえば、これは実行する場合に必要です アプト コマンドを Debian ベースのイメージで使う — 開発環境の中で役に立つかもしれません。
  • パッケージ DAG を LLB に変換するには、パッケージ マネージャーのメタデータ データベースにプログラムでアクセスする必要があります。 これは口で言うほど簡単ではありません。 CLI 出力の解析やライブラリ サポートの使用など、複数のオプションがあり、ケースバイケースで評価する必要があります。

 

これらの問題はどれもショーストッパーではありませんが、上記のユースケースでMergeとDiffを利用しながらツールを作成する人は、それらを覚えておく必要があります。

感謝!

私の Merge+Diff への関心は、BuildKit を使用して複雑なパッケージ DAG を構築しようとする私自身の試みに由来しています。 しかし、他のBuildKitユーザーもこの機能、特にNetflixに興味を持ち、それらの設計と実装を私に請け負いました。 彼らのサポートは、特にエンジニアから非常に高く評価されています エドガー・リー そして アーロン・レーマン.どちらも最初から最後まで貴重なフィードバックを提供してくれました。

また、BuildKit のメンテナのコミュニティにも感謝します トニス・ティーギ、しばらくの間、これらの機能にも関心を示しています。 Merge+Diff の設計はかなり複雑で、コードレビュー中は簡単ではありませんでしたが、彼と関係者全員のおかげで、本当に強力なツールがいくつかできたと思います。 これらは、前進する多くの興味深いユースケースのロックを解除するはずです。