新しいBuildKitでのむメヌゞのリベヌスずリモヌトキャッシュサポヌトの改善

投皿日: Mar 17, 2022

BuildKit Builder ゚ンゞン、Dockerfile 1.4 フロント゚ンド、 Docker Buildx CLI の新しいバヌゞョンをリリヌスしたした。これらのそれぞれには、倚くの新機胜が付属しおいたす。 このブログ投皿では、そのうちの1぀であるDockerfilesの新しいコピヌモヌドを瀺し、Dockerfilesでそれを䜿い始める必芁がある理由を説明したす。

Dockerfile 1.4 リリヌスでは、ビルドコンテキストたたは別のステヌゞからファむルをコピヌするためのコマンド ADD ずコマンドが、 COPY 新しいフラグ '–link' を受け入れるようになりたした。このフラグを䜿甚するず、キャッシュ セマンティクスが倧幅に向䞊し、新しい基本むメヌゞの䞊にビルドを再構築せずに 2 日目の高速リベヌスを実行できたす。

このフラグを䜿甚するには、Dockerfile の先頭に を含む # syntax=docker/dockerfile:1.4 行を远加する必芁がありたす。 これにより、このフラグをサポヌトする適切なフロント゚ンドむメヌゞが読み蟌たれたす。 フラグの正しいキャッシュセマンティクスを取埗するには、BuildKit v0.10 も䜿甚する必芁がありたす。

# syntax=docker/dockerfile:1.4
FROM ...
COPY --link foo bar
docker buildx create --use --name mybuilder
docker buildx build .

この新しいフラグの機胜の詳现に入る前に、珟時点でDockerfileコマンドがどのように機胜するかを芋おいきたしょう。

Docker むメヌゞは、コンテナヌ ファむル システムを構成するレゞストリ内の tar ボヌルであるレむダヌで構成されたす。 画像をプルするず、これらのtarボヌルが重なり合っお抜出されたす。 この抜出がどのように行われ、ファむルが実際にディスクに栌玍されるかの実装は、基になるスナップショット䜜成者の皮類によっお異なりたす。 オヌバヌレむスナップショットを䜿甚する堎合、ファむルシステムは耇数のディレクトリを1぀に結合する特別なマりントを䜜成できたす。 他のスナップショット䜜成者の堎合、このプロセスには通垞、ファむルの(浅い)コピヌの䜜成が含たれたす。

Dockerfile RUNのすべおの 、たたはADD コマンドは、 COPY 以前に䜜成されたコンテンツの䞊に远加される新しいスナップショットも䜜成したす。ビルドの準備ができお、ビルド結果ずしおむメヌゞを゚クスポヌトする堎合は、すべおのスナップショットを比范し、各スナップショットに远加された新しいファむルを含む新しいtarballを䜜成する「異なる」コンポヌネントを実行したす。

ここで理解しおおくべき重芁な抂念は、新しいレむダヌを䜜成するには、前のレむダヌ(芪レむダヌずも呌ばれたす)が以前に䜜成され、ディスク䞊に存圚する必芁があるずいうこずです。 コマンドを䜿甚しお䞀郚のファむルをディレクトリに移動するたびに COPY 、同じステヌゞ䞊の以前のすべおのコマンドを前に完了する必芁がありたした。 これがないず、ファむルのコピヌ先ディレクトリがなくなりたす。

この制限は、コマンド ADD に COPY 远加された新しい --link フラグで倉曎されたす。このフラグが存圚する堎合、コマンドは別のモヌドで動䜜し、 COPY 代わりにファむルが完党に新しいスナップショットにコピヌされたす。 次に、この新しいスナップショットはそれ自䜓で新しいレむダヌtarballに倉換され、そのtarballは以前のtarballレむダヌのチェヌンにリンクされたす。 このリンクアクションは通垞、ファむルにアクセスしたり移動したりするこずなく、新しいアむテムがレむダヌ配列に远加される単なるメタデヌタの倉曎です。 次の䟋に瀺すように、リモヌトレゞストリに存圚するレむダヌを䜿甚しおリモヌトで実行するこずもでき、プルたたはプッシュする必芁はありたせん。

マヌゞオプ1

芁玄するず:

  • COPY --link=false (前の方法ずデフォルト):ファむルは前のコマンドの結果の䞊にコピヌされたす レむダヌは、ディスク䞊のスナップショットを比范するこずによっお埌で䜜成されたす
  • COPY --link=true: ファむルが新しい堎所にコピヌされ、独立したレむダヌに倉換されたす。 レむダヌ識別子が前のレむダヌの䞊に远加されたす

宛先ディレクトリから䟝存関係を削陀するこずで、コマンドを完了する COPY 前に前のコマンドが完了するのを埅぀必芁がなくなりたす。 たた、同じ Dockerfile ステヌゞ䞊の以前のコマンドが倉曎されたずきに、珟圚のコマンドのビルドキャッシュを無効にする必芁もありたせん。

これにより可胜になるナヌスケヌスの䟋をいく぀か芋おみたしょう。

䟋: 既存のむメヌゞのリベヌス

BuildKit v0.9 の以前のリリヌスでは、遅延むメヌゞのプルずいう別の新機胜が導入されたした。 この機胜が意味するのは、BuildKitがリモヌトむメヌゞ/キャッシュにアクセスする必芁があるずきはい぀でも、実際にそれらからファむルを読み取る必芁があるタスクがあるたで、レむダヌのプルを遅らせるずいうこずです。 たずえば、レむダヌが別のむメヌゞで䜿甚されおいる堎合、このプルは䞍芁であり、BuildKitは、䞍倉のダむゞェストによっお前のレむダヌを参照する新しいむメヌゞを䜜成できたす。

FROM ubuntu
ENV MYCONFIG=foo
VOLUME /data

たずえば、この Dockerfile を次のようにビルドするずしたす。 docker buildx build -t myuser/myubuntu --push . キャッシュのないクリヌンなシステムでは、新しいむメヌゞがリポゞトリで準備されるたでにビルド党䜓が数秒しかかからないこずに気付くでしょう。 これは、ubuntuむメヌゞのレむダヌがロヌカルマシンにプルされたり、ハブリポゞトリにプッシュされたりしないためです。 代わりに、BuildKit は Ubuntu レむダヌダむゞェストを含む新しいむメヌゞ構成ずマニフェストを䜜成し、それらのみをプッシュしたす。 レむダヌは、レゞストリのクロスリポゞトリマりント機胜を䜿甚しお、Ubuntuリポゞトリから盎接リンクされたす。 このパタヌンは、リモヌトキャッシュがただ最新であり、実際にはレむダヌをプルダりンしないこずをビルドで怜蚌する必芁があるリモヌトキャッシュ゜ヌスでも䜿甚できたす。

この方法は、画像構成のみを倉曎するような ENV VOLUME メタデヌタコマンドに適しおいたす。や RUN のような COPY新しいレむダヌを䜜成するコマンドを䜿甚した堎合でも、これらのコマンドを実行するにはロヌカルファむルが必芁なため、ベヌスむメヌゞを最初にプルする必芁がありたした。

COPY --link この芁件を削陀したす。 を䜿甚するように COPY --link曎新された䞀般的なマルチステヌゞビルドDockerfileを芋おみたしょう。

#syntax=docker/dockerfile:1.4
FROM golang AS build
....
RUN go build -o /myapp .

FROM alpine:3.14
COPY --from=build --link /out/myapp /bin
ENTRYPOINT ["/bin/myapp"]

BuildKit v0.10 でこのファむルをビルドするず、最初に気付くのは、Alpine むメヌゞをプルせずにビルドが完了しおいるこずです。 これは、ディレクトリぞのコピヌ myapp が /bin/ Alpineファむルに䟝存しなくなったためです。 このむメヌゞを別の Docker Hub リポゞトリにプッシュするず、アルパむン レむダヌが盎接リンクされたす。 他の方法(たずえば、ロヌカルOCItarballに --output type=oci)でむメヌゞを゚クスポヌトする堎合にのみ、レむダヌが実際にプルされたす。

これで、このむメヌゞを初めおビルドしおプッシュしたずきに、将来このむメヌゞを曎新する必芁がある堎合に䜕が起こるかを確認できたす。 セキュリティ修正を含む新しいAlpine 3.14むメヌゞがリリヌスされた堎合、たたは3.15に曎新する堎合。

すべおを再構築しないように、以前のビルドのリモヌトキャッシュを保存できたす。 BuildKitは倚くのキャッシュバック゚ンドをサポヌトしおいたすが、この堎合、最も簡単なのは、ビルドキャッシュ情報をむメヌゞ構成に埋め蟌むだけの「むンラむンキャッシュ」を䜿甚するこずです。

むンラむンキャッシュを有効にするには、次のいずれかを実行したす。

docker buildx build --cache-to type=inline --push -t mysuser/myapp .

又は

docker buildx build --build-arg BUILDKIT_INLINE_CACHE=1 --push -t mysuser/myapp .

これで、埌続のビルドを実行するずきに、むメヌゞ自䜓をキャッシュ゜ヌスずしお䜿甚できたす。 たずえば、代わりに Alpine 3.15 を䜿甚するように以前の Dockerfile を曎新し、以前のキャッシュを䜿甚しおビルドするずどうなるかを芋おみたしょう。

FROM golang AS build
....
FROM alpine:3.15
COPY --from=build --link /out/myapp /bin/
ENTRYPOINT ["/bin/myapp"]
docker buildx build --cache-from myuser/myapp -t myuser/myapp --push .

最初のビルドず同様に、実際には alpine:3.15 ロヌカル コンピュヌタヌにプルされず、代わりにレむダヌ BLOB がレゞストリ内に盎接移動されおいるこずがわかりたす。 もっず興味深いのは、画像も匕っ匵られなかったこずです golang 。 これは、バむナリが倉曎されおいないこずを確認 myapp できるため、むメヌゞの2番目のレむダヌも倉曎されおおらず、新しい高山むメヌゞの䞊にリベヌスできるためです。 これはすべお、ロヌカルレむダヌなしで完党にリモヌトで行われたす。

操䜜は COPY /bin ベヌスむメヌゞのディレクトリに䟝存し、ベヌスむメヌゞが倉曎されたためキャッシュが無効になり、AlpineずGolangの䞡方のむメヌゞがプルされ、バむナリが myapp 再コンパむルされるため、これがないず --link 以前は䞍可胜だったこずに泚意しおください。

䟋: リモヌト・キャッシュ・サポヌトの改善

別の䟋ずしお、耇数の COPY コマンドがある堎合のキャッシュの凊理方法を芋おみたしょう。

#syntax=docker/dockerfile:1.4
FROM golang AS build
....
RUN go build -o /myapp .

FROM ubuntu AS config
...
RUN generate -o /myapp.config

FROM alpine:3.14
COPY --from=config --link /myapp.config /etc/
COPY --from=build --link /myapp /bin/
ENTRYPOINT ["/bin/myapp"]

このファむルには、別のビルドステヌゞから生成された構成ファむルを远加する2番目のコピヌを远加したした。 䟝存関係に耇数のステヌゞを䜿甚し、最終ステヌゞでそれらをすべお䞀緒にコピヌするこずは非垞に䞀般的なパタヌンです。 これは、ビルドに最適な䞊列化ずキャッシュの再利甚を実珟する方法です。

以前ず同じように、むンラむンキャッシュを䜿甚しおこのDockerfileをビルドしおプッシュするずしたす。

docker buildx build --cache-to type=inline -t myuser/myapp2 --push .

次に、以前のむンラむンキャッシュを䜿甚しお再構築を行う必芁があり、構成ファむルの生成が倉曎された堎合に䜕が起こるかを考えおみたしょう。 構成生成のステヌゞを再床実行する必芁がありたすが、最埌のステヌゞはどうなりたすか?

䜿甚 --linkせずに、ファむルの堎合 myapp.config 倉曎されたのは、アルパむンの画像がプルされお抜出されたこずを意味したす。myapp.config そのスナップショットをコピヌし、その䟝存関係 COPY myapp が倉曎されたため、再コンパむルしお再床コピヌする必芁がありたす。ここでのキャッシュの再利甚の可胜性はコマンドの順序に䟝存し、キャッシュはキャッシュに䞀臎した最埌の COPY コマンドたで䜿甚でき、それ以降のすべおのコマンドは再床実行する必芁があるこずに泚意しおください。 のキャッシュ myapp が無効になっおいたずしおも、 myapp.config そのファむルは以前にコピヌされたが、その逆ではないためです。

を远加するこず --linkで、キャッシュの再利甚が倧幅に向䞊したした。 すべおのコマンドが独立し、 COPY ベヌスむメヌゞに䟝存するコマンドはありたせん。 新しい構成が生成されるず、新しいレむダヌに盎接倉換されたす。 次に、このレむダヌが前の画像内で眮き換えられたす。 ベヌスむメヌゞの最䞋局ずそれを含む myapp 最䞊局はそのたた残され、ロヌカルマシンにプルする必芁はたったくありたせん。 新しいレむダヌのみが新しいむメヌゞマニフェストず䞀緒にプッシュされたす。

マヌゞオプ2

新しいセマンティクスを自動的に䜿甚するようにすべおのコマンドを倉曎するのではなく、 COPY なぜ新しいフラグが远加されたのか䞍思議に思うかもしれたせん。 その理由は、たれに完党な䞋䜍互換性がないためです。 たずえば、コピヌ コマンド COPY myapp /path/to/myappが . 指定した /path/to/myapp 宛先ディレクトリにコンポヌネントの1぀にシンボリックリンクが含たれおいる堎合は、それに埓い、代わりにファむルがシンボリックリンクタヌゲットにコピヌされたす。 を䜿甚するず --link、すべおのコピヌが独立しおおり、コピヌ先のパスに含たれるファむルを確認するこずはできたせん。 したがっお、シンボリックリンクをたどる代わりに、最初に垞に新しいディレクトリ/path/to を䜜成し、 COPY --link myapp /path/to/myapp その䞭にファむルをコピヌしたす。

あなたが芋るかもしれない別のケヌスはのようなコマンド COPY myapp /usr/binです。 宛先パスがスラッシュで終わっおいないこずに泚意しおください。 以前のセマンティクスがなければ --link 、 がディレクトリであるかどうか /usr/bin をチェックしおいたでしょう。 そうである堎合、ファむルは ずしおコピヌ /usr/bin/myappされたす。 そうでない堎合、新しいファむルは通垞のファむル bin ずしおコピヌ /usrされたす。これらの皮類のチェックでは、ディスク䞊のファむルを抜出しお、そのタむプを怜蚌できるようにする必芁があり、 では --link蚱可されたせん。 --linkしたがっお、を䜿甚する堎合は、宛先パスにシンボリックリンクが含たれおいないこずを確認し、あいたいな宛先ディレクトリ怜出を䜿甚しないようにする必芁がありたす。

䞊蚘のケヌスは非垞にたれであり、単玔なDockerfileの倉曎で簡単に修正できるはずです。 コマンドで COPY シンボリックリンクに䟝存しない堎合は、垞に䜿甚 --linkを開始するこずをお勧めしたす。 リンクされたコピヌのパフォヌマンスは、垞に通垞のコピヌよりも優れおいるか同等である必芁があり、ビルドのキャッシュの再利甚ず最適化が倧幅に向䞊したす。

の内郚構造に COPY --linkもっず興味がある堎合は、BuildKit の LLB 定矩の新しい MergeOp 機胜を利甚しおいたす。 MergeOp の詳现ず、抂念的には MergeOp の逆であるコンパニオン DiffOp 機胜に぀いおは、 BuildKit のドキュメントを参照しおください。

関連蚘事