Amazon Web Services ブログ

Amazon EKS のコンテナ起動時間を Bottlerocket のデータボリュームを活用することで短縮

この記事は Reduce container startup time on Amazon EKS with Bottlerocket data volume (記事公開日: 2023 年 10 月 19 日) を翻訳したものです。

はじめに

コンテナは、モダンでスケーラブルなアプリケーションをデプロイするための頼りになるソリューションになっています。これらのコンテナの起動時間は、特に大きなコンテナイメージを必要とするワークロードを処理する場合に大きな課題となる可能性があります。たとえばデータ分析や機械学習のワークロードには、1 GiB を超えるサイズのイメージが含まれることがよくあります。generative AI などのこの種のワークロードを Amazon Elastic Kubernetes (Amazon EKS) で実行する場合、Amazon Elastic Container Registry (Amazon ECR) などのイメージレジストリからこれらの大きなイメージを取り出して抽出するのに数分かかることがあります。これはパフォーマンスに悪影響を及ぼし、ユーザーエクスペリエンスの低下につながります。
イメージをプリフェッチして Pod をより速く起動する方法を紹介した既存の投稿があります。Amazon EventBridgeAWS System Manager を使用してコンテナイメージをノードにキャッシュし、新しいイメージがイメージレジストリにプッシュされたときにキャッシュを更新します。既存のワーカーノードや継続的なイメージキャッシュに適しています。しかし、クラスターがスケールアップするにつれて新しいワーカーノードが追加されると、すべてのイメージを新しいワーカーノードに取り込むのに時間がかかります。
この投稿では、Bottlerocket で実行されるインスタンスを使用して、この課題に取り組むためのソリューションを紹介します。Bottlerocket は、AWS がコンテナの実行専用に設計した、オープンソースの Linux ベースのオペレーティングシステム (OS) であり、大きなイメージのコンテナ起動時間を短縮するのに役立ちます。

ソリューションの概要

コンテナランタイムが新しいコンテナを開始すると、まずローカルディスクに必要なイメージコンテンツがあるかどうかを確認します。image pull policy が明示的に Always に設定されていない限り、これはデフォルトの動作です。これにより、システムは常にイメージリポジトリからイメージを取得します。
イメージがローカルに存在しない場合は、イメージリポジトリからイメージを取得します。この取得プロセスには、特にサイズが数 GB の大きなイメージの場合、数分以上かかることがあります。
コンテナイメージの取得プロセスを高速化するには、次のようないくつかのオプションがあります。

  1. スリムなベースイメージを選択して、コンテナイメージのサイズを最小化する
  2. マルチステージビルドを使用して、不要な中間コンテンツを取り除く
  3. より大きなインスタンスサイズでより高い帯域幅を採用することで、コンテナイメージのダウンロードを高速化する
  4. コンテナイメージの内容をローカルでプリフェッチすることで、イメージをダウンロードする必要をなくす

シナリオによっては、前述のオプション 1 と 2 を適用した後でも、コンテナイメージのサイズがまだ大きい場合があります。明らかにこのようなシナリオでは、オプション 4 の方が高速でコスト効率の高い選択肢です。
この記事では、Bottlerocket オペレーティングシステムのデータボリューム機能をどのように利用してコンテナイメージをプリフェッチできるかを詳しく説明します。これにより、特にデータ分析や AI/ML などのシナリオでは、コンテナを起動するまでの時間を短縮できる可能性があります。

Bottlerocket とは

Bottlerocket は Linux ベースのオープンソースオペレーティングシステムで、アマゾン ウェブ サービスがコンテナの実行専用に開発したものです。安全で一貫性があり、効率的に運用できるように設計されています。
Bottlerocket には、OS ボリュームとデータボリュームの2つのボリュームがあります。OS ボリュームは OS データとブートイメージの保存に使用されます。Bottlerocket は、一貫性を保証するために、毎回まったく同じ OS イメージから起動します。一方、データボリュームは、コンテナのメタデータや、イメージやエフェメラルボリュームなどのストレージの保存に使用されます。Bottlerocket について詳しく知りたい場合は、こちらのドキュメントをご覧ください。

なぜ Bottlerocket を選択するのか

Amazon EKS は、Amazon Linux と Bottlerocket を利用した Amazon EKS 最適化 Amazon Machine Images (AMI) を提供します。Amazon Linux AMI は、Amazon EKS マネージド型ノードグループのデフォルトの選択肢であり、最も一般的な AMI です。ただし、デフォルトでは OS とコンテナデータが共有されるボリュームは 1 つだけです。Amazon Linux AMI とは異なり、Bottlerocket AMI はコンテナデータ用のボリュームをネイティブで提供します。
Bottlerocket のこの設計により、OS バイナリの更新とセキュリティパッチのサイクルとは別に、プリフェッチされたイメージを含むデータボリュームを簡単にアタッチできます。

Volumes-of-Bottlerocket

図 1. Bottlerocket のボリューム
出典: Bottlerocket – a container-optimized Linux.

コンテナイメージをプリフェッチするにはどうすればよいか

このソリューションでは、Bottlerocket データボリュームの Amazon Elastic Block Store (Amazon EBS) スナップショットを作成し、そのスナップショットを Amazon EKS ノードグループで再利用して、ワーカーノードが起動した時点で必要なすべてのイメージがローカルディスクにプリフェッチされるようにします。
このプロセスはスクリプトによって自動化されており、詳細は以下のとおりです。

  1. Amazon EKS 最適化 Bottlerocket AMI を使用して Amazon Elastic Compute Cloud (Amazon EC2) インスタンスを起動します
  2. イメージリポジトリからアプリケーションイメージを取得します
  3. Bottlerocket データボリュームの Amazon EBS スナップショットを取得します
  4. Amazon EKS ノードグループを作成し、スナップショットをそのデータボリュームにマッピングします

図 2.アーキテクチャ
出典: Caching Container Images for AWS Bottlerocket Instances

次のセクションでは、ソリューション全体を段階的に説明します。コンテナの起動時間を短縮し、ビジネス目標をより効率的に達成する方法を学びます。

前提条件

本ブログのウォークスルーを実行する前に、以下の準備が必要です。

  • AWS アカウント
  • 以下のコマンドラインツールのインストール
    • eksctl: 0.147.0
    • kubectl: Major: version 1, Minor: version 27, GitVersion: v1.27.2
    • Amazon EKS cluster version: 1.24
    • jq: jq-1.6
    • Optional – Karpenter: v0.31

AWS Cloud9 環境をセットアップ

AWS Cloud9 統合開発環境 (IDE) は、いくつかのプログラミング言語とランタイムデバッガ、そしてビルトインターミナルをサポートし、リッチなコード編集体験を提供します。この記事では、AWS Asia Pacific(東京)リージョン内の AWS Cloud9 ですべてのコマンドを実行します。セットアップのガイドラインはこちらです。

リポジトリをクローンする

以下のコマンドを実行します。

cd ~/environment
git clone https://github.com/aws-samples/containers-blog-maelstrom.git 
cd containers-blog-maelstrom/bottlerocket-images-cache
find . -name "*.sh" | xargs chmod +755

AWS CLI などの必要なツールをインストール

ウォークスルーを実行するために、jq、eksctl、kubectl、AWS Command Line Interface(AWS CLI)などのコマンドラインツールをインストールする必要があります。以下のコマンドを実行して、これらのツールをインストールします。

cd ~/environment/containers-blog-maelstrom/bottlerocket-images-cache
./cloud9_init.sh

初期設定が成功したかどうかを以下のコマンドで確認します。

eksctl version
aws sts get-caller-identity --query Arn | grep eksworkshop-admin -q && echo "IAM role valid" || echo "IAM role NOT valid"

環境変数を設定

AWS Cloud9 のターミナルで、以下のコマンドを実行します。

export ACCOUNT_ID=$(aws sts get-caller-identity --output text --query Account)
export AWS_REGION=$(curl -s 169.254.169.254/latest/dynamic/instance-identity/document | jq -r '.region')
export AZS=($(aws ec2 describe-availability-zones --query 'AvailabilityZones[].ZoneName' --output text --region $AWS_REGION))
export EKS_CLUSTER_NAME=bottlerocket-eks-simulation

test -n "$AWS_REGION" && echo AWS_REGION is "$AWS_REGION" || echo AWS_REGION is not set

echo "export ACCOUNT_ID=${ACCOUNT_ID}" | tee -a ~/.bash_profile
echo "export AWS_REGION=${AWS_REGION}" | tee -a ~/.bash_profile
echo "export AZS=(${AZS[@]})" | tee -a ~/.bash_profile
echo "export AZS=(${AZS[@]})" | tee -a ~/.bash_profile
echo "export EKS_CLUSTER_NAME=${EKS_CLUSTER_NAME}" | tee -a ~/.bash_profile
aws configure set default.region ${AWS_REGION}
aws configure get default.region

ウォークスルー

ステップ 1: Bottlerocket 用の Amazon EBS スナップショットをビルドする

この投稿では、次の 2 つのイメージを Amazon EBS ボリュームにプリフェッチします。

  • ecr.aws/kubeflow-on-aws/notebook-servers/jupyter-pytorch: 1.12.1-cpu-py38-ubuntu20.04-ec2-v1.2
  • ecr.aws/eks-distro/kubernetes/pause: 3.2

Amazon EBS スナップショットワークフローを自動化するスクリプトを作成しました。このスクリプトは ~/environment/containers-blog-maelstrom/bottlerocket-images-cache にあります。次のコマンドを実行してスナップショットの作成を開始します。

cd ~/environment/containers-blog-maelstrom/bottlerocket-images-cache
./snapshot.sh -r $AWS_REGION public.ecr.aws/eks-distro/kubernetes/pause:3.2,public.ecr.aws/kubeflow-on-aws/notebook-servers/jupyter-pytorch:1.12.1-cpu-py38-ubuntu20.04-ec2-v1.2

複数のイメージのURL をカンマで区切ったコマンドに入れることができることに注意してください。
snapshot.sh スクリプトは、自動的に次のタスクを完了します。

  1. Amazon EKS 最適化 Bottlerocket AMI を使用して Amazon EC2 インスタンスを起動します
  2. イメージリポジトリからアプリケーションイメージを取得します
  3. Amazon EC2 インスタンスを停止します。
  4. Bottlerocket データボリュームの Amazon EBS スナップショットを取得します
  5. Amazon EC2 インスタンスを削除します

Amazon EBS スナップショットの作成には約 5 分かかります。ただし、イメージサイズが大きい場合は、時間がかかる場合があります。最終的に、必要なコンテナイメージがプリフェッチされた Amazon EBS スナップショットができあがります。
以下は成功した出力の例です。

[1/8] Deploying EC2 CFN stack ...

Waiting for changeset to be created..
Waiting for stack create/update to complete
Successfully created/updated stack - Bottlerocket-ebs-snapshot
[2/8] Launching SSM . done!
[3/8] Stopping kubelet.service .. done!
[4/8] Cleanup existing images .. done!
[5/8] Pulling ECR images:
  public.ecr.aws/eks-distro/kubernetes/pause:3.2 - amd64 ... done
  public.ecr.aws/eks-distro/kubernetes/pause:3.2 - arm64 ... done
  public.ecr.aws/kubeflow-on-aws/notebook-servers/jupyter-pytorch:1.12.1-cpu-py38-ubuntu20.04-ec2-v1.2 - arm64 ... done
 done!
[6/8] Stopping instance ... done!
[7/8] Creating snapshot ... done!
[8/8] Cleanup.
--------------------------------------------------
All done! Created snapshot in ap-northeast-1: snap-xxxxxxxxx

Amazon EBS snapshot id を環境変数に出力

Amazon EBS スナップショットコマンドを正常に実行した後、この Amazon EBS snapshot id を確認することができます。これから利用するためにこの値を環境変数として出力します。

EBS_SNAPSHOT_ID=snap-xxxxxxx
echo "export EBS_SNAPSHOT_ID=${EBS_SNAPSHOT_ID}" | tee -a ~/.bash_profile

ステップ 2: Amazon EKS のクラスターをセットアップ

eksctl を利用して、クラスター設定を作成

AWS Cloud9 のターミナルを開き、新しい Amazon EKS クラスターを作成するための以下のコマンドを実行します。新しいクラスターを作成する時間は、15 分〜 20 分程度かかります。

cd ~/environment/containers-blog-maelstrom/bottlerocket-images-cache
cat <<EOF> eks-clusterconfig.yaml
---
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig

metadata:
  name: $EKS_CLUSTER_NAME
  region: $AWS_REGION
  version: '1.24'
iam:
  withOIDC: true
managedNodeGroups:
  - name: no-prefetch-mng
    instanceType: m5.2xlarge
    desiredCapacity: 1
    minSize: 1
    maxSize: 2
    privateNetworking: true
    amiFamily: Bottlerocket
  - name: prefetch-mng
    instanceType: m5.2xlarge
    desiredCapacity: 1
    minSize: 1
    maxSize: 2
    privateNetworking: true
    amiFamily: Bottlerocket
    additionalVolumes:
      - volumeName: '/dev/xvda' # OS Volume
      - volumeName: '/dev/xvdb'  # Data Volume
        snapshotID: $EBS_SNAPSHOT_ID
EOF

eksctl create cluster -f eks-clusterconfig.yaml

Bottlerocket のためのマネージド型ノードグループをセットアップ

この YAML ファイルでは、次の 2 つの Amazon EKS ノードグループを含む新しい Amazon EKS クラスターを作成しました。

  • no-prefetch-mng ノードグループは、ステップ 2 で Amazon EBS Snapshot_ID を使用せずに Amazon EKS ノードを起動します。つまり、Amazon EKS ノードにはプリフェッチされたイメージがありません
  • prefetch-mng ノードグループは、Amazon EBS Snapshot_ID が /dev/xvdb にマップされた Amazon EKS ノードを起動します。つまり、イメージはすでに Amazon EKS ノードでプリフェッチされています。詳細については、YAML ファイルの additionalVolumes セクションを参照してください

Amazon EKS クラスターに接続

aws eks update-kubeconfig --name $EKS_CLUSTER_NAME --region $AWS_REGION

ノードの準備が完了することを待つ

次のコマンドを使用して、ノードの準備が整っているかどうかを確認できます。

watch kubectl get nodes

2 つのノードが準備完了状態になるまで待ってください。次のような出力が得られます。

Every 2.0s: kubectl get nodes -o wide 

NAME                                                 STATUS   ROLES    AGE     VERSION                INTERNAL-IP       EXTERNAL-IP   OS-IMAGE                                KERNEL-VERSION   CONTAINER-RUNTIME      
ip-192-168-xxx-xxx.ap-northeast-1.compute.internal   Ready    <none>   2m36s   v1.24.15-eks-fae4244   192.168.xxx.xxx   <none>        Bottlerocket OS 1.14.2 (aws-k8s-1.24)   5.15.117         containerd://1.6.20+bot
tlerocket                                                                                                                                                                                                             
ip-192-168-xxx-xxx.ap-northeast-1.compute.internal   Ready    <none>   2m30s   v1.24.15-eks-fae4244   192.168.xxx.xxx   <none>        Bottlerocket OS 1.14.2 (aws-k8s-1.24)   5.15.117         containerd://1.6.20+bot
tlerocket 

ステップ 3: 両方のノードグループにデプロイを開始する

Kubernetes Deployment では public.ecr.aws/kubeflow-on-aws/notebook-servers/jupyter-pytorch: 1.12.1-cpu-py38-ubuntu20.04-ec2-v1.2 イメージを使用しています。イメージサイズは 4.93 GB です。ワーカーノードがイメージをプリフェッチしていない場合、Pod が起動する前に Amazon ECR からイメージをダウンロードするのにしばらく時間がかかります。
各ノードグループに 2 つの Pod を作成するには、以下のコマンドを使用します。

kubectl apply -f - << EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: inflate-no-prefetch
spec:
  replicas: 1
  selector:
    matchLabels:
      app: inflate-no-prefetch
  template:
    metadata:
      labels:
        app: inflate-no-prefetch
    spec:
      terminationGracePeriodSeconds: 0
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
              - matchExpressions:
                - key: "alpha.eksctl.io/nodegroup-name"
                  operator: "In"
                  values: ["no-prefetch-mng"]
      containers:
        - name: inflate-amazon-linux
          image: public.ecr.aws/kubeflow-on-aws/notebook-servers/jupyter-pytorch:1.12.1-cpu-py38-ubuntu20.04-ec2-v1.2
          resources:
            requests:
              cpu: 1
EOF

kubectl apply -f - << EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: inflate-prefetch
spec:
  replicas: 1
  selector:
    matchLabels:
      app: inflate-prefetch
  template:
    metadata:
      labels:
        app: inflate-prefetch
    spec:
      terminationGracePeriodSeconds: 0
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
              - matchExpressions:
                - key: "alpha.eksctl.io/nodegroup-name"
                  operator: "In"
                  values: ["prefetch-mng"]
      containers:
        - name: inflate-bottlerocket
          image: public.ecr.aws/kubeflow-on-aws/notebook-servers/jupyter-pytorch:1.12.1-cpu-py38-ubuntu20.04-ec2-v1.2
          resources:
            requests:
              cpu: 1
EOF

コンテナのデフォルトの image pull policy は ifNotPresent です。つまり、イメージがまだローカルに存在していない場合にのみイメージがプルされます。image pull policy が Always に設定されている場合、プリフェッチ機能は動作しません。

inflate-no-prefetch Pod のイベントをチェック

kubectl get events コマンドを使用して、Pod 内のイベントをトレースできます。

NO_PREFETCH_POD=$(kubectl get pod -l app=inflate-no-prefetch -o jsonpath="{.items[0].metadata.name}")
kubectl get events -o custom-columns=Time:.lastTimestamp,From:.source.component,Type:.type,Reason:.reason,Message:.message  --field-selector involvedObject.name=$NO_PREFETCH_POD,involvedObject.kind=Pod

inflate-no-prefetch Deployment からの Pod イベントは次のとおりです。

Time                   From                Type     Reason      Message
2023-08-07T17:13:52Z   default-scheduler   Normal   Scheduled   Successfully assigned default/inflate-no-prefetch-6c45cd9b4c-c7dn8 to ip-192-168-180-250.ap-northeast-1.compute.internal
2023-08-07T17:13:53Z   kubelet             Normal   Pulling     Pulling image "public.ecr.aws/kubeflow-on-aws/notebook-servers/jupyter-pytorch:1.12.1-cpu-py38-ubuntu20.04-ec2-v1.2"
2023-08-07T17:14:41Z   kubelet             Normal   Pulled      Successfully pulled image "public.ecr.aws/kubeflow-on-aws/notebook-servers/jupyter-pytorch:1.12.1-cpu-py38-ubuntu20.04-ec2-v1.2" in 48.003125227s
2023-08-07T17:14:41Z   kubelet             Normal   Created     Created container inflate-amazon-linux
2023-08-07T17:14:41Z   kubelet             Normal   Started     Started container inflate-amazon-linux

Pod は 2023-08-07T 17:13:52 Z に予定されていて、2023-08-07T 17:14:41 Z に開始されました。Amazon ECR から大きなイメージを取得するのに時間がかかるため、コンテナを起動するのに 49 秒かかりました。

inflate-prefetch Pod のイベントを確認

PREFETCH_POD=$(kubectl get pod -l app=inflate-prefetch -o jsonpath="{.items[0].metadata.name}")
kubectl get events -o custom-columns=Time:.lastTimestamp,From:.source.component,Type:.type,Reason:.reason,Message:.message  --field-selector involvedObject.name=$PREFETCH_POD,involvedObject.kind=Pod

inflate-prefetch Deployment からの Pod イベントは次のとおりです。

Time                   From                Type     Reason      Message
2023-08-07T17:13:53Z   default-scheduler   Normal   Scheduled   Successfully assigned default/inflate-prefetch-f5ff79858-g87cb to ip-192-168-97-187.ap-northeast-1.compute.internal
2023-08-07T17:13:54Z   kubelet             Normal   Pulled      Container image "public.ecr.aws/kubeflow-on-aws/notebook-servers/jupyter-pytorch:1.12.1-cpu-py38-ubuntu20.04-ec2-v1.2" already present on machine
2023-08-07T17:13:55Z   kubelet             Normal   Created     Created container inflate-bottlerocket
2023-08-07T17:13:56Z   kubelet             Normal   Started     Started container inflate-bottlerocket

Pod は 2023-08-07T 17:13:53 Z に予定されていて、2023-08-07T 17:13:56 Z に開始されました。コンテナイメージはすでに Amazon EKS ノードに存在していたため、コンテナを起動するのに 3 秒しかかかりませんでした。

ステップ 4: 結果

サイズの大きなコンテナイメージをプリフェッチすることで、Pod の起動にかかる時間を 49 秒からわずか 3 秒に短縮できました。

Comparison-Node図3. 本ソリューションの適用/未適用における比較

他参考情報

Karpenter

Karpenter は、Kubernetes クラスタ内の Pod のスケジューリングを処理するために、コンピューティングリソースを自動的にスケーリングするためのオープンソースプロジェクトです。また、Amazon EBS スナップショットを使用して Bottlerocket ワーカーノードを起動するためにも使用できます。Karpenter Provisioner とノードテンプレートのサンプルは次のとおりです。
尚、Karpenter は alpha から beta に昇格することとなっており、その過程で APIに変更が入っています。以下に記載したサンプルは、変更前のサンプルとなります。変更についての詳細は、こちらのブログをご参照ください。

apiVersion: karpenter.sh/v1alpha5
kind: Provisioner
metadata:
  name: bottlerocket-provisioner
spec:
  providerRef:
    name: bottlerocket
  labels:
    billing-team: map-team
  annotations:
    example.com/owner: "my-team"
  requirements:
    - key: "node.kubernetes.io/instance-type"
      operator: In
      values: ["m5.2xlarge"]
    - key: "karpenter.sh/capacity-type" 
      operator: In
      values: [ "spot", "on-demand" ]

  limits:
    resources:
      cpu: 1000

  ttlSecondsAfterEmpty: 30

  weight: 20
---
apiVersion: karpenter.k8s.aws/v1alpha1
kind: AWSNodeTemplate
metadata:
  name: bottlerocket
spec:
  subnetSelector:
    karpenter.sh/discovery: xxxxxxxx-stack
  securityGroupSelector:
    karpenter.sh/discovery: xxxxxxxx-stack
  amiFamily: Bottlerocket
  tags:
    managed-by: "karpenter"
    intent: "api-server"
  blockDeviceMappings:
    - deviceName: /dev/xvda
      ebs:
        volumeSize: 10Gi
        volumeType: gp3
    - deviceName: /dev/xvdb
      ebs:
        volumeSize: 80Gi
        volumeType: gp3
        snapshotID: snap-xxxxxxxxxx

subnetSelector のスタック名と、blockDeviceMappings の snapshotID を忘れずに更新してください。

Automation

コンテナイメージのビルドが完了した直後に Amazon EBS スナップショットの作成を開始したい場合は、コンテナビルド後に snapshot.sh を継続的インテグレーション (CI) ツールで実行できます。GitHub Actions を使用した自動化スクリプトのサンプルを作成し、そのスクリプトを GitHub にプッシュしています。
説明のために、GitHub Action YAML のセクションを抽出しました。Job セクションは、build_llm_image と build_ebs の 2 つの部分に分かれています。

  • build_llm_image は主にコンテナイメージをビルドします。これは標準の Docker ビルドプロセスなので、次の YAML ファイルではこの部分は省略します
  • build_ebs は、Amazon EBS スナップショットを取る際の核となる部分です
name: Build LLM Model and EBS Snapshot

on:
  push:
    branches: [ master ]
    paths:
      - 'sd-gen-sdkxl-consumer/**'

jobs:
  build_llm_image:
    runs-on: ubuntu-latest
    steps:
      // ...
      // skip build steps for docker login, docker build, docker push
      // for the complete script, please reference GitHub repo
      
  build_ebs:
    runs-on: ubuntu-latest
    # Only Run after `build_llm_image` job success (image have been pushed to ECR)
    needs: build_llm_image
    steps:
      - name: Checkout source code
        uses: actions/checkout@v2

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: YOUR_AWS_REGION

      - name: Build EBS Snapshot
        id: build-ebs-snapshot
        env:
          SNAPSHOT_ID: ""
        run: |
          cd sd-gen-sdkxl-consumer/automation
          . ./../run.sh
          echo "SNAPSHOT_ID:" $SNAPSHOT_ID

      - name: Commit and Push NodeTemplate
        id: update-node-template
        run: |
          echo "commit and push now"
          echo "SNAPSHOT_ID" $SNAPSHOT_ID
  • Configure AWS credentials ステップでは、GitHub Action ランナーが Amazon EBS スナップショットスクリプトを実行するための正しい AWS IAM アクセス権限を提供します。必要な AWS IAM 権限は、こちらで確認できます。この AWS IAM ポリシーを本番環境で使用する場合は、アクセス権限をできるだけ減らすことをお勧めします。尚、2023/12/5 時点においては、GitHub Actions から OIDC を利用し IAM Role を assume する方法が主流となっております。詳細については、GitHub のドキュメント Configuring OpenID Connect in Amazon Web Services をご参照ください。
  • Build EBS Snapshot ステップでは、Amazon EBS スナップショットスクリプトの実行が開始されます
  • Commit and Push NodeTemplate ステップでは、Amazon EBS SNAPSHOT_ID を取得します。その後、それを Amazon EKS YAML GitHub リポジトリにコミットして、ノードテンプレートを更新できます。

GibHub Action YAML ファイル全体に説明を追加していますので、ご参照ください。

クリーンアップ

Amazon EKS クラスタとノード

この記事で紹介したウォークスルーに従った場合料金が発生します。それを避ける場合には、以下のコマンドを実行して作成したリソースを削除することができます。

kubectl delete deploy inflate-no-prefetch --force
kubectl delete deploy inflate-prefetch --force

eksctl delete nodegroup no-prefetch-mng --cluster $EKS_CLUSTER_NAME 
eksctl delete nodegroup prefetch-mng --cluster $EKS_CLUSTER_NAME

eksctl delete cluster --name $EKS_CLUSTER_NAME

AWS Cloud9 環境

AWS Cloud9 環境を忘れずに削除してください。以下に削除方法を記載します。
以下の AWS CLI で AWS Cloud9 環境の Id を取得します。

aws cloud9 list-environments

// Output:
{
    "environmentIds": [
        "5d36cbf9b1e54fa0af7aa80eef9bbb42"
    ]
}

AWS Cloud9 環境を削除するには、以下の CLI コマンドを使用することもできます。以下のコマンドの YOUR_CLOUD9_ENV_ID を置き換えて、AWS Cloud9 環境を削除してください。

aws cloud9 delete-environment --region $AWS_REGION --environment-id YOUR_CLOUD9_ENV_ID

Amazon EBS スナップショット

作成された Amazon EBS スナップショットを削除するには、次の AWS CLI コマンドを実行します。

snapshot.sh.
aws ec2 delete-snapshot --snapshot-id $EBS_SNAPSHOT_ID

まとめ

この記事では Bottlerocket インスタンスのデータボリュームを使用してコンテナイメージをプリフェッチすることで、Amazon ECR から大きなイメージを引き出すのに必要な時間を大幅に短縮できることを紹介しました。この最適化により、起動時間が短縮され、Amazon EKS 上でのコンテナ起動の効率とパフォーマンスが劇的に改善されました。
このソリューションは、大きなコンテナイメージに依存するコンテナワークロードを持ち、起動時間を短縮することでアプリケーションの起動パフォーマンスを向上させようとしている組織にとって、大きなメリットになると確信しています。Bottlerocket の詳細については、Bottlerocket の公式ウェブサイトにアクセスしてください。

謝辞

私たちにインスピレーションを与え、この投稿を可能にするコードを提供してくれた同僚の Walkely He と Dongdong Yang に心から感謝します。

翻訳はパートナーソリューションアーキテクトの髙橋達矢が担当しました。原文はこちらです。