FLINTERS Engineer's Blog

FLINTERSのエンジニアによる技術ブログ

AWS ECSテストをスムーズに!ローカル環境の強力ツール

こんにちは。株式会社FLINTERS エンジニアの瀬田です。

FLINTERSブログ祭りの企画に沿って今回の記事を執筆しました。

はじめに

Amazon ECS(Elastic Container Service)は、DockerコンテナをAmazon Web Service(AWS)上で実行・管理するためのサービスとして広く利用されています。しかし、AWS環境でのデプロイとローカル環境での開発・テストの間にはさまざまな問題が潜んでおり、コンテナをローカル環境で正確にテストするのは難しいことがあります。

特に、AWS Software Development Kit(SDK) を使って他のAWSサービスを操作する際には次のような処理を書くことがあると思います。

# Python版のSDK(boto3)でのコード
url_path = os.environ.get("AWS_CONTAINER_CREDENTIALS_RELATIVE_URI")
url = urljoin("http://169.254.170.2", url_path)
res = requests.get(url, timeout=3).json()
os.environ["AWS_ACCESS_KEY_ID"] = res["AccessKeyId"]
os.environ["AWS_SECRET_ACCESS_KEY"] = res["SecretAccessKey"]
os.environ["AWS_SESSION_TOKEN"] = res["Token"]

self.session = boto3.Session(
    aws_access_key_id=os.environ["AWS_ACCESS_KEY_ID"],
    aws_secret_access_key=os.environ["AWS_SECRET_ACCESS_KEY"],
    aws_session_token=os.environ["AWS_SESSION_TOKEN"],
)

上記は、ECS上では必要な処理でありますが、ローカル環境で動かそうとするとエラーが起きます。

またECSで用いるIAMロールも含めて検証しようとすると、都度ECSにデプロイして動作確認する必要がありました。

その解決策として、AWSからAmazon ECS Local Container Endpointsが提供されています。このツールはECSのモックエンドポイントをローカル環境で提供し、開発者がローカル環境でのテストをクラウド環境と同じようにシンプルかつ効率的に行えるようにします。

Amazon ECS Local Container Endpointsとは?

github.com

Amazon ECS Local Container Endpointsは、ローカル環境でのECSタスクの実行をシームレスにするためのツールです。これにより、開発者はクラウドのリソースをローカルに模擬し、デプロイ前に詳細なテストを行うことができます。

主要な機能は次のとおりです:

  • モックエンドポイントの提供: ローカル環境でECS Task IAM RolesとECS Task Metadataのモックエンドポイントを立て、AWS上のような振る舞いをシミュレートします。
  • IAMポリシーの検証: AWSコンテナランタイムに似た仕組みをローカルで再現し、IAMポリシーの検証を容易にします。
  • ローカルデバッグ: AWS環境にデプロイする前に、細かなデバッグ作業をローカルで行うことができます。

なぜ使うべきか?

ECS Local Container Endpointsを使用する利点はいくつかあります。

1. 開発・デプロイのスピードアップ

AWS上にデプロイせずに、ローカル環境で即座にテスト・デバッグが可能です。これにより、開発サイクルが高速化します。

2. 一貫性のある環境の再現

モックエンドポイントを用いることで、AWS環境とローカル環境での挙動が一致し、不整合を減少させます。結果として、本番環境における予測外の動作を事前に察知しやすくなります。

3. コスト削減

テストをAWS環境上で実行する必要がないため、開発コストが下がります。必要に応じてモックエンドポイントを活用することで、有料リソースの利用を抑えられます。

セットアップ方法

前提

以下のコードを動かすことを前提とします。

S3からファイルを取得し、内容を出力するコードです。 sample.py

import os
import boto3
import requests
from urllib.parse import urljoin

# AWS_CONTAINER_CREDENTIALS_RELATIVE_URI環境変数からURLパスを取得
url_path = os.environ.get("AWS_CONTAINER_CREDENTIALS_RELATIVE_URI")

# ECSタスクロールエンドポイントのURLを構築
url = urljoin("http://169.254.170.2", url_path)

# ECSタスクロールエンドポイントから認証情報を取得し、JSONとしてパース
res = requests.get(url, timeout=3).json()

# 取得した認証情報を環境変数に設定
os.environ["AWS_ACCESS_KEY_ID"] = res["AccessKeyId"]
os.environ["AWS_SECRET_ACCESS_KEY"] = res["SecretAccessKey"]
os.environ["AWS_SESSION_TOKEN"] = res["Token"]

# 取得した認証情報を使用して、Boto3セッションを作成
session = boto3.Session(
    aws_access_key_id=os.environ["AWS_ACCESS_KEY_ID"],
    aws_secret_access_key=os.environ["AWS_SECRET_ACCESS_KEY"],
    aws_session_token=os.environ["AWS_SESSION_TOKEN"],
)

# S3クライアントを作成
s3_client = session.client("s3")

# S3バケットからオブジェクトを取得
response = s3_client.get_object(Bucket="ecs-local-endpoints-sample", Key="sample.txt")
content = response['Body'].read().decode('utf-8')

print(content)

1. ローカル環境での準備

ディレクトリ構成

構成は以下のようになります。

.
├── sample.py
├── Dockerfile
├── compose.yml
└── compose.override.yml

Docker関連のファイル作成

ファイルはそれぞれ以下の内容で作成します。

Dockerfile

FROM python:3.10-slim-buster

RUN pip install --upgrade pip
RUN pip install boto3 requests

WORKDIR /usr/app/sample

ENTRYPOINT ["tail", "-f", "/dev/null"]

compose.yml

services:
  sample-app:
    build:
      context: .
    volumes:
      - .:/usr/app/sample
    ports:
      - 8000:80

compose.override.yml

networks:
  credentials_network:
  # ECSのメタデータをローカルで再現するためのネットワークを作成
  # ローカルメタデータサービスが特定のIPアドレスにバインドできるように構成
    driver: bridge
    ipam:
      config:
        - subnet: "169.254.170.0/24"
          gateway: 169.254.170.1
services:
    ecs-local-endpoints:
      image: amazon/amazon-ecs-local-container-endpoints
      volumes:
        # /var/runをマウントして、docker.sockにアクセスしてDockerと通信できるようにする
        - /var/run:/var/run
        # AWS CLIとAWS SDKで使用される共有構成ディレクトリをマウント
        - $HOME/.aws/:/home/.aws/:ro
      environment:
        # ホームフォルダを定義
        # 資格情報は$HOME/.awsから読み取られる
        HOME: "/home"
        # 使用するAWS CLIプロファイルを指定
        AWS_PROFILE: <profile name>
      networks:
        credentials_network:
          # ECSメタデータのエンドポイント
          ipv4_address: "169.254.170.2"

    sample-app:
      depends_on:
        - ecs-local-endpoints
      networks:
        credentials_network:
          ipv4_address: "169.254.170.3"
      environment:
        AWS_DEFAULT_REGION: "ap-northeast-1"
        # タスクロールを用いない場合
        # AWS_CONTAINER_CREDENTIALS_RELATIVE_URI: "/creds"
        # タスクロールを用いる場合
        AWS_CONTAINER_CREDENTIALS_RELATIVE_URI: "/role/<AWSで用いているロール名>"

2. AWS上での準備

S3バケットの構成

S3バケットecs-local-endpoints-sampleには、sample.txtを以下のように配置してください。

ecs-local-endpoints-sample
└── sample.txt

IAMロールの設定

ポリシーの設定

今回使用するIAMロールに対して、以下のポリシーを設定してください

  • AmazonEC2ContainerServiceRole
  • AmazonS3ReadOnlyAccess
信頼ポリシーの設定

ECSで用いるIAMロールに対し、以下の信頼ポリシーを設定してください

{
    "Version": "2008-10-17",
    "Statement": [
        {
            "Sid": "statement1",
            "Effect": "Allow",
            "Principal": {
                "Service": "ecs.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        },
        {
            "Sid": "Statement2",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::<AWSアカウントID>:root"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}

3. コンテナの起動

以下のコマンドを実行し、コンテナを起動します。

docker compose -f compose.yml -f compose.override.yml up -d

これで、開発中のコンテナに加えてモックエンドポイントが起動し、ローカル環境でもAWS環境さながらをテストが可能になります。

おわりに

Amazon ECS Local Container Endpointsは、ローカル環境でAWS ECSのテストを簡潔かつ効率的に行うための非常に強力なツールです。 これにより、開発者はローカルとAWS環境での不整合を避けつつ、高速なデバッグと開発を実現できます。

ぜひこのツールを活用し、検証を一層スムーズに進めてみてください。