Carpe Diem

備忘録。https://github.com/jun06t

ECSのオートスケール戦略

概要

ECSはコンテナのスケールアウトとインスタンスのスケールアウトのタイミングが重要です。
よく起きる問題としては

  • インスタンスのスケールアウトが遅くてコンテナのスケールアウトも遅くなってしまう
  • しきい値が不適切でインスタンスがスケールアウトせず、コンテナがスケールアウトしたくてもできない

で、こういった事が起きないようにしないといけません。

スケールアウトの方針

対象 方針
インスタンス インスタンスの空きリソースがコンテナ1台分だけの状態がn分間続いたら
コンテナ コンテナの使用率がn%を超えたら

インスタンスもう1台もコンテナを追加するリソースがない状況になったら早めにスケールアウトします。
コンテナは一般的なやり方で使用率が高くなったらスケールアウトすればOKです。

スケールインの方針

対象 方針
インスタンス コンテナがn分間ずっと1台を下回ったら
コンテナ コンテナの使用率がn%以下の状態がm分続いたら

インスタンス1台もコンテナが載っていなければスケールインします。不要ですからね。 コンテナは一般的なやり方で使用率が低くなったらスケールインすればOKです。

具体的な設定

コンテナはCPUやメモリの使用率を使えばいいのであまり難しくありませんが、インスタンスはちょっと複雑です。
ここで利用するメトリクスがCPUReservationMemoryReservationです。

ECSではtask definitionでcpu, memory, memoryReservationといったパラメータであらかじめ使用するリソースを確保できます。
これらの数値はUtilizationと異なり確保した量なので、確保したリソース量を超えた使用量でなければ一定です。なのでコンテナ数の計算に向いています。

CPUUtilizationとCPUReservationの比較

f:id:quoll00:20171019193856p:plain

MemoryUtilizationとMemoryReservationの比較

f:id:quoll00:20171019193909p:plain

どちらのReservationも基本的に一定ですね。

スケールアウト

コンテナが増えた時のリソースの遷移

CPU Unitが2048で、そこで実行するコンテナのタスクのcpu割当が768だとします。この場合

コンテナ数 残りリソース CPU Reservation
1 1280 768 / 2048 = 37.5%
2 512 1536 / 2048 = 75%
3 -256 2304 / 2048 = 112.5%

と、3台目で枯渇してしまいます。つまり75%のときにはすでにインスタンスを増やさなければいけません。

算出式

ただこのような数値は環境によって異なるため、統一して算出できる数式が欲しいです。それが以下です。

 Threshold = (1\ -\ \frac{Container\ Reservation}{Total\ Capacity\ of\ a\ Single\ Container\ Instance}) \times100

※英語なのは日本語だとtexの表示が変になるためです。

先程の例で算出

今回のケースだと、

 Threshold = (1\ -\ \frac{765}{2048}) \times100 = 62.5

62.5%インスタンススケールアウトのしきい値となります。
この設定にすれば先程の例でも

コンテナ数 残りリソース CPU Reservation インスタンス
スケールアウトするか
1 1280 768 / 2048 = 37.5% しない
2 512 1536 / 2048 = 75% する

と、コンテナが2台になったらインスタンスがスケールアウトしてくれるので、3台めのコンテナも生成することができるようになります。

複数のサービスが混在する場合

先程の例は1サービスのみ考えればいい例ですが、複数のサービスが混在してcpu割当もそれぞれ異なるケースも当然あります。その場合は以下の方針で考えればいいです。

算出式

最もReservationの高いタスクを元に算出する、という方針です。

 Threshold = (1\ -\ \frac{max(Container\ Reservation)}{Total\ Capacity\ of\ a\ Single\ Container\ Instance}) \times100

具体例

例えばcpu割当が

  • web-task: 256
  • batch-task: 512
  • proxy-task: 128

みたいなケースでは、最大であるbatch-task512を使います。

 Threshold = (1\ -\ \frac{512}{2048}) \times100 = 75

75%になりました。これをしきい値として設定しておけば、どのコンテナであってもスケールできます。

web数 batch数 proxy数 残りリソース CPU Reservation インスタンス
スケールアウトするか
1 1 1 1152 896 / 2048 = 43.75% しない
1 2 1 640 1408 / 2048 = 68.75% しない
2 2 2 256 1792 / 2048 = 87.5% する

NG例

これがもしproxy-task128に合わせてしきい値を設定すると

 Threshold = (1\ -\ \frac{128}{2048}) \times100 = 93.75

93.75%となり、

web数 batch数 proxy数 残りリソース CPU Reservation インスタンス
スケールアウトするか
1 1 1 1152 896 / 2048 = 43.75% しない
2 2 2 256 1792 / 2048 = 87.5% しない

と、残りリソースが256になってもインスタンスがスケールしないため、batchコンテナが次にスケールアウトしたくてもこれ以上増えることはできません
なので最もReservationの高いタスクを元に算出することが重要です。

スケールイン

インスタンスをスケールインしたいのはタスクによるコンテナが1つもない状態です。
ただecs-agentや監視用エージェントが動いているため、単純に0とか固定値にすることはできません。

算出式

なので最もReservationの低いタスクを元に算出する方針が良いです。

 Threshold = \frac{min(Container\ Reservation)}{Total\ Capacity\ of\ a\ Single\ Container\ Instance} \times100

先程の例で算出

先程のweb, batch, proxyがある環境であれば、

 Threshold = \frac{128}{2048} \times100 = 6.25

6.25%になります。

web数 batch数 proxy数 残りリソース CPU Reservation インスタンス
スケールインするか
2 2 2 256 1792 / 2048 = 87.5% しない
1 1 1 1152 896 / 2048 = 43.75% しない
0 0 1 1920 128 / 2048 = 6.25% しない
0 0 0 2048 0 / 2048 = 0%
(実際は他プロセスで0にはならないはず)
する

となります。問題なくスケールインできますね。

NG例

もしこれがwebを元に算出すると、

 Threshold = \frac{256}{2048} \times100 = 12.5

12.5%しきい値になります。この場合

web数 batch数 proxy数 残りリソース CPU Reservation インスタンス
スケールインするか
2 2 2 256 1792 / 2048 = 87.5% しない
1 1 1 1152 896 / 2048 = 43.75% しない
0 0 1 1920 128 / 2048 = 6.25% する

となり、proxyが残っているのにサーバがシャットダウンしてしまうという事になります。

まとめ

ECSのインスタンスをスケールアウトする時は

 Threshold = (1\ -\ \frac{max(Container\ Reservation)}{Total\ Capacity\ of\ a\ Single\ Container\ Instance}) \times100

スケールインする時は

 Threshold = \frac{min(Container\ Reservation)}{Total\ Capacity\ of\ a\ Single\ Container\ Instance} \times100

で算出したしきい値で設定すると問題なくスケールできます。

CPUReservationMemoryReservationのどちらを使うかは、そのクラスタに属するタスクがcpu heavyなのかmemory heavyなのか、どちらの負荷特性を持つかで判断してください。

ソース