コンテナイメージサイズ削減はがんばりすぎなくてよい。Seekable OCIがあればね


先日の以下の記事では、AlpineやDistrolessなどの軽量コンテナを含めた比較をしました。

AWSのコンテナのベースイメージ何がいいのか

では、なぜ軽量コンテナを利用するのでしょうか? 一般的には以下のふたつの理由があると思います。

  • 余計なファイルが含まれるとそれだけ攻撃の足掛かりになったり、脆弱性対応が必要になる
  • コンテナイメージサイズが大きくなるとイメージのPullに時間がかかり、起動までに時間がかかる

後者については、データプレーンがEC2であればイメージキャッシュを活用することでPullの時間を削減することができます。 しかし、Fargateではその仕組み上イメージキャッシュを利用できません。

そんな悩めるFargateのために、 Seekable OCI(SOCI) というAWSで開発されたコンテナイメージを遅延ロードする仕組みがFargateでも利用できるようになりました。

https://aws.amazon.com/about-aws/whats-new/2023/07/aws-fargate-container-startup-seekable-oci/

今回はこちらを使ってコンテナ起動時間がどれだけ改善するのかを確認していきたいと思います。

SOCI有効化用のセットアップ

SOCIを利用するためには、もともとのコンテナイメージのマニフェストから、 SOCI用のインデックスなどの追加のマニフェストを作成する必要があります。

そのためにいくつかの手段が用意されています。

  • SOCI Snapshotterを使い手元の環境でSOCIインデックスを作成する
  • CodePipelineに含めてイメージビルドのパイプラインに組込む
  • ECRへのPushを検知して追加でSOCIインデックスを作成する

最初は、手元にSOCI Snapshotter環境を構築しようとしたのですが、手元のWSLではcontainerdで名前解決がうまくいきませんでした。 まずはSOCIの効果を試すために、今回はECRのPushから作成する方式で試してみました。 WSLでは、ずっとrootless Podmanを使っているので、何か干渉しているのか、はたまた他の原因があるのか。それも今後セットアップ方法を確立していきたいですね。

さて、ECRとの連携ですが、AWS側でサンプルのCloudformationが用意されているので、これをデプロイするだけで簡単にセットアップすることができます。

https://aws-ia.github.io/cfn-ecr-aws-soci-index-builder/

これをデプロイすると、上記ドキュメントにあるような構成がセットアップされます。

図にあるように以下のようなECRへのプッシュ成功を検知するEventBridgeルールが生成されるので、 Pushに成功するとLambdaが走って自動でSOCIインデックスを作成してくれるというわけです。

{
  "detail-type": ["ECR Image Action"],
  "source": ["aws.ecr"],
  "detail": {
    "result": ["SUCCESS"],
    "action-type": ["PUSH"]
  },
  "region": ["us-east-1"]
}

また、全部のイメージが対象になるわけではなく、前段の ImageActionEventFilter というLambdaで フィルタリングされるので、デフォルトではすべてが対象ですが、特定のリポジトリや特定のタグだけ 次のSOCIインデックス作成処理に回すことも可能です。

起動時間の計測

以前の記事で作成したコンテナイメージのうち、もっとも軽いdistrolessともっとも重いcorretto on Amazon Linuxのイメージについて、 SOCIインデックスあり、なしで起動時間を計測してみました。

イメージ名ベースイメージイメージサイズ (圧縮後サイズ)
OpenJDK on distrolessgcr.io/distroless/java17-debian12:latest246 MB (103.80 MB)
Amazon Corretto on Amazon Linuxdocker.io/library/amazoncorretto:17-al2023478 MB (234.36 MB)

実際、SOCIインデックスを作成した側にはもろもろメタデータが追加されていることが確認できます。

SOCI無し

SOCI有り

計測対象

計測区間としては以下の3点で測りました。

  • (1) Fargateのタスク開始 - タスク作成完了
  • (2) タスク作成完了 - Javaアプリ起動後最初のログが出力されるまで

(1)は、イメージPullの時間になりますが、実際はそれだけではありません。Fargateの実行ホストの準備、ENIのアタッチなどの時間も含まれます。 ENIのアタッチについても結構時間がかかる作業なので、その影響によるブレが発生するのですが、そこは複数回測定して平均を取ることで全体としてならして検証しようと思います。

(2)は、SOCIのしくみが遅延読込みということで、コンテナ起動は速くても実際に動きだすまでには追加の時間がかかることも想定されます。そこを確認するために計測してみました。

計測結果

それぞれ5回ずつ計測し、平均値をとった結果は以下のようになりました。

イメージSOCI(1) タスク作成時間(秒)(2) Javaアプリ起動時間(秒)合計(秒)
Corretto-35.9660.11536.081
Corretto17.0773.84120.918
distroless-23.1190.11223.231
distroless20.3001.49221.792

期待通り、タスク作成までにかかる時間はSOCIを利用する方が早いという結果になりました。 一方で、懸念していたとおり、コンテナの起動は速くてもJavaアプリの起動には通常時に比べて時間がかかっています。 総じてトータルでは速いという結果になったもの、distrolessのこの差であれば、わざわざやらなくてもよいかな、という気がしますね。

使いどころの考察

コンテナイメージが大きい場合、SOCIの利用を検討したほうがよいと思います。

478 MB (234.36 MB)という、Javaであればそこまで大きすぎないイメージで、タスク起動の時間が半減、アプリ起動にその分のオーバーヘッドがかかるとはいえ、それでもトータルで4割程度の時間削減ができています。

どうしてもデバッグなどの都合で各種周辺ツールを入れないといけない場合、もっとイメージサイズが大きくなることも想定されるため、 SOCIを積極的に活用することで、スケールアウトやリリースの際にすばやくコンテナ起動することができるでしょう。

その一方で、軽量コンテナを使っていて、シェルなども不要で済む場合は、SOCIを使ってもそこまでの効果はみこめません。 今回試したコンテナイメージよりもさらに軽いコンテナイメージの場合、 アプリ起動のオーバーヘッドを含めるとかえって不利になる可能性もあるので使わないほうがよいのではないかと思います。

いずれの場合も、ためしに一度やってどれほどの効果があるのかを、机上検討ではなく実際に測定して判断することをおすすめします。

まとめ

今回は、Fargateのタスク起動を高速化するSeekable OCIについて、その効果を検証しました。 実際、イメージサイズが大きいコンテナの場合、起動速度の大幅な貢献につながると思いますので、 タスク起動の時間に悩んでいる人は、ぜひ使ってみてください。


関連記事