Amazon Web Services ブログ

詳解 : Seekable OCI を使用した AWS Fargate におけるコンテナイメージの遅延読み込み

この記事は Under the hood: Lazy Loading Container Images with Seekable OCI and AWS Fargate (記事公開日 : 2023 年 7 月 18 日) の翻訳です。

コンテナワークロード向けのサーバーレスコンピューティングエンジンである AWS Fargate が、Seekable OCI (SOCI) を使用してインデックス化したコンテナイメージの遅延読み込みをサポートしました。SOCI を使用してコンテナイメージを遅延読み込みすることで、AWS Fargate 上で Amazon Elastic Container Service (Amazon ECS) タスクを起動する時間を短縮できます。Donnie Prakoso 氏によるローンチ記事では、AWS Fargate と SOCI の使用方法について詳細に書かれているので、先に目を通していただくことをお勧めします。

この記事では、SOCI の詳細と、コンテナイメージの内容を変更したり既存のツールやワークフローを変更することなく、SOCI を用いてコンテナイメージのインデックスを作成する方法について説明します。また、SOCI インデックスを利用してコンテナイメージを遅延読み込みする remote containerd snapshotter、SOCI snapshotter についても説明します。最後に、AWS Fargate で SOCI を使用する際の考慮点について説明します。

Seekable OCI の仕組み

containerd では、コンテナのファイルシステムを管理するコンポーネントを snapshotter と呼びます。デフォルトの snapshotter である overlayfs は、コンテナイメージ全体を取得・解凍した後でコンテナを起動します。遅延読み込みを行う snapshotter (stargz snapshotterSOCI snapshotter など) を使用すると、コンテナイメージ全体をダウンロードする前にコンテナを起動し、Amazon Elastic Container Registry (Amazon ECR) のような OCI 互換のレジストリからファイルを遅延読み込みします。コンテナイメージ全体のダウンロードを待たずにコンテナが起動するため、多くの場合 overlayfs と比較して起動時間が短くなります。overlayfs では、イメージの取得にかかる時間とコンテナイメージのサイズの間には相関関係があります。そのため、遅延読み込みを行う snapshotter では、コンテナイメージのサイズが大きくなるにつれて、overlayfs と比較した場合の起動時間が短くなります。

SOCI snapshotter がコンテナイメージを遅延読み込みする際、イメージのコンテンツに関するメタデータが必要となります。コンテナイメージは複数のコンテナイメージレイヤーで構成され、圧縮された tar アーカイブとして OCI 互換レジストリに保存されます。SOCI snapshotter がコンテナイメージを遅延読み込みするには、各レイヤーにどのファイルがあり、圧縮された tar アーカイブのどこに格納されているか、そしてアプリケーションが必要とするファイルのみを解凍する方法について、知っている必要があります。SOCI では、これらのメタデータはすべて SOCI インデックスに格納されます。

ローンチ記事では、soci create コマンドを使用してコンテナイメージをインデックス化し、SOCI インデックスマニフェストを作成する方法を紹介しました。soci push コマンドでは、SOCI インデックスマニフェストを OCI 互換レジストリに push し、SOCI snapshotter で使用できるようにします。この 2 つのステップについて、詳しくみてみましょう。

SOCI インデックスマニフェストの作成

soci create コマンドを実行すると、裏側でコンテナイメージレイヤーごとに zTOC (SOCI メタデータの一部) が作成されます。zTOC は、以下の 2 つの要素で構成されます。

  • Table of Contents (TOC) – この表には、そのレイヤーに含まれるすべてのファイルの一覧と、tar アーカイブ内でのそのファイルの位置を示すオフセットが含まれます。
  • zInfo – SOCI において、圧縮された tar アーカイブは span と呼ばれる論理的なチャンクに分割されます。span の一覧は zInfo と呼ばれるテーブルに格納されます。各 span は、レジストリへの範囲指定を含んだ GET リクエストによって個別に取得し、zInfo に格納されたデータを使用して解凍することができます。span を使用することで、バックグラウンドプロセスが tar アーカイブ全体を取得する前に、SOCI snapshotter はレジストリから必要なファイルのみをダウンロードできます。

soci cli では、コンテナイメージのすべての zTOC の一覧とそのコンテナイメージへの参照を含んだ SOCI インデックスマニフェストを、コンテナイメージごとに作成します。SOCI インデックスマニフェストは、soci index info コマンドを使用してローカルで確認できます。

SOCI インデックスマニフェストには、重要な構成要素が 2 つあります。

  • layers – SOCI インデックスにおいて、レイヤーは、コンテナイメージレイヤーの一覧ではなく、対応するコンテナイメージのすべての zTOC の一覧となります。アノテーションは、その zTOC がどのコンテナイメージレイヤーに対応するのかを表します。
  • subject – コンテナイメージマニフェストのダイジェスト、メディアタイプ、およびサイズを指定することで、SOCI インデックスが参照するコンテナイメージを識別します。subject フィールドを読み込み、SOCI インデックスがどのイメージを参照しているかを判断するのは、クライアント (この場合 SOCI snapshotter) の仕事です。

下図は、SOCI インデックスマニフェストとコンテナイメージマニフェストの関係を示しています。

SOCI インデックスマニフェストとコンテナイメージマニフェストの関係

(訳注 : SOCI インデックスは、SOCI インデックスマニフェストとイメージレイヤーごとの zTOC の “実体” で構成されます。つまり、上図右半分の構成要素全体を指します。一方、SOCI インデックスマニフェストは、上図右上に示すように zTOC の “一覧” とコンテナイメージへの参照を含みます。)

SOCI インデックスマニフェストの push

soci push コマンドでは、SOCI インデックスマニフェストとすべての zTOC (コンテナイメージレイヤーごとに 1 つ) を OCI 互換レジストリに push します。SOCI インデックスマニフェストと同時に、もう一つのマニフェストである OCI イメージインデックスもレジストリに push します。すでにレジストリに存在する場合には、OCI イメージインデックスを更新します。OCI イメージインデックスは sha-<digest-of-container-image> というタグを持ち、コンテナイメージに関連するすべての SOCI インデックスの一覧を含みます。

この OCI イメージインデックスを使用することで、レジストリが OCI 1.0 distribution specification のみをサポートしている場合におけるクライアント管理のコンテナイメージの参照を実現します。OCI 1.1 image/distribution specification がサポートされた場合、soci cli は Referrers API をサポートするレジストリに対して、この 2 つ目のアーティファクトの push を停止します。

SOCI インデックスを使用したコンテナの実行

インデックス化されたコンテナイメージを持つコンテナを起動する前に、SOCI snapshotter は SOCI インデックスマニフェストとすべての zTOC をコンテナホストにダウンロードします。また、コンテナイメージレイヤーごとに FUSE ファイルシステムを作成します。さらに、コンテナが起動すると、snapshotter はバックグラウンドプロセスを開始し、コンテナイメージ全体をローカルストレージに移します。ワークロードがまだローカルに存在しないファイルにアクセスしようとすると、snapshotter は次のような挙動を示します。

SOCI snapshotter による span の操作

  1. まず、SOCI snapshotter がファイルを含むレイヤーを検索します。
  2. snapshotter は、レイヤーの tar アーカイブ内でのファイルの位置を TOC から取得します。
  3. snapshotter は、オフセットと zInfo テーブルを使用して、そのファイルのデータを含む span (つまり、圧縮された tar アーカイブ内の論理的なチャンク) の集合を見つけます。
  4. 最後に、必要な span だけをダウンロード・解凍し、ファイルのデータを取得します。SOCI は、一度ダウンロードした span をローカルにキャッシュすることで、各 span のダウンロードと解凍を一度のみ実行するようにします。

SOCI インデックスの作成を自動化する

SOCI プロジェクトの主な目標の 1 つは、お客様が既存のワークフローやツールを変更することなく、遅延読み込みを実現することでした。SOCI によるコンテナイメージのインデックス作成は、コンテナイメージのデータに影響を与えないため、既存の署名やダイジェストの検証プロセスに存在する信頼チェーンをそのまま利用することができます。また、お客様が SOCI インデックスの作成を自動化したいと考えていることも理解していました。そのため、AWS Fargate 上で SOCI を使用したコンテナイメージの遅延読み込みをサポート開始すると同時に、AWS Infrastructure Automation の一環として SOCI Index Builder プロジェクトをリリースしました。

SOCI Index Builder アーキテクチャ

SOCI Index Builder は、コンテナイメージを Amazon ECR に push した際に SOCI インデックスの作成を自動化するためのブループリントを提供します。このプロジェクトのソースコードはオープンソースであり、GitHub で確認できます。ブループリントは AWS CloudFormation テンプレートであり、Amazon EventBridge ルールと 2 つの AWS Lambda 関数で構成されます。Amazon ECR リポジトリへのコンテナイメージの push が成功すると、Amazon EventBridge ルールが AWS Lambda 関数をトリガーします。最初の関数はコンテナイメージを検証し、2 つ目の関数は SOCI インデックスを作成して ECR に push し、コンテナイメージと同じ場所に保存します。これにより、SOCI インデックス作成の自動化を実現します。

プロジェクトを利用開始し、ご自身のアカウントにデプロイするには、ここでホストされているドキュメントを参照してください。

追加のツール

SOCI Index Builder に加えて、既存のワークフローに SOCI インデックスの作成を組み込む方法を紹介するために、SOCI snapshotter on AWS Fargate toolbox リポジトリを作成しました。サンプルリポジトリには 2 つのツールがあります。

  • インデックスビルダーコンテナ – Docker Engine を開発ワークフローの一部として活用しているお客様、例えば Docker Desktop を利用しているお客様は、soci cli でコンテナイメージを見つけられないことに気づくでしょう。これは、デフォルトで Docker Engine がコンテナイメージを containerd イメージストアではなく、Docker Engine イメージストアに保存するためです。Docker Engine のイメージストアを containerd に移行する作業が Moby プロジェクトで実施されていますが、記事執筆時点では、containerd イメージストアはまだデフォルトではありません。お客様が Docker Engine を利用しながら SOCI インデックスを作成するために、このイメージビルダーコンテナを使用できます。このツールは、(Docker in Docker のように) コンテナ内で containerd を実行し、リモートレジストリからコンテナイメージを取得し、コンテナイメージのインデックスを作成し、インデックスを OCI 互換レジストリに push することで動作します。
  • AWS CodePipeline Demo – 多くのお客様は、継続的インテグレーション / 継続的デリバリーパイプライン上で本番コンテナイメージをビルドしています。toolbox リポジトリには AWS CodePipeline を使用したブループリントがあり、CI/CD パイプラインの一部としてコンテナイメージをビルドし、インデックスを作成する方法を示しています。この例は、既存の CI/CD パイプラインに SOCI を統合するためのインスピレーションを提供することを期待しています。

AWS Fargate におけるコンテナイメージの遅延読み込み

AWS Fargate でコンテナイメージを遅延読み込みすることで、新しい Amazon ECS タスクの起動時間を短縮できることが示されていますが、すべてのワークロードとコンテナイメージがその恩恵を受けるわけではありません。社内のテストでは、大きなコンテナイメージ (> 250 MB) を用いて SOCI を使用した場合に、最大の恩恵を受けることがわかりました。しかし、ワークロードが頻繁にファイルシステムのメタデータにアクセスする場合や、アプリケーション起動後すぐにすべてのイメージデータにアクセスする必要がある場合は、SOCI のメリットが減少します。この部分のパフォーマンスを改善し、SOCI のメリットを享受できるワークロードの数を増やすべく、試行錯誤を続けています。

このセクションでは、AWS Fargate におけるコンテナイメージの遅延読み込みの実装の詳細について、説明します。

Amazon ECS タスク内のすべてのコンテナイメージの SOCI インデックスマニフェストを作成する

AWS Fargate 上の SOCI snapshotter は、Linux プラットフォームバージョン 1.4 上にデプロイされたすべての Amazon ECS タスクで有効化されています。タスク定義内で SOCI を有効化/無効化するフラグを設定する必要はありません。代わりに、Fargate はタスク定義内で定義されたすべてのコンテナイメージについて、OCI 互換レジストリに SOCI インデックスマニフェストが存在するかどうかを確認します。すべてのコンテナイメージのインデックスが検出された場合、AWS Fargate はコンテナイメージを遅延読み込みします。いずれかのコンテナイメージに SOCI インデックスが存在しない場合、AWS Fargate はコンテナを起動する前にコンテナイメージ全体を取得します。

コンテナイメージが遅延読み込みされたかどうかを確認する

Amazon ECS タスク内には、タスクメタデータエンドポイントが存在します。タスクメタデータエンドポイントには、コンテナの仕様、使用状況メトリクス、コンテナ起動時に使用された containerd snapshotter など、Amazon ECS タスクに関する有用な情報が含まれます。実行中のタスク内でこのエンドポイントを使用することで、遅延読み込みされたかどうかを判断できます。

$ curl -s $ECS_CONTAINER_METADATA_URI_V4 | jq -r '.Snapshotter'
soci

$ curl -s $ECS_CONTAINER_METADATA_URI_V4/task | jq -r '.Containers | .[0].Snapshotter'
soci

アプリケーションの修正なしでこのエンドポイントを利用する例として、このエンドポイントにクエリを実行し、情報を Amazon CloudWatch Logs に格納する init コンテナのサンプルを SOCI snapshotter on AWS Fargate toolbox リポジトリで確認できます。

コンテナイメージのサイズ

コンテナイメージのサイズが大きくなると、イメージ全体をダウンロードするのにかかる時間も大きくなります (そのため、コンテナイメージのサイズが大きくなると、AWS Fargate の起動時間に影響します)。社内のテストでは、大きなコンテナイメージ (> 250 MB) を用いて SOCI を使用した場合に、タスクの起動時間を大きく短縮できることがわかりました。

小さなコンテナイメージの場合、SOCI の影響は少なく、AWS Fargate タスクの起動時間が遅くなる可能性すらあります。これは、SOCI アーティファクトのダウンロードと FUSE ファイルシステムのセットアップにオーバーヘッドが発生するためです。私たちのテストでは、コンテナイメージが 250 MB 以上の大きさで圧縮されている場合、SOCI が大きな影響を与え始めることが分かりました。250 MB 未満のコンテナイメージの場合、起動後の遅延読み込みによるオーバーヘッドは、従来の方法でコンテナイメージ全体を取得するのにかかる時間よりも大きい可能性があります。

コンテナイメージレイヤーのサイズ

soci create コマンドで SOCI インデックスを作成する際、クライアントが zTOC を生成するコンテナイメージレイヤーの最小サイズを制御するパラメーターが存在します。デフォルトでは、10 MB 未満のレイヤーでは zTOC を生成しないように設定されており、--min-layer-size フラグで調整できます。SOCI インデックスを作成する際、クライアントが小さなレイヤーを検出すると、そのレイヤーの zTOC の生成をスキップし、そのレイヤーは実行時に遅延読み込みされません。社内のテストでは、起動時にシンプルにレイヤー全体をダウンロードする方が、小さなレイヤーを遅延読み込みするよりもパフォーマンスが高いことが示されています。小さなコンテナイメージにおいて、コンテナイメージ内のすべてのレイヤーが 10 MB 未満の場合、SOCI インデックスは作成されません。

サイドカーコンテナ用の SOCI インデックスを作成する際の考慮点として、ロギングや監視のためのコンテナイメージでは、すべてのコンテナイメージレイヤーが 10 MB 未満である可能性があります。そのため、それらの SOCI インデックスを作成するには、このパラメータを調整する必要があります。

まとめ

この記事では、Seekable OCI と SOCI snapshotter について解説しました。また、SOCI インデックスの仕組みや SOCI インデックスがコンテナイメージのコンテンツを管理する仕組み、そして AWS 上または既存の CI/CD パイプライン上で SOCI インデックスの作成を自動化する方法を紹介しました。最後に、AWS Fargate で SOCI を使用する際の考慮点について確認しました。

SOCI snapshotter は、Google の CRFS プロジェクトで初めて議論され、その後 stargz snapshotter で議論されたコンテナイメージを遅延読み込みするための基礎的な技術をベースにしています。今回の AWS Fargate でのローンチにより、お客様は AWS Fargate 上の Amazon ECS タスクでコンテナイメージを遅延読み込みできるようになりました。将来的には、他のコンテナオーケストレーターにおいても SOCI をより簡単に実行できるようにする予定です。ぜひ GitHub の soci-snapshotter プロジェクトにて、最新の開発状況を確認してみてください。