ECS on Fargateのスケーリングポリシーを動かして理解する

記事タイトルとURLをコピーする

はじめに

本ブログではAmazon ECS on AWS Fargate(以下、ECS on Fargate)のスケーリングポリシーを実際に動かして理解します

ECS on Fargateのスケーリングポリシーとは

ECS on Fargateのアーキテクチャで構成されたサービスコンポーネント内のタスクを自動的にスケールイン/スケールアウトさせる仕組みです

Amazon ECS サービスを自動的にスケールする - Amazon Elastic Container Service

前提

構成

ベースとするアーキテクチャはAWS公式ドキュメントのECSサンプルをデプロイしたものです
https://docs.aws.amazon.com/ja_jp/cdk/v2/guide/ecs_example.html

  • Amazon ECR 上のパブリックイメージを利用
  • インターネットから接続できるApplication Load Balancer が前面にある
  • データプレーンはAmazon ECS on Fargate を利用

構成図

デプロイ

  • デプロイ方法:AWS CDK
  • 言語:TypeScript

デプロイ手順

詳細は公式ドキュメントをご参照ください
ちなみにですが、CDK初期化した状態から以下の数行のソースコードの修正のみで簡単にECS on Fargate 構成のPHP Webアプリケーションをデプロイできます
CDKめちゃめちゃ便利ですね

ファイル:lib/my_ecs_construct-stack.ts

  • インポート
import * as ec2 from "aws-cdk-lib/aws-ec2";
import * as ecs from "aws-cdk-lib/aws-ecs";
import * as ecs_patterns from "aws-cdk-lib/aws-ecs-patterns";
  • コンストラクタ
    const vpc = new ec2.Vpc(this, "MyVpc", {
      maxAzs: 3 // Default is all AZs in region
    });

    const cluster = new ecs.Cluster(this, "MyCluster", {
      vpc: vpc
    });

    // Create a load-balanced Fargate service and make it public
    new ecs_patterns.ApplicationLoadBalancedFargateService(this, "MyFargateService", {
      cluster: cluster, // Required
      cpu: 512, // Default is 256
      desiredCount: 6, // Default is 1
      taskImageOptions: { image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample") },
      memoryLimitMiB: 2048, // Default is 512
      publicLoadBalancer: true // Default is true
    });

デプロイが完了すると、ブラウザでインターネットからアクセスできるPHPのサンプルwebサイトが起動します
- URL確認方法:サービス > 設定とネットワークタブ > ネットワーク設定 > DNS名

URL確認

DNS名へブラウザからアクセス

リソース情報

デプロイされる各リソースの概要は以下の通りです

コンテナイメージ(Amazon ECR)

使用するイメージはAmazon ECR Public Galleryのamazon/amazon-ecs-sampleです amazon/amazon-ecs-sample

タスク定義

CDKのソースコードで定義されている部分は以下の通りでしたが、デフォルトの設定値のままでは少し過剰なリソースなので、一部Default値へ変更することにしました。

変更前

    new ecs_patterns.ApplicationLoadBalancedFargateService(this, "MyFargateService", {
      cluster: cluster, // Required
      cpu: 512, // Default is 256
      desiredCount: 6, // Default is 1
      taskImageOptions: { image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample") },
      memoryLimitMiB: 2048, // Default is 512
      publicLoadBalancer: true // Default is true

変更後

    new ecs_patterns.ApplicationLoadBalancedFargateService(this, "MyFargateService", {
      cluster: cluster, // Required
      cpu: 256, // Default is 256
      desiredCount: 1, // Default is 1
      taskImageOptions: { image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample") },
      memoryLimitMiB: 512, // Default is 512
      publicLoadBalancer: true // Default is true
    });

各コンポーネントの要素数

リソース
変更前

変更後
ソースコード指定箇所
クラスター 1 1 new ecs.Cluster();
サービス 1 1 new ecs_patterns.ApplicationLoadBalancedFargateService():
タスク 6 1 desiredCount:

クラスター

クラスターとはECSを司る最も大きなコンポーネントです。 サンプルCDKでは1つのクラスターが定義されており、この中にサービス>タスクが起動します。

クラスター

クラスターのページでは以下のような情報が確認できます

サービス

クラスター内で起動されているサービスの数が表示されます

タスク

クラスター内で実行されているタスクの、保留中と実行中の数が表示されます 保留中や実行中とは、ECSのライフサイクル状態を指しています

Amazon ECS タスクライフサイクル - Amazon Elastic Container Service

タスクはサービスの一部として開始されてから終了あるいは停止されるまでさまざまな状態を経由します

以下は公式ドキュメントに記載されていたECSのライフサイクルフロー図です

ECS ライフサイクル

サンプルのデプロイが完了するとこのうちPending(保留中)の0件とRunning(実行中)が表示されます

なお、CodeDeployなどのCI/CDパイプラインも自動で作成されますが本ブログでは割愛します

タスクの数を手動で変更してみる

サービスを更新より、必要なタスクを1から0、または0から1へ変更してみます
タスク数の変化に応じて保留中や実行中の表示が切り替わることを確認できます

サービスを更新
「サービスを更新」より、 「必要なタスク」を更新

必要なタスクを1から0へ更新直後

実行中のタスクなし
必要なタスクを0から1へ更新直後
1件が保留中 | 0件が実行中

自動でタスク数をスケールさせる

本ブログのメインテーマです
ターゲットメトリクス値を使用してサービス内のタスク数をスケールさせることができます
詳細や考慮事項については以下公式ドキュメントを参照してください
ターゲットメトリクス値を使用して Amazon ECS サービスをスケールする - Amazon Elastic Container Service

スケーリングポリシーを設定する

AutoScalingのスケーリングポリシー同様に任意のメトリクスを指標にして自動でタスク数をスケールイン、スケールアウトする様設定します
手動で変更した時と同様に、「サービスを更新」より 「サービスの自動スケーリング - オプション」項目からタスクの最小数とタスクの最大数を更新します
「サービスの自動スケーリングを使用」をチェックし、タスクの最大数を2に設定します

タスクの最小数、タスクの最大数

スケーリングポリシーを以下の通り設定します

サービスの自動スケーリング- オプション

  • タスクの最小数:1
  • タスクの最大数:2
  • スケーリングポリシータイプ
  • ターゲットの追跡
  • ポリシー名:alb-req
  • ECS サービスメトリクス:ALBRequestCountPerTarget
  • ターゲット値:50
  • スケールアウトクールダウン期間:10
  • スケールインクールダウン期間:10
  • スケールインをオフにする:無効

なお、「ECS サービスメトリクス」は事前定義済みメトリクスの中から選択できます

  • ECSServiceAverageCPUUtilization:サービスの平均 CPU 使用率
  • ECSServiceAverageMemoryUtilization:サービスのメモリ平均使用率
  • ALBRequestCountPerTarget:Application Load Balancer ターゲットグループ内の ターゲットごと に完了したリクエストの数

今回は ALBRequestCountPerTarget をメトリクスに設定します

設定ポリシーを確認する

サービスの設定とネットワークタブから、設定したポリシーを確認できます

設定とネットワーク
Auto Scaling 設定確認

アラームの列にフォーカスすると、CloudWatchのアラーム設定内容を確認できるURLリンクが表示されます

アラーム設定へ遷移
...AlermHigh... が、スケールアウト(タスクの増加)を動作させるためのアラームで、
...AlermLow...が、スケールイン(タスクの減少)を動作させるためのアラームです。

動作確認

実際に負荷をかけてスケールアウト/スケールインする様子を動作確認しました

リクエストの負荷をかける方法はcurlコマンドとwatchコマンドを組み合わせて実行しました
詳細は以下ブログで紹介しています
【Auto Scaling】ターゲットトラッキングスケーリングポリシーを設定してみた 〜その② 動作確認〜 - サーバーワークスエンジニアブログ

ネットワーク設定 > DNS 名

ネットワーク設定 タブで確認したDNS名を指定し、コマンドを実行します

watch -n 0.1 curl -s http://[DNS名で確認したURL]/

アラーム画面のグラフからアラームステータスを確認

CloudWatch > アラーム

負荷をかけ始めてから約5分後にアラーム状態の判定となったことを確認しました

サービス > 正常性とメトリクス タブ

サービス > タスクタブ

1件が実行中 | 1件が保留中 | 2個が必要

タスク数:2件が実行中

負荷をかけてからタスク数が増加するまでに発生したイベントは以下の通りです。

2024年5月02日 14:17 (UTC+9:00)    service ContainerPatternAioStack-MyFargateServiceF490C034-jRbO6iNAXMpP has reached a steady state.  65f57d40-d120-4c1e-84ce-cd5ebdc6c5ff
2024年5月02日 14:16 (UTC+9:00)   service ContainerPatternAioStack-MyFargateServiceF490C034-jRbO6iNAXMpP registered 1 targets in target-group Contai-MyFar-B5ADXCZXKWQF   1762c93e-2dbf-479b-a3f5-cf323bc2808e
2024年5月02日 14:16 (UTC+9:00)   service ContainerPatternAioStack-MyFargateServiceF490C034-jRbO6iNAXMpP has started 1 tasks: task 32c00521245349e7a2005623074f6448.  04ffbb90-239e-4179-b0e2-741db86cbe12
2024年5月02日 14:16 (UTC+9:00)   メッセージ: Successfully set desired count to 2. Change successfully fulfilled by ecs. 原因: monitor alarm TargetTracking-service/ContainerPatternAioStack-MyCluster4C1BA579-9fGNAp6dK6Oe/ContainerPatternAioStack-MyFargateServiceF490C034-jRbO6iNAXMpP-AlarmHigh-7c5969aa-4a80-4222-8355-7c1968a2b453 in state ALARM triggered policy alb-req  7dd15a78-9f99-498a-aff4-c1e9eb2303e1

タスクが増えたことでリクエストは半分に分散されたため、メトリクスも半分に下がっていますが、引き続き閾値を超えているリクエスト数が発生しており、アラーム状態は続いている状況です
しかし、最大タスクが2となっていることで、アラーム状態が続いてもこれ以上タスクが増えることはありません
ちなみに、この時は何もイベントは発生しません

アラーム状態が継続していても、、、

最大タスクに達しているのでイベント発生しない

最大タスクを3へ変更する

負荷をかけ続けたまま、タスクの最大数を3へ変更します

必要なタスク数は2のまま
タスクの最大数を3へ変更
必要なタスクは2のままで最大数を変更するのがポイントです

イベントを確認

イベント

イベントメッセージ

2024年5月02日 14:28 (UTC+9:00)    メッセージ: Successfully set desired count to 3. Waiting for change to be fulfilled by ecs. 原因: monitor alarm TargetTracking-service/ContainerPatternAioStack-MyCluster4C1BA579-9fGNAp6dK6Oe/ContainerPatternAioStack-MyFargateServiceF490C034-jRbO6iNAXMpP-AlarmHigh-7c5969aa-4a80-4222-8355-7c1968a2b453 in state ALARM triggered policy alb-req 83e1bdc0-c112-4e9f-9984-794914031baa

RequestCountPerTargetの遷移をアラーム画面のグラフで確認

タスク数が1から2、2から3へと増えるごとにターゲットが捌ける数が減って、閾値に近づいていく様子がわかります

タスク数ごとの期間

その他

動作確認で気づいた点をまとめます

必要なタスク数は自動的に変化する

Auto Scaling > 必要なタスク数

最大タスク数を元に戻そうとしたところ、「必要なタスク」の数を変更していないにも関わらず3に変わっていることがわかりました
必要なタスクの設定値はスケーリングポリシーの挙動に応じて変化する様です

Amazon ECS サービスを自動的にスケールする - Amazon Elastic Container Service

サービススケジューラは常に必要数を優先しますが、サービスにアクティブなスケーリングポリシーとアラームがある限り、サービスの自動スケーリングはユーザーが手動で設定した必要数を変更できます。

また、最大タスク数を変更する際は必要なタスク数以下に設定する必要があり、自動的に変更された必要なタスク数も合わせて更新しないといけない場合もあります

アラームが新しいものに変わる

スケーリングポリシーのターゲット値を50から300へ変更したところ、既存のアラームが削除されて新しい名前で作成されました
既存の設定値が変更されると思っていましたが、更新するとアラームの設定自体が置き換わる挙動の様です

アラームの名前が置き換わる

最後に

ECS on Fargateのコンテナアーキテクチャのスケーリングポリシーを実際に動かして理解しました AWSのコンテナサービスは初学者にとってはパラメーターが複数あり、ドキュメントなどの資料を読んで学ぶには理解し難い側面があります 実際に手を動かすことで体系的に学ぶことができ、より理解が深まると思います

折戸 亮太(執筆記事の一覧)

2021年10月1日入社
クラウドインテグレーション部技術1課

屋根裏エンジニア