小さな Docker コンテナを構築する 5 つの方法

小さな Docker コンテナを構築する 5 つの方法

[51CTO.com クイック翻訳] この記事では、Linux コンテナのサイズを最適化し、小さなイメージを構築する 5 つの方法について説明します。

数年前、Docker の爆発的な発展により、コンテナとコンテナ イメージの概念が一般に公開されました。 Linux コンテナは以前から存在していましたが、Docker はユーザーフレンドリーなコマンドライン インターフェイスとわかりやすい Dockerfile 形式により、イメージ構築のハードルを大幅に下げました。しかし、始める際の難しさは軽減されたとはいえ、強力でありながらコンパクトなコンテナ イメージを構築するのに役立つ微妙な違いやテクニックがまだいくつかあることは認めざるを得ません。

[[237837]]

第一レベル: コンテンツのクリーンアップ

以下にリストされている例のいくつかは、従来のサーバーと同様のクリーンアップ方法を使用していますが、特定の要件はより厳格です。イメージのサイズは高速移動にとって非常に重要であり、ディスク上にデータの不必要なコピーを複数保存すると、間違いなく多くのリソースが浪費されます。そのため、コンテナイメージの「サイズ」をできるだけ制御する技術が必要になります。

ストレージスペースを節約するために、画像からキャッシュ ファイルを削除する方法を見てみましょう。まず、dnf を使用してメタデータありとメタデータなしで Nginx をインストールし、2 つのイメージ サイズの違いを確認します。次に、yum を使用してキャッシュをクリーンアップします。

  1. #キャッシュ付きDockerfile
  2. fedora:28より
  3. LABEL メンテナー Chris Collins <collins.christopher@gmail.com>
  4.  
  5. dnf install -y nginxを実行します
  6.  
  7. -----  
  8.  
  9. # キャッシュなしの Dockerfile
  10. fedora:28より
  11. LABEL メンテナー Chris Collins <collins.christopher@gmail.com>
  12.  
  13. dnf install -y nginx \ を実行します。
  14. && dnfすべてをクリーンアップ\
  15. && rm -rf /var/cache/yum
  16.  
  17. -----  
  18.  
  19. [chris@krang] $ docker build -t cache -f Dockerfile 。
  20. [chris@krang] $ docker images --format "{{.Repository}}: {{.Size}}"  
  21. |ヘッド-n 1
  22. キャッシュ: 464 MB
  23.  
  24. [chris@krang] $ docker build -t no -cache -f Dockerfile-wo-cache 。
  25. [chris@krang] $ docker images --format "{{.Repository}}: {{.Size}}" |ヘッド-n 1  
  26. キャッシュなし: 271 MB

ご覧のとおり、両者の音量には大きな違いがあります。 dnf キャッシュを含むバージョンは、メタデータとキャッシュのないイメージのほぼ 2 倍のサイズになります。実際、パッケージ マネージャーのキャッシュ、Ruby gem の一時ファイル、nodejs のキャッシュ、さらにはダウンロードしたソース コードの tarball も、クリーンアップの主な候補です。

階層化 - 潜在的な問題

残念ながら(または後で説明しますが、幸運なことに)、コンテナは階層化されて使用されるため、単に RUN rm -rf /var/cache/yum を Dockerfile に追加して終了することはできません。 Dockerfile 内の各命令はレイヤーに保存され、レイヤー間の変更は最終的に最上位レイヤーに適用されます。したがって、次のようなことを行ったとしても、

  1. dnf install -y nginxを実行します
  2. dnf clean all を実行 
  3. rm -rf /var/cache/yum を実行します。

…それでも、キャッシュをすべて含む 1 つのレイヤーと、イメージからキャッシュを「削除」する 2 つの中間レイヤーの 3 つのレイヤーが残ります。ただし、1 つのファイル システムを別のファイル システムの上にマウントする場合と同様に、キャッシュは実際にはまだ存在しています。ファイルはそこに存在しますが、表示したりアクセスしたりすることはできません。

前のセクションの例では、キャッシュのクリーンアップが、キャッシュを永続化する同じ Dockerfile 命令にリンクされていることに注意してください。

  1. dnf install -y nginx \ を実行します。
  2. && dnfすべてをクリーンアップ\
  3. && rm -rf /var/cache/yum

これは、最終的にイメージ内のレイヤーになる単一の命令です。この方法では、Docker キャッシュの一部が破棄されます。つまり、イメージの再構築には少し時間がかかりますが、キャッシュされたデータは最終イメージに残ります。良い妥協策として、関連するコマンド (hum install と hum clean all、またはソース tarball のダウンロード、抽出、削除など) を単純に連鎖させることで、最終イメージのサイズを大幅に削減しながら、Docker キャッシュを活用して開発を高速化し続けることができます。

ただし、ここでのレイヤーは、前述のレイヤーよりも微妙なものになります。イメージの各レイヤーには各レイヤーの特定の変更が記録されるため、追加されたファイルを除くすべてのファイルの変更が含まれます。たとえば、ファイルのモードを変更した場合でも、そのファイルのコピーを作成するために、画像に新しいレイヤーが表示されます。

たとえば、次の docker images 出力には、2 つのイメージに関連する情報が表示されます。最初の layer_test_1 は、ベース CentOS イメージに 1 GB のファイルを 1 つ追加することによって作成されました。 2 番目のイメージ layer_test_2 は layer_test_1 から直接作成されますが、1 GB ファイルのモードは chmod u+x コマンドを使用して変更されます。

  1. layer_test_2 最新 e11b5e58e2fc 7 秒前 2.35 GB
  2. layer_test_1 最新 6eca792a4ebe 2分前 1.27 GB

ご覧のとおり、新しいイメージは以前のイメージよりも 1 GB 以上大きくなっています。実際には、layer_test_1 は layer_test_2 の最初の 2 つのレイヤーのみを表していますが、2 番目のイメージにはさらに 1 GB のファイルが隠されています。イメージ構築プロセス中に、ファイルを削除、除去、または変更すると、この結果になります。

専用画像とフレキシブル画像

逸話:私たちが Ruby on Rails アプリケーションを大量に採用していたとき、同僚たちは徐々にコンテナを採用し始めていました。私たちの最初の仕事は、すべてのチームが使用できる Ruby ベースイメージの公式セットを作成することでした。簡単にするために、rebenv を使用して最新の 4 つの Ruby バージョンをイメージにインストールし、開発者が単一のバージョンを使用してすべてのアプリケーションをコンテナー イメージに移行できるようにしました。これにより、共同作業チーム間のすべての作業の基本をカバーする、非常に大規模でありながら柔軟性の高い (少なくとも私たちはそう考えています) 画像セットが効果的に作成されます。

しかし、結局それはすべて時間の無駄でした。特定のイメージの単一のリビジョンを維持することは比較的簡単に自動化できます。特定のイメージに対して特定のリビジョンを選択すると、重大な変更が導入される前に元のアプリケーションが次の要件に適さなくなったことを認識できるため、重大な損害を回避できます。さらに、大きすぎる画像もリソースを浪費します。異なる Ruby バージョンを分割すると、同じベースを共有する複数の画像セットが作成されます。サーバーに同時に保存する場合、複数のバージョンを含む巨大な画像と比較すると、占有する余分なスペースは実際にはそれほど多くありませんが、転送速度ははるかに速くなります。

これは、柔軟なイメージを構築することに意味がないと言っているわけではありません。私たちの場合、専用のイメージを作成することで、ストレージ スペースとメンテナンスの時間を節約できるだけでなく、各チームがそのメリットを享受しながら共有ベース イメージに必要な変更を加えることができるようになりました。

ゼロから始める: 空白の画像に必要なものを追加する

Dockerfile のユーザーフレンドリーさと使いやすさと同様に、完全なオペレーティング システムを必要とせずに、非常に柔軟な方法で、標準の Docker ベース イメージと同じくらい小さい、Docker 互換の小さなコンテナー イメージを作成できるツールが他にもあります。

Buildah については以前にも書きましたが、非常に柔軟性が高く、ホスト上のツールを使用して最初からイメージを作成したり、パッケージ化されたソフトウェアをインストールしたり、イメージの内容を変更したりできるため、もう一度言及します。さらに重要なのは、これらのツールは常に画像の外側に存在するため、画像自体のサイズは増加しないということです。

Buildah は docker build コマンドを置き換えます。これを使用すると、コンテナ イメージのファイル システムをホストにマウントし、ホスト内のツールを使用して操作できるようになります。

上記の Nginx の例を使用して、Biuldah の効果を確認してみましょう (今のところキャッシュは無視します)。

  1. #!/usr/bin/env バッシュ
  2. -o errexitを設定する
  3.  
  4. # コンテナを作成する
  5. コンテナ=$(最初からbuildah )
  6.  
  7. # コンテナファイルシステムをマウントする
  8. マウントポイント=$(buildah マウント $container)
  9.  
  10. # 最小限のパッケージとnginxを含む基本的なファイルシステムをインストールします
  11. dnf インストール--installroot $mountpoint --releasever 28 glibc-minimal-langpack nginx --setopt install_weak_deps=false -y  
  12.  
  13. # コンテナを画像として保存する
  14. ビルドコミット  --format docker $container nginx  
  15.  
  16. # 掃除
  17. buildah は $container をアンマウントします
  18.  
  19. # イメージをDockerデーモンにプッシュして保存する
  20. buildah push nginx:latest docker-daemon:nginx:latest

イメージの構築に Dockerfile を使用しなくなり、代わりに単純な Bash スクリプトを使用していることに気づいたかもしれません。スクラッチ(または空白)イメージのセットを使用して構築します。この Bash スクリプトは、コンテナのルート ファイル システムをホスト上のマウント ポイントにマウントし、ホスト コマンドを使用して各ソフトウェア パッケージをインストールします。この方法では、パッケージ マネージャーはコンテナー自体を超える必要さえありません。

dnf などのベース イメージ内の追加コンテンツなどの余分な部分がなければ、イメージ自体のサイズはわずか 304 MB であり、これは以前に Dockerfile を使用して構築された Nginx イメージよりも 100 MB 以上小さくなります。

  1. [chris@krang] $ docker イメージ | nginxをgrep
  2. docker.io/nginx buildah 2505d3597457 4 分前 304 MB

注: イメージ名には、Docker デーモンの名前空間にプッシュされるため docker.io の部分が含まれますが、それでも上記のビルド スクリプトを使用してローカルでビルドされたイメージです。

ベースイメージ自体は約 300 MB しかないことを考慮すると、100 MB の節約は明らかにかなり印象的です。ソフトウェア マネージャーを使用して Nginx をインストールすると、多くの依存関係も導入されます。ホストが提供するツールによって処理されるソース コードのコンパイルを使用すると、不要な追加ファイルをインポートする代わりに正確な依存関係を選択できるため、さらにストレージ スペースを節約できます。

Buildah を使用してイメージをビルドすると、完全なオペレーティング システムとビルド ツールを効果的に削除できるため、イメージ サイズがさらに圧縮されます。特定の種類のイメージについては、同じ方法を使用して、アプリケーション自体のみを含むイメージを作成することもできます。

静的にリンクされたバイナリのみを使用してイメージを作成する

同じ哲学に従って、さらに一歩進んで、イメージから管理ツールとビルド ツールをクリーンアップすることができます。必要な専門知識があり、コンテナ内でトラブルシューティングする必要がなくなった場合は、Bash を削除できますか? GNU コアはまだ必要ですか?基本的な Linux ファイル システムは依然として必要ですか?これは任意のコンパイル言語で実行できます。つまり、静的にリンクされたライブラリを含むバイナリ ファイルを作成します。プログラムの実行に必要なすべてのライブラリと関数がバイナリ ファイルにコピーされ、保存されます。

これは Golang コミュニティで人気のあるアプローチなので、Go アプリケーションを使用してこれを実証します。次の Dockerfile は、小さな Go Hello-World アプリケーションを取得し、それを FROM golang:1.8 イメージにコンパイルします。

  1. golang:1.8より
  2.  
  3. 環境変数 GOOS=linux
  4. ENV appdir=/go/src/gohelloworld
  5.  
  6. コピー ./ /go/src/goHelloWorld
  7. ワークディレクトリ /go/src/goHelloWorld
  8.  
  9. 走って行け
  10. go build -o /goHelloWorld -a を実行します。
  11.  
  12. コマンド [ "/goHelloWorld" ]

結果のイメージにはバイナリ、ソース コード、ベース イメージ レイヤーが含まれ、合計サイズは 716 MB になります。ただし、最終的にアプリケーションに本当に必要なのはコンパイルされたバイナリだけであり、他のものはすべて不要です。

統計を行うときに CGO_ENABLED=0 で cgo を無効にすると、C ライブラリをパッケージ化しないバイナリのセットを作成できます。

  1. GOOS=linux CGO_ENABLED=0 ビルド -a goHelloWorld.go

生成されたバイナリは、空のイメージ、または「最初から構築された」イメージに追加できます。

  1. ゼロから
  2. コピー goHelloWorld /
  3. コマンド [ "/goHelloWorld" ]

次に、2 つの画像セットの音量の違いを比較してみましょう。

  1. [ chris@krang ] $ docker イメージ
  2. リポジトリ タグ イメージ ID 作成サイズ 
  3. goHello スクラッチ a5881650d6e9 13秒前 1.55 MB
  4. goHello ビルダー 980290a100db 14秒前 716 MB

ご覧のとおり、その違いは非常に大きいです。 golang:1.8 によってビルドされたイメージには、goHelloWorld ライブラリ (「builder」としてマークされています) が含まれており、これは純粋なバイナリ ファイル イメージの 460 倍の大きさです。バイナリのみのイメージはわずか 1.55 MB です。つまり、ビルダーによってビルドされたイメージを使用すると、まったく存在する必要のないデータが約 713 MB 含まれることになります。

適切な場合は圧縮方法を検討する

すべてのコマンドをレイヤーに連結してスペースを節約するもう 1 つの方法は、イメージ圧縮 (squash) を使用することです。画像を縮小すると、実際には画像がエクスポートされ、すべての中間レイヤーが削除され、画像の現在の状態が単一のレイヤーとして保存されます。これにより、画像の実際のサイズが効果的に制御されます。

以前は、圧縮されたレイヤーを復元するには、コンテナの内容をエクスポートして単一レイヤーのイメージとして再インポートしたり、docker-squash などのツールを使用したりといった、創造的なソリューションを使用する必要がありました。しかし、バージョン 1.13 以降、Docker は便利なフラグ squash を導入しました。これにより、ビルド プロセス中に同じことを実行できます。

  1. fedora:28より
  2. LABEL 管理者 Chris Collins <collins.christopher@gmail.com>
  3.  
  4. dnf install -y nginxを実行します
  5. dnf clean all を実行 
  6. rm -rf /var/cache/yum を実行します。
  7.  
  8. [chris@krang] $ docker build -t squash -f Dockerfile-squash --squash .  
  9. [chris@krang] $ docker images --format "{{.Repository}}: {{.Size}}" |ヘッド-n 1  
  10. スカッシュ: 271 MB

docker squash を使用してこの多層 Dockerfile を処理すると、前にリンクした説明イメージと同じ機能を持つ、サイズが 271 MB のイメージが作成されます。しかし、これにより新たな潜在的な問題が生じます。

極端すぎる:圧縮されすぎ、薄すぎ、専門的すぎる

レイヤーは画像間で共有できます。ベースは x MB かもしれませんが、一度だけプル/保存すれば、他のイメージで使用できます。レイヤーによって共有される各画像の実際のサイズは、ベース レイヤーと特定の変更との間の差です。この方法により、追加のスペースをほとんど投資せずに、同じイメージの何千もの修正バージョンを作成できます。

これはまさに画像圧縮や特殊化方法の欠点です。画像を単一のレイヤーに圧縮すると、他の画像とレイヤーを共有する機会が完全に失われます。各画像セットは、最終的にはその単一レイヤーのボリュームと一致するようになります。したがって、少数のイメージを使用し、その中で多数のコンテナを実行するだけであれば、過剰圧縮でも問題ありません。しかし、さまざまな画像がたくさんある場合、長期的にはストレージ容量を消費することになります。

Nginx の圧縮例をもう一度見て、この場合は「間引き」プロセスが問題ではないことを確認しましょう。最終的に、Nginx 付きの Fedora をインストールし、キャッシュをクリアして、効果的な圧縮を実現しました。ただし、Nginx 自体はそれほど多くの機能を備えておらず、通常は、構成ファイル、他のソフトウェア パッケージ、さらにはアプリケーション コードなど、さまざまな対象操作をカスタマイズされた方法で実行する必要があります。これらの各操作により、Dockerfile にさらに多くの命令が追加されます。

従来の方法でイメージを構築する場合、イメージ内に Fedora をホストする個別のベース イメージ レイヤー、Nginx がインストールされたレイヤー (キャッシュありまたはなし)、そして各カスタマイズに独自のレイヤーが含まれます。 Fedora、Nginx などの他のイメージでもこれらのレイヤーを共有できるようになります。

この場合、必要な画像は次のとおりです。

  1. [アプリ 1 レイヤー (5 MB)] [アプリ 2 レイヤー (6 MB)]
  2. [ Nginx レイヤー ( 21 MB) ] ------------------^  
  3. [ Fedora レイヤー (249 MB) ]

ただし、イメージを圧縮すると、Fedora ベース レイヤーも圧縮されます。 Fedora ベースの圧縮イメージでは、関連する Fedora コンテンツを解放する必要があるため、各イメージのサイズは 249 MB 増加します。

  1. [ Fedora + Nginx + アプリ 1 (275 MB)] [ Fedora + Nginx + アプリ 2 (276 MB) ]

高度に特殊化された、非常に小さなイメージを多数作成する場合、これは大きな問題になる可能性があります。

なぜなら、人生の他のすべてと同様に、ミラーの音量コントロールに関しては節度が鍵となるからです。また、イメージ レイヤーの動作原理を考慮すると、コンテナ イメージの圧縮と特殊化が徐々に進むにつれて、基本イメージ レイヤーを他の関連イメージと共有できなくなり、圧縮によってもたらされる軽量化効果は減少するか、消滅することもあります。

ある程度カスタマイズされた画像はベースレイヤーを共有できます。前述のように、このベース レイヤーは x MB になる場合がありますが、一度だけプル/保存すれば、すべてのイメージで使用できます。すべての画像の有効サイズは、ベース レイヤーに、それぞれの特定の変更によって生じた差を加えたサイズになります。この方法により、追加のスペースをほとんど投資せずに、同じイメージの何千もの修正バージョンを作成できます。

  1. [特定のアプリ] [特定のアプリ2]
  2. [ カスタマイズ ] --------------^  
  3. [ ベースレイヤー ]

しかし、画像が過度に圧縮されていたり、変更や特殊な調整が多すぎたりすると、大量の画像を処理しなければならなくなります。これらのイメージは同じ共有ベース レイヤー セットを共有していないため、それぞれがディスク上のストレージ スペースを占有します。

  1. [特定のアプリ 1] [特定のアプリ 2] [特定のアプリ 3]

要約する

コンテナ イメージに必要なストレージ容量と転送帯域幅を効果的に削減できる方法はさまざまありますが、最も効果的な方法は、間違いなくイメージ自体のサイズを縮小することです。キャッシュを単純にクリーンアップする(中間レイヤーに保持されないようにする)、すべてのレイヤーを 1 つのレイヤーに圧縮する、または空のイメージに静的バイナリを追加するなど、いずれの方法を選択する場合でも、イメージ内に存在する可能性のある不要なコンテンツを調査し、適切なレベルまで削減するために時間を費やす価値はあります。

元のタイトル: 小さなコンテナイメージの作成、著者: Chris Collins

[51CTOによる翻訳。パートナーサイトに転載する場合は、元の翻訳者と出典を51CTO.comとして明記してください。

<<:  クラウドミドルウェアツールが増加しています。 2018 年はクラウド ミドルウェアの年になるでしょうか?

>>:  Kubernetesの未来はサーバーレス

推薦する

中小規模のウェブサイトは、質の高い人材不足というボトルネックをどう打破できるでしょうか?

昨今、質の高い人脈がウェブサイトの発展にますます大きな影響を与えていることは否定できません。無名の新...

コレクションEコマースサイトの運営における4つの課題を分析

電子商取引は以前ほど利益が出ないとはいえ、コレクター向け電子商取引の収益性は依然としてかなり良好で、...

Google が検索結果ページをアップグレード: ナレッジグラフを強調

改訂された Google 検索結果ページでは、左側のサイドバーが検索ボックスの下に移動され、右側のナ...

直線対曲面、曲面は超薄型製品の未来のリーダーに挑戦できるでしょうか?

はじめに: サムスンや LG など多くのカラーテレビメーカーが推進する曲面 LED バックライト付き...

SEOにおけるウェブエディターのユニークな位置についての簡単な説明

SEO を研究し実践する人が増えています。SEO がウェブサイトに良いトラフィックをもたらすことは間...

ソーシャルメディアコンテンツマーケティングにおけるSEO

今日、コンテンツ マーケティングはあらゆる企業のマーケティング戦略の中核となっています。これまでの記...

クラウドネイティブ向けに誕生した、第3世代のShenlongクラウドサーバーがリリースされました

[51CTO.com からのオリジナル記事] 過去 10 年間で、クラウド コンピューティングは生活...

完全にGoogleのようなSEOはBaiduでは機能しない

最近、Dianshi Interactiveで多くの人がBaiduのアップデートの話題を議論している...

2014 年のウェブサイト SEO 開発の 5 つの主な方向性

電子商取引業界にとって、2014 年は激動の生死を分ける年となることが予想されます。電子商取引大手の...

SEO を行う際、毎日何をしていますか?

最近、ある質疑応答サイトで「SEO担当者として、毎日何をしていますか?」という非常に興味深い質問を見...

Baidu Accelerator Smart DNSが再度アップグレードされ、マルチラインカバレッジを実現

Baidu Accelerator は、ウェブマスター専用のサービスを提供することを目的として 2 ...

K8s-サービス メッシュ プラクティス - メッシュの構成 (グレー リリース)

前の記事 k8s-Service Mesh Practice-Istio の概要では、Istio を...

アリババとテンセントが相互接続、ピンドゥオドゥオはパニックに陥っているのか?

WeChatでTaobaoを閲覧すると、中国のインターネット史上最も象徴的なシーンがついに現実のもの...