helen's blog

ずっとおもしろいことしてたいな。

AWSのAuto ScalingでTarget Tracking Scaling PolicyのALBRequestCountPerTargetを本番試用した

自分が初めて使った Amazon EC2 Auto Scaling についてのログと感想を残します

Auto Scaling

Auto ScalingはEC2やRDSなどのサービスに対し、正常なインスタンス数を保ったり
スケジュール・負荷に合わせてスケーリングしてくれるAWSの便利サービスです
もちろん手動でのインスタンス増減も対応していて、
ヘルスチェック、Warming time、Instance Drawningを利用して安全に増減させてくれます

Amazon EC2 Auto Scaling の動的スケーリングとして現在3つのスケーリングポリシーがあります

Target tracking scaling: 
  特定のメトリクスのターゲット値に基づいて、グループの現在の容量を増減させます。
  これはサーモスタットで家の温度を管理する方法と似ています(温度を選択すれば、後はサーモスタットがすべてを実行する)。
Step scaling:
  アラーム超過のサイズに応じて変動する一連のスケーリング調整値(ステップ調整値)に基づいて、
  グループの現在の容量を増減させます。
Simple scaling: 
  1つのスケーリング調整値に基づいて、グループの現在の容量を増減させます。 
https://docs.aws.amazon.com/ja_jp/autoscaling/ec2/userguide/as-scale-based-on-demand.html

そして続けてこんな記載があります

If you are scaling based on a utilization metric 
that increases or decreases proportionally to the number of instances in an Auto Scaling group,
we recommend that you use target tracking scaling policies. 
Otherwise, we recommend that you use step scaling policies.

インスタンスの使用率に基づく場合はTarget Tracking Policy、
そうでなければStep Scaling Policyがおすすめだそうです

今回はインスタンスのキャパシティがわかっていたため Target Tracking Policyを選択してみたくなりました

Simple Scale, Step Scale

Target Tracking Policyは2017/7/12にリリースされました
Introducing Target Tracking Scaling Policies for Auto Scaling

それまでに追加されていたポリシーはSimple Scale Policyとして扱われます

スケーリングアクティビティまたはヘルスチェック交換の最中でも、
アラームを継続的に評価し、グループをロックしない

という点から、Simple Scale PolicyよりStep Scaling, Target Tracking Scalingが推奨されています

Step Scaling, Target Tracking Scalingは

Auto Scaling グループ内のインスタンス数に比例して増減するメトリクスに基づいてスケールする場合は、
代わりにターゲット追跡スケーリングポリシーを使用することをお勧めします

という基準があります
ちなみにAWSコンソールからEC2 Auto Scale Policyを追加するとデフォルトはTarget Tracking Policyになっています
日本語では"ターゲット追跡スケーリングポリシー"などと表記されます

docs.aws.amazon.com

Target Tracking Scale

Target Tracking Scaleの特徴は下記です

ターゲットの追跡スケーリングポリシーは、メトリクスをターゲット値近くに維持することに加えて、
負荷パターンの変動によるメトリクスの変動に合わせて調整し、
Auto Scaling グループの容量の急速な変動を最小化します。
https://docs.aws.amazon.com/ja_jp/autoscaling/ec2/userguide/as-scaling-target-tracking.html

このドキュメントでの "事前定義されたメトリクス" は現在4つあります

ASGAverageCPUUtilization: 
  average CPU utilization of the Auto Scaling group
ASGAverageNetworkIn: 
  average number of bytes received on all network interfaces by the Auto Scaling group
ASGAverageNetworkOut: 
  average number of bytes sent out on all network interfaces by the Auto Scaling group
ALBRequestCountPerTarget: 
  number of requests completed per target in an Application Load Balancer target group
https://docs.aws.amazon.com/ja_jp/autoscaling/ec2/APIReference/API_PredefinedMetricSpecification.html

事前定義されたメトリクスを使用する場合、CloudWatchAlarmの作成も自動で行われます
Alarmを定義しなくて済むのはすごく便利でした

今回はRequest数に基づいてスケールさせたかったので(本当は私のTryしてみたい思いが99.8%です)ALBRequestCountPerTargetを選択したのですが自分の理想通りになかなかいきませんでした

Terraformする

Target Tracking PolicyをTerraformするとこうなります
なお、今回のTarget Value閾値)は4200と仮定します

resource "aws_autoscaling_policy" "scale_out_policy" {
  name                      = "scale-out-policy"
  autoscaling_group_name    = "<Auto Scaling Group Name>"
  policy_type               = "TargetTrackingScaling"
  estimated_instance_warmup = "180"

  target_tracking_configuration {
    predefined_metric_specification {
      predefined_metric_type = "ALBRequestCountPerTarget"
      resource_label = "<ALB ARN Suffix>/<TargetGroup ARN Suffix>"
    }
    target_value = 4200 # ↓で使う仮のTargetValue
    disable_scale_in = true # チキンなのでScale inはDisable
  }
}

Simple Policy, Step Policyでは閾値を超えたときにインスタンスのを追加するなどの指定がありますが、
Target Tracking Policyでは閾値を超えたときの挙動をALBとターゲットグループ以外指定しません
Target Tracking Policyの場合、閾値を上回らないようにAuto Scaleしてくれるので指定する必要がありません

ALBRequestCountPerTarget

ポリシーを作成すると、CloudWatchAlarmが作成されます
ALBRequestCountPerTarget(Scaling Policy作成画面ではApplication Load Balancer Request Count Per Targetと表記されます)を選択したときに作成されるAlarmの一部をピックアップします

Title Description Value
State Details アラームの状態(OK, ALARM or INSUFFICIENT_DATA)とその理由 OK. 理由: 3データポイントがthresholdを超えていませんでした
Threshold ALARMになる条件 RequestCountPerTargetが3分間の3データポイントでthresholdを超えたとき
Statistic メトリクスの統計方法 Sum
Period データポイントの粒度 1 minute

Thresholdは

RequestCountPerTarget > 4200 for 3 datapoints within 3 minutes

などが表示されていて、
詳細モニタリングが有効になっていて1分に1回メトリクスが投稿されているとすると、
3分間に3つのデータポイントがあり、TargetValue(4200)を超えると閾値をオーバーしたとみなします

RequestCountPerTargetはCloudWatch MetricsのApplicationELB > TargetGroup内にあり、

RequestCountPerTarget
Statistics: The only valid statistic is Sum. Note that this represents the average not the sum.
https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-cloudwatch-metrics.html

StatisticのSumはSumという名の平均値になります

Alarm状態を眺める

ALARM状態になったときのState Reasonの例は下記です

Threshold Crossed: 3 datapoints [5200, 5300, 4250] were greater than the threshold (4200.0).

この[5200, 5300, 4250]はRequestCountPerTargetの値で、
ApplicationELBのRequestCountをTargetGroup内で稼働しているインスタンス数で割った数におよそ一致するはずです
[5200, 5300, 4250]のように3datapointsがthe threshold(4200)を超えるとALARMとなります

Target Valueを考える

(1秒間に捌けるリクエスト数) * 60(秒) * 係数(という気持ち)

チューニングすれば1秒間に捌けるリクエスト数が増えるし、
係数はモニタリングしながら調整かなぁとやりながら思いました
同様に総リクエスト数を考慮してAuto ScaleのMax, Minを指定します

仮に秒間100Requestsをギリギリ捌けるインスタンスをTargetとした場合、
100 * 60(1minute) = 6000のRequestCountPerTargetはギリギリ捌けることになり、
インスタンスが2台ある場合、1分間に12000リクエストをさばくことができると想定されます
そこでScale PolicyのTarget Valueを4200くらい(6000さばけるけど余裕見て0.7掛け)にしておくと、
1分間に8400Request(4200*2)を超えた状態が3分続くとスケールアウトされる計算になります

この計算が全然わからなくて苦労しました
クッ...Sumに惑わされた...

おまけ

実際にインスタンスを増減させる際にはこんな挙動も起こります

ターゲット値と実際のメトリクスデータポイント間にギャップが発生する場合があります。
これは、追加または削除するインスタンス数を決定するときに、
その数を切り上げまたは切り捨てて常に控えめに動作するためです。
これにより、不十分な数のインスタンスを追加したり、インスタンスを過剰に削除したりすることがなくなります。
https://docs.aws.amazon.com/ja_jp/autoscaling/ec2/userguide/as-scaling-target-tracking.html

結局、リクエストが瞬間的に増加するが継続時間は2,3分程度で、
スケールアウトする頃にはリクエストが落ち着いているというよくある状態になってしまいました

感想

構築時は1秒間に捌けるリクエスト数がわかっていてCPU使用率が高騰していてもさばけるときに
ALBRequestCountPerTargetを使うといいのかなぁと考えていたのですが
リクエストがバーストしてもインスタンスの各使用率が高騰せず済んでしまうほどチューニングが優秀過ぎて
余裕でアクセスをさばくインスタンス郡が増設されるだけになってしまいました😂
一度バーストするとしばらく継続される状況でAuto Scale Inもさせるなら良さそうでした

瞬間にバーストしてすぐ落ち着くサービスは難しいなぁ🤔
台数用意して耐えきるほうが現実的なのかなぁ🤔