ECS on Fargateでターゲット追跡ServiceAutoScalingを利用してみる

はじめに

こんにちは。
今回は「ターゲット追跡型の Service Auto Scaling」を利用して、CPU使用率に応じてタスク数を自動で増減させる設定手順をご紹介します。

ターゲット追跡スケーリングでは、目標とするターゲット値(例:CPU使用率70%)を設定するだけで、その値を維持するようにAWSが自動でタスクの追加や削除を行ってくれます。
この仕組みを導入することで、アクセス集中時にはタスクを増やしてアプリケーションの可用性を高めつつ、
閑散期には最小限のタスク数に戻してコストを最適化することが可能になります。

なお、検証環境の構成としては、「ALB + ECS Fargate(nginx)」となります。

オートスケーリング条件の設定

既存のECSサービスに対してオートスケーリング(ターゲット追跡)を有効化し、条件を設定していきます。
今回は、以下の条件でスケーリングするように設定します。

  • タスクの最小数: 1
  • タスクの最大数: 4
  • ターゲット値(目標とするCPU使用率): 70%

通常時は1台で稼働して、CPU使用率が70%以上の負荷がかかった場合は、
自動的に最大4台までタスクを増やして負荷を分散させる構成になります。

では、実際に設定をしていきましょう。

1. サービスの更新画面を開く
Amazon ECSコンソールを開き、対象のクラスターを選択します。
「サービス」タブから今回オートスケーリングを設定するサービス名を開き、画面右上にある [サービスを更新] をクリックします。

2. サービスの自動スケーリングを有効化する
更新画面を下へスクロールし、[サービスの自動スケーリング - オプション] の項目を展開します。
「サービスの自動スケーリングを使用」 にチェックを入れ、タスク数の設定項目を以下のように入力します。

タスクの最小数: 1
タスクの最大数: 4

3. スケーリングポリシーを設定する
続いて、どのようにタスクを増減させるかをポリシーとして設定します。

スケーリングポリシータイプ: ターゲットの追跡 を選択
ポリシー名: 任意の名前
ECSサービスメトリクス: ECSServiceAverageCPUUtilization
ターゲット値: 70
スケールアウト/スケールインクールダウン期間:60
※今回は検証結果を早く確認するため、それぞれ 60(秒)に変更しておきます。

4. 設定を保存する

負荷テストの準備

オートスケーリングの設定が完了したので、実際に負荷をかけてタスクが増えるかを検証していきます。

負荷をかける方法として、外部からcurlコマンド等でALBに大量のアクセスを送る方法もありますが、
この方法だと無駄な通信データ転送量が発生し、思わぬ課金に繋がる恐れや、共用環境では他のリソースへの影響も懸念されます。
そこで今回は、外部からの通信を一切発生させず、コンテナの内部だけで意図的にCPU使用率を100%にするために、
既存のnginxのタスク定義をもとに、新しいリビジョンを作成し、起動時に負荷をかけるコマンドを仕込みます。

1. 新しいリビジョンの作成
ECSコンソールの左メニューから「タスク定義」を開き、現在使用しているタスク定義を選択して、
画面右上の [新しいリビジョンの作成] をクリックします。

2. コマンドの設定
画面をスクロールして「コンテナ - 1」のブロック内にある [Docker 設定 - オプション]を展開します。
「コマンド」の入力欄に、以下のコマンドを入力します。

sh,-c,yes > /dev/null & nginx -g "daemon off;"


このコマンドについて簡単に説明します。
yes > /dev/null & : yesコマンドをバックグラウンドで無限ループさせて、意図的にCPUを100%に張り付かせます。
nginx -g "daemon off;" : 表側では正常にnginxを起動させ続けます。
これによりALBのヘルスチェックには正常に応答できるため、タスクが異常終了するのを防ぎます。

3. タスク定義を保存する
その他の設定は変更せず、画面下の [作成] をクリックします。

これで、「起動した瞬間にCPUが100%になる」検証用のタスク定義(リビジョン)が完成しました!
負荷検証の準備は完了です。

タスク定義更新:負荷用タスクをデプロイし、スケールアウトを発生させる

次に先ほど作成した検証用のタスク定義(リビジョン)をサービスに適用して、実際にコンテナのCPUを急上昇させます。

1. サービスを更新する
ECSコンソールのサービスタブから対象のサービスを選択し、画面右上の [サービスを更新] をクリックします。

2. 新しいリビジョンを適用
一番上の「デプロイ設定」にある [タスク定義のリビジョン] の項目から、
検索窓をクリックして先ほど作成した最新のリビジョン番号(yesコマンドを仕込んだもの)を選択します。

3. 更新を実行
その他の設定は変更せず、画面一番下までスクロールして [更新] をクリックします。
これで、起動した瞬間から内部でCPUを100%消費し続けるタスクがデプロイされます!

実行結果の確認①(スケールアウト):タスクが4台に増加することを確認する

1. デプロイとCPU使用率の挙動確認
サービス更新後は、タスクの入れ替え(デプロイ)が発生します。
この動きをサービスの「タスク」タブから確認してみます。

古いタスクと新しいタスクが一時的に共存したあとに、古いタスクが終了して新しいタスクのみ表示されました!
この新しいタスクが起動した瞬間から内部でCPUを消費し続けます。
「タスク」タブの隣にある、「正常性とメトリクス」タブに移動すると、以下のようにCPUが上昇する様子を確認することができます。

2. アラーム発報とタスクの増加
この状態が継続すると、CloudWatchアラームがalarm状態になり、スケールアウトが発動します。
AWS側がCPU使用率の目標値70%に近づけるために、あと何台必要であるかを自動で計算して、増設します。
今回は4台が上限ですが一度に4台増やすのではなく、状況を見ながら1台ずつ増やしていきます。 最終的には以下のように4台すべて起動したことを確認することができます。

実行結果の確認②(スケールイン):タスクが1台に戻ることを確認する

CPU負荷がターゲット値より増大した際の動きが確認できたので、次は負荷が収まった場合4台から1台の構成に戻るか確認してみます。

1. タスク定義を元に戻す
再度 [サービスを更新] をクリックし、タスク定義のリビジョンを「元のリビジョン」に戻して [更新] を実行します。

2. CPUの低下とスケールインの待機
タスクの入れ替わり時については、先ほどと同じように古いタスク(負荷タスク)と新しいタスクが一時的に共存して、
古いタスクが終了し、新しいタスクに入れ替わります。
この後からメトリクスのCPUグラフは落ちはじめます。

CPUの低下の様子は、先ほどと同じように「タスク」タブの隣にある「正常性とメトリクス」タブのほか、
以下のようにCloudWatchでも確認することができます。

※CloudWatchの左メニュー「アラーム」> 自動作成されたスケールイン用のアラーム名 ...-AlarmLow-... をクリックすると確認できます。
※スケールアウトを確認する場合は...-AlarmHigh-... を確認します。

3. タスクの減少を確認
アプリケーションの可用性を高めるためにスケールアウトはメトリクスに比例して可能な限り高速で行われます。
一方で、スケールインに関してはより緩やかになります。
そのため、タスクが切り替わり、CPUが低下したとしても即座にはタスクは減少しません。
今回の検証でも、タスクが減り始めるまでに最低15分は要しました。
※CloudWatchの画面から、スケールイン用アラーム(AlarmLow)の詳細を確認すると「データポイント15/15」(15分間連続で下回った場合にアラーム発報)となっていました。
実際の環境や設定値にもよりますのであくまで参考としていただきつつ、実際に挙動を確認することをお勧めします。

アラームが発報された後は、以下のように徐々にタスクが減少する様子を確認することができます。

まとめ

今回は、ECS on Fargateで 「ターゲット追跡 Service Auto Scaling」 を設定し、タスク数を自動増減させる挙動を確認しました。
目標値の維持をAWSが自動化してくれることで、負荷増大時には素早くスケールアウトし、
閑散時にはスケールインすることでコストを削減することができます。
負荷テストを実施する際には、タスク定義の設定を利用することでコンテナ内部で負荷をかけられ、
外部通信による無駄なコストや負荷を減らし、簡単に検証できることがわかりました。
マネジメントコンソールから簡単な設定で、安定稼働とコスト最適化が両立できる非常に便利な機能です。
ぜひ、参考になれば嬉しいです。