Amazon Web Services ブログ

EC2 Image builder とイメージキャッシュ戦略による Windows コンテナ起動時間の高速化

この記事は、Speeding up Windows container launch times with EC2 Image builder and image cache strategy を翻訳したものです。

本投稿は、Sr. Solution Architect – Microsoft Specialist の Marcio Morales により寄稿されました。

お客様から「コンテナイメージのサイズが原因で、Windows コンテナの起動が速くない」という話を何度も聞いたことがあります。これは部分的には真実ですが、「大きなイメージ」を解明し、ディスク上での高コストな操作 (前処理、原文では extract・ion という言葉を本記事では「前処理」としています) を回避し、Windowsコンテナの起動を高速化するためのキャッシュ戦略をどのように実施するかが重要です。

また、多くの場合、次のような比較を耳にします ー Linux コンテナ対 Windows コンテナ、Windows と比較して Linux はどれだけ速いか。これは事実ですが、それぞれのプラットフォームが異なる立ち位置にあり、それぞれの課題に対応しているため、この比較はあまり価値がありません。例えば、開発者は Linux コンテナで ASP.NET アプリケーションを実行するべきではありませんし、Windows コンテナで Python を実行するべきでもありません。

比較はそれとして、Windows コンテナの起動をはじめることにしましょう。

問題の解決方法を掘り下げる前に、前提を理解しておきましょう。まず Windows ベースの Amazon Elastic Container Service (Amazon ECS) クラスターや、Windows ノードグループを持つ Amazon Elastic Kubernetes Service (Amazon EKS) クラスターを運用しているとします。クラスターの容量を追加するために EC2 Auto Scaling が頻繁にトリガーされるようなハイプレッシャーなコンテナ環境では、EC2 Auto Scaling がトリガーされてから Windows コンテナがトラフィックを受け入れるまで、すなわちコンテナが準備できるようになるまでに 4~8 分程度かかることがあります。高コストな I/O 操作を避けるための適切なアプローチを用いなければ、これが現実になるかもしれません。

Windows コンテナイメージの解説

ECS/EKS Optimized Windows AMI には、2 つのコンテナベースイメージがあります。

mcr.microsoft.com/windows/servercore
mcr.microsoft.com/windows/nanoserver

組み込みベースイメージは、ECS/EKS 最適化 Windows AMI 上ですでに前処理されています。プッシュ/プル操作時には、イメージを構成するレイヤーのみがリポジトリにアップロード/ダウンロードされます。以下の例では、302.25MB にしか圧縮されていない iis-dnn-a82378d43adb という Amazon Elastic Container Registry (Amazon ECR) イメージを示しています。これは、プッシュ/プル操作時のアップロード/ダウンロードのサイズです。

しかし、以下のような docker image ls の出力では、ディスク上の iis-dn-a82378d43adb のイメージサイズが 5.73GB と表示されていますが、その容量をプルして前処理したわけではありません。プル操作で起きたことは、先ほどの圧縮された 302.25MB のみがダウンロードされ、前処理されたということです。

REPOSITORY                                                          TAG                 IMAGE ID            CREATED             SIZE
mcr.microsoft.com/windows/servercore                                ltsc2019            152749f71f8f        2 weeks ago         5.27GB
010101011575.dkr.ecr.us-east-1.amazonaws.com/iis-dnn-a82378d43adb   latest              de4f5f1edfe0        3 weeks ago         5.73GB

サイズ (SIZE) 欄には、全体のサイズが 5.73GB と表示されています。その内訳は、

組み込みベースイメージ = 5.27GB
アプリケーションレイヤー = 圧縮後 302.25MB / ディスク上に展開後 = 460MB
ディスク上の総イメージサイズ = 5.73GB

ベースイメージは既にディスク上に存在しているため、ディスク上の追加容量は 460MB となります。今後このような GB 単位のサイズを目にしても、あまり気にしないでください。80% 以上が既に組み込みベースイメージとしてディスク上に存在している可能性があります。ここまでの説明を分析すると、Windows コンテナの起動が遅い主な問題は、全体のイメージサイズではないことがわかります。むしろ、Pull/Extraction といった操作による追加レイヤーのプル、前処理、そして利用可能にするのにかかる時間が問題なのです。

コンテナイメージキャッシュ戦略の効果

Windows コンテナのデプロイを高速化するために、Amazon EC2 Image Builder を使用して、AMI のビルドパイプライン中に Amazon ECR リポジトリからコンテナイメージをプルします。プルするコンテナイメージは、アプリケーションイメージや Fluentd、Fluent Bit などのサイドカーコンテナ、またはソリューションに必要なその他のコンテナイメージなど、ソリューション全体に必要なものでなければなりません。

このアプローチでは、高コストな I/O 操作 (ファイルの前処理) はコンテナの起動ではなく、AMI のビルド作成時に行われます。その結果、必要なイメージレイヤーはすべて AMI 上に前処理され、すぐに使用できるようになり、Windows コンテナが起動してトラフィックの受け入れを開始するまでの時間を短縮することができます。

以下の出力は、Amazon EKS でホストされた Windows Pod 上で ASP.NET アプリケーションを実行した結果を示しています。この環境は 2 つの異なるノードグループで構成されており、1 つはバニラの EKS 最適化 Windows AMI、もう 1 つはこの記事で提案しているソリューションを使用して構築されたカスタムの EKS 最適化 AMI を持つノードグループです。

この最初の例では、kubectl describe pod により、Pod がノードにスケジューリングされてから「Started」ステータスになるまでの時間が出力されます。ご覧のように、イメージを「Pulled」するのにかかった時間は 54 秒です。(これは、Docker がすでに存在するイメージのメタデータをチェックするのにかかった時間です。この例では、キャッシュ戦略は Windows ノードに実装されています。)

Events:
  Type    Reason     Age   From               Message
  ----    ------     ----  ----               -------
  Normal  Scheduled  70s   default-scheduler  Successfully assigned default/iis-dnn-ondemand-deployment-574c8789bd-f986k
 to ip-172-31-42-147.ec2.internal
  Normal  Pulled     *54s*   kubelet            Container image "010101010575.dkr.ecr.us-east-1.amazonaws.com/iis-dnn-a823 78d43adb" already present on machine
  Normal  Created    53s   kubelet            Created container iis-dnn-ondemand
  Normal  Started    16s   kubelet            Started container iis-dnn-ondemand

次の例では、イメージを「Pulled」するのにかかった時間は 6 分です。(これは、Docker イメージがマシンに提示されていないことを識別し、Amazon ECR から追加のレイヤーをプルしてディスクに展開した時間です。) 前処理は最もコストのかかる操作であり、Windows コンテナの起動が遅れる最も一般的な根本原因です。

2つの結果を比較すると、キャッシュイメージ戦略を使用することで、最初の Pod が「Started」ステータスに到達し、トラフィックを受信し始めるまでの時間を最大 6 倍に短縮できることが明らかになりました。

Events:
  Type    Reason     Age    From               Message
  ----    ------     ----   ----               -------
  Normal  Scheduled  7m16s  default-scheduler  Successfully assigned default/iis-dnn-ondemand-deployment-7f7545cf48-82gx
n to ip-172-31-1-96.ec2.internal
  Normal  Pulling    *7m4s*   kubelet            Pulling image "010101010575.dkr.ecr.us-east-1.amazonaws.com/iis-dnn-a8237 8d43adb"
  Normal  Pulled     64s    kubelet            Successfully pulled image "010101010575.dkr.ecr.us-east-1.amazonaws.com/i is-dnn-a82378d43adb" in *6m0.7576396s*
  Normal  Created    63s    kubelet            Created container iis-dnn-ondemand
  Normal  Started    62s    kubelet            Started container iis-dnn-ondemand

Windows コンテナの起動を高速化するカスタム EKS/ECS Windows AMI の構築

これを実現するにはどうすればよいでしょうか?

前提条件と仮定:

この記事では、Amazon EKS をオーケストレーターとして使用しています。キャッシュ戦略は AMI レベルで構築されていますが、Amazon ECS クラスターにも同じアプローチを使用できます。

この記事では、以下のタスクを行います。

  1. EC2 Image Builder カスタムコンポーネントを作成します。
  2. カスタム EKS/ECS 最適化 Windows AMI パイプラインを構築します。
  3. カスタム EKS 最適化 Windows AMI を使用して、EKS ノードグループを作成します。
  4. 結果を確認します。

1. EC2 Image Builder カスタムコンポーネントの作成

Image Builder では、コンポーネント管理アプリケーション (AWSTOE) を使用して、複雑なワークフローのオーケストレーション、システム構成の変更、システムのテストを行います。AWS マネジメントコンソールで Image Builder を使用する場合や、ユーザーに代わって AWSTOE と対話する Image Builder コマンドを使用する場合、追加のサーバー設定は必要ありません。

AWSTOE は、YAML ドキュメントを使用して、イメージをカスタマイズするスクリプトを定義します。ドキュメントには、ビルド、検証、テストの各フェーズを含めることができます。YAML ドキュメントの詳細については、「Document schema and definitions  (ドキュメントスキーマおよび定義)」を参照してください。

1.1 AWS マネジメントコンソールの EC2 Image Builder にて、画面左のメニューの [コンポーネント] をクリックしてから [コンポーネントを作成] をクリックします。

1.2 コンポーネントタイプでは [ビルド] タイプを選択し、コンポーネントの詳細のイメージオペレーティングシステム (OS) にて [Windows] を選択します。

1.3 定義ドキュメントの [コンテンツ] に以下の内容を追加し、Amazon ECR の URL とイメージの URL を環境に合ったものに置き換えます。

name: DockerPull
description: DockerImageCacheStrategy.
schemaVersion: 1.0

phases:
  - name: build
    steps:
      - name: Dockerpull
        action: ExecutePowerShell
        inputs:
          commands:
            - (Get-ECRLoginCommand).Password | docker login --username AWS --password-stdin 01010101.dkr.ecr.us-east-1.amazonaws.com
            - docker pull 01010101575.dkr.ecr.us-east-1.amazonaws.com/iis-dnn-a82378d43adb
            - docker pull 01010101575.dkr.ecr.us-east-1.amazonaws.com/fluentd-a729311dbs

1.4 ページ最下部の [コンポーネントの作成] をクリックします。

2. カスタム EKS/ECS 最適化 Windows AMI パイプラインの構築

このステップでは、カスタムの EKS/ECS 最適化 Windows AMI を自動的にビルドするためのイメージパイプラインを作成します。EC2 Image Builder のメインページにて [イメージパイプラインを作成する] をクリックします。

2.1 [パイプライン名]、[説明]、[ビルドスケジュール] を入力します。この例では、パイプラインは毎週自動的に実行され、最新の Windows アップデートがイメージにインストールされるようにします。入力したらページ下部の [次へ] をクリックします。

2.2 [新しいレシピを作成する] を選択します。イメージタイプでは [Amazon マシンイメージ (AMI)] を選択してください。

2.3 一般にて [名前] と [バージョン] を入力し、ベースイメージにてイメージオペレーティングシステム (OS) を [Windows]、イメージのオリジンは [クイックスタート (Amazon 管理)] を選択します。

2.4 ここは重要なステップです。[イメージ名] にて Windows ノードのベースイメージとして提供される Windows Server イメージを選択する必要があります。この例では、[Windows Server 2004 English Core Base x86] を選択します。Amazon ECS  の場合、EC2 Image Builder には ECS エージェントがインストールされているイメージがすでに用意されていますが、EKS の場合はそうではありません。EKS コンポーネントを搭載するために Windows Server 2004 を準備します。

2.5 もう 1 つの重要なオプションは、自動バージョニングオプションにて [利用可能な最新の OS バージョンを使用する] というオプションを選択することです。これは、AWS が生成した時点でのすべての Windows アップデートと AMI の新機能やパフォーマンスの改善が含まれています。

2.6 [コンポーネント] パネルでは、ステップ 1 で作成したコンポーネントをイメージパイプラインにアタッチしますが、まず、この AMI にコアコンポーネントを追加することを確認します。検索ボックスに「EKS」と入力し、表示された [eks-optimized-ami-windows] を選択します。 

続いてシーケンスパネルには 2 つのオプションが表示されます。パイプラインで最新の Kubelet および Docker バージョンを使用するか、特定のバージョンを使用するかを設定できます。注意すべきことは、説明にて EKS バージョンが 1.16 (この翻訳記事時点の最新版では 1.17) となっていることです。ここではシーケンスパネルにおいて [使用可能な最新のコンポーネントバージョンを使用する]、つまり AWS がリリースした最新バージョンを使用するように選択します。この記事投稿の時点では Kubernetes 1.20 (翻訳記事時点の最新版は 1.21) です。詳細は公式ドキュメントで確認できます。

2.7 AMI に最新のセキュリティアップデートをインストールするために、update-windows コンポーネントを追加することをお勧めします。[コンポーネント] パネルにて「update-windows」と入力して検索してください。

2.8 いよいよ Docker pull と呼ばれるキャッシュ戦略コンポーネントを追加します。検索を [ユーザー所有] に変更し、Docker pull またはステップ 1 で選択した名前で検索します。

2.9 最終的には以下の 3 つのコンポーネントを持つことになります。

  • eks-optimized-ami-windows
  • update-windows
  • docker pull

2.10 EC2 Image Builder で生成された IAM インスタンスプロファイルは、Amazon ECR にログインするために必要なポリシーをすでに持っています。(ステップ 1.3 において、Amazon ECR リポジトリにログインするために以下のコマンドを使用しています。)

(Get-ECRLoginCommand).Password | docker login --username AWS --password-stdin 01010101575.dkr.ecr.us-east-1.amazonaws.com

2.11 パイプラインとインフラストラクチャの作成が完了するまで、必要な次のステップをすべて実行します。EC2 Image Builder の配布設定を使用してイメージを配布します。

3. カスタム EKS 最適化 Windows AMI を使用した Amazon EKS ノードグループの作成

結果をテストするには、お好みのデプロイツールを使用して新しい AMI を使用して新しいノードグループを追加するか、既存の Auto Scaling グループにアタッチされている既存の起動テンプレートを編集します。この時点では、AMI を生成するために EC2 Image Builder パイプラインを手動で起動する必要があります。

このブログ記事では、eksctl を使用して新しい Amazon EKS ノードグループを作成し、カスタム AMI を指定します。

3.1 eksctl 設定ファイルを調整して新しいノードグループを追加し、カスタム AMI を指定します。重要なステップとして、AMI ID と AMI Family を指定する必要があります。amiFamily を指定しないと、そのノードはクラスターに参加できません。

apiVersion: eksctl.io/v1alpha5
  kind: ClusterConfig
  
  metadata:
    name: eks-windows
    region: us-east-1
    version: '1.20'  
  availabilityZones: 
      - us-east-1f
      - us-east-1b
  
  nodeGroups:
    - name: windows-ng-sac2004-customami-test
      instanceType: c5.xlarge
      minSize: 1
      ami: ami-0556136c21149e0ac
      amiFamily: WindowsServer2004CoreContainer
.......

また、EC2 Image Builder に最新の Amazon Machine Image (AMI) を参照する既存の起動テンプレートの新バージョンを作成させ、EC2 Auto Scaling を自動的に更新させることもできます。

まとめ

このブログ記事では、Windows コンテナの起動を高速化するためにキャッシュコンテナイメージ戦略を使用する方法を紹介しましたが、OS に関係なく、コンテナのサイドカーや CI ビルドコンテナなど、あらゆるコンテナワークロードを高速化するために、同じアプローチを使用することもできます。

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