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 実装)

推薦する

ウェブサイトアイコンを追加するにはどうすればいいですか?

最初のステップは、アイコン作成ソフトウェアを準備することです。まず、いわゆるアイコンは拡張子 .ic...

LinuxにKafkaをインストールする

目次1.1 前提条件としてのJava環境1.2 Zookeeperのインストールと設定1.3 Kaf...

AES_ENCRYPT() と AES_DECRYPT() を使用して MySQL を暗号化および復号化する正しい方法の例

序文最近、仕事でAES_ENCRYPT()関数を使用してプレーンテキストを暗号化し、MySQL に保...

テーブルを使用する場合と CSS を使用する場合 (経験の共有)

TW のメインテキスト ページは、以前は小さなモニターと低解像度のユーザーを考慮して幅が 850 ピ...

JavaScript配列をツリー構造に変換する方法

1. 需要バックエンドは、フロントエンドがツリー構造(重複データなし)に変換するためのデータを提供し...

MySQL 8.0.19 winx64 インストールチュートリアルと Windows 10 での初期パスワードの変更

この記事では、参考までにMySQL 8.0.19 winx64のインストールチュートリアルを紹介しま...

nginx リクエスト ヘッダー データ読み取りプロセスの詳細な説明

前回の記事では、nginx がリクエスト ラインのデータを読み取って、リクエスト ラインを解析する方...

JDBC を使用して Mysql データベースに接続する際に発生する可能性のある問題の概要

まず、いくつかの概念を明確にします。 JDBC: Javaデータベース接続、Oricalによって規定...

MySQLの詳細な分析で使用法と結果を説明します

序文日常業務では、実行に時間のかかる SQL ステートメントを記録するために、スロー クエリを実行す...

CentOS7でFTPサーバーを設定する方法

FTP は主にファイル転送に使用され、Linux では vsftpd で実装されるのが一般的です。F...

js配列forEachインスタンスの詳細な使用方法

1. forEach() は map() に似ています。これも渡された関数に各要素を順番に適用します...

ウィンドウ環境設定Mysql 5.7.21 windowx64.zip無料インストール版チュートリアル詳細説明

1. 公式サイトのmysqlダウンロードページからmysql-5.7.21-windowx64.zi...

MySQL クエリのソートとクエリ集計関数の使用法の分析

この記事では、例を使用して、MySQL クエリのソート関数とクエリ集計関数の使用方法を説明します。ご...

HTML iframe で親ページと子ページ間の双方向メッセージングを実装する例

ある日、リーダーはメイン ページに iframe を埋め込み、親ページと子ページ間で双方向にメッセー...

MySQLデータベースの基礎知識

目次1. データベースを理解する1.1 データベースとデータ構造の関係1.2 なぜデータベースが必要...