1. ホーム

[解決済み】Dockerfileの複数RUNと1つの連鎖RUN、どっちがいい?

2022-04-11 23:03:35

質問

Dockerfile.1 は、複数の RUN :

FROM busybox
RUN echo This is the A > a
RUN echo This is the B > b
RUN echo This is the C > c

Dockerfile.2 が加わる。

FROM busybox
RUN echo This is the A > a &&\
    echo This is the B > b &&\
    echo This is the C > c

RUN はレイヤーを作成するので、レイヤーは少ない方がいいとずっと思っていて、そのため Dockerfile.2 が良いですね。

の場合、当然そうなります。 RUN が追加したものを削除します。 RUN (例) yum install nano && yum clean all ) が、すべての RUN を追加する場合、いくつか考慮しなければならない点があります。

  1. レイヤーは前のレイヤーの上に差分を追加するだけなので、後のレイヤーが前のレイヤーで追加されたものを削除しない場合、どちらの方法でもディスクスペースを節約する利点はあまりないはずです。

  2. レイヤーはDocker Hubから並列に引き出されるため Dockerfile.1 の方が若干大きいかもしれませんが、理論的にはダウンロードが速くなります。

  3. もし、第4文を追加する場合(つまり echo This is the D > d ) とローカルに再構築します。 Dockerfile.1 はキャッシュのおかげでビルドが速くなりますが Dockerfile.2 を実行すると、4つのコマンドをすべて再実行しなければなりません。

では、質問です。 Dockerfileを行うにはどちらが良いのでしょうか?

どのように解決するのですか?

可能な限り、私はいつもファイルを作成するコマンドと同じファイルを削除するコマンドをマージして、1つの RUN という行があります。これは、各 RUN の行は画像にレイヤーを追加し、出力は文字通りファイルシステムの変更であり、それを docker diff を一時的なコンテナ上に作成します。別のレイヤーで作成されたファイルを削除しても、ユニオンファイルシステムが行うのは新しいレイヤーでのファイルシステムの変更を登録するだけで、ファイルはまだ前のレイヤーに存在し、ネットワーク経由で配送されてディスクに保存されます。ですから、ソースコードをダウンロードし、それを展開し、バイナリにコンパイルし、最後にtgzファイルとソースファイルを削除する場合、画像サイズを小さくするために、本当はこれをすべて1つのレイヤーで行いたいのです。

次に、個人的には、他の画像で再利用できる可能性と予想されるキャッシュ使用量に基づいてレイヤーを分割しています。4 つのイメージがあり、そのすべてが同じベースイメージ (例: debian) である場合、それらのイメージのほとんどに共通するユーティリティのコレクションを最初の実行コマンドに引っ張ってきて、他のイメージがキャッシュの恩恵を受けられるようにすることがあります。

イメージキャッシュの再利用を検討する際には、Dockerfileの順序が重要です。私は、ごく稀にしか更新されないコンポーネント、おそらくベースイメージが更新されたときだけ更新されるコンポーネントを調べ、それらをDockerfileの上の方に配置します。例えば、ホスト固有のUIDを持つユーザの追加や、フォルダの作成とパーミッションの変更などです。もしコンテナが、活発に開発されている解釈コード(JavaScriptなど)を含んでいる場合は、できるだけ遅く追加して、再構築がその単一の変更だけを実行するようにします。

これらの変更の各グループにおいて、できる限りレイヤーを少なくするように統合しています。たとえば、4つの異なるソースコードフォルダーがあったとしても、それらをひとつのフォルダーに収め、ひとつのコマンドで追加できるようにしています。apt-get などからインストールしたパッケージは、可能な限り単一の RUN にマージして、パッケージマネージャのオーバーヘッド(更新とクリーンアップ)の量を最小限に抑えられるようにしています。


多段ビルドに対応したアップデートを行いました。

私は、多段ビルドの最終段階でない段階での画像サイズの縮小については、あまり心配していません。これらのステージがタグ付けされて他のノードに出荷されない場合、各コマンドを別々の RUN という行があります。

しかし、ステージ間でコピーするのはファイルだけで、環境変数の設定やエントリポイント、コマンドといった画像の残りのメタデータはコピーされないため、レイヤーをつぶすための完璧な解決策とは言えません。また、Linuxディストリビューションでパッケージをインストールする場合、ライブラリやその他の依存関係がファイルシステム全体に散らばっていることがあり、すべての依存関係をコピーすることが困難になっています。

このため、私はCI/CDサーバでバイナリをビルドする代わりに多段ビルドを使用しており、CI/CDサーバには、以下のツールを実行するためだけに必要です。 docker build jdk、nodejs、go、その他のコンパイルツールはインストールされていません。