ECS Cluster Auto Scalingについて調べたこと

## 使うと何が嬉しいのか
* 今まではECSのタスクとEC2のコンテナインスタンスを別々に管理しないといけなかった
* ECS Cluster Auto Scaling(CAS)を設定すると、ECSでタスクに合わせてASGをスケールイン/アウトしてくれる
  - ASGを使うことが前提

## デモ環境構築メモ
参考: https://dev.classmethod.jp/articles/aws-ecs-cluster-auto-scaling/

* ECSで空のクラスタを作成
* ASGを作成
  - 起動テンプレートも必要に応じて
  - 「スケールインからのインスタンス保護」を有効にすること
* ECSクラスターでキャパシティプロバイダを作成する
  - 設定項目について
      - `マネージドスケーリング`: 有効になっているとECSがASGのスケールを行ってくれる
      - `ターゲットキャパシティー(%)`: ASG内のインスタンスをどのくらい使うか。(20%くらいは空けておきたいという人は80を記入)
      - `マネージドターミネーション保護`: インスタンスがスケールインするときにタスクが終了するのを防いでくれる。→タスクが終了するまでインスタンスは終了せずに待っていてくれる
          - ASGの設定で「スケールインからのインスタンス保護」が有効になっていること
* クラスター更新でデフォルトのキャパシティープロバイダー戦略にて↑で作ったキャパシティプロバイダを設定する
* サービスを作ってタスクを起動させる
  - 「キャパシティープロバイダー戦略」欄が表示される
    - `ベース`: 指定されたキャパシティープロバイダーで実行するタスクの最小限の数を指定できる。(指定できるのは複数あったら１つのみ)
    - `ウェイト`: 起動済みタスクの総数に対する、それぞれのキャパシティプロバイダで起動するタスク数の相対的な割合を指定できる


### 動かしてみたメモ
* インスタンス0の状態でタスクがrunningになるまでの時間
  - 何回かやったけど、早い時で1分半、遅い時で10分以上とまちまちだった
* ASGのキャパシティを直接変更した時の挙動
    - キャパシティプロバイダの方に設定が反映された。が、しばらくするとまた別の数字に上書きされた
        - → ECSのマネージドスケーリングによって値が上書きされたもの。この値がどのように決められているのかは後述
* タスクを0にしてもなぜか2か3台くらい稼働し続けた
    * → キャパシティプロバイダのターゲットキャパシティの設定を80%にしていたので、タスクがないのに2,3台稼働し続けていた分は20%に当たる余剰分だったと思われる。
        * 同じ理由でASGの「希望するキャパシティ」を0にしても、しばらくすると2,3台に上書かれてインスタンスが起動してくる。ただしASGの「インスタンスの最大値」以上の数にはならないので、ここを0にするとインスタンスは0になる

## 疑問
### ？CASがどのようにインスタンスのキャパシティを決めているのか

本家のブログに記事があった

[Amazon ECS クラスターの Auto Scaling を深く探る](https://aws.amazon.com/jp/blogs/news/deep-dive-on-amazon-ecs-cluster-auto-scaling/)

長いのと難しいのとであまり理解できなかった。
とりあえず読み取れたこととしては、

* **タスク配置の最適値を求めるアルゴリズムの開発は難しい**
    * ECSの配置戦略や配置制約、ASGのインスタンスの設定、タスク定義で指定しているcpuやmemoryなど変数が多いため
* 理想の状態は「全てのインスタンスがタスクを1つ以上実行している」としている
    * この状態にあるとインスタンスはスケールをする必要がないと判断する
* CAS は追加すべきインスタンス数の最適値の下限を見積る
    * 流れとしては、
    * すべてのプロビジョニングタスクを、リソース要件が正確に等しくなるようにグループ化する
    * ASG に最も新しく追加されたインスタンスのインスタンスタイプと属性を取得
    * 配置戦略と制約を踏まえて、グループ化したタスクを配置するのに必要なインスタンス数を求める
    * 上記を全てのグループに対して計算し、その最大値を必要なインスタンス数と定義し、スケールする

インスタンス数を決める主な変数としては、「希望されているインスタンス数(稼働中のもの+これから稼働される予定のもの)」と「現在稼働しているインスタンス数」があり、スケールしてインスタンス数が変動することで計算され直しているそう。なので1度のスケールで適切な値にならないこともある。何回か繰り返しスケールされるけど、必ずしも無駄なこととも限らないよ...とのこと

タスクの配置の完全な最適化は難しい。
(インスタンスサイズとか固定にすればある程度はいけるかもだけど、スポットインスタンスを使う場合はいろんなサイズのを混ぜて使うことになるのでやっぱり難しいと思う)
タスクが乗らないことはないけど、「詰めればもっと乗るはずなのにな🤔」ということはあるかもしれない。

#### その他のブログメモ

* CWのメトリクス`CapacityProviderReservation`でスケーリングポリシーの指標となる数字が見れる
    - `CapacityProviderReservation = CASが必要であると判断したインスタンス数 / 稼働中のインスタンス数 * 100`
    - 100だとちょうどよくて、100以上だとインスタンスが余っているのでスケールインされる、100より少なければ足りていないので増える方向に働く
* 以前はタスクが起動するときは利用可能なキャパシティに基づいて実行されるか否だったが、CASを導入すると、利用可能なキャパシティの有無に限らずプロビジョニング状態に入る　→　空きリソースを探す　→　あれば起動、見つからなければ失敗　のようになる
* 最大100までプロビジョニング状態にできる
* プロビジョニング状態のタスクは15minでタイムアウトする

### ？キャパシティプロバイダの設定変更はできないのか
現段階では新しいものを作り直すしかないみたい。
**ASGとキャパシティプロバイダは1:1なので、ASGも合わせて作り直す必要がある。**
既存のとは別に新しいリソースを作成し、クラスタやサービスで設定している箇所を変更し、古い方を無効にする。

ちなみにコンソールからだとキャパシティプロバイダの設定内容をすべて見ることができず不便さを感じた。(`describe-capacity-providers`コマンドを使うことになる)

無効にするときはコンソールからだと対象を選択して無効ボタンを押すのだが、確認画面も出ずあっさり消えるので間違えないように注意する必要がありそう。
※あとで調べてわかったことだが、無効にされただけで削除されるわけではないらしい。describeコマンドからは存在が確認できる。名前やASGの設定が被って作れなくなるといったこともあるらしいので注意

参考: [Auto Scaling Capacity Providerの再作成時にASGが既に使用されているというエラーが発生した時のメモ](https://dev.classmethod.jp/articles/asg-already-used-error-while-recreating-auto-scaling-capacity-provider/)

作り直しをするときは注意が要りそう

## 追加で検証したいこと
* サービス稼働時にプロバイダーを変更したらタスクに影響はあるか、あるとしたら影響が出ないように設定を更新する方法