Dockerレイヤーについて


27

に次のブロックがありますDockerfile

RUN yum -y update
RUN yum -y install epel-release
RUN yum -y groupinstall "Development Tools"
RUN yum -y install python-pip git mysql-devel libxml2-devel libxslt-devel python-devel openldap-devel libffi-devel openssl-devel

RUN作成されたドッカーレイヤーを削減するには、これらのコマンドを統合する必要があると言われました。

RUN yum -y update \
    && yum -y install epel-release \
    && yum -y groupinstall "Development Tools" \
    && yum -y install python-pip git mysql-devel libxml2-devel libxslt-devel python-devel openldap-devel libffi-devel openssl-devel

私はdockerを初めて使いますが、複数のRUNコマンドを指定するこれら2つのバージョンの違いを完全に理解しているわけではありません。いつRUNコマンドを単一のコマンドに統合し、複数のRUNコマンドを使用するのが理にかなっていますか?


回答:


35

Dockerイメージは、実際にはファイルシステムレイヤーのリンクリストです。Dockerfileの各命令は、対応する命令の実行前後のファイルシステムの違いを記述するファイルシステム層を作成します。docker inspectサブコマンドは、ファイルシステム層のリンクされたリストであることの性質を明らかにするためにドッカー画像に使用することができます。

画像で使用されるレイヤーの数は重要です

  • 同時に発生するアップロードまたはダウンロードの数に影響するため、画像をプッシュまたはプルするとき。
  • コンテナを開始するとき、レイヤーが一緒に組み合わされて、コンテナで使用されるファイルシステムが生成されます。レイヤーが多くなるほどパフォーマンスは低下しますが、これにより異なるファイルシステムバックエンドが異なる影響を受けます。

これには、イメージの構築方法にいくつかの影響があります。私ができる最初で最も重要なアドバイスは:

アドバイス#1ソースコードが関係するビルドステップがDockerfileでできる限り遅くなり、a &&またはa を使用する以前のコマンドに関連付けられていないことを確認します;

これは、前のステップがすべてキャッシュされ、対応するレイヤーを何度もダウンロードする必要がないためです。これは、ビルドとリリースの高速化を意味します。これはおそらくあなたが望むものです。興味深いことに、Dockerキャッシュを最適に使用することは驚くほど困難です。

2番目のアドバイスはそれほど重要ではありませんが、メンテナンスの観点からは非常に役立ちます。

アドバイス#2 Dockerfileに複雑なコマンドを書くのではなく、コピーして実行するスクリプトを使用します。

A Dockerfileようになり、このアドバイスを、次の

COPY apt_setup.sh /root/
RUN sh -x /root/apt_setup.sh
COPY install_pacakges.sh /root/
RUN sh -x /root/install_packages.sh

等々。いくつかのコマンドをバインドするアドバイスの&&範囲は限られています。スクリプトを使用すると、冗長性を回避したり、ドキュメント化のために関数などを使用したりするのがはるかに簡単になります。

プリプロセッサとによって引き起こされる小さなオーバーヘッドを回避するために喜んでいる人達COPYの手順を、実際にオンザフライ生成されDockerfile場所

COPY apt_setup.sh /root/
RUN sh -x /root/apt_setup.sh

シーケンスは

RUN base64 --decode … | sh -x

ここで、はのbase64エンコードバージョンですapt_setup.sh

3番目のアドバイスは、ビルドの長さを犠牲にして、サイズとレイヤー数を制限したい人向けです。

アドバイス#3with -idiomを使用して、中間層には存在するが、結果のファイルシステムには存在しないファイルを回避します。

いくつかのdocker命令によって追加され、後の命令によって削除されたファイルは、結果のファイルシステムには存在しませんが、構築中のdockerイメージを構成するdockerレイヤーで2回言及されます。1回は、命令を追加した結果のレイヤー内の名前と完全なコンテンツ、1回は命令を削除した結果のレイヤー内の削除通知として。

たとえば、一時的にCコンパイラと何らかのイメージが必要であると仮定し、

# !!! THIS DISPLAYS SOME PROBLEM --- DO NOT USE !!!
RUN apt-get install -y gcc
RUN gcc --version
RUN apt-get --purge autoremove -y gcc

(より現実的な例では、単に--versionフラグでその存在をアサートするのではなく、コンパイラーでいくつかのソフトウェアをビルドします。)

Dockerfileスニペットは3つのレイヤーを作成します。最初のレイヤーには完全なgccスイートが含まれているため、最終的なファイルシステムに存在しない場合でも、対応するデータは同じ方法で画像の一部であり、ダウンロード、アップロード、アンパックする必要があります最終画像です。

with-idiom単離リソースの所有権およびリソースを使用して、ロジックから解放する関数型プログラミングにおける一般的な形態です。このイディオムをシェルスクリプトに置き換えるのは簡単です。以前のコマンドを次のスクリプトに言い換えCOPY & RUNて、アドバイス#2のように使用できます

# with_c_compiler SIMPLE-COMMAND
#  Execute SIMPLE-COMMAND in a sub-shell with gcc being available.

with_c_compiler()
(
    set -e
    apt-get install -y gcc
    "$@"
    trap 'apt-get --purge autoremove -y gcc' EXIT
)

with_c_compiler\
    gcc --version

複雑なコマンドを機能に変換して、に送ることができますwith_c_compiler。いくつかのwith_whatever関数の呼び出しを連鎖させることも可能ですが、おそらくあまり望ましくありません。(シェルのより難解な機能を使用して、with_c_compiler複雑なコマンドを受け入れることは確かに可能ですが、これらの複雑なコマンドを関数にラップすることはすべての面で望ましいです。)

アドバイス#2を無視したい場合、結果のDockerfileスニペットは

RUN apt-get install -y gcc\
 && gcc --version\
 && apt-get --purge autoremove -y gcc

難読化のため、読みやすく保守が容易ではありません。シェルスクリプトバリアントが重要な部分に重点を置いているのgcc --versionに対して、チェーン&&バリアントはノイズの中でその部分を埋めています。


1
スクリプトを使用してビルドし、1つのRUNステートメントで複数のコマンドを使用した後に、ボックスサイズの結果を含めることができますか?
030

1
イメージベースの構成(つまり、OSのもの)とライブラリを、作成したソースのセットアップと混合することは、私にとって悪い考えのようです。あなたは「ソースコードが関係するビルドステップができるだけ遅くなるようにしてください」と言います。その部分を完全に独立したアーティファクトにすることに問題はありますか?
ジミージェームズ

1
@ 030「箱」サイズとはどういう意味ですか?どのボックスを参照しているかわかりません。
マイケルルバルビエグリューネ

1
ドッカーの画像サイズ
030

1
@JimmyJames展開シナリオに大きく依存します。コンパイルされたプログラムを想定する場合、「行うべき正しいこと」は、それをパッケージ化し、そのパッケージの依存関係とパッケージ自体を2つの異なる最終段階に近いものとしてインストールすることです。これにより、Dockerキャッシュの有用性が最大になり、同じファイルで何度もレイヤーがダウンロードされるのを防ぎます。イメージの長い依存関係チェーンを構築するよりも、ビルドレシピを共有してドッカーイメージを構築する方が簡単です。後者の方が再構築が難しくなるからです。
マイケルルバルビエグリューネ

13

Dockerfileで作成する各命令により、新しいイメージレイヤーが作成されます。各レイヤーは、結果の画像の一部ではない追加データをもたらします。たとえば、あるレイヤーでファイルを追加し、後で別のレイヤーで削除した場合、最終画像のサイズには、削除したものの特別な「ホワイトアウト」ファイルの形式で追加されたファイルサイズが含まれます。

次のDockerfileがあるとします。

FROM centos:6

RUN yum -y update 
RUN yum -y install epel-release

結果の画像サイズは

bigimage     latest        3c5cbfbb4116        2 minutes ago    407MB

逆に、「類似した」Dockerfileの場合:

FROM centos:6

RUN yum -y update  && yum -y install epel-release

結果の画像サイズは

smallimage     latest        7edeafc01ffe        3 minutes ago    384MB

単一のRUNステートメントでyumキャッシュを消去すると、サイズがさらに小さくなります。

そのため、読みやすさ/メンテナンスの容易さとレイヤー数/画像サイズのバランスを保つ必要があります。


4

RUN文は、それぞれ一つの層を表します。パッケージをダウンロードしてインストールし、削除したいとします。3つのRUNステートメントを使用する場合、個別のレイヤーがあるため、画像サイズは縮小しません。1つのRUNステートメントを使用してすべてのコマンドを実行すると、ディスクイメージのサイズが小さくなる可能性があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.