Dockerイメージのサイズを縮小する6つの方法

Dockerイメージのサイズを縮小する6つの方法

2017 年に Vulhub に取り組み始めてから、私は厄介な問題に悩まされてきました。Dockerfile を書くときに、 docker buildによって生成されるイメージのサイズを縮小するにはどうすればよいでしょうか?この記事では、画像のサイズを縮小するために私が使用した 6 つの方法をまとめます。

1. Alpine Linuxの使用

Alpine Linux は、BusyBox と Musl Libc をベースにした Linux ディストリビューションです。最大の利点はサイズが小さいことです。純粋なベースの Alpine Docker イメージは、圧縮するとわずか 2.67 MB になります。

多くの公式 Docker イメージには、PHP などの Alpine バージョンがあります。

比較してみると、アルパインバージョンの画像の大きさは通常バージョンの約1/5であることがわかります。

しかし、Docker Hub では、Mysql や PHP-Apache など、ほとんどのイメージに Alpine バージョンがありません。これらの環境をベースに開発する必要がある場合は、自分で Alpine バージョンを作成するか、サードパーティのイメージを探す必要があります。

さらに、Alpine のもう 1 つの欠点は、従来の glibc の代わりに Musl Libc を使用していることです。ソフトウェアをコンパイルするときに、予期しない問題が発生する可能性があり、多くの無駄な時間を費やすことになります。

2. 最小限の依存関係のみをインストールする

apt-get、yum、apk などのパッケージ マネージャーは、イメージをコンパイルするときに使用する必要があるツールです。純粋な Docker ベースのイメージには通常、wget、curl、git、gcc などのツールがないため、手動でインストールする必要があります。

apt を例に挙げてみましょう。ソフトウェアをインストールするときに、apt-get はオプション--no-install-recommendsを指定できます。このパラメータを指定すると、必須でない依存関係の一部は一緒にインストールされなくなります。たとえば、wget をインストールするときにこのオプションを追加すると、インストールされるパッケージの数は 6 から 3 に減ります。

これにより、イメージのサイズはある程度縮小されますが、その副作用として、対象ソフトウェアの一部の機能が不足する可能性があります。

たとえば、この時点で wget はサーバー証明書の信頼性を検証できず、コマンド エラーが発生します。

したがって、私たちの一般的なやり方は、apt を使用するときに--no-install-recommendsを追加し、後でエラーが発生した場合にすぐに修正することです。 wget のような既知の問題は事前に予測して対処できます。

apt-get install --no-install-recommends wget ca-certificates

3. aptの混乱を解消する

一部のツールはコンパイル段階でのみ使用されます。貴重なイメージ容量を占有させたくありません。イメージのコンパイルが完了したら、これらの中間依存関係を削除できます。

apt を例に挙げてみましょう。apt を使用した後は、次の操作を行う必要があります。

  • 不要な依存関係を削除します: apt-get pruge --autoremove ...
  • ローカルパッケージリストを削除します: rm -rf /var/lib/apt/lists/*

このプロセスでは、どの依存関係が「不要」なのかという非常に難しい問題に遭遇します。

たとえば、PHP をコンパイルする場合、wget、libxml、gcc の 3 つのツールを使用できます。 PHP をコンパイルする前に、これら 3 つのツールをインストールする必要があります。しかし、コンパイルが完了した後、wget と gcc はアンインストールできますが、libxml はアンインストールできません。

その理由は、libxml は PHP が依存するダイナミック リンク ライブラリだからです。これをアンインストールすると、共有リンク ライブラリが見つからないというエラーが発生します。

ルート@8eab53da8d5b:/#php -v
php: 共有ライブラリのロード中にエラーが発生しました: libxml2.so.2: 共有オブジェクトファイルを開けません: そのようなファイルまたはディレクトリはありません

では、「共有リンク ライブラリ」ではない依存関係のみを自動的に検出して削除する、より便利な方法はありますか?

もちろんあります。もっと簡単な方法は、新しくコンパイルされた実行可能ファイルを走査し、ldd コマンドを使用してそれが依存する共有リンク ライブラリ ファイル名を一覧表示し、ソース内でこのファイル名に対応するパッケージ名を検索することです。

これらのパッケージはすべて、PHP が依存するダイナミック リンク ライブラリです。次に、 apt-markを使用してこれらのパッケージを「手動でインストールされたパッケージ」として宣言し、 apt purgeによって自動的にアンインストールされるのを防ぎます。

その後、残りの未使用のパッケージを自動的にアンインストールできます。完全なシェル スクリプトは次のとおりです。

/usr/local を見つけます -type f -executable -exec ldd '{}' ';' \
 | awk '/=>/ { $(NF-1) を印刷 }' \
 | ソート -u \
 | xargs -r dpkg-query --search \
 | カット -d: -f1 \
 | ソート -u \
 | xargs -r apt-mark マニュアル \
; \
apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false;

4. 中間依存関係を1ステップでインストールおよびアンインストールする

Docker イメージは、レイヤーで構成された階層化されたケーキです。docker docker history <image name>コマンドを使用すると、イメージがどのレイヤーで構成されているか、および各レイヤーのサイズを確認できます。

Dockerfile の場合、後者のレイヤーが前のレイヤーに保存されたファイルを削除したとしても、これらのレイヤーのデータはイメージに保存されます。

たとえば、次の Dockerfile があります。

アルパインより:3.12
実行 truncate -s 50M /sample.dat
rm -rf /sample.dat を実行します。

このコンパイルされたイメージの大きさは 58 MB です。

比較すると、通常の alpine:3.12 はわずか 5.57 MB です。つまり、 /sample.datファイルを削除しても、最終イメージにはそのようなコンテンツは存在しませんが、イメージ履歴には常に残ります。

そのため、前述の「中間依存関係」を削除する際には、確実に領域を解放するために、インストール、使用、アンインストールの 3 つの部分を 1 つのステップで記述する必要があります。例えば:

debian:buster より

apt-get update を実行する\
 && apt-get install gcc \
 && gcc ... \
 && apt-get purge --autoremove gcc \
 && rm -rf /var/lib/apt/lists/*

5. 多段階コンパイル

Docker バージョン 17.05 以降では、マルチステージ ビルドの概念が導入され、上記のすべての操作が大幅に簡素化されます。

簡単に言えば、マルチステージ ビルドを使用すると、Docker イメージのコンパイルを複数の「ステージ」に分割できます。たとえば、一般的なソフトウェアのコンパイルの場合、コンパイル段階を分離し、ソフトウェアのコンパイルが完了した後にバイナリファイルを新しいベースイメージに直接コピーすることができます。これの最大の利点は、2 番目のイメージにはコンパイル段階で使用される中間依存関係が含まれなくなり、クリーンで明確になることです。

最も一般的な Java プロジェクトを例にとると、Jar パッケージをコンパイルするときには JDK や Maven などのツールを使用する必要がありますが、実際の運用段階では JRE 環境のみが必要になります。 2 つのイメージ ( maven:3-openjdk-8openjdk:8-jreのサイズを比較してみましょう。

その差は2倍以上です。

Vulhub の Shiro 1.2.4 環境を例にとると、Dockerfile には 2 つのFROMコマンドがあります。

maven:3-jdk-8 AS ビルダーから

ラベル MAINTAINER="phithon <[email protected]>"

./code/ /usr/src/ をコピー

ワークディレクトリ /usr/src

実行cd /usr/src; \
 mvn -U クリーンパッケージ -Dmaven.test.skip=true

openjdk:8u102-jre から

ラベル MAINTAINER="phithon <[email protected]>"

コピー --from=builder /usr/src/target/shirodemo-1.0-SNAPSHOT.jar /shirodemo-1.0-SNAPSHOT.jar

エクスポーズ8080

CMD ["java", "-jar", "/shirodemo-1.0-SNAPSHOT.jar"]

最初のFROMmaven:3-jdk-8環境に入り、Maven を使用してソース コードをコンパイルするために使用されます。2 番目のFROM 、より小さいopenjdk:8u102-jre環境に入り、 COPY --from=構文を使用して、前のステージのコンパイル結果から jar ファイルを jre 環境にコピーします。

最終的に、マシン上に 2 つのイメージが残ります。1 つはビルダーで、もう 1 つは最終的に必要となる shiro 1.2.4 環境です。後者は他のユーザーが独立して使用できますが、前者は直接削除できます。

ユーザーは、ソフトウェアをコンパイルしてイメージを小さくするときに、中間依存関係をどのように削除するかを心配する必要がなくなりました。いずれにしても、最初の段階で使用された依存関係は、正式な本番環境に残されません。

しかし、マルチステージコンパイルでは、ダイナミックリンクライブラリに依存するという前述の問題が依然として残ります。コンパイル結果をコピーするときに実行可能ファイルのみをコピーすると、新しい環境で実行したときに共有リンクライブラリが見つからないというエラーが依然として発生します。そのため、個人的には、マルチステージコンパイルは、Java や golang などのクロスプラットフォームまたは静的コンパイルが可能な言語にのみ適しており、C や Python などの依存関係が多いプロジェクトにはまだ適していないと感じています。

6. 画像のスリムバージョンを使用する

注意深い学生なら、公式の Docker Debian イメージに、デフォルト バージョンの 2 倍以上のサイズのスリム バージョンがあることに気付いたかもしれません。

slim の中国語の意味は「スリム」です。名前が示すように、 debian:stretch-slimコンテナで使用されない man ドキュメントなどの多くのファイルを削除するため、実際にははるかにスリムです。

一部の上位レベルのイメージは、Python など、Debian のスリム バージョンに基づいて作成されています。 Python プロジェクトを開発する場合は、 python:slimベースイメージを使用できます。

まとめると、6 つの方法は互いに影響を及ぼさず、同時に使用できます。しかし、5番目の多段階コンパイルは、将来的には主流の方法になるでしょう。

以上で、Docker イメージのサイズを小さくする 6 つの方法についての説明は終了です。Docker イメージのサイズを小さくする方法の詳細については、123WORDPRESS.COM の過去の記事を検索するか、以下の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。

以下もご興味があるかもしれません:
  • Alibaba Cloud イメージリポジトリの Docker 構成変更の実装
  • SpringBootでDockerイメージを構築する3つの方法の詳しい説明
  • Springboot docker jenkins でイメージを自動的にデプロイしてアップロードするための詳細な手順
  • Dockerイメージ内のファイルを表示する方法
  • Dockerとイメージの操作方法

<<:  MySQL NULLがピットを引き起こした

>>:  階段を転がす特殊効果を実現する JavaScript (jQuery 実装)

推薦する

VPSサーバーでよく使われるパフォーマンステストスクリプトの概要

これは、VPS サーバー用の一般的なワンクリック パフォーマンス テスト スクリプトです。マシンの構...

ページのキャッシュを防ぐソリューション

解決: <head> に次のコードを追加します。コードをコピーコードは次のとおりです。 ...

mysql 5.7.17 winx64.zip インストールと設定方法のグラフィックチュートリアル

はじめに: Windows 10 を再インストールし、同時にファイルを整理しました。しかし、MySQ...

Vueは州、都市、地区のカスケード選択を実現します

最近、省、市、地区のカスケード選択効果を実装する必要があります。省、市、地区のデータはすべてローカル...

Webフロントエンド開発におけるエラーを見つけるための基本的な考え方

WEB開発は主に2つのインタラクション(B/Sデータ)から構成されますブラウザ: 1html、css...

Dockerコンテナのホスト間通信におけるダイレクトルーティングの詳細な説明

概要Docker 自体の現在のデフォルト ネットワークについては、単一ホスト上の異なる Docker...

17 個の JavaScript ワンライナー

目次1. DOMとBOM関連1. 要素にフォーカスがあるかどうかを確認する2. 要素の兄弟ノードをす...

Centos8でdockerがインストールできない問題の解決方法

問題 [root@zh ~]# [root@zh ~]# [root@zh ~]# yum -y d...

MySQLが大量のデータを処理する際にクエリ速度を最適化するいくつかの方法

実際に参加したプロジェクトでは、MySQL テーブルのデータ量が数百万に達すると、通常の SQL ク...

Vue の foreach 配列と js の traversal 配列の書き方の説明

Vue foreach配列を記述し、jsで配列をトラバースする方法シナリオVueでAxiosを使用し...

CentOS 8 VMware 仮想マシンがインターネットにアクセスするための静的 IP ネットワーク カードの設定の詳細な説明

最初のステップ: VMwareで、「編集」-「仮想ネットワークエディタ」をクリックします。下図に示す...

問題におけるJS演算子の調査

問題は、誰もが「メモリ リーク」について知っていることです。一般的なシナリオはいくつかあります。クロ...

MySQL でテーブルデータをクリアする 2 つの方法とその違い

MySQL でデータを削除するには 2 つの方法があります。切り詰めは大まかな伐採の一種である削除は...

MySQL でシンプルな検索エンジンを実装するためのサンプルコード

目次序文導入ngram全文パーサー全文インデックスを作成する検索方法1. 自然言語検索(自然言語モー...

CSS3で線形グラデーションを実装するためのコードの詳細な説明

序文デモでは古いバージョンのブラウザのグラデーションが実装されています[IE9-]。 IE9 より前...