Amazon Web Services ブログ

AWS Fault Injection Simulator の Amazon ECS に関する新機能のお知らせ

はじめに

Amazon Elastic Container Service (Amazon ECS) や Amazon Elastic Kubernetes Service (Amazon EKS) で稼働するワークロードに様々なフォールトインジェクション (障害注入) ができる AWS Fault Injection Simulator (FIS) の新機能をお知らせします。このブログでは、Amazon ECS に関する新しい AWS FIS アクションの利用方法を紹介します。

AWS FIS は、アプリケーションの耐障害性を試験するためのフルマネージドサービスです。AWS FIS はカオスエンジニアリングの原則に従っており、AWS 環境における障害をシミュレートできます。ネットワーク停止、インフラ障害、サービス中断などをシミュレートできます。AWS FIS を活用した障害試験は、本番環境で障害が発生する前に潜在的な問題を発見し修正するのに役立ちます。

Amazon ECS タスクの新しいアクション

Amazon ECS ワークロードを対象に、新しく 6 個のフォールトインジェクションアクションを追加しました。新しい Amazon ECS タスクアクションには、「CPU 負荷をかける」「ストレージ I/O 負荷をかける」「プロセスの停止」「ネットワークトラフィックの停止」「ネットワークレイテンシーの増加」「パケットロス」などがあります。これらの新しいアクションにより、さまざなま障害シナリオによるアプリケーションの信頼性や回復力を簡単に評価できるようになりました。なお、AWS Fargate を利用している場合は、「CPU 負荷をかける」「ストレージ I/O 負荷をかける」のアクションは利用できますが、それ以外のアクションは利用できません。

アクション名 説明 利用できるコンピュートリソース
aws:ecs:task-cpu-stress CPU 負荷をシミュレート
設定できるパラメーターは、負荷をかける期間・目標の CPU 使用率・使用するストレッサーの数
Amazon EC2、AWS Fargate
aws:ecs:task-io-stress ストレージ I/O 負荷
をシミュレート設定できるパラメーターは、負荷をかける期間・負荷をかける際のファイルシステムの空き容量の割合・様々な I/O 負荷をかけるストレッサーの数
Amazon EC2、AWS Fargate
aws:ecs:task-kill-process 特定のプロセスを停止するシミュレート
設定できるパラメーターは、停止するプロセスの名前・送信するシグナル名
Amazon EC2 のみ
aws:ecs:task-network-blackhole-port ネットワークトラフィックの停止
設定できるパラメーターは、停止する期間・対象のポート番号やプロトコル
Amazon EC2 のみ
aws:ecs:task-network-latency ネットワークレイテンシーのシミュレート
設定できるパラメーターは、ネットワークレイテンシーを追加する期間・対象のネットワークインターフェース・追加する遅延やジッタ―のミリ秒・送信元の指定
Amazon EC2 のみ
aws:ecs:task-network-packet-loss ネットワークパケットロスのシミュレート
設定できるパラメーターは、パケットロスを実行する期間・対象のネットワークインターフェース・パケットロスの割合・送信元の指定
Amazon EC2 のみ

Amazon ECS タスクにフォールトインジェクションを行う仕組み

次の図は、AWS FIS が Amazon ECS タスクにフォールトインジェクションをどのように行うかを表現しています。AWS FIS は AWS Systems Manager SSM Agent を使って、フォールトインジェクションを実行しています。Amazon ECS タスク内で、サイドカーとして SSM Agent を動かすことで、AWS FIS がフォールトインジェクションを実行できるようにしています。これにより、Systems Manager の Run Command 経由で様々な障害試験を行うことで、潜在的な問題を発見し改善しやすくなります。AWS FIS のフォールトインジェクションを行うために、ECS のタスク定義に、SSM Agent のサイドカーを追加する必要があります。

ウォークスルー

次のステップで、AWS FIS を使ったフォールトインジェクションを体験できます。

  1. CDK を使用して、インフラストラクチャの構築とサンプルアプリケーションのデプロイ
  2. CDK によって自動生成された、ECS のタスク定義の確認
  3. AWS FIS にフォールトインジェクションを行うための権限設定
  4. CPU に負荷を与える試験
  5. ECS タスク内のプロセスを停止する試験

前提条件

このウォークスルーを行うためには、次の環境が必要です。

Step 1: インフラストラクチャの構築

AWS FIS を活用した障害試験を行うため、AWS CDK を利用して、Amazon VPC (Amazon Virtual Private Cloud) や Amazon ECS クラスター、Amazon ECS Service、AWS IAM (AWS Identity and Access Management) ロール、2 つのAmazon EC2 インスタンスを作成します。CDK コードは、GitHub リポジトリに公開されている ECS Blueprints を利用します。

サンプルコードをリポジトリからクローンします。

git clone https://github.com/aws-ia/ecs-blueprints.git
cd ecs-blueprints/cdk/examples/fis_service/

利用している環境に合わせて、AWS アカウントとリージョンの環境変数を設定します。次に、ECS Blueprints の CDK テンプレートで利用する .env ファイルを生成します。この記事では、オレゴンリージョン (us-west-2) を利用していきます。

export AWS_ACCOUNT=$(aws sts get-caller-identity --query 'Account' --output text)
export AWS_REGION=${AWS_REGION:=us-west-2}

sed -e "s/<ACCOUNT_NUMBER>/$AWS_ACCOUNT/g" \
-e "s/<REGION>/$AWS_REGION/g" sample.env > .env

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

# virtualenv を作成 
python3 -m venv .venv

# virtualenv を有効化
source .venv/bin/activate

# 依存しているライブラリをインストール
python -m pip install -r ../../requirements.txt

初めて CDK でインフラストラクチャを作成する場合は、Bootstrap を実行します。

cdk bootstrap aws://${AWS_ACCOUNT}/${AWS_REGION}

CDK を利用して、Amazon ECS クラスター を作成し、AWS FIS で試験を行うためのサンプルアプリケーションを稼働します。次のデプロイは、検証環境などで行うことを推奨します。

次のコマンドで、CDK スタックのデプロイを行います。

cdk deploy --all --require-approval never

Step 2: 生成された Amazon ECS タスク定義の確認

CDK を利用して、Amazon ECS クラスター・タスク定義・2 つの m5.large EC2 インスタンス・ロードバランサーを作成しました。このタスク定義は、Web アプリケーションと SSM Agent サイドカーで構成されています。SSM Agent サイドカーは、タスク定義で「必須 (essential)」と設定されています。そのため、サイドカーを停止すると Amazon ECS はタスク全体を停止して、タスクの再作成を行います。サイドカーは、activation script を実行して、Amazon ECS タスクを AWS Systems Manager のマネージドインスタンスとして登録します。

サイドカーは、IAM ロール (名前がFISService-SSMManagedInstanceRole から始まるもの) を利用して Amazon ECS タスクを AWS Systems Manager のマネージドインスタンスとして登録します。AWS FIS は AWS Systems Manager を利用して、Amazon ECS タスクにフォールトインジェクションを行います。この IAM ロールは AmazonSSMManagedInstanceCore ポリシーと、次の権限が付与されています。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": "ssm:DeleteActivation",
            "Resource": "*",
            "Effect": "Allow"
        },
        {
            "Action": "ssm:DeregisterManagedInstance",
            "Resource": "arn:aws:ssm:${AWS_REGION}:${AWS_ACCOUNT}:managed-instance/*",
            "Effect": "Allow"
        }
    ]
}

Amazon ECS タスクは、次の権限が付与されている IAM ロールを利用します。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": "iam:PassRole",
            "Resource": "arn:aws:iam::${AWS_ACCOUNT}:role/{MANAGED_INSTANCE_ROLE_NAME}",
            "Effect": "Allow"
        },
        {
            "Action": [
                "ssm:CreateActivation",
                "ssm:AddTagsToResource"
            ],
            "Resource": "*",
            "Effect": "Allow"
        }
    ]
}

Step 3: AWS FIS で障害試験を行うための権限設定

AWS FIS がお客様の AWS アカウントで障害試験を行うために、IAM ロールが必要です。まず、AWS FIS が利用する IAM ロールで必要な信頼ポリシーを作成します。

cat > fis-trust-policy.json << EOF
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": [
                  "fis.amazonaws.com"
                ]
            },
            "Action": "sts:AssumeRole"
        }
    ]
}
EOF

次に、AWS FIS が利用する IAM Role を作成します。

aws iam create-role --role-name ecs-fis-role \
 --assume-role-policy-document file://fis-trust-policy.json

フォールトインジェクションを行うには、ECS タスクに権限の追加が必要です。権限の追加を行うためのファイルを作成します。

cat > fis-ecs-experiment-policy.json << EOF
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "ssm:SendCommand",
                "ssm:ListCommands",
                "ssm:CancelCommand"
            ],
            "Effect": "Allow",
            "Resource": "*"
        }
    ]
}
EOF

IAM ロールにフォールトインジェクションに必要な権限を追加します。

aws iam put-role-policy --role-name ecs-fis-role \
  --policy-name ecs-fis-policy \
  --policy-document file://fis-ecs-experiment-policy.json

AWS FIS は、障害試験を安全に実行するためのコントロールとガードレールを提供します。Amazon CloudWatch のアラームに基づいた停止条件を設定し、アラームがトリガーされた時に障害試験を停止させることができます。

実行中のタスクの数を基に、障害試験の停止条件を設定できます。サンプルサービスはデフォルトで 3 つのタスクを稼働しています。もし障害試験によってタスクに障害が発生し、タスクのレプリカ数が 3 未満になった場合、AWS FIS はそれ以上のサービス停止を防ぐために障害試験を即座に終了します。

CloudWatch アラームを作成します。

aws cloudwatch put-metric-alarm \
  --alarm-name 'ECS Sample service task count alarm' \
  --actions-enabled \
  --metric-name 'RunningTaskCount' \
  --namespace 'ECS/ContainerInsights' \
  --statistic 'Average' \
  --dimensions '[{"Name":"ServiceName","Value":"fis-service"},{"Name":"ClusterName","Value":"ecs-blueprint-infra"}]' \
  --period 300 \
  --evaluation-periods 1 \
  --datapoints-to-alarm 1 \
  --threshold 3 \
  --comparison-operator 'LessThanThreshold' \
  --treat-missing-data 'missing' \
  --region $AWS_REGION

Step 4: CPU に負荷を与える試験を行う

CPU に負荷を与える試験は、CPU の負荷が高い状況でアプリケーションにどのようなパフォーマンス影響があるのか分析できます。例えば、アプリケーションが必要な CPU を利用できない場合、レスポンス時間の増加が発生する場合があります。CPU に負荷を与える試験は、基盤となるシステムが高負荷にさらされている場合に、ワークロードの堅牢性とお客様への影響を把握できます。負荷を与えている時に、アプリケーションの動作を観察することで、システムの改善点を発見できます。

例えば、CPU の負荷が高い状況でアプリケーションがリモートプロシージャーコールを行うと、操作が完了するまで時間がかかる場合があります。時間がかかることが問題となる場合、タイムアウト時間の調整やリトライを実装することが考えられます。また、レスポンスに時間が掛かる問題を改善するために、アプリケーションをスケールする方法も考えられます。

Amazon ECS サービスの配下で稼働している 3 つの ECS タスクに、AWS FIS のテンプレートを利用して 66 % の CPU 負荷 (2/3) をかけてみましょう。対象の Amazon ECS タスクを指定する際に、ARN・タグ・フィルター・クラスター名やサービス名といったパラメーターを利用できます。

66 % の負荷を指定する理由 : このクラスターは、2 つの Amazon EC2 インスタンス上で、3 つのタスクが稼働しています。100 % を指定すると、片方の EC2 インスタンスの負荷が高くなるため、均等に負荷を与えることを意識して 66 % と指定します。自身のクラスターで試験を行う際は、クラスターの状況に適した負荷の割合を指定してください。

AWS FIS の実験テンプレートを作成します。

cat > fis-ecs-experiment-template-cpu-stress.json << EOF
{
  "description": "ecs-task-cpu-stress",
  "targets": {
    "Tasks-Target-1": {
      "resourceType": "aws:ecs:task",
      "parameters": {
        "cluster": "ecs-blueprint-infra",
        "service": "fis-service"
      },
      "selectionMode": "PERCENT(66)"
    }
  },
  "actions": {
    "ecs-task-cpu": {
      "actionId": "aws:ecs:task-cpu-stress",
      "parameters": {
        "duration": "PT5M"
      },
      "targets": {
        "Tasks": "Tasks-Target-1"
      }
    }
  },
  "stopConditions": [
      {
         "source": "aws:cloudwatch:alarm",
         "value": "arn:aws:cloudwatch:${AWS_REGION}:${AWS_ACCOUNT}:alarm:ECS Sample service task count alarm"
       }
  ],
  "roleArn": "arn:aws:iam::${AWS_ACCOUNT}:role/ecs-fis-role",
  "tags": {}
}
EOF

FIS_EXPERIMENT_TEMPLATE_ID=$(aws fis create-experiment-template \
  --region $AWS_REGION \
  --tags Name=ecs-cpu-stress \
  --cli-input-json file://fis-ecs-experiment-template-cpu-stress.json \
  --query 'experimentTemplate.id' \
  --output text)

この試験の対象は、前の手順で作成したサンプルアプリケーションに紐づくすべてのタスクです。試験を 5 分間動かします。

試験を開始します。

aws fis start-experiment \
  --experiment-template-id $FIS_EXPERIMENT_TEMPLATE_ID \
  --region $AWS_REGION

AWS マネジメントコンソールで、Amazon ECS の画面を開き、ecs-blueprint-infra をクリックし、サービスタブから fis-service をクリックし、正常性とメトリクス のタブを開きます。

試験を実行してから 5 ~ 10 分ほど待つと、CPU の負荷が高くなっている事を確認できます。5 分ほど待機すると、グラフが更新されるはずです。

試験を行っている間、レスポンス時間の 95 %は、約 200 ミリ秒から、約 430 ミリ秒に増加していることがわかります。こういったレスポンス時間の増加に対応するために、CPU が不足している場合は、Amazon EC2 インスタンスをスケールアップするか、トラフィックを処理するタスク数を増やすことが考えられます。

5 分ほど待機して試験が終了するのを待つか、手動で試験を停止させたあとに、次のステップに進みます。

Step 5: AWS FIS を使って稼働中のプロセスを停止

次の試験は、AWS FIS を使ってタスクとして稼働しているプロセスを停止します。デプロイしたサンプルアプリケーションは、Flask を利用している Web アプリケーションです。この Web アプリケーションのコンテナは、タスク定義の中で「必須 (essential)」と定義されています。Python プロセスを停止すると、Amazon ECS はタスク全体を停止し、タスクを再作成します。AWS FIS を使って、Web アプリケーションのコンテナで稼働している Python プロセスを停止し、Amazon ECS が新しいタスクを再作成する様子を確認します。

AWS FIS を利用してプロセスの停止する試験を実施することで、実際の環境でプロセスが停止してしまった時のビジネス影響を評価するのに役立ちます。停止したプロセスがサービス提供において重要なコンポーネントの場合、Amazon ECS が停止したタスクの代わりに新しいタスクを再作成している間、サービス停止などの影響が発生し得ます。もし復旧までの時間が長く、ビジネスに影響がある場合は、予期しない障害を考慮してレプリカ数を増やすことが考えられます。また、正常な終了処理 (グレースフル・ターミネーション) を実装し、中断による影響を最小限に抑えることも重要です。

サンプルアプリケーションでは、3 つのタスクを実行しています。3 つタスクのうち 1 タスクを対象にして、「必須 (essential)」と定義されているプロセスを停止します。アプリケーションは 1/3 のキャパシティが減ることで、レスポンス時間の増加や、タイムアウト (ALB の 5xx エラー) などの影響が予想できます。

AWS FIS の実験テンプレートを作成します。

cat > fis-ecs-experiment-template-kill-proc.json << EOF
{
  "description": "ecs-task-kill-process",
  "targets": {
    "Tasks-Target-1": {
      "resourceType": "aws:ecs:task",
      "parameters": {
        "cluster": "ecs-blueprint-infra",
        "service": "fis-service"
      },
      "selectionMode": "COUNT(1)"
    }
  },
  "actions": {
    "ecs-task-kill-proc": {
      "actionId": "aws:ecs:task-kill-process",
      "parameters": {
        "processName": "python"
      },
      "targets": {
        "Tasks": "Tasks-Target-1"
      }
    }
  },
  "stopConditions": [
    {
      "source": "none"
    }
  ],
  "roleArn": "arn:aws:iam::${AWS_ACCOUNT}:role/ecs-fis-role",
  "tags": {}
}
EOF

FIS_EXPERIMENT_ID=$(aws fis create-experiment-template \
  --region $AWS_REGION \
  --tags Name=ECS-kill-process \
  --cli-input-json file://fis-ecs-experiment-template-kill-proc.json \
  --query 'experimentTemplate.id' \
  --output text)

Amazon ECS の画面に戻り、fis-service という名前の ECS サービスを選択し、タスクタブに移動します。下の画像のように、フィルタ条件を「実行中のタスク」から「すべてのタスク」に変更することで、次の手順で試験を実行したときに、タスクが 1 個停止される様子が確認しやすくなります。

試験を開始しましょう。

aws fis start-experiment \
  --experiment-template-id $FIS_EXPERIMENT_ID \
  --region $AWS_REGION

数秒後、AWS FIS が「必須 (essential)」と設定されているコンテナ内の python プロセスを停止したため、Amazon ECS はタスク全体を停止します。Amazon ECS の画面で停止したタスクの詳細画面を開くと、以下のエラーメッセージが表示されています。

試験の影響を可視化するために、Locust を使ってウェブアプリケーションにリクエストを投げた結果が以下の通りです。ご覧の通り、AWS FIS で「必須 (essential)」と設定されたプロセスを停止した場合、ウェブサイトへのアクセス中にエラーが発生しました。また、リクエストを処理するために十分なタスクがなかったため、95 パーセンタイルのレイテンシが増加しました。

Amazon ECS がタスクを再作成している間、ALB メトリクスで、5xx エラーが発生していることがわかります。

注意点 : aws:ecs:task-kill-process アクションは、Amazon ECS のタスク定義で、pidMode の値を task に設定する必要があります。Amazon ECS は、デフォルトでタスク内のコンテナをそれぞれ個別の PID namespace で実行します。pidMode を task に設定した場合、タスク内のすべてのコンテナは PID namespace を共有します。それにより、サイドカーコンテナはタスク内で実行されている他のコンテナのプロセスを参照・停止できるようになります。

作成した環境の削除

export AWS_PAGER=""

# Get experiment ids
# AWS FIS の実験テンプレート ID を取得
fis_expmt1=$(aws fis list-experiment-templates --query 'experimentTemplates[?description == `ecs-task-kill-process`].id' --output text)
fis_expmt2=$(aws fis list-experiment-templates --query 'experimentTemplates[?description == `ecs-task-cpu-stress`].id' --output text)

# Delete the alarm
 # CloudWatch Alarm の削除
aws cloudwatch delete-alarms --alarm-names 'ECS Sample service task count alarm' --region $AWS_REGION

# Delete experiments
# AWS FIS の実験を削除
aws fis delete-experiment-template --id $fis_expmt1
aws fis delete-experiment-template --id $fis_expmt2

# Delete ecs-fis IAM role
# IAM Role ecs-fis の削除
aws iam delete-role-policy --role-name ecs-fis-role --policy-name ecs-fis-policy
aws iam delete-role --role-name ecs-fis-role

# Destroy CDK stack
# CDK スタックの削除
cdk destroy --force –all

料金

AWS FIS は利用した分だけお支払い頂く従量課金です。初期費用や最低料金はかかりません。フォールトインジェクションのアクションがアクティブな期間に応じて料金が発生します。詳しくは、AWS FIS の料金ページをご参照ください。

aws:ecs:task-kill-process アクションは、実行期間を持たないので無料です。

初期状態では、AWS FIS で必要以上のリソースを作成してしまうことを防ぐために、アクションごとのタスク数にクォータがあります。このクォータは AWS マネージメントコンソールから、引き上げるリクエストを申請できます。

まとめ

こちらの記事では、Amazon ECS を利用しているお客様がカオスエンジニアリングを簡単に始められる AWS FIS アクションを紹介しました。Amazon ECS タスクの CPU に負荷をかける方法や、プロセスを停止する方法を紹介しました。AWS FIS は様々な障害試験を柔軟に設定でき、フォールトインジェクションを簡単に行えます。AWS FIS は、特定の条件を満たしたときに自動的に試験をロールバック、もしくは停止するといった本番環境で求められるガードレールを提供します。新しい AWS FIS のアクションは、Amazon ECS クラスターで稼働するアプリケーションが持つ、潜在的な問題を発見するために役立ちます。

Amazon ECS のアクションは、AWS GovCloud (米国) リージョンを含む、AWS FIS が利用可能なすべての AWS リージョンで利用可能です。

執筆者
Jooyoung Kim
Re Alvarez-Parmar

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