単一のソリューションではなく、さまざまな手法が関係しています。次のいくつかの操作を行うことをお勧めします。
最初に、再利用のために画像レイヤーを最適化します。頻繁に変更する手順を後でDockerfileに入れて、以前のビルドから初期のレイヤーがキャッシュされる可能性を増やします。再利用されたレイヤーは、でより多くのディスク容量として表示されdocker image ls
ますが、基礎となるファイルシステムを調べると、各レイヤーのコピーが1つだけディスクに保存されます。つまり、それぞれが2 GBの3つのイメージですが、ビルドの最後のいくつかのレイヤーで50 MBしか異なるものではないため、2.1 GBのディスク容量しか占有しません。再利用された各レイヤーをダブルカウントします。
レイヤーの再利用により、ビルドの依存関係がめったに変更されないイメージが表示されるのは、コードをコピーする前にそれらを最初にインストールするためです。次のようなパターンを持つPythonの例を参照してください。
FROM python
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
# note how the code is copied only after the pip install
# since code changes but requirements.txt doesn't
COPY . .
CMD ["gunicorn", "app:app"]
最小限の基本画像を選択します。あなたは、人々が行くから見るのはこのためですubuntu
へdebian:slim
(スリムな変異体は、より少ないツールで出荷し、小さい)、あるいはalpine
。これにより、開始点のサイズが小さくなり、ベースイメージの新しいバージョンを常にプルする場合に非常に役立ちます。ただし、ベースイメージがめったに変更されない場合、レイヤーを再利用すると、最小限のベースイメージの利点の多くが失われます。
選択できる最小のベースイメージはですscratch
。これは、シェルやライブラリではなく、静的にコンパイルされたバイナリでのみ有用です。それ以外の場合は、必要のない多くのツールを使用せずに、必要なツールを含むベースイメージを選択します。
次に、ファイルを変更または削除するステップは、そのファイルを作成する前のステップと組み合わせる必要があります。そうしないと、ファイル許可の変更などでもコピーオンライトを使用する階層化ファイルシステムは、前のレイヤーに元のファイルを保持し、ファイルを削除しても画像サイズは縮小しません。これが、rm
コマンドが結果のディスク容量に影響を与えない理由です。代わりに、次のようにコマンドを連鎖できます。
RUN apt-get update \
&& apt-get install -y \
a-package \
wget \
&& ... \
&& apt-get purge -y wget \
&& rm -r a-build-dir \
&& apt-get purge -y a-package
コマンドチェーンの過剰使用は、前提条件が変更されるたびに同じツールセットを再インストールする必要があるため(たとえば、wgetでプルされるコード)、ビルドの速度が低下する可能性があることに注意してください。より良い代替案については、以下のマルチステージを参照してください。
作成したイメージで必要のないファイルは、作成するステップで削除する必要があります。これには、パッケージキャッシュ、ログ、マニュアルページなどが含まれます。各レイヤーで作成されているファイルを見つけるには、wagoodman / diveなどのツールを使用できます(完全にルートアクセスで実行されるため、私は個人的に吟味しておらず、注意を表明します)または、中間コンテナを削除せずにDockerイメージを構築し、次のようにして差分を表示できます:
# first create and leave containers from any RUN step using options on build
docker image build --rm=false --no-cache -t image_name .
# review which layers use an unexpectedly large amount of space
docker image history image_name
# list all containers, particularly the exited ones from above
docker container ps -a
# examine any of those containers
docker container diff ${container_id}
# ... repeat the diff for other build steps
# then cleanup exited containers
docker container prune
それらの中間のコンテナのそれぞれと、diffはファイルを追加、変更、またはそのステップで削除されているものが表示されます(これらはで表示されA
、C
またはD
各ファイル名の前に)。diffが示しているのは、コンテナ固有の読み取り/書き込みファイルシステムです。これは、コピーオンライトを使用してイメージ状態からコンテナによって変更されたファイルです。
画像サイズを縮小する最良の方法は、出荷された画像からコンパイラなどの不要なコンポーネントを削除することです。そのために、マルチステージビルドを使用すると、1つのステージでコンパイルし、結果のアーティファクトのみをビルドステージから、アプリケーションの実行に最低限必要なランタイムイメージにコピーできます。これにより、結果のイメージとともに出荷されないため、ビルドステップを最適化する必要がなくなります。
FROM debian:9 as build
# still chain update with install to prevent stale cache issues
RUN apt-get update \
&& apt-get install -y \
a-package \
wget \
RUN ... # perform any download/compile steps
FROM debian:9-slim as release
COPY --from=build /usr/local/bin/app /usr/local/bin/app
CMD [ "/usr/local/bin/app" ]
マルチステージは、静的にコンパイルされたバイナリで理想的です。これをベースイメージとしてスクラッチで実行したり、JDKのようなコンパイル環境からJREのようなランタイムに移行したりできます。これは、高速なビルドを維持しながら、イメージサイズを劇的に削減する最も簡単な方法です。以前のステップで作成されたファイルを変更または削除するステップがある場合、リリースステージでステップのチェーンを実行できますが、ほとんどの場合、COPY
別のステージからリリースステージを以前のビルドステージで経験したレイヤーの膨張から分離します。
レイヤーの再利用を排除することで1つの画像のサイズを縮小するため、画像を潰すことはお勧めしません。つまり、同じイメージの今後のビルドでは、更新を送信するためにより多くのディスクとネットワークトラフィックが必要になります。最初の例に戻ると、スカッシュによりイメージが2 GBから1 GBに削減される場合がありますが、3つのイメージではなく2.1 GBの代わりに3 GBが使用される場合があります。
2.37
vs.1.47 GB