80. スケジューラは差分を埋め続ける制御ループである — desired countと現実のずれが仕事のリスト
ECSサービスの中心には「望ましい状態」と「現実」を突き合わせて差分を埋め続ける1つのループがあります。この回では、そのループが何を観測し・どう差分を計算し・どんな順序で行動するのかを、上から下へたどる図で追っていきます。
① 命令ではなく宣言で動く差分ループ
- 公式:「If a task in a service stops, the scheduler launches a new task to replace it. This process continues until your service reaches your desired number of tasks」
- 差分が +1 なら起動、−1 なら停止、0 なら何もしない
- タスクの失敗・停止も基盤(インスタンス)の故障も、次の観測で「差分」として現れ、同じループが埋める。公式:「if the underlying infrastructure fails, the service scheduler reschedules a task」
- CS基礎ではこれを制御ループ / リコンシリエーション(reconciliation)と呼ぶ。Kubernetesも全く同じ原理(desired stateとactual stateを突き合わせるcontroller loop)で動く
スケジューラは手順を実行する存在ではなく、宣言と現実の差分を検出して埋め続ける制御ループそのもの。「タスクが死んだら誰が復旧を命じるのか」という問いの答えは「誰も命じない。次の観測で差分が現れ、ループが勝手に埋める」。
② 置き換えの原則 — 先に代わり、あとで撤去
- maximumPercent = RUNNING/STOPPING/PENDING状態のタスク数の上限。desiredCountに対する割合で切り捨て。REPLICAの既定は200%(DAEMONは100%)
- 例: desiredCount=4, max=200% なら4つ起動してから4つ止められる
- 公式:「the service scheduler will first start a replacement task」
- 公式:「If the maximumPercent parameter limits the scheduler from starting a replacement task first, the scheduler will stop an unhealthy task one at a time at random to free up capacity」
- 「先に代わりを起動してから古い方を止める」はローリングアップデートの余剰キャパシティと同じ発想。max=200%は「一時的に定員の倍まで許すから無停止で入れ替えられる」という宣言
置き換えの既定は「先に代わり、あとで撤去」——この順序が可用性を守る。資源不足のときだけ「1つ空けてから起動」に退避し、決着後に超過分の健康なタスクをランダムに間引いて、最終的に総数をdesiredCountぴったりに収束させる。
③ 起動失敗のスロットル — あえてリトライを遅くする
- 発動条件は「RUNNING状態に入らずに停止した」場合。正常起動後に落ちたケースとは区別される
- 公式:「If a task is stopped without having entered a RUNNING state, the service scheduler starts to slow down the launch attempts and sends out a service event message.」
- 目的は公式:「prevents unnecessary resources from being used for failed tasks before you can resolve the issue」
- API Gateway編・メッセージング編で見たバックオフ(再試行は間隔を空ける)の再登場。バックオフは通信に限らず「失敗し続ける行動全般」に効く一般原理
制御ループは「速く回せば良い」わけではない。素朴なループなら「不足→起動→即失敗→また即起動」を高速で回して資源を燃やし尽くすところを、スロットルで試行間隔を空け、イベントで人間に知らせる——無限高速リトライで資源を溶かさない抑制もループ設計の一部。
④ スケジューリング戦略 — 指定数維持か、各インスタンスに1つか
- 公式 REPLICA:「places and maintains the desired number of tasks across your cluster. By default, the service scheduler spreads tasks across Availability Zones.」
- 公式 DAEMON:「deploys exactly one task on each active container instance that meets all of the task placement constraints ... there is no need to specify a desired number of tasks」
- 公式:「Fargate tasks do not support the DAEMON scheduling strategy.」Fargateはインスタンスという概念をユーザーに見せないので当然
- DAEMONはログ収集やモニタリングのエージェント(全ホストに1つ欲しいもの)に向く
REPLICAは「合計N個をどこかに保て」、DAEMONは「各マシンに1個ずつ貼れ」。前者は数を宣言し、後者は「インスタンスの数」がそのままdesiredになる。Fargateではインスタンスが見えないのでDAEMONは使えない。
⑤ 配置戦略 — random / spread / binpack
- 公式 random:「randomly places tasks on available candidates」
- 公式 spread:「spreads placement across available candidates evenly based on the field parameter」
- 公式 binpack:「places tasks on available candidates that have the least available amount of the resource ... if you binpack on memory, a task is placed on the instance with the least amount of remaining memory but still enough to run the task」
- binpackは教科書に載るビンパッキング問題(限られた容器に荷物を無駄なく詰めるNP困難な最適化問題)の名が、そのままAPIのenum値になっている
- spreadがAZにタスクを散らすのは、DynamoDB編でハッシュ関数がキーをパーティションに散らして偏りを防いだのと同じ「均等分散で偏りを避ける」発想
spreadは可用性のため散らし、binpackはコストのため詰める——正反対の目的が同じ「配置戦略」の枠に同居している。binpackは名前どおりビンパッキング問題の実装で、「設定値の名前は、その裏にある問題を指している」ことがあると教えてくれる。
⑥ AZリバランス — 偏りも差分とみなす
- 新規サービス: 値を指定しなければ既定はDISABLED(執筆時=2026年に現行記述を確認済み)
- 既存サービス: 指定しなければ既存値を維持、過去にも未設定ならDISABLED
- 公式:「Indicates whether the service uses Availability Zone rebalancing. The valid values are ENABLED and DISABLED.」
- REPLICAは既定でAZ分散するが、障害復旧やスケール後に偏りが残ることがある。ENABLEDはその「偏りという差分」もループの仕事に加える設定
ENABLEDにすると、ループが監視する「あるべき姿」に「AZ間でバランスが取れていること」が加わる——制御ループは、何を差分とみなすかを設定で拡張できる。タスクの死・基盤故障・デプロイ・AZの偏り、起きることは千差万別でも対処するのはたった1つの制御ループであり、この型はKubernetesをはじめ現代のオーケストレーションが共有する骨格そのものだ。