こんにちは!オルターブースの池田です!
ハロウィンモードから一転してすっかりクリスマスモード突入ですね🎄 子供のプレゼントをそろそろ考えないとな~と思っている今日この頃です🙂
さて、今回はAWS FIS(AWS Fault Injection Simulator)を使用し、ECS FargateのタスクにCPU負荷を与える事でAuto Scalingが正常に動作するか実験してみようと思います。
AWS FIS(AWS Fault Injection Simulator) とは意図的にシステムへ負荷を与えるイベントを作成し、システムの回復力や復元力をテストできるマネージドサービスです。
今回は、aws:ecs:task-cpu-stress アクションを使用してECSタスクにCPU負荷をかける実験を行いたいと思います。
やってみる
事前準備
前提として、AWS ECS Fargateでタスクが1つ起動中である事として進めます。
また、Auto Scalingの設定では、Target trackingでECSServiceAverageCPUUtilization の閾値を70% と設定しておきます。
因みに余談にはなりますが、Auto Scalingに関してFargateでは無くEC2インスタンスの場合や、3つの設定方法の違いは以下の動画が非常に分かりやすかった為記載しておきます。
aws: ecs: task アクションを使用してECSタスクに障害を発生させるにはSSM エージェントコンテナをサイドカーとしてECSタスクに追加する必要があります。このコンテナはECSタスクをSSMマネージドインスタンス(管理対象インスタンス)として登録します。
また、ECS Exec を有効にしている場合は無効にする必要があるので、以下のコマンドで確認をしておきます。
aws ecs describe-services --cluster <ClusterName> --services <ServiceName> | grep enableExecuteCommand "enableExecuteCommand": true
#trueの場合は、無効にする aws ecs update-service --cluster <ClusterName> --service <ServiceName> --disable-execute-command
次にマネージドインスタンスロールを作成します。
信頼ポリシー
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "ssm.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
許可ポリシーにはAmazonSSMManagedInstanceCoreの他、以下を割り当てました。
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"ssm:AddTagsToResource",
"ssm:CreateActivation",
"ssm:DeleteActivation",
"ssm:DeregisterManagedInstance"
],
"Resource": "*",
"Effect": "Allow"
}
]
}
次に現在稼働しているECSタスクIAMロールに以下の権限を持ったポリシーを作成し追加します。
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"ssm:AddTagsToResource",
"ssm:CreateActivation",
"ssm:DeleteActivation",
"ssm:DeregisterManagedInstance"
],
"Resource": "*",
"Effect": "Allow"
},
{
"Action": "iam:PassRole",
"Resource": "<先程作成したマネージドインスタンスロールロールARN>",
"Effect": "Allow"
}
]
}
ここでのPassRoleはECSタスクロールにマネージドインスタンスの操作権限を与えています。
次に、現在のタスク定義を取得し、新しいタスク定義に書き換えていきます。
#現在のタスク定義をjsonで取得 aws ecs describe-task-definition --task-definition <taskFamilyName> --query taskDefinition > current-task-def.json
取得したjsonのcontainerDefinitionsに以下のコンテナ定義を追加します。
環境変数のMANAGED_INSTANCE_ROLE_NAMEは先程作成したマネージドインスタンスロール名を記述します。
{
"name": "amazon-ssm-agent",
"image": "public.ecr.aws/amazon-ssm-agent/amazon-ssm-agent:latest",
"cpu": 0,
"links": [],
"portMappings": [],
"essential": false,
"entryPoint": [],
"command": [
"/bin/bash",
"-c",
"set -e; yum upgrade -y; yum install jq procps awscli -y; term_handler() { echo \"Deleting SSM activation $ACTIVATION_ID\"; if ! aws ssm delete-activation --activation-id $ACTIVATION_ID --region $ECS_TASK_REGION; then echo \"SSM activation $ACTIVATION_ID failed to be deleted\" 1>&2; fi; MANAGED_INSTANCE_ID=$(jq -e -r .ManagedInstanceID /var/lib/amazon/ssm/registration); echo \"Deregistering SSM Managed Instance $MANAGED_INSTANCE_ID\"; if ! aws ssm deregister-managed-instance --instance-id $MANAGED_INSTANCE_ID --region $ECS_TASK_REGION; then echo \"SSM Managed Instance $MANAGED_INSTANCE_ID failed to be deregistered\" 1>&2; fi; kill -SIGTERM $SSM_AGENT_PID; }; trap term_handler SIGTERM SIGINT; if [[ -z $MANAGED_INSTANCE_ROLE_NAME ]]; then echo \"Environment variable MANAGED_INSTANCE_ROLE_NAME not set, exiting\" 1>&2; exit 1; fi; if ! ps ax | grep amazon-ssm-agent | grep -v grep > /dev/null; then if [[ -n $ECS_CONTAINER_METADATA_URI_V4 ]] ; then echo \"Found ECS Container Metadata, running activation with metadata\"; TASK_METADATA=$(curl \"${ECS_CONTAINER_METADATA_URI_V4}/task\"); ECS_TASK_AVAILABILITY_ZONE=$(echo $TASK_METADATA | jq -e -r '.AvailabilityZone'); ECS_TASK_ARN=$(echo $TASK_METADATA | jq -e -r '.TaskARN'); ECS_TASK_REGION=$(echo $ECS_TASK_AVAILABILITY_ZONE | sed 's/.$//'); ECS_TASK_AVAILABILITY_ZONE_REGEX='^(af|ap|ca|cn|eu|me|sa|us|us-gov)-(central|north|(north(east|west))|south|south(east|west)|east|west)-[0-9]{1}[a-z]{1}$'; if ! [[ $ECS_TASK_AVAILABILITY_ZONE =~ $ECS_TASK_AVAILABILITY_ZONE_REGEX ]]; then echo \"Error extracting Availability Zone from ECS Container Metadata, exiting\" 1>&2; exit 1; fi; ECS_TASK_ARN_REGEX='^arn:(aws|aws-cn|aws-us-gov):ecs:[a-z0-9-]+:[0-9]{12}:task/[a-zA-Z0-9_-]+/[a-zA-Z0-9]+$'; if ! [[ $ECS_TASK_ARN =~ $ECS_TASK_ARN_REGEX ]]; then echo \"Error extracting Task ARN from ECS Container Metadata, exiting\" 1>&2; exit 1; fi; CREATE_ACTIVATION_OUTPUT=$(aws ssm create-activation --iam-role $MANAGED_INSTANCE_ROLE_NAME --tags Key=ECS_TASK_AVAILABILITY_ZONE,Value=$ECS_TASK_AVAILABILITY_ZONE Key=ECS_TASK_ARN,Value=$ECS_TASK_ARN Key=FAULT_INJECTION_SIDECAR,Value=true --region $ECS_TASK_REGION); ACTIVATION_CODE=$(echo $CREATE_ACTIVATION_OUTPUT | jq -e -r .ActivationCode); ACTIVATION_ID=$(echo $CREATE_ACTIVATION_OUTPUT | jq -e -r .ActivationId); if ! amazon-ssm-agent -register -code $ACTIVATION_CODE -id $ACTIVATION_ID -region $ECS_TASK_REGION; then echo \"Failed to register with AWS Systems Manager (SSM), exiting\" 1>&2; exit 1; fi; amazon-ssm-agent & SSM_AGENT_PID=$!; wait $SSM_AGENT_PID; else echo \"ECS Container Metadata not found, exiting\" 1>&2; exit 1; fi; else echo \"SSM agent is already running, exiting\" 1>&2; exit 1; fi"
],
"environment": [
{
"name": "MANAGED_INSTANCE_ROLE_NAME",
"value": "<ManagedInstanceRoleName>"
}
],
"environmentFiles": [],
"mountPoints": [],
"volumesFrom": [],
"secrets": [],
"dnsServers": [],
"dnsSearchDomains": [],
"extraHosts": [],
"dockerSecurityOptions": [],
"dockerLabels": {},
"ulimits": [],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": <CloudWatch LogGroupName>,
"awslogs-region": "ap-northeast-1",
"awslogs-stream-prefix": "test-stream"
},
"secretOptions": []
},
"systemControls": []
}
取得したjsonのままCLIコマンドでタスク定義を更新しようとすると不要なプロパティが含まれているとエラーが出るので、taskDefinitionArn、revision、status、requiresAttributes、compatibilities、registeredAt、registeredByこれらのプロパティは削除してから更新を行います。
#タスク定義の更新 aws ecs register-task-definition --cli-input-json file://current-task-def.json
#新しいタスク定義でサービスを更新 CLUSTER=<ClusterName> SERVICE=<ServiceName> TASK=<TaskDefinitionName>:<ModificationNumber> aws ecs update-service --cluster $CLUSTER --service $SERVICE --task-definition $TASK
ECSサービスが更新されると、amazon-ssm-agent というコンテナが追加され起動している事が確認できます。

SSMマネージドノードにも追加されている事も確認できました。

AWS FIS
ここまでで、準備は整ったのでAWS FISを実行していきます。 まず、FIS実行用のロールを作成します。
信頼ポリシー
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "fis.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
許可ポリシーにはAWSFaultInjectionSimulatorECSAccessを割り当てます。
FISテストのログをCloudWatchへ記録する場合はそれらの許可も割り当てます。
コンソールからAWS FISを検索し、実験テンプレートの作成を選択します。

ターゲットを追加
- 名前:
任意 - リソースタイプ:
aws:ecs:task - ターゲットメソッド:
リソースID - リソースID:
プルダウンで選択 - 選択モード:
全て
アクションを追加
- 名前:
任意 - アクションタイプ:
ECS-aws:ecs:task-cpu-stress - 次の後開始:
何のイベントの後に開始するか選択できるが、今回は無し - ターゲット:
前に作成したターゲットを選択 - アクションパラメータ
- Duration:
CPU ストレステストの持続時間 今回は10分
- installDependencies:
実行に必要な依存関係がインストールされる。デフォルトはtrueなのでそのままに。
- percent:
0から100までの目標CPU負荷率。デフォルトは100。今回はそのまま
- workers:
使用するCPUストレッサーの数。デフォルトは 0 で、すべての CPU ストレッサーを使用する。今回はそのまま
- Duration:
サービスアクセスロール
- 既存のIAMロールを使用する:
前もって作成したIAMロールを選択
停止条件
- 予期せぬ負荷などを避ける為にCloudWatch Alarmを設定し停止条件にする事ができる。今回はなし。
ログ
- CloudWatchのロググループを選択できる。今回はなし。
全ての設定が完了したら、実験を開始を押下します。
しばらくすると、状態がRunningに切り替わりました。

CloudWatchを確認すると、CPU使用率が上がっていきアラーム状態へと変わりました。

ECSタスクを確認してみると、タスク数が増えスケーリングが行われている事が確認できました。

最後に
今回は、aws:ecs:task-cpu-stress アクションを行いましたが、他にも様々なアクションがあります。
組み合わせる事でテストの幅が広がりますね。
拙い内容で恐縮ですが、最後までご覧頂き有難うございました!🙏


