TL;DR;
以下のコードでCDKからSOCIインデックスをビルドし、ECRにプッシュすることができます。
# 依存関係のインストール
npm install deploy-time-build
import { SociIndexBuild } from 'deploy-time-build; const asset = new DockerImageAsset(this, 'Image', { directory: 'example-image' }); // DockerImageAssetに対応するSOCIインデックスをビルド・プッシュ SociIndexBuild.fromDockerImageAsset(this, 'Index', asset);
はじめに
先日AWS FargateでSeekable OCI (SOCI)が利用できるようになりました。これにより、ECSタスクの起動時間を高速化することができます。
こちらのポストによれば、特にサイズの大きなイメージで効果を発揮するようです。 (141MBで25%、1GBで75%の高速化)
I did a few quick experiments on SOCI support for Fargate / ECR, comparing the gains for different image sizes.
— Apparent Order (@apparentorder) July 18, 2023
tl;dr – for big images, this is a huge win. For small images it doesn't make much of a difference.
Performance
One thing upfront: Several places, including the quoted… https://t.co/beq9yaJ56F
この機能を利用するためには、対象のコンテナイメージに対応するSOCIインデックスをビルドし、同じECRリポジトリにプッシュする必要があります。 このために主に次の方法が用意されており、それぞれ以下の特徴があります *1 。
- soci-snapshotter CLIの利用
- cfn-ecr-aws-soci-index-builder ソリューションの利用
- ✅ CloudFormation一発で構築可能
- ❌ 非同期にインデックスがプッシュされるため、タスク実行時にはまだインデックスが存在しない状況が起こりえる
- ❌ Lambda上でインデックスをビルドするため、扱えるコンテナイメージサイズに上限がある
ということで、どちらも微妙に面倒なんですよね。巷のベンチマーク結果だけ見て、うちは(試すのも大変だし)良いかと見送った人もいるんじゃないでしょうか。
さて、AWS CDKではコンテナイメージをCDKデプロイ時にビルドする慣習があります。 コンテナイメージと同様に、CDKからSOCIインデックスをデプロイできると便利そうですよね。 この記事では、そのための機能を作った話や使い方をまとめます。
作ったもの
deploy-time-build
というコンストラクトライブラリを開発しました。これにより、CDKのデプロイ中にSOCIインデックスをビルド・プッシュできるようになります。
使い方
イメージのタグとECRリポジトリを指定することで、イメージに対応するインデックスをビルドし、同リポジトリにプッシュします。
以下は someRepository
という名前のECRリポジトリの中の someTag
タグがついたイメージに対して、インデックスを付加する例です:
import { Repository } from 'aws-cdk-lib/aws-ecr'; import { SociIndexBuild } from 'deploy-time-build; new SociIndexBuild(this, 'Index', { imageTag: 'someTag', repository: Repository.fromRepositoryName(this, "Repo", "someRepository") });
実用的には、CDKの DockerImageAsset
に対してインデックスを付加したい場合が多いと思います。これも簡単に書けて、以下は example-image
ディレクトリのDockerfileをビルドしつつ、そのイメージのインデックスを付加する例です:
import { DockerImageAsset } from 'aws-cdk-lib/aws-ecr-assets'; const asset = new DockerImageAsset(this, 'Image', { directory: 'example-image' }); SociIndexBuild.fromDockerImageAsset(this, 'Index', asset);
CDKのECSモジュールからコンテナイメージを参照する際は、AssetImage
クラスを使うことも多いと思います。その場合は、先に定義した DockerImageAsset
から AssetImage
作成すると良いでしょう:
import { AssetImage } from 'aws-cdk-lib/aws-ecs'; const assetImage = AssetImage.fromDockerImageAsset(asset);
SOCIインデックスがプッシュされてない状態では、ECSサービスをデプロイしたくない場合もあると思います。その場合は、ECSサービスのリソースに対する依存関係を設定すれば良いです:
import { FargateService } from 'aws-cdk-lib/aws-ecs'; const service = new FargateService(/* 略 */); const index = SociIndexBuild.fromDockerImageAsset(/* 略 */); // indexをデプロイした後にECSサービスをデプロイする service.node.defaultChild!.node.addDependency(index);
Python CDKユーザーの人も同様に使えます:
pip install deploy-time-build from deploy_time_build import SociIndexBuild asset = DockerImageAsset(self, "Image", directory="example-image") SociIndexBuild(self, "Index", image_tag=asset.asset_hash, repository=asset.repository) SociIndexBuild.from_docker_image_asset(self, "Index2", asset)
上記が基本的な使い方となります。仕組みが気になる方は、次をご覧ください。
仕組み
CDKデプロイ時にCodeBuildプロジェクトのジョブを開始し、そのジョブ内でインデックスをビルド・プッシュしています。
CodeBuild上でSOCIインデックスをビルドするためにスタンドアロンなツールがほしかったので、soci-wrapperというCLIを作成・利用しています。これは上で紹介した cfn-ecr-aws-soci-index-builder のコードを流用しているため、生成物も同一になります。 このCLIの背景についてはこちらのIssueも参考になると思います: Ability to run soci create command in CodeBuild #760
理想的にはCDK CLIに同機能が組み込まれていると良いでしょう。(コンテナイメージ自体のビルドはローカルで行うため。)しかしながら、現状soci-snapshotterがLinux上でしか動かないことを考えると、Linux/Windows/Macで同様に動作する必要があるCDK CLIでは難しそうに思います。とりあえずIssueだけは立てています: core: push SOCI index when publishing docker image assets #26413
まとめ
AWS CDKから簡単にSOCIインデックスをデプロイするためのCDKコンストラクトライブラリを紹介しました。
昨日のJAWSコンテナ支部でSOCIインデックスが話題に出ていたので、半年前書きかけた記事を完成させられました。ありがとうございます!
本日の資料ですhttps://t.co/hFDDmTRP89#jawsug_ct
— takahash (@_takahash) March 7, 2024
*1:他には aws-samples/aws-fargate-seekable-oci-toolbox リポジトリも参考になります