オンデマンドトレーニング

画像の詳細とベストプラクティス

 

写し

こんにちは、画像の詳細に関するこの技術トークセッションへようこそ。 簡単な紹介の後、画像レイヤーの概念を紹介します。 次に、いくつかのビルディングベースのプラクティスを示し、マウント、HEREDOCサポート、およびマルチアーキテクチャビルドで終了します。

 

目次

 

はじめに (0:21)

そこで、最初にコンテナについて簡単に紹介します。 ソフトウェア業界では、物事が急速に進んでいます。 多くの新しいフレームワーク、ライブラリ、コンポーネントがソフトウェアの準備を進め、ますます複雑になっているのを目の当たりにしています。 ミスや問題が発生する可能性が高くなります。 インターネットでは、アプリケーションの数とそれらをどれだけ速く配信するかがビジネスにとって重要です。 開発と提供のための共通の方法がなければ、それは大きな問題です。 幸いなことに、コンテナがあります。 これらは、アプリケーションを簡単にパッケージ化、ビルド、共有、および実行するための標準的な方法を提供します。 コンテナ仕様について。

2015年6月、DockerはLinux Foundationと協力して、Open Content Container Initiative(OCI)を設立し、3つの仕様を定義しました。イメージ仕様は、コンテナイメージの構築方法、ファイルシステムの作成方法、どのファイルなどを指定する方法です。 ランタイム仕様は、イメージからコンテナを作成して実行する方法、CPU、メモリストレージ、ネットワーク、ディストリビューション仕様などを定義します。 これは、完全なコンテナワークフローを実現するためのすべてのAPIです。 問題は、アプリケーションを実行するために、すべてのバイナリ、ファイル、構成などをどのようにコンテナに入れるかです。 その答えは、コンテナイメージのおかげです。

 

イメージ&コンテナ 101 (1:42)

つまり、イメージは、ファイルシステム、コードバイナリ、ツール、ランタイム、依存関係など、アプリケーションを実行するために必要なすべてのもので構成されています。 これらのイメージは、Docker Hub などのレジストリに格納されます。 イメージを作成するには、イメージ仕様の実装であるDockerファイルが非常に便利なソリューションです。 Docker ファイルは、イメージのビルド方法を説明する一連のコマンドを含むファイルです。 つまり、画像はいくつかのスライスを持つケーキのようなものです。 これは複数のレイヤーで構成され、これらのレイヤーは Docker ファイルで定義されます。

したがって、画像のおかげで、コンテナを実行します。 コンテナは、イメージの実行中のインスタンスであると言えます。 コンテナは、イメージから命令とファイルを取得し、アプリケーションを実行するための環境を作成します。 コンテナは仮想化環境のように実行される分離されたプロセスであり、分離されているため、他のプロセスに干渉できないことを知っておくことは非常に重要です。 ホストとカーネルを持つコンテナの間でファイルとディレクトリを共有することが可能です。 この機能は、永続的な共有データや設定ファイルの提供に役立ちます。

 

ユニオンファイルシステムを理解する (3:00)

イメージがどのように構造化され、コンテナが使用するファイルシステムに変換されるかを理解するために、少し時間を費やすことが重要です。 例として、nginxを引っ張ります。 私たちはいくつかのものが引っ張られているのを見ます、そして時々、いくつかの要素がすでに存在しているのを見ます。 しかし、その理由を見てみましょう。 先ほども言ったように、イメージは層のあるケーキのようなものです。 プルされる各アイテムは、画像内のレイヤーです。 これらの各レイヤーは、一連のファイルシステムの変更を表します。 各レイヤーは、ファイルを追加、更新、置換、または削除できます。 たとえば、ここではイメージの構築を開始します。 レイヤー 1では、4つのファイルを追加しました。 レイヤー 2では、ファイル2 を更新し、ファイル5を追加しました。 したがって、最後に、すべてのレイヤーをマージするために、結合されたファイルシステムが作成されます。

理解すべき重要なことは、すべてのレイヤーが不変であるということです。 イメージを構築するときは、新しいファイルシステムの差分を作成するだけで、プレビューを変更する必要はありません。 また、上位レイヤーのファイルは、下位レイヤーのファイルを置き換えます。 ファイルを削除するには、ホワイトアウトファイルを使用して新しいレイヤーを作成します。 ホワイトアウトファイルは、前のファイルをマスクまたは非表示にします。 そのため、unioned ファイル システムでは表示されません。 このイメージから作成されたコンテナは、オリジナルがまだ出荷されていても、ファイルを見ることができません。 レイヤーは不変であることを忘れないでください。 では、ここで少し専門用語をご紹介しましょう。 下位ディレクトリは、イメージから取得されるレイヤーです。 また、上位ディレクトリは、コンテナに固有の一意で書き込み可能なスペースであるスクラッチスペースです。 一時的なファイルストレージ領域のように見ることができます。 イメージは、新しいコンテナから手動で作成し、すべてのステップで手動コマンドを使用して作成できます。 各コマンドの結果はレイヤーになります。 たとえば、docker run –rm -ti ubuntu コマンドを実行して、対話型モードで新しいコンテナーを作成できます。 このコンテナ内では、コマンド apt update && apt install -y nodejs を使用して Node.js をインストールできます。

この時点で、run node —-version を使用して Node コマンドを実行し、機能するかどうかを確認できます。 別のターミナルでは、実行中のコンテナのIDを取得し、Docker Commitコマンドを実行して変更を新しいイメージとして保存できます。 このイメージから新しいコンテナをDocker runコマンドで起動し、ノード—-versionが機能しているかどうかを確認できます。 したがって、それは機能しますが、より複雑なケースでは、痛みを伴う可能性があります。 そのため、この方法でイメージを構築する代わりに、Dockerfile を使用する傾向があります。 Docker DesktopのGUIを実行すると、前のコマンドの詳細が表示されます。

 

Dockerfile を使用したイメージの作成 (6:40)

しかし、Dockerfileについて話しましょう。 したがって、結局のところ、Dockerfile は Docker イメージを定義してビルドする簡単な方法です。 このファイルはソースコードリポジトリに含めることができるため、バージョン管理したり、チームの他のメンバーと共有したり、CI/CDシステムでイメージをビルドしたりすることもできます。 Dockerfileは、FROM(from命令)などのさまざまな命令をサポートしており、ビルド元の親イメージを指定します。 右の例では、Ubuntuイメージから開始するため、オペレーティングシステムに関して何もインストールする必要はありません。 そこでの作業指示は、作業ディレクトリを設定することであり、その後、Dockerファイル内の他のすべての命令がこのディレクトリで実行されます。

COPYコマンドを使用すると、ホストシステムからDockerイメージにファイルまたはフォルダをコピーできます。 RUN命令は、画像に新しいレイヤーを作成するためのコマンドを実行します。 この例では、RUNコマンドは、必要なすべての依存関係を持つ新しいレイヤーをイメージに作成します。 CMD コマンドは、コンテナーの開始時に実行される命令を指定します。 この例では、node.jsを開始し、index.jsファイルを実行します。 そして、ビルドの準備ができたら、Docker buildコマンドを使用してビルドを行います。 イメージをビルドしたら、Docker image history コマンドを使用してレイヤーと一部の詳細を表示できます。 また、Docker デスクトップのイメージ分析ビューでも、これらの詳細の一部を確認できます。 これにより、イメージ内に存在する可能性のある脆弱性についての洞察も得られます。 画像を掘り下げるためによく使用される別の優れたツールがあります。 これがダイブです。 これにより、各レイヤーのファイルシステムを確認し、追加、変更、削除されたファイルを確認できます。 Dive はオープンソースプロジェクトです。

 

イメージ構築のベストプラクティス (8:44)

基本的な理解がわかったところで、イメージの構築に関するいくつかのベスト プラクティスについて詳しく見ていきましょう。 まず、いくつかの簡単なベストプラクティスについてお話ししましょう。 シークレットを画像に含めないでください。 シークレットをボリュームとしてマウントしたり、環境、変数を使用したり、ボールトを使用したりします。 信頼できるコンテンツのみを使用してください。 つまり、Docker、Docker Hub、そしてあなたの組織から。 たとえば、Dockerには3種類の信頼できるコンテンツがあります。 Dockerの公式イメージ、検証済みの発行者、およびスポンサー付きのオープンソースプロジェクト。 画像を使用するときは、危険すぎるため、:latestタグ(デフォルトのタグ)を使用しないでください。

イメージを構築する際には、いくつかの目標があります。 ここでの最初の目標である、画像のサイズを小さくすることについて話しましょう。 なぜこれが重要なのですか? 画像が小さいほど、画像のプッシュとプルに必要な時間と帯域幅が削減されます。 さて、誤って画像のサイズを大きくする可能性のあるいくつかの一般的な方法。何ができるか見てみましょう。 さて、ここに行くときは、RUN apt installの行でクリーンアップする必要があります。 Pythonをインストールしていますが、Docker DesktopのGUIを見ると、新しいレイヤーであるnumber 7が作成され、サイズは 300 メガバイトを超えていることがわかります。 次に、RUN apt autoremoveの行を使用して、クリーニングを行います。 そこで、新しいレイヤーを作成しますが、レイヤー番号 7 はまだここにあります。 それは隠されているだけです。 ホワイトアウト ファイルの概念を思い出してください。 イメージを構築するときにスペースを節約したい場合は、この場合、コマンドを複数の行でチェーンできます。

次に、最後に、小さなレイヤーを作成し、ファイルをインストールし、インストールをクリーンアップしてファイルを削除します。 この時点で、作業内容を保存し、新しい小さなレイヤーを作成します。 そのため、可能な限りコマンドをチェーンダウンして、ファイルシステムの変更のセットをつぶします。 この簡単な変更で、スペースを約 66%節約できます。

 

ビルド間のレイヤー再利用の最大化 (11:17)

それでは、ビルド間でのレイヤーの再利用を最大化することに焦点を当てたベスト プラクティスについて詳しく見ていきましょう。 これには、主に 2 つの利点があります。 ビルドの実行が速くなり、ビルドのプッシュとプルが速くなります。 イメージを構築する際には、1 つのルールがあります。 レイヤーを再利用してみてください。 画像を操作しているときは、すべてがキャッシュに格納されますが、変更するたびにキャッシュの一部が無効になります。 レイヤーのキャッシュが無効になると、レイヤーは再構築されますが、すべての子レイヤーも再構築されます。 では、これを例を挙げて説明しましょう。 私はDockerfileと4つの大きなファイルを含むディレクトリを持っています。 そして、イメージを作成し、Dockerfileをディレクトリの内容と同期します。 画面の右上にあるのは、私のDockerfileです。 最初の 3 つのファイルを使用してイメージを作成します。 そして、最初のビルドを実行すると、ビルドが3つのファイルをコピーしていることがわかります。 そこで、合計で 6 ギガバイト以上のレイヤーを3つ作成しました。 Docker Desktop の GUI を使用すると、ビルドに関する情報 (期間など) を取得できます。

次に、Dockerfileを更新し、4番目のファイルをイメージにコピーします。 次のビルドでは、最初の 3 つのファイルがキャッシュから取得され、4 番目のファイルのみがコピーされていることがわかります。 Docker Desktop のビルド ビューに戻ると、3 つのレイヤーがキャッシュ内にあること、およびビルド期間のレイヤーが短いことを確認できます。 次に、2 番目のファイルに行を追加します。 そこで、2番目のファイルを更新しました。 イメージを再構築すると、最初のレイヤーはキャッシュに残りますが、他のレイヤーはキャッシュに残りません。ビルドでは次の 3 つのレイヤーが再作成されます。 そして、もう一度確認することができますが、期間が長く、キャッシュされたレイヤーが1つだけ使用されました。 別のベスト プラクティスは、セットアップをコードから分離することです。 このDockerfileを使用すると、Webアプリケーションのソースコードを更新するたびに、npm installが実行されます。 なぜなら、最初にソースコードをイメージにコピーするからです。 ベストプラクティスは、依存関係を最初に1つのレイヤーにインストールし、ソースコードを変更するだけであれば、依存関係レイヤーは再構築されません。

 

マルチステージビルド (14:05)

最後に、必要なものだけを使用して画像のサイズを縮小することについて話しましょう。 そのために、マルチステージビルドがあります。 これは、画像の縮小に役立つ優れたツールです。 それでは、使い方を見てみましょう。 結局のところ、マルチステージビルドを使用すると、Dockerfile内にパイプラインを作成でき、ステージはアーティファクトをビルドするか、ビルドステージから同じアーティファクトを抽出して次のステージで使用できます。 しかし、例を見てみましょう。 私は囲碁プログラムを持っています。 通常、最も簡単な方法は、アプリケーションをビルドして実行するためのすべてのツールを持っているため、現在のイメージを使用することです。 しかし、画像のサイズを確認すると、非常に小さなプログラムの場合、 800 メガバイト以上が付属していることがわかります。 もちろん、やりすぎです。

マルチステージビルドを使用する場合は、最初に現在のイメージでビルダーステージを書いています。 バイナリをビルドしてから、小さなイメージで新しいステージを作成します。 たとえば、ここではUbuntuイメージを使用していますが、これは小さいですが、小さいものを見つけることができます。 そして、私はビルダーステージからプログラムのバイナリのみをコピーします。 そして今回は、はるかに優れています。 私の画像のサイズは約 70 メガバイトです。 ただし、スリムな画像などの小さな画像を使用することで、より適切に行うことができます。 Dockerスリムイメージは、特定のアプリケーションまたはサービスを実行するために必要な重要なコンポーネントのみを含む、できるだけ小さくなるように設計されたDockerイメージの一種です。 ところで、脆弱性の数を減らしていることに気づくでしょう。 アプリケーションが標準の静的実行可能ファイルである場合、つまり依存関係がない場合、最後の段階でスクラッチイメージを使用することもできます。 Docker スクラッチ イメージは、基本的に空のイメージです。 最もミニマルな画像です。 Docker イメージには何も含まれていないため、バイナリを実行するために必要な最小限のイメージをベースとすることができます。 私の例では、新しいイメージのサイズは 2 MB 未満です。

小さな画像を持つことは非常に重要です。 たとえば、攻撃対象領域が最小限に抑えられ、CI/CD の効率が向上するため、システムのセキュリティが向上します。 スクラッチ画像はその究極の例です。 ほとんどの場合、すべての要件を満たすスリムな画像が見つかります。 そして、この場合、私にはもう脆弱性がないことがわかります。 そして最後のヒントは、前の画像の結果であるビルダーとしてビルダーを使用できることです。 この例では、既存のイメージからバイナリをコピーします。 だから私は別の画像のレイヤーを使用しています。 ですから、常に再利用について考えてください。

 

マウント&マルチアーチ (17:28)

Dockerファイルに関連する便利な機能がいくつかあります(マウントなど)。 ビルド中に、追加のストレージやボリュームスケッチなどをビルドにアタッチすると役立つことがよくあります。 現在サポートされているマウントには 4 種類あります。 この例では、NPM リポジトリにアクセスするための資格情報を提供するシークレット マウントがあります。 シークレットを使用する場合、Docker ビルド コマンドでシークレット データのソースを示す追加の引数が必要です。 2 番目のマウントはキャッシュ マウントです。 これは、ビルドがこのキャッシュを使用してビルドを高速に実行できるという考え方です。 スライドの下部には、NPMでキャッシュを使用した例へのリンクがあります。

もう1つの優れた機能は、DockerファイルでHEREDOCステートメントを使用できる機能で、アンパサンドやバックスラッシュステートメントを多数使用せずに複数のコマンドやスクリプトを実行できます。 そして最後に、マルチアーキテクチャビルドにはいくつかの優れた機能があります。 Docker ファイルでは、特定のステージで使用するプラットフォームを指定できます。 ここで、ビルド プラットフォーム変数はネイティブ プラットフォームを表します。 TARGETOS と TARGETARCH は、ビルド プラットフォーム変数から計算されます。 そして、マルチアーキテクチャイメージの構築が非常に簡単になります。 したがって、この例では、IMD64 と ARM64のマルチアーキテクチャ イメージを作成します。 スライドの下部には、マルチアーキテクチャの詳細な例へのリンクがあります。 以上です。 この部分のまとめとして、非常に単純なビルドからかなり複雑なビルドまでの「Build with Docker」ガイドがあります。 素晴らしいリソースですので、迷わず読んでみてください。 ご清聴ありがとうございました。

 

さらに詳しく

Dockerイメージと、小さくて安全なイメージを迅速に構築するためのベストプラクティスについて詳しく説明します

このブログでは、Dockerイメージがどのように構造化されているかを深く掘り下げて、ビルドと使用を容易にします。 また、ビルドツールとビルドのベストプラクティスについても説明し、小さくて安全なDockerイメージを迅速にビルドします。

登壇者

フィリップ・シャリエール

シニア・ソリューション・アーキテクト
港湾労働者