Amazon Web Services ブログ

コンテナイメージのプリフェッチによる Pod の起動高速化

この記事は Start Pods faster by prefetching images (記事公開日: 2023 年 5 月 9 日) を翻訳したものです。

はじめに

多くの AWS のお客様は、機械学習ワークロードを実行するために Amazon Elastic Kubernetes Service (Amazon EKS) を利用しています。コンテナ化によって機械学習エンジニアはモデルを容易にパッケージ化して配布することができ、Kubernetes によってそれらのデプロイ、スケーリング、および改善ができます。Kubernetes で機械学習のトレーニングジョブを実行するお客様において、データセットとモデルのサイズが大きくなるにつれてコンテナイメージのサイズも大きくなり、その結果コンテナの起動が遅くなるということが確認されています。

コンテナの起動が遅いという問題は、機械学習や AI に限ったことではありません。ビルド環境やデータ分析でも同様の問題が発生しています。これらのワークロードのコンテナイメージには、データやライブラリ、依存関係などが含まれています。そのため、これらのイメージは数百 MB から 数十 GB まで様々となります。

コンテナイメージが数百 MB を超えると、コンテナの時間に数分かかる場合があります。この主な理由として、コンテナランタイムがコンテナを起動する前にコンテナイメージをプルする必要があることが挙げられます。Kubernetes はノード上にコンテナイメージをキャッシュすることでこの問題を解決しています。これにより、2 回目以降のコンテナの起動時間は短縮されますが、初回のコンテナイメージのプルには課題が残ります。

Kubernetes-image-puller のようないくつかのコミュニティプロジェクトは、より大きなコンテナイメージをプリフェッチすることでこの問題を解決しようと試みています。その方法として、Pod がスケジュールされる前にノード上でコンテナイメージをプルします。これらのプロジェクトでは、大きなコンテナイメージのキャッシュを保つために DaemonSet と CronJob を利用します。そのため、お客様が管理しなければならないコンポーネントが追加されます。この記事では、インフラストラクチャや Kubernetes リソースの管理をすることなく、ノード上でイメージを事前にプルできる設計を提案します。

AWS Systems Manager を利用した Amazon EKS データプレーンの管理

AWS Systems Manager は、AWS リソースの安全なエンドツーエンド管理ソリューションです。AWS リソース管理の簡素化、運用タスクの自動化、規模に応じた IT 運用を効率化するための運用管理ツール群を提供します。

AWS Systems Manager を利用することで、インスタンスの完全な管理、パッチやソフトウェアのインストールの自動化、運用データの収集と監視、インスタンスへのリモートアクセスの有効化などが可能になります。これは、AWS インフラストラクチャを効率的に管理するための重要なツールです。また、AWS Systems Manager を利用することでノードを作成する際にカスタマイズすることができます。

この記事では、AWS System Manager State Manager を利用して、ノード上にコンテナイメージをキャッシュします。キャッシュを最新の状態に保つために、このソリューションではイベント駆動アーキテクチャを使用して、新しいコンテナイメージがイメージレジストリにプッシュされた際にキャッシュを更新します。コンテナイメージをノードにキャッシュすることで、コンテナの起動時間を大幅に短縮することができます。コンテナが作成される際、コンテナランタイムはコンテナレジストリからイメージをプルする必要がありません。その結果、水平スケーリングがほぼ即座に行われます。

ソリューションの概要

Pod がノードにスケジュールされる前に、サンプルアプリケーションのコンテナイメージをキャッシュすることで、Pod の起動時間を短縮します。このソリューションでは、Amazon EventBridge と AWS Systems Manager を使用することで自動化を行います。サンプルアプリケーション用の Amazon Elastic Container Registry (Amazon ECR) レポジトリに新しいコンテナイメージをプッシュするたびに、AWS Systems Manager はすべてのワーカーノードで新しいコンテナイメージをプルするコマンドを実行します。

新しいワーカーノードが起動すると、AWS Systems Manager は同様のオートメーションを実行してコンテナイメージをプルします。以下の図は、SSM Automation を使用して Amazon EKS ノードにデータをプリフェッチするイベント駆動プロセスを設定するアーキテクチャ図です。

SSM Automation を使用して EKS ノードにデータプリフェッチするイベント駆動プロセスの設定

このソリューションを実施するためのプロセスは次の通りです。

  • はじめに、コンテナイメージを取得するための Amazon ECR リポジトリを特定します。
  • 次に、コンテナイメージが Amazon ECR にプッシュされると Amazon EventBridge によってイベントベースのルールがトリガーされ、Amazon ECR からコンテナイメージをプリフェッチする AWS Systems Manager (SSM) Automation が実行されます。
  • 新しいワーカーノードがクラスターに追加されるたびに、ワーカーノードのタグに基づいて、AWS Systems Manager State Manager の関連付けが新しいノードにコンテナイメージをプリフェッチします。

前提条件

このソリューションを実行するには、次の前提条件が必要です。

  • AWS サービスを操作するための AWS CLI バージョン 2.10 またはそれ以上
  • Amazon EKS クラスターを作成および管理するための eksctl
  • Amazon EKS クラスターで kubectl コマンドを実行するための kubectl
  • 環境変数の置換を行うための envsubst (envsubst は gettext パッケージに含まれています)
  • コマンドラインで JSON 処理を行うための jq

ウォークスルー

この記事で利用するソースコードは、GitHub の AWS-Samples で公開しています。

git clone https://github.com/aws-samples/containers-blog-maelstrom/
cd containers-blog-maelstrom/prefetch-data-to-EKSnodes

まずは、環境変数の設定から始めます。

export EDP_AWS_REGION=us-east-1
export EDP_AWS_ACCOUNT=$(aws sts get-caller-identity --query 'Account' --output text)
export EDP_NAME=prefetching-data-automation

Amazon EKS クラスターを作成します。

envsubst < cluster-config.yaml | eksctl create cluster -f -

サンプルアプリケーションのコンテナイメージを格納するための Amazon Elastic Container Registry リポジトリを作成します。

aws ecr create-repository \
    --cli-input-json file://repo.json \
    --repository-name ${EDP_NAME} \
    --region $EDP_AWS_REGION

サイズが大きなコンテナイメージを作成します。

./build-docker-image.sh

Amazon EventBridge で利用する AWS Identity and Access Management (AWS IAM) ロールを作成します。

aws iam create-role \
    --role-name $EDP_NAME-role \
    --assume-role-policy-document file://events-trust-policy.json

Amazon EventBridge が AWS Systems Manager を使用して EKS クラスターのワーカーノード上でコマンドを実行することを許可するポリシーをロールにアタッチします。

aws iam put-role-policy \
    --role-name ${EDP_NAME}-role \
    --policy-name ${EDP_NAME}-policy \
    --policy-document "$(envsubst < events-policy.json)"

Amazon ECR リポジトリのプッシュイベントを検知する Amazon EventBridge ルールを作成します。

envsubst  events-rule-updated.json
aws events put-rule \
    --cli-input-json file://events-rule-updated.json \
    --region $EDP_AWS_REGION
rm events-rule-updated.json

System Manager Run Command をターゲットとしてアタッチします。Amazon ECR のリポジトリに新しいコンテナイメージをプッシュするたびに、Amazon EventBridge は SSM Run Command をトリガーして、ワーカーノードに新しいコンテナイメージをプルします。

envsubst '$EDP_AWS_REGION $EDP_AWS_ACCOUNT $EDP_NAME'  events-target-updated.json
aws events put-targets --rule $EDP_NAME \
  --cli-input-json file://events-target-updated.json \
  --region $EDP_AWS_REGION
rm events-target-updated.json

新しいワーカーノードでサンプルアプリケーションのコンテナイメージをプリフェッチするために、AWS Systems Manager State Manager の関連付けを作成します。

envsubst '$EDP_AWS_REGION $EDP_AWS_ACCOUNT $EDP_NAME'  statemanager-association-updated.json
aws ssm create-association --cli-input-json \
  file://statemanager-association-updated.json \
  --region $EDP_AWS_REGION
rm statemanager-association-updated.json

注意: AWS SSM State Manager の関連付けのステータスは、初回実行まで「失敗」の状態になります。

バリデーション

これで、自動化の準備が整いました。新しいコンテナイメージをリポジトリにプッシュして検証します。Pod をスケジュールする前にワーカーノードにコンテナイメージがプルされれば、自動化が正しく動作していることがわかります。

既存ノードでのコンテナイメージのプリフェッチテスト

Amazon ECR リポジトリにログインします。

aws ecr get-login-password \
    --region $EDP_AWS_REGION | docker login \
    --username AWS \
    --password-stdin $EDP_AWS_ACCOUNT.dkr.ecr.$EDP_AWS_REGION.amazonaws.com

先ほど作成したコンテナイメージをプッシュします。

docker push $EDP_AWS_ACCOUNT.dkr.ecr.$EDP_AWS_REGION.amazonaws.com/$EDP_NAME

Amazon ECR にコンテナイメージをプッシュすると、Amazon EventBridge にイベントが発行されます。その結果、Amazon EventBridge で作成したルールが AWS Systems Manager Run Command をトリガーします。
ルールの起動は、AWS Management Console で確認することができます。AWS Management Console で Amazon EventBridge のページに移動し、イベントのモニタリングタブに切り替えます。FailedInvocations にデータが表示されていなければ、EventBridge はイベントを AWS Systems Manager に正常に配信しています。

Amazon EventBridge Rule モニタリングのスクリーンショット

注意: モニタリンググラフにデータポイントが表示されるまで 3〜5 分かかる場合があります。

AWS Systems Manager Run Command が Amazon EventBridge によってトリガーされたか確認します。以下のコマンドを実行し、実行履歴を確認します。DocumentName が AWS-RunShellScript であること、実行時間を示す RequestedDateTime、Run Command が成功したかどうかを示す Status の項目を確認します。

aws ssm list-command-invocations \
    --details \
    --filter "[{\"key\": \"DocumentName\", \"value\": \"arn:aws:ssm:${EDP_AWS_REGION}::document/AWS-RunShellScript\"}]" \
    --region $EDP_AWS_REGION

Output:

{
    "CommandInvocations": [
        {
            "CommandId": "eeb9d869-421d-488f-b1ba-ce93a69db2b0",
            "InstanceId": "i-0e1a4977c389*****",
            "InstanceName": "ip-192-168-29-214.ec2.internal",
            "Comment": "",
            "DocumentName": "arn:aws:ssm:us-east-1::document/AWS-RunShellScript",
            "DocumentVersion": "$DEFAULT",
            "RequestedDateTime": "2023-02-17T17:35:48.520000-06:00",
            "Status": "Success",
            "StatusDetails": "Success",
            .......
            .......

ワーカーノードがコンテナイメージをプルしたことを確認します。ワーカーノードにログインするために SSM エージェントを使用します。

aws ec2 describe-instances \
    --filters "Name=tag:eks:cluster-name,Values=$EDP_NAME" "Name=tag:eks:nodegroup-name,Values=nodegroup" \
    --query "Reservations[*].Instances[*].InstanceId" \
    --output text \
    --region $EDP_AWS_REGION | xargs -I {} aws ssm start-session \
    --target {} \
    --document-name AWS-StartInteractiveCommand \
    --parameters "command=echo \$(curl -s http://169.254.169.254/latest/meta-data/instance-id) && sudo ctr -n k8s.io images ls | grep ${EDP_NAME}" \
    --region $EDP_AWS_REGION
Output:

Starting session with SessionId: i-084630900d41ea7bf-0412132f6a6ec65b0
i-051edb69ee3c4de4e
0123456789.dkr.ecr.us-east-2.amazonaws.com/prefetching-data-automation:latest

注意: Cannot perform start session: EOF error が表示された場合は、コマンドを再実行してください。このエラーは、Amazon Systems Manager SSM エージェントの問題によって発生します。

新しいノードでのコンテナイメージのプリフェッチテスト

新しいバージョンのコンテナイメージがプッシュされると同時に、既存ノードがそのイメージをプルすることを確認しました。ここでは、クラスターに新しく参加したノードでも最新バージョン (直近にプッシュされたもの) のコンテナイメージがプルされることを検証します。

このプロセスをテストするために、既存の EKS ノードグループのサイズを大きくして新しいノードを作成します。

eksctl scale nodegroup \
  --cluster $EDP_NAME \
  --name nodegroup \
  --nodes 2 \
  --nodes-min 1 \
  --nodes-max 3 \
  --region $EDP_AWS_REGION

新しいノードが利用可能になったら、そのノードにコンテナイメージがキャッシュされているか確認します。

aws ec2 describe-instances \
    --filters "Name=tag:eks:cluster-name,Values=$EDP_NAME" "Name=tag:eks:nodegroup-name,Values=nodegroup" \
    --query "Reservations[*].Instances[*].InstanceId" \
    --output text \
    --region $EDP_AWS_REGION | xargs -I {} aws ssm start-session \
    --target {} \
    --document-name AWS-StartInteractiveCommand \
    --parameters "command=echo \$(curl -s http://169.254.169.254/latest/meta-data/instance-id) && sudo ctr -n k8s.io images ls | grep ${EDP_NAME}" \
    --region $EDP_AWS_REGION
Output:

Starting session with SessionId: i-084630900d41ea7bf-0412132f6a6ec65b0
i-051edb69ee3c4df2f
0123456789.dkr.ecr.us-east-2.amazonaws.com/prefetching-data-automation:latest                                                  application/vnd.docker.distribution.manifest.v2+json      sha256:eb0703bef7c8312b517495a9f2d0cc41384b6fdd66b2dc10d266e0032613fb63 1.1 GiB   linux/amd64                io.cri-containerd.image=managed

コンテナの起動改善

コンテナイメージをプリフェッチすることで、コンテナの起動時間を大幅に短縮することができました。ノードがコンテナイメージのキャッシュを持っていない場合、コンテナを起動する前にコンテナイメージ全体をダウンロードする必要があります。

例として、この記事で使用した 1GB のテストイメージを使用したコンテナの起動には 60 秒かかります。同じコンテナでも、ノードにコンテナイメージのキャッシュがあれば、1 秒で起動します。

コンテナの起動時間を秒単位で表したグラフ

設計上の注意

このソリューションはコンテナイメージをキャッシュする設計になっていますが、古いコンテナイメージを削除することはありません。未使用のコンテナイメージを削除するには、kubelet のガベージコレクションを利用します。ルートボリュームの使用率が 85% を超えると、kubelet は自動的に未使用のコンテナイメージを削除します。

コンテナイメージをプリフェッチすると、ノードのローカルストレージが逼迫することがあります。ストレージ容量の問題に当たらないように、キャッシュするコンテナイメージの数を制限してください。

また、このソリューションではコンテナイメージのプルがノード上で Pod がスケジュールされる前に行われることを想定しています。コンテナイメージがキャッシュされる前に Pod がスケジュールされた場合、ノードはコンテナレジストリからイメージをプルする必要があるため、この手法は有効ではありません。お客様は、オーバープロビジョニングを利用して、新しいノードで Pod がスケジュールされる前にプリフェッチが実行されるようにすることができます。

クリーンアップ

この記事で作成したリソースを削除するまではコストが発生し続けます。この記事で作成したリソースを削除するには、以下のコマンドを使用します。

./cleanup.sh

コンテナイメージの遅延読み込み

遅延読み込みとは、アプリケーションの起動と並行してレジストリからデータをダウンロードする手法です。Seekable OCI (SOCI) は AWS がオープンソース化したテクノロジーで、コンテナイメージを遅延読み込みすることによりコンテナの起動を高速化することができます。

遅延読み込みを使用するには、お客様はコンテナイメージの SOCI インデックスを構築する必要があり、コンテナビルドプロセスに追加のステップが足されます。ビルドプロセスをコントロールできるお客様は、コンテナの起動時間を改善するために SOCI の利用を検討することもできます。

まとめ

この記事では、ノードにコンテナイメージをキャッシュすることで Pod の起動を高速化する方法を紹介しました。AWS Systems Manager (SSM) を利用して、Amazon EKS クラスターのワーカーノードでコンテナイメージをプリフェッチすることで、サイズが大きなイメージであっても Pod の起動時間を数秒まで大幅に短縮することができます。この手法は、機械学習、シミュレーション、データ分析、コードビルドなどのワークロードを実行しているお客様に大きなメリットをもたらし、コンテナ起動のパフォーマンスとワークロード全体の効率を向上させます。Kubernetes ベースの環境におけるコンテナ起動の遅さという問題を対処するために、この手法はインフラストラクチャや Kubernetes リソースを追加で管理する必要がない、コスト効率に優れたサーバーレスソリューションを提供します。

翻訳はソリューションアーキテクトの鈴木が担当しました。原文はこちらです。