CenturyLinkラボでは、最近さまざまなDockerイメージの構築に取り組んでいます。 作成の実験を開始すると、アセンブリが急速に大量に膨張することがわかりました(1 GB以上のイメージを組み立てることが一般的でした)。 もちろん、ローカルマシン上にある2つのギグの画像について話している場合、サイズはそれほど重要ではありません。 しかし、インターネット経由でこれらの画像を常にダウンロード/送信し始めると、これは問題になります。
アセンブリのサイズを縮小するために何ができるかを理解するために、ドッカー画像の作成プロセスがどのように機能するかをもう少し掘り下げて理解する価値があると判断しました。
小さな余談として、Adriaan de Jongeは最近、「 可能な限り最小のDockerコンテナーの作成 」に関する記事を公開しました。 彼のイメージは非常に小さく、3.6 Mbです。 ここでは、そのような極端なことは考えません。 PythonやRubyなどの言語を使用することに慣れている人として、OSからのわずかに高いレベルのサポートが必要です。依存関係
apt-get install
修正する
apt-get install
Debianと
apt-get install
実行できるように、100メガバイトの空き領域を喜んで犠牲に
apt-get install
ます。 したがって、私はエイドリアンの小さなイメージがうらやましいが、より広範なアプリケーションへのサポートが必要であるため、彼のアプローチは実用的ではありません。
レイヤー
画像を縮小するトピックに進む前に、レイヤーについて説明する必要があります。 レイヤーの概念は、ルートファイルシステム( rootfs )、 コピーオンライトメカニズム、カスケードマウントマウント( union mount )などのさまざまな低レベルの技術的詳細に関係しています。 幸いなことに、このトピックは他の場所で十分に説明されているため、ここでは再説明しません。 この目的のために、Dockerfileの各命令は新しいイメージレイヤーを作成することを理解することが重要です。
Dockerfileの例を見て、実際の動作を見てみましょう。
FROM debian:wheezy RUN mkdir /tmp/foo RUN fallocate -l 1G /tmp/foo/bar
完全に役に立たない画像ですが、それは私たちが言われたことを実証するのに役立ちます。 ここでは、
debian:wheezy
をベースイメージとして使用し、
/tmp/foo
作成し、その中に
bar
ファイル用に1 GBのスペースを割り当てます。
この画像を組み立てましょう:
$ docker build -t sample . Sending build context to Docker daemon 2.56 kB Sending build context to Docker daemon Step 0 : FROM debian:wheezy ---> e8d37d9e3476 Step 1 : RUN mkdir /tmp/foo ---> Running in 3d5d8b288cc2 ---> 9876aa270471 Removing intermediate container 3d5d8b288cc2 Step 2 : RUN fallocate -l 1G /tmp/foo/bar ---> Running in 6c797329ee43 ---> 3ebe08b36733 Removing intermediate container 6c797329ee43 Successfully built 3ebe08b36733
docker build
結果を見ると、Dockerがイメージをビルドするために何をしているのかがわかります。
-
FROM
ステートメントの値を使用して、Dockerはdebian:wheezy
イメージ(コンテナID:3d5d8b288cc2
)に基づいてコンテナを起動します - このコンテナ内で、Dockerはコマンド
mkdir /tmp/foo
- コンテナーは停止され、コミットされ(その結果、ID
9876aa270471
新しいイメージ9876aa270471
)、その後削除されます - Dockerは、今度は前のステップで保存されたイメージから別のコンテナーを起動します(このコンテナーのIDは
6c797329ee43
) - 実行中のコンテナー内で、Dockerはコマンド
fallocate -l 1G /tmp/foo/bar
- コンテナは停止され、コミットされ(その結果、ID
3ebe08b36733
新しいイメージ3ebe08b36733
)、削除されます
docker images --tree
実行して最終結果を確認できます(残念ながら、
--tree
フラグ
--tree
古く、今後のリリースで削除される可能性が高いです)。
$ docker images --tree Warning: '--tree' is deprecated, it will be removed soon. See usage. └─511136ea3c5a Virtual Size: 0 B Tags: scratch:latest └─59e359cb35ef Virtual Size: 85.18 MB └─e8d37d9e3476 Virtual Size: 85.18 MB Tags: debian:wheezy └─9876aa270471 Virtual Size: 85.18 MB └─3ebe08b36733 Virtual Size: 1.159 GB Tags: sample:latest
ここで、
debian:wheezy
としてマークされた画像を見ることができます。その後、前述の2つのコンテナーがあります(Dockerfileの各命令に1つ)。
レイヤーと画像については、それらが異なるものであるかのようにしばしば話します。 しかし、実際には、各レイヤーは画像であり、画像レイヤーは他の画像の単なるコレクションです。
私たちと同じように:
名前のないレイヤーの1つを簡単に起動できます。docker run -it sample:latest /bin/bash
docker run -it 9876aa270471 /bin/bash
それと別の両方-どのコンテナを開始できるかに基づいたイメージ。 唯一の違いは、最初の名前が付けられ、2番目の名前が付けられないことです。 任意のレイヤーからコンテナを実行するこの機能は、Dockerfileをデバッグするときに非常に役立ちます。
画像サイズ
画像が他の画像のコレクションにすぎないことを知っていると、明らかな結論に達することができます。画像のサイズは、それを構成する画像のサイズの合計に等しくなります。
docker history
出力を見てみましょう:
$ docker history sample IMAGE CREATED CREATED BY SIZE 3ebe08b36733 3 minutes ago /bin/sh -c fallocate -l 1G /tmp/foo/bar 1.074 GB 9876aa270471 3 minutes ago /bin/sh -c mkdir /tmp/foo 0 B e8d37d9e3476 4 days ago /bin/sh -c #(nop) CMD [/bin/bash] 0 B 59e359cb35ef 4 days ago /bin/sh -c #(nop) ADD file:1e2ba3d9379f 85.18 MB 511136ea3c5a 13 months ago 0 B
sample
イメージのすべてのレイヤーと、その作成とサイズにつながったコマンドを見ることができます(
docker images --tree
docker history
内のレイヤーの順序は、
docker images --tree
表示される順序とは逆です)。
イメージにとって意味のあることを行う命令は、
ADD
命令(
debian:wheezy
から継承)と
fallocate
コマンドの2つだけです。
イメージをtarアーカイブに保存し、重みがどのようになるかを見てみましょう。
$ docker save sample > sample.tar $ ls -lh sample.tar -rw-r--r-- 1 core core 1.1G Jul 26 02:35 sample.tar
この方法で画像をtarファイルに保存すると、各レイヤーに関するさまざまなメタデータもそこに配置されるため、最終的なサイズはすべてのレイヤーのサイズの合計よりもわずかに大きくなります。
Dockerfileに別の命令を追加します。
FROM debian:wheezy RUN mkdir /tmp/foo RUN fallocate -l 1G /tmp/foo/bar RUN rm /tmp/foo/bar
新しい命令は、その作成
fallocate
直後にファイルを削除します。
更新されたDockerfileに対してdocker
docker build
を実行し、履歴を再度確認すると、次のように表示されます。
$ docker history sample IMAGE CREATED CREATED BY SIZE 9d9bdb929b00 8 seconds ago /bin/sh -c rm /tmp/foo/bar 0 B 3ebe08b36733 24 minutes ago /bin/sh -c fallocate -l 1G /tmp/foo/bar 1.074 GB 9876aa270471 24 minutes ago /bin/sh -c mkdir /tmp/foo 0 B e8d37d9e3476 4 days ago /bin/sh -c #(nop) CMD [/bin/bash] 0 B 59e359cb35ef 4 days ago /bin/sh -c #(nop) ADD file:1e2ba3d9379f 85.18 MB 511136ea3c5a 13 months ago 0 B
rm
呼び出しは新しいレイヤー(0バイト)を追加しましたが、他のすべては同じままであることに注意してください。 更新された画像を保存すると、サイズがあまり変更されていないことがわかります(追加されたレイヤーのメタデータによるわずかな違いがあります)。
$ docker save sample > sample.tar $ ls -lh sample.tar -rw-r--r-- 1 core core 1.1G Jul 26 02:55 sample.tar
このイメージに対して
docker run
を呼び出して
/tmp/foo
を調べると、空であることがわかります(最終的にはファイルが削除されました)。 ただし、Dockerfileは1 GBのファイルを含むレイヤーを生成したため、イメージの不可欠な部分になりました。
Dockerfile内の追加の各命令は、全体的な画像サイズのみを増加させます。
もちろん、この例は手に負えません。 しかし、画像を縮小する方法を探す際には、画像が画像を構成するレイヤーの合計であるという事実を理解することが重要です。 以下に、これを行ういくつかの方法を説明します。
ベースを選択してください
かなり明白なアドバイス。 ただし、基底の選択は、画像の最終サイズに大きく影響する可能性があります。 たとえば、人気のあるベース画像とそのサイズのリストは次のとおりです。
$ docker images REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE scratch latest 511136ea3c5a 13 months ago 0 B busybox latest a9eb17255234 7 weeks ago 2.433 MB debian latest e8d37d9e3476 4 days ago 85.18 MB ubuntu latest ba5877dc9bec 4 days ago 192.7 MB centos latest 1a7dc42f78ba 2 weeks ago 236.4 MB fedora latest 88b42ffd1f7c 10 days ago 373.7 MB
私のチームと私は、
ubuntu
をベースとして使用していました。これは主に、ほとんどの人がすでにそれに慣れていたためです。 しかし、
debian
で少し遊んだ後、私たちはニーズを完全に満たし、同時に100 MB以上のスペースを節約できるという結論に達しました。
便利なデータベースのリストは異なる場合があり、ニーズによって異なりますが、必ず確認してください。 BusyBoxで十分な場合にUbuntuを使用すると、膨大なスペースが無駄になります。
Dockerリポジトリに表示される画像のサイズを教えてください。 しかし、残念ながら、サイズを調べるには、画像をダウンロードする必要があります。
ベースを再利用する
レイヤーアプローチの利点の1つは、異なるイメージ間でレイヤーを再利用できることです。 以下の例は、
debian:wheezy
をベースとして使用した3つの画像を示しています。
$ docker images --tree Warning: '--tree' is deprecated, it will be removed soon. See usage. └─511136ea3c5a Virtual Size: 0 B Tags: scratch:latest └─e8d37d9e3476 Virtual Size: 85.18 MB Tags: debian:wheezy ├─22a0de5ea279 Virtual Size: 85.18 MB │ └─057ac524d834 Virtual Size: 85.18 MB │ └─bd30825f7522 Virtual Size: 106.2 MB Tags: creeper:latest ├─d689af903018 Virtual Size: 85.18 MB │ └─bcf6f6a90302 Virtual Size: 85.18 MB │ └─ffab3863d257 Virtual Size: 95.67 MB Tags: enderman:latest └─9876aa270471 Virtual Size: 85.18 MB └─3ebe08b36733 Virtual Size: 1.159 GB └─9d9bdb929b00 Virtual Size: 1.159 GB Tags: sample:latest
それらはそれぞれ
debian:wheezy
上に構築されていますが、これらはDebianの3つのコピーではありません。 コピーする代わりに、各イメージにはDebianレイヤーのインスタンスへのリンクが含まれています(私が
docker images --tree
好きな理由の1つは、異なるレイヤー間の接続を明確に示していることです)。
つまり、
debian:wheezy
をダウンロードすると、これらのレイヤーを再度ドラッグする必要がなくなり、画像で使用される各ビットが1回だけスペースを占有します。
そのため、さまざまな画像に共通のベースを使用して、かなりのスペースとインターネットトラフィックを節約できます。
チームをグループ化する
上記の例では、ファイルを作成し、すぐに削除します。 状況は、はるかにフェッチされますが、画像を構築するときによく似たことが起こります。 もっと現実的なものを見てみましょう:
FROM debian:wheezy WORKDIR /tmp RUN wget -nv RUN tar -xvf someutility-v1.0.0.tar.gz RUN mv /tmp/someutility-v1.0.0/someutil /usr/bin/someutil RUN rm -rf /tmp/someutility-v1.0.0 RUN rm /tmp/someutility-v1.0.0.tar.gz
私たちはtarアーカイブをダウンロードし、解凍し、何かを移動し、自分でクリーンアップします。
前に見たように、これらの各命令は個別のレイヤーを作成します。 アーカイブおよび抽出されたファイルを削除するという事実にもかかわらず、それらはまだイメージの一部のままです。
$ docker history some utility IMAGE CREATED CREATED BY SIZE 33f4a99 16 seconds ago /bin/sh -c rm /tmp/someutility-v1.0.0.tar.gz 0 B fec7b5e 17 seconds ago /bin/sh -c rm -rf /tmp/someutility-v1.0.0 0 B 0851974 18 seconds ago /bin/sh -c mv /tmp/someutility-v1.0.0/someuti 12.21 MB 5b6b996 19 seconds ago /bin/sh -c tar -xvf someutility-v1.0.0.tar.gz 99.91 MB 0eebad5 20 seconds ago /bin/sh -c wget -nv http://centurylinklabs.com 55.34 MB d6798fc 8 minutes ago /bin/sh -c #(nop) WORKDIR /tmp 0 B e8d37d9 5 days ago /bin/sh -c #(nop) CMD [/bin/bash] 0 B 59e359c 5 days ago /bin/sh -c #(nop) ADD file:1e2ba3d9379f7685a1 85.18 MB 511136e 13 months ago 0 B
wget
実行すると、55 MBのレイヤーが表示され、99 MBのレイヤーにアーカイブが展開されます。 これらのファイルは必要ありません。つまり、単に150 MB以上を無駄にするだけです。
これを修正するには、Dockerfileを少しリファクタリングします。
FROM debian:wheezy WORKDIR /tmp RUN wget -nv && \ tar -xvf someutility-v1.0.0.tar.gz && \ mv /tmp/someutility-v1.0.0/someutil /usr/bin/someutil && \ rm -rf /tmp/someutility-v1.0.0 && \ rm /tmp/someutility-v1.0.0.tar.gz
各コマンドを個別の
RUN
命令で実行する代わりに、
&&
演算子を使用してそれらをグループ化しました。 そして、Dockerfileが少し読みにくくなっていますが、レイヤーがコミットする前にtarballと抽出されたディレクトリを削除することができます。
結果は次のとおりです。
$ docker history some utility IMAGE CREATED CREATED BY SIZE 8216b5f 7 seconds ago /bin/sh -c wget -nv http://centurylinklabs.com 12.21 MB d6798fc 17 minutes ago /bin/sh -c #(nop) WORKDIR /tmp 0 B e8d37d9 5 days ago /bin/sh -c #(nop) CMD [/bin/bash] 0 B 59e359c 5 days ago /bin/sh -c #(nop) ADD file:1e2ba3d9379f7685a1 85.18 MB 511136e 13 months ago 0 B
最終的に同じイメージが得られ、いくつかの余分なレイヤーを取り除き、150 MBの空き容量を節約することに注意してください。
Dockerfileのすべてのコマンドを1行で早急に書き直すことはお勧めしません。 ただし、ファイルを作成してから削除したときに似たような状況があることに気付いた場合は、いくつかの指示を1つにまとめると、イメージサイズを最小限に抑えることができます。
画像を「スラム」
上記の戦略はすべて、独自のイメージを作成している、または少なくともDockerfileにアクセスできるという前提に基づいています。 ただし、他の人によって作成されたイメージがあり、それを少し簡単にしたい場合は状況が可能です。
この場合、コンテナを作成するとすべてのレイヤーが1つにマージされるという事実を利用できます。
sample
画像(
fallocate
と
rm
)に戻って実行してみましょう:
$ docker run -d sample 7423d238b754e6a2c5294aab7b185f80be2457ee36de22795685b19ff1cf03ec
実際、イメージは何もしないので、すぐに作業を終了します。 これにより、イメージのすべてのレイヤーをマージした結果、停止したコンテナーが得られます(コンテナーIDを表示するためだけに
-d
フラグを使用しました)。
出力を
docker import
にリダイレクトしてこのコンテナーをエクスポートすると、イメージに戻すことができます。
$ docker export 7423d238b | docker import - sample:flat 3995a1f00b91efb016250ca6acc31aaf5d621c6adaf84664a66b7a4594f695eb $ docker history sample:flat IMAGE CREATED CREATED BY SIZE 3995a1f00b91 12 seconds ago 85.18 MB
新しい
sample:flat
ストーリー
sample:flat
画像には、85 MBのレイヤーが1つしか表示されていないことに注意してください。ギガバイトファイルを含むレイヤーはなくなりました。
また、これは非常に軽快なトリックですが、重大な欠点があることに注意する必要があります。
- すべてのレイヤーをマージすると、レイヤーを異なる画像で共有するという前述の利点が失われます。
sample:flat
イメージには、debian:wheezy
組み込みコピーが含まれています。 - 通常、画像とともに保存されているすべてのメタデータは、起動/エクスポート/インポートプロセス中に失われます。 開いているポート、環境変数、デフォルトのコマンド-元のイメージで宣言できるものはすべて失われます。
したがって、すべての画像を「折りたたむ」ように急ぐことは絶対にお勧めしません。 しかし、これは便利な場合があります。他の人の画像を最適化しようとしている場合、または自分の画像をどれだけ絞り込めるかを知りたい場合です。
- ソース: Dockerイメージの最適化