🐷

ECSスケールアップ&予測スケーリングポリシーでレスポンスタイムのスパイクを抑制する

に公開

はじめに

dely株式会社で26卒内定者インターンをしています、kaito(@kaito_bq)です。

クラシルリワードのインフラでは、ECS, Fargateを使用しています。
この記事では、サービスの安定性を向上させるために、ECSのタスクサイズとスケーリングポリシーを見直した取り組みについて紹介します。

従来の構成と課題

これまでは以下のような構成で運用してきました。

ECSの構成

  • 2 vCPU
  • メモリ 7GB

スケーリングポリシー

課題

  • アイドルタイムからピークタイム移行時にタスク数が急増し以下の二つが発生する
    • CPU使用率がスパイクする
    • レスポンスタイムがスパイクする
  • 平常時にも何らかの原因でスパイクすることがある


従来の構成では、アイドルタイムからピークタイムへと移行するタイミングで、CPU使用率(MAX値)が一時的に100%近くまでスパイクする現象が頻発していました。
これによってレスポンスタイムの一時的な悪化が発生しており、サービスを安定して提供できない状況がありました。

アプローチと結果

ECSスケールアップ

弊社ではRailsを採用しているため、Pumaの設定によりCPUが枯渇している可能性もありました。
それによってレスポンスタイムの悪化につながっている可能性もありますが、まずはECSを一時的にスケールアップし、課題がどのように解消されるのかを観察することにしました。

Before After
vCPU 2 4
メモリ 7GB 8GB

スケールアップによって期待していたことは以下です。

  • CPU使用率のスパイク防止
  • CPU使用率のスパイクによるレスポンスタイム悪化の防止

結果


スケールアップしたところ、ピークタイムにおけるCPU使用率のMAX値が80%程度に収まり、これまでのような急激なスパイクは発生しなくなりました。
また、副次的な効果として、タスク数の減少、タスクあたりのリクエスト処理数(RequestCountPerTarget)の上昇が確認できました。
しかし、依然としてピークタイム時に移行するタイミングのスパイクは発生していました。

Before After
RequestCountPerTarget 2000 5000~6000
CPU使用率のスパイク あり なし
レスポンスタイム悪化 あり あり

これにより、CPU使用率、タスク数を引き下げることができましたが、レスポンスタイムに明確な改善は見られませんでした。

スケールアップによって、レスポンスタイムの改善はできませんでしたが、CPU使用率のMAX値が大幅に下がり、タスク数も減少したことで、サービスがより健全な状態で運用できるようになりました。
また、タスク数が大幅に減少したことにより、コストに変動がなかったことから、スケールアップを一時的な検証から、恒常的な対応とすることになりました。

スケーリングポリシー

既存のポリシー

既存のスケーリングポリシーでは、CPU使用率50%をターゲットとしたルールのみを設定していました。
過去のメトリクスから、ピークタイム直前がアイドルタイムであること、それによりピークタイム移行時にリクエスト数が急増していることがわかっていました。
これに対する仮説として、以下の二つを立てました。

  • スケールアウトが間に合っていない。
  • スケールインしすぎている。

これらを解決するための策としては以下が考えられます。

解決策の検討

上記二つの方法はどちらも効果的だと見込めますが、後者はコスト上昇が懸念されるため、スケジュールドスケーリングポリシーを検討しました。
しかし、スケジュールドスケーリングポリシーでは、以下の点が懸念されます。

  • スケールするタスク数は静的な値
  • 単一のスケジュールドスケーリングポリシーでは対応が難しい
  • メンテナンスコストがかかる

そこで目をつけたのが予測スケーリングポリシーです。
ECSの予測スケーリングポリシーは、過去の傾向を元に機械学習をしてよしなにスケールアウトしてくれます。
https://aws.amazon.com/jp/blogs/news/optimize-compute-resources-on-amazon-ecs-with-predictive-scaling/

  • 複数のスケーリングポリシー (予測スケーリングとターゲット追跡スケーリングなど) を有効化した場合、それぞれが独立して必要なタスク数を見積もります。それぞれの見積もりの最大値が、最終的なタスクの必要数になります。
  • 現在のタスク数が予測したタスクの必要数を上回っている場合には、Amazon ECS はサービスをスケールインしません。これは、実際に必要な値よりも少ない値を予測した場合にスケールインするのを防ぎ、常に必要なキャパシティを確保できるようにするためです。予測スケーリングと (ターゲット追跡ポリシーなどの) リアクティブなスケーリングポリシーの両方が、現在のタスク数よりも少ない値を見積もった場合にのみ、ECS サービスはスケールインします。

CPU使用率50%のターゲット追跡スケーリングと予測スケーリングポリシーを有効にしている場合、どちらかが優先されるということはなく、それぞれの見積もりの最大値にスケールされるため、予測スケーリングによって既存のターゲット追跡スケーリングが妨害されることもありません。
また、予測スケーリングとターゲット追跡スケーリングは、どちらも現在のタスク数より少ないタスク数を見積もった場合にのみスケールインを実行します。これにより、予測が外れた場合でも過剰なスケールインを防ぎ、サービスの可用性を維持できる点も大きなメリットです。

予測スケーリングポリシーの導入

検討の結果、予測スケーリングポリシーを導入することになりました。
予測スケーリングポリシーには、予測のみという機能が用意されています。
実際に導入する前に、過去の実際のタスク数の遷移と、予測スケーリングが算出したタスク数を比較することができます。

CPU使用率50%をターゲットにした予測


少しわかりづらいですが、実際に観測されたタスク数よりも、予測されるタスク数の方が多くなっています。
ターゲット追跡ポリシーと同様に、CPU使用率のターゲットを50%にして導入しました。

Terraform
ecs-autoscaling.tf
resource "awscc_applicationautoscaling_scaling_policy" "cpu_50_predictive_forecast" {
  policy_name        = "cpu-50-percent-predictive-scaling"
  policy_type        = "PredictiveScaling"
  resource_id        = aws_appautoscaling_target.ecs_autoscaling_target.resource_id
  scalable_dimension = aws_appautoscaling_target.ecs_autoscaling_target.scalable_dimension
  service_namespace  = aws_appautoscaling_target.ecs_autoscaling_target.service_namespace

  predictive_scaling_policy_configuration = {
    metric_specifications = [
      {
        predefined_metric_pair_specification = {
          predefined_metric_type = "ECSServiceCPUUtilization"
        }
        target_value = 50
      }
    ]
    mode                         = var.cpu_50_predictive_mode
    scheduling_buffer_time       = var.predictive_scheduling_buffer_time
    max_capacity_breach_behavior = "HonorMaxCapacity"
  }
}

結果


予測スケーリングを導入したことで、事前にタスク数を増やしてくれるようになりました。


これまで発生していた、ピークタイム前のレスポンスタイムのスパイクが発生しなくなりました。

まとめ

今回は、ECSの安定稼働を目指し、「ECSのスケールアップ」と「予測スケーリングポリシーの導入」という2つのアプローチに取り組みました。

  • ECSスケールアップ: CPUリソースを増強することで、CPU使用率のスパイクを抑制し、タスク数を削減できました。コストを増やさずに、より健全なサービス基盤を構築できましたが、レスポンスタイムの改善には至りませんでした。
  • 予測スケーリングポリシー: 過去の負荷傾向から将来の必要タスク数を予測し、ピークタイム前にスケールアウトさせることで、レスポンスタイムのスパイクを防ぐことに成功しました。

おまけ

アイドルタイム移行時の無駄遣い削減

タスク数、CPU使用率のメトリクスを見ると、急激にスケールインしている箇所があります。

画像を見ればわかる通り、リクエスト数に対して過剰なリソースがある状態です。
toCサービスではよくある現象だと思います。
この無駄遣いを防止するためには、スケジュールドスケーリングの設定や、スケールインのクールダウンタイムを見直すことなどが挙げられます。
しかし、これらは全体の可用性を損ないかねない行為であるためあまり取りたくない手段です。

クールダウンタイムの見直し

前述しましたが、今回導入した予測スケーリングの公式ドキュメントには以下のような記述があります。

現在のタスク数が予測したタスクの必要数を上回っている場合には、Amazon ECS はサービスをスケールインしません。これは、実際に必要な値よりも少ない値を予測した場合にスケールインするのを防ぎ、常に必要なキャパシティを確保できるようにするためです。予測スケーリングと (ターゲット追跡ポリシーなどの) リアクティブなスケーリングポリシーの両方が、現在のタスク数よりも少ない値を見積もった場合にのみ、ECS サービスはスケールインします。

予測スケーリングを導入したことで、予測スケーリング、ターゲット追跡スケーリングポリシーの両方が現在のタスク数よりも少ない値を算出しない限り、ECSはスケールインしなくなりました。
本来、クールダウンタイムを短くし過ぎると、CPU使用率の振れ幅が大きい場合にタスク数の増減が激しくなります(フラッピング)。
そのため、ECSはデフォルトでスケールインのクールダウンタイムを300秒に設定しています。
CPU使用率のターゲット追跡スケーリングポリシーのみで運用していた時は、これを短くすることは危険でしたが、予測スケーリングの導入によってクールダウンタイムを短くすることができる可能性が出てきました。
これは、ターゲット追跡スケーリングポリシーが現在のタスク数より少ない値を算出したとしても、予測スケーリングが現在より少ない値を算出しない限りスケールインしないためです。
弊社では、リソースの無駄遣いを削減するため、クールダウンタイムの調整を検討しています。
この取り組みの結果についても、また別の機会にご紹介できればと思います。

Kurashiru Tech Blog

Discussion