Docker Scoutの早期アクセス リリースにより、Docker Hub はついにイメージの内部を視覚化し始めました。これは素晴らしい!なぜDocker Hub は何年も前にこれを行わなかったのでしょうか?そのビジネスモデルのためです。
先に進む前に、Docker イメージが何で構成されているかを簡単に確認しましょう。
コマンド ラインで docker docker pull
を実行して Docker イメージをダウンロードすると、Docker CLI はイメージをプルするときに各レイヤーのダウンロードの進行状況を表示します。 Docker イメージをダウンロードしたことがある場合は、おそらく次の画像を見たことがあるでしょう。
以前に Docker を使用したことがある場合は、独自のイメージ用にこれらのレイヤーのいくつかを定義したことがあるでしょう。開発者は、Dockerfile を作成するときにこれらのレイヤーを暗黙的に定義します。たとえば、この Dockerfile の各行はレイヤーを生成します。
FROM ubuntu:22.04 # install apt dependencies RUN apt-get update RUN apt-get install -y iputils-ping python3 python3-pip # install python dependencies RUN pip3 install numpy # cleanup apt lists RUN rm -rf /var/lib/apt/lists/* CMD ["/bin/bash"]
レイヤーはアーカイブされた Linux ディレクトリであり、ファイル システムの tarball です。 Docker はすべてのイメージ レイヤーをダウンロードし、それぞれを別のディレクトリに解凍します。 docker run
コマンドを使用して Docker イメージからコンテナーを起動すると、Docker デーモンはイメージ レイヤーを結合してコンテナーを形成します。
製品としての Docker の付加価値の多くは、これらの詳細が抽象化されており、ユーザーが階層化されたコンテナーがどのように機能するかを考えることなくその利点を享受できることです。しかし、すべての抽象化は漏洩し、Docker も例外ではありません。場合によっては、カーテンを引く必要があります。
Docker イメージ レイヤーには、コンテナーのファイル システムに存在するすべてのバイナリの元のストーリーが含まれています。 Docker イメージの最初の行は「FROM 行」です。これは、Dockerfile がその上に構築される Docker イメージ (したがって、イメージ レイヤー) を定義します。
現在の Dockerfile のレイヤーとその親イメージ チェーンのレイヤーを検査することで、開発者はコンテナーのルート ファイル システム内のすべてのファイルがどこから来たのかを判断できます。これは非常に貴重な情報です。それは開発者にとって次のことに役立ちます。
ビジュアライゼーション内のレイヤーをクリックして、Docker イメージのバージョン間でファイルの変更を追跡することを想像してください。自動セキュリティ スキャンによってイメージの 1 つの脆弱性が特定された場合、レイヤー検査ツールを使用して脆弱性がどのようにして導入されたかを特定することを想像してください。
画像サイズが大きすぎると、企業に多額の費用がかかる可能性があります。多くのCI/CD パイプラインはプル リクエストごとに Docker イメージをプルするため、Docker イメージのダウンロードに時間がかかるとパイプラインの速度が低下し、開発者の効率が低下し、CPU 時間が無駄になる可能性があります。組織は通常、インフラストラクチャのコストを時間単位で支払うため、無駄な時間はすべて不必要な出費となります。
無駄なコンピューティング リソースだけでなく、肥大化した Docker イメージにより、過剰なネットワーク転送コストが発生する可能性があります。組織が Docker イメージを AWS リージョン間でダウンロードしたり、AWS からオープン インターネットにダウンロードしたりすると、Docker イメージの肥大化はインフラストラクチャの無駄な支出に直結します。これはすぐに加算されます。
Docker イメージ レイヤーに誤って肥大化が生じてしまうのは簡単です。前に示した Dockerfile には古典的な例が含まれています。4 行目からのレイヤーはディスク上に 40MB のファイルを保存し、後で 11 行目からのレイヤーによって削除されます。Docker イメージの仕組みにより、そのデータは依然としてイメージの一部です。不要な画像サイズは40MB。
これは簡単な例です。実際、これは Docker のドキュメントからそのまま抜粋したものです。より複雑な Dockerfile では、この間違いを見つけるのがはるかに困難になる可能性があります。
コマンド ラインを使用して Docker イメージ レイヤーを操作するのは難しい場合がありますが、Docker Scout が最近リリースされる前は、コマンド ラインで最先端の機能を見つけることができました。ここでは 2 つの基本的なアプローチを紹介します。
これは、飾り気のない、自分で行う Unix の方法です。必要なのは、Docker デーモンを実行しているホストだけです。それは簡単なアプローチです:
docker create
実行して、イメージからコンテナーを起動します。docker inspect
を使用して、新しいコンテナーのレイヤー ディレクトリを見つけます。cd
、頭の中でレイヤーについて考えてみましょう。
これは面倒です。数か月間使用してきたものの、最近サイズが大幅に増大した Docker イメージ内のイメージの肥大化を追跡しようとしているとします。
まず、イメージからコンテナを作成します。
where-the@roadmap-ends ~ $ docker create --name example where-the-roadmap-ends 339b8905b681a1d4f7c56f564f6b1f5e63bb6602b62ba5a15d368ed785f44f55
次に、 docker inspect
、ダウンロードされたイメージのレイヤー ディレクトリがファイル システム上のどこに配置されたかを示します。
where-the@roadmap-ends ~ $ docker inspect example | grep GraphDriver -A7 "GraphDriver": { "Data": { "LowerDir": "/var/lib/docker/overlay2/1c18bd289d9c3f9f0850e301bf86955395c312de3a64a70e0d0e6a5bed337d47-init/diff:/var/lib/docker/overlay2/wbugwbg23oefsf678r7anbn4f/diff:/var/lib/docker/overlay2/j0dekt7y8xgix11n0lturmf8t/diff:/var/lib/docker/overlay2/zd57mz6l4zrsjk9snc2crphfq/diff:/var/lib/docker/overlay2/83za1pmv9xri44tddzyju0usm/diff:/var/lib/docker/overlay2/8c639b22627e0ad91944a70822b442e5bff848968263a37715a293a15483c170/diff", "MergedDir": "/var/lib/docker/overlay2/1c18bd289d9c3f9f0850e301bf86955395c312de3a64a70e0d0e6a5bed337d47/merged", "UpperDir": "/var/lib/docker/overlay2/1c18bd289d9c3f9f0850e301bf86955395c312de3a64a70e0d0e6a5bed337d47/diff", "WorkDir": "/var/lib/docker/overlay2/1c18bd289d9c3f9f0850e301bf86955395c312de3a64a70e0d0e6a5bed337d47/work" }, "Name": "overlay2"
この調査で調べたいレイヤーは、「LowerDir」ディレクトリのリストです。他のディレクトリは Docker イメージ自体の一部ではないため、無視してかまいません。
そこで、コマンドラインで「LowerDir」ディレクトリのリストを解析します。
where-the@roadmap-ends ~ $ docker inspect example | grep GraphDriver -A7 | grep LowerDir | awk '{print $2}' | sed 's|"||g' | sed 's|,||g' | sed 's|:|\n|g' /var/lib/docker/overlay2/1c18bd289d9c3f9f0850e301bf86955395c312de3a64a70e0d0e6a5bed337d47-init/diff /var/lib/docker/overlay2/wbugwbg23oefsf678r7anbn4f/diff /var/lib/docker/overlay2/j0dekt7y8xgix11n0lturmf8t/diff /var/lib/docker/overlay2/zd57mz6l4zrsjk9snc2crphfq/diff /var/lib/docker/overlay2/83za1pmv9xri44tddzyju0usm/diff /var/lib/docker/overlay2/8c639b22627e0ad91944a70822b442e5bff848968263a37715a293a15483c170/diff
これらは、画像内のレイヤーのリストであり、最下位のレイヤーが最初に順番に並べられています。ここで、これらのレイヤー ディレクトリを、それらを生成した Dockerfile 行と手動で関連付ける必要があります。
残念ながら、Docker では、これらの行を Docker イメージから直接抽出する方法を提供しません。これは、 docker history
を使用して取得できる最善の方法です。
where-the@roadmap-ends ~ $ docker history where-the-roadmap-ends IMAGE CREATED CREATED BY SIZE COMMENT 6bbac081b2a7 2 hours ago CMD ["/bin/bash"] 0B buildkit.dockerfile.v0 <missing> 2 hours ago RUN /bin/sh -c rm -rf /var/lib/apt/lists/* #… 0B buildkit.dockerfile.v0 <missing> 2 hours ago RUN /bin/sh -c pip3 install numpy # buildkit 70MB buildkit.dockerfile.v0 <missing> 2 hours ago RUN /bin/sh -c apt-get install -y iputils-pi… 343MB buildkit.dockerfile.v0 <missing> 2 hours ago RUN /bin/sh -c apt-get update # buildkit 40.1MB buildkit.dockerfile.v0 <missing> 8 months ago /bin/sh -c #(nop) CMD ["bash"] 0B <missing> 8 months ago /bin/sh -c #(nop) ADD file:550e7da19f5f7cef5… 69.2MB
この出力を使用すると、どのレイヤーにディレクトリがあるか、およびどの Dockerfile コマンドがレイヤーを作成したか (CREATED BY 列に表示) を特定できます。
docker history
コマンドは、 docker inspect
レイヤー ディレクトリをリストするのと同じ順序でコンテナー内のレイヤーを出力します。これを知っていると、2 つの出力を手動でマージして、どのレイヤーが他のレイヤーより大きいか、どの Dockerfile コマンドで作成されたか、各レイヤーがどのディレクトリに含まれているかを確認できます。
apt-get update
を実行するレイヤー A の内容は次のとおりです。
where-the@roadmap-ends ~ $ du -hs /var/lib/docker/overlay2/83za1pmv9xri44tddzyju0usm/diff/var/lib/apt/lists 38.2M /var/lib/docker/overlay2/83za1pmv9xri44tddzyju0usm/diff/var/lib/apt/lists
レイヤー B のコンテンツと比較すると、レイヤー A から残ったファイルが削除されます。
where-the@roadmap-ends ~ $ du -hs /var/lib/docker/overlay2/wbugwbg23oefsf678r7anbn4f/diff/var/lib/apt/lists 4.0K /var/lib/docker/overlay2/wbugwbg23oefsf678r7anbn4f/diff/var/lib/apt/lists
/var/lib/apt/lists
ディレクトリは両方の層に存在しますが、層 B では、このディレクトリはスペースをほとんど使用しません。
これは、レイヤー B のディレクトリに「ホワイトアウト ファイル」が含まれているためです。Docker はこれを使用して、最終的なコンテナ ファイル システムから除外するファイルをマークします。
このため、レイヤー B でファイルが「削除」されたにもかかわらず、ファイルはレイヤー A にまだ存在し、画像全体のサイズ (38.2 MB の不必要な肥大化) の原因となります。
さて、それは簡単ではありませんでしたか? 😉
手動による方法は非常に複雑で扱いにくいため、オープンソース コミュニティはこのタスク専用のツール ( dive
と呼ばれます) を作成しました。 Dive は、画像を入力として受け取り、そのファイル システムを解析し、テキストベースの対話型 UI を端末に表示する CLI ツールです。画像レイヤーを関連付けて、レイヤー ディレクトリをより簡単に検査できるようにします。
上記の Dockerfile の Docker イメージに対して実行すると、次のようになります。
┃ ● Layers ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ Aggregated Layer Contents ├─────────────────────────────────────────────────────────────────────────────── Cmp Size Command └── var 69 MB FROM acee8cf20a197c9 └── lib 40 MB RUN /bin/sh -c apt-get update # buildkit └── apt 343 MB RUN /bin/sh -c apt-get install -y iputils-ping python3 python3-pip # buildkit └── lists 70 MB RUN /bin/sh -c pip3 install numpy # buildkit ├── auxfiles 0 B RUN /bin/sh -c rm -rf /var/lib/apt/lists/* # buildkit ├── lock ├── partial │ Layer Details ├─────────────────────────────────────────────────────────────────────────────────────────── ├── ports.ubuntu.com_ubuntu-ports_dists_jammy-backports_InRelease ├── ports.ubuntu.com_ubuntu-ports_dists_jammy-backports_main_binary-arm64_Packages.lz4 Tags: (unavailable) ├── ports.ubuntu.com_ubuntu-ports_dists_jammy-backports_universe_binary-arm64_Packages.lz4 Id: 2bc27a99fd5750414948211814da00079804292360f8e2d7843589b9e7eb5eee ├── ports.ubuntu.com_ubuntu-ports_dists_jammy-security_InRelease Digest: sha256:6e6fb36e04f3abf90c7c87d52629fe154db4ea9aceab539a794d29bbc0919100 ├── ports.ubuntu.com_ubuntu-ports_dists_jammy-security_main_binary-arm64_Packages.lz4 Command: ├── ports.ubuntu.com_ubuntu-ports_dists_jammy-security_multiverse_binary-arm64_Packages.lz4 RUN /bin/sh -c apt-get update # buildkit ├── ports.ubuntu.com_ubuntu-ports_dists_jammy-security_restricted_binary-arm64_Packages.lz4 ├── ports.ubuntu.com_ubuntu-ports_dists_jammy-security_universe_binary-arm64_Packages.lz4 │ Image Details ├─────────────────────────────────────────────────────────────────────────────────────────── ├── ports.ubuntu.com_ubuntu-ports_dists_jammy-updates_InRelease ├── ports.ubuntu.com_ubuntu-ports_dists_jammy-updates_main_binary-arm64_Packages.lz4 Image name: where-the-roadmap-ends ├── ports.ubuntu.com_ubuntu-ports_dists_jammy-updates_multiverse_binary-arm64_Packages.lz4 Total Image size: 522 MB ├── ports.ubuntu.com_ubuntu-ports_dists_jammy-updates_restricted_binary-arm64_Packages.lz4 Potential wasted space: 62 MB ├── ports.ubuntu.com_ubuntu-ports_dists_jammy-updates_universe_binary-arm64_Packages.lz4 Image efficiency score: 90 % ├── ports.ubuntu.com_ubuntu-ports_dists_jammy_InRelease ├── ports.ubuntu.com_ubuntu-ports_dists_jammy_main_binary-arm64_Packages.lz4 Count Total Space Path ├── ports.ubuntu.com_ubuntu-ports_dists_jammy_multiverse_binary-arm64_Packages.lz4 2 28 MB /var/lib/apt/lists/ports.ubuntu.com_ubuntu-ports_dists_jammy_universe_binary-arm64_Pack ├── ports.ubuntu.com_ubuntu-ports_dists_jammy_restricted_binary-arm64_Packages.lz4 2 7.3 MB /usr/bin/perl └── ports.ubuntu.com_ubuntu-ports_dists_jammy_universe_binary-arm64_Packages.lz4 2 4.4 MB /usr/lib/aarch64-linux-gnu/libstdc++.so.6.0.30 2 2.9 MB /var/lib/apt/lists/ports.ubuntu.com_ubuntu-ports_dists_jammy_main_binary-arm64_Packages 2 2.0 MB /var/lib/apt/lists/ports.ubuntu.com_ubuntu-ports_dists_jammy-updates_main_binary-arm64_ 2 1.7 MB /var/lib/apt/lists/ports.ubuntu.com_ubuntu-ports_dists_jammy-updates_universe_binary-ar
Dive は素晴らしいツールであり、それが存在することを嬉しく思いますが、時には不十分なことがあります。テキストベースのインターフェイスは決して使いやすいものではありません。場合によっては、 Grafana の方がtop
よりも優れている場合があります。
また、大きな画像は Dive の機能を圧倒する可能性があります。大きな画像を検査する場合、Dive は大量のメモリを消費します。場合によっては、データを出力する前にカーネルが Dive プロセスを強制終了してしまいます。
開発者の観点から見ると、Docker イメージ レイヤーの視覚化が Docker Hub に存在することを期待するのは常に理にかなっています。結局のところ、Docker イメージ データはすでに Docker Hub のバックエンドに存在しています。
私は、次のような UI を使用して、ブラウザーで Docker イメージの内部を検査することをよく想像しました。
Docker Scout により、Docker は企業としてその方向に進んでいるように見えます。現在、Docker Hub の最新の Postgres イメージに移動すると、次のようなメッセージが表示されます。
開発者として、これはエキサイティングです。この新しい UI を使用すると、イメージ レイヤーの詳細を視覚的に参照して、脆弱性やイメージ サイズの問題を希望どおりに強調表示できます。
Docker Hub が最初に起動されたとき、Docker Hub には 2 種類のユーザーがいました。
そして、Docker はこれら 2 つのタイプのユーザーに異なる方法でアプローチする必要がありました。
2013 年に Docker が登場すると、開発者はすぐにその製品を使用したいと考えました。 Docker コンテナは、全員のソフトウェアのパッケージ化と展開を標準化します。コンテナはソフトウェア開発者コミュニティで急速に普及しました。
しかし、ソフトウェアメーカーはもっと躊躇していました。なぜ彼らは、ソフトウェア製品間の差別化を取り除くことを核とするプラットフォームにソフトウェアをアップロードする必要があるのでしょうか?
Docker Hubを成功させるためには、Dockerが彼らを説得する必要がありました。そのため、開発者を惹きつけることに重点を置くのではなく、Docker Hub の設計と機能セットはソフトウェア発行者に合わせたものになりました。
パブリッシャーにとって、Docker Hub はマーケティング サイトでした。それは彼らに自社の製品を宣伝する場所を与えました。自動車ディーラーがエンジン ブロックを分解できるのと同じように、開発者は Docker イメージの内部詳細を見ることができませんでした。内部エンジニアリングの詳細は表示されず、隠されていたため、買い物客は製造方法ではなく、製品自体に集中し続けました。
Docker Hub には、イメージ サイズの最適化やサプライ チェーンのイントロスペクションといった開発者向けの機能が欠けていました。これは、Docker がこれらの機能を持たずに開発者を魅了していたためです。実際、これらの機能はソフトウェア発行者に悪印象を与える傾向があり、Docker Hub が成功するためには彼らを説得する必要があるユーザーでした。
ソフトウェア発行者と開発者の両方にサービスを提供するこのダイナミクスにより、Docker Hub は両面プラットフォームになりました。
両面プラットフォームとは、2 つの異なるカテゴリーのユーザーが存在し、機能するには両方のユーザーがプラットフォームに参加する必要があるビジネスです。通常、ユーザーをさまざまなグループに分けるのは、プラットフォームを使用する動機です。
2 つのユーザー グループは相互に直接取引することはないかもしれませんが、両面プラットフォームは市場のようなものです。ベンダーが売りに来たり、買い物客が買いに来たりしない限り、価値は生まれません。
テクノロジー業界では、両面プラットフォームが普及しています。これらのビジネス モデルが成功すると、ビジネスの持続的な成長と収益性を推進するネットワーク効果を生み出す傾向があります。ある時点を過ぎると、プラットフォームのサイズと空間内での確立された位置が、新しいユーザーをプラットフォームに引き込みます。
探しているものがわかれば、両面プラットフォームを簡単に見つけることができます。以下に 3 つの例を示します。
LinkedIn には、従業員と採用担当者の 2 種類のユーザーがいます。どちらのグループもさまざまな理由で参加しています。従業員は仕事を望んでおり、採用担当者は人材を採用したいと考えています。
どちらのグループも、もう一方のグループが参加し始めるまで、サイトから望むものを得ることができませんでした。各グループの十分な数がサインアップすると、それはいずれかのグループの新規メンバーのデフォルトの場所となり、サイトの成長は永続的に進み、マイクロソフトに 260 億ドルで買収されるまでになりました。
YouTubeにはコンテンツ作成者がいて、視聴者がいます。視聴者はビデオを見るためにサイトに来ます。コンテンツ作成者は、良い雰囲気、名声、富を求めてサイトに公開します。
YouTube がコンテンツ クリエイターが成功できる場所としての評判を確立してから、ますます多くのコンテンツ クリエイターが現れ、彼らがより多くのコンテンツを生み出すにつれて、より多くの視聴者が訪れるようになりました。プラットフォームが一定の規模を超えると、コンテンツ作成者と視聴者はプラットフォームを使い続ける以外に選択肢がなくなり、各グループがお互いを必要とし、YouTube を通じてしかお互いを見つけることができなくなりました。
Docker Hub が適切であるためには、ソフトウェア発行者がイメージをアップロードし、開発者がイメージをダウンロードする必要がありました。十分な数のパブリッシャーがイメージを Docker Hub にプッシュすると、開発者がイメージをプルするデフォルトの場所になります。プラットフォームが成長し続けるにつれて、Docker Hub はそれらすべてを支配する 1 つのレジストリとしての優位性を固めることになります。
少なくとも、それが計画だった。実際には、Docker (会社) は衰退し、Docker Hub が「その 1 つ」になることはありませんでした。両面プラットフォームを構築する場合、スタートアップ企業は製品を 1 つ持っていますが、製品と市場の適合性を 2 回見つける必要があります。 Docker にとって、この負担は耐えられないほど重く、最終的には Docker を真っ二つに分割してしまいました。
現在、Docker はエンタープライズ ビジネスを売却した後、開発者とその雇用主のみを対象としたサブスクリプション サービスに焦点を当てて、自らを再発明しました。 Docker Hub はもはや両面プラットフォームではなく、単純な SaaS です。顧客はイメージのプッシュとプルと引き換えに月額料金を支払います。
これにより計算が変わります。 Docker が好む「発行者」というユーザー ペルソナはもう存在しません。Docker はすべて開発者に依存しています。 Docker Hub の場合、これにより Docker Scout のイメージ レイヤー検査機能への道が開かれます。遠くから見ている私たちにとって、それはスタートアップのビジネスモデルとその製品提供の間の微妙で基本的な結合を示しています。