Tight-race expireAfter Soak — シナリオ P
この検証の対象
leadTime が expireAfter と本気で競争する状態を 12 時間持続させた実 EKS ソーク。実証: (1) graceful surge が常に先着(expired は 0 のまま)、(2) graceful が組めなくなった瞬間に forceful fallback が決定的に発火。
プールは固定 expireAfter: 2h12m — 導出 leadTime 1h12m のすぐ上 — で、サブデイリーウィンドウ(30 分周期 × 48 本/日)の下で実行。§3.2 の保証をライブで実証した。前提の validated 切り替えは §7.2、先行検証は シナリオ O、メトリクス定義は ランブック を参照。
実施: 2026-07-14T14:20:29Z(T0)→ +12h、EKS Auto Mode、K8s 1.36、us-west-2。 正本記録: test/e2e/eks-automode/VALIDATION.md(§ "Run: 2026-07-15 — Scenario P")。
判定: CLEAN PASS(12 PASS; 基準 13 は N/A — 未行使)。
| 71/71 | 0 | 68.3 分 | 56 秒 |
|---|---|---|---|
| graceful 回転(12h 内 60 = 5.0/h) | expired・failure・fallback・short_lead・restart | deadline margin 最小値 | エピローグ: 解凍 → fallback |
導出スケジュール
過去の PoC は日次ウィンドウで巨大な expireAfter(E=336h)を使い、レースは常に大差勝ちだった。本検証は E を 2h12m に置き、12 時間の本気レースを持続。surge.forcefulFallback は 装備したまま(静穏性を同時実証)。
| 量 | 値 |
|---|---|
t_rot(上界) | 5m + 5m + 2m = 12m |
t_rot_est(予測) | 5m + 5m = 10m |
leadTime | 2·30m + 12m = 1h12m |
ageThreshold(auto) | 2h12m − 1h12m = 1h |
C(ウィンドウあたり) | ceil(28m / 12m) = 3(6/h) |
| 定常負荷 | N=5、5/h → 予測の 83%(タイト) |
| 期待 findings | warn RotationSpansNextWindow が 1 件のみ |
導出値は internal/schedule/schedule_test.go の TestDeriveScenarioPSoak にピン留め。
margin の全体像
T0 から記録終了(T0+14.1h)まで: 71 回転すべてが新規 provision ノードへの graceful make-before-break surge。[T0, T_end] 内 60(= 5.0/h)、エピローグ中に 11 回転追加(~12 分間隔維持)。
margin(deadline − 完了): 68.3–71.2 分(全 71 回転、分散 < 3 分)。劣化の蓄積なし。
図 1 — 全 72 回転の deadline margin
横軸: T0 からの経過時間。縦軸: 完了時点で deadline までに残っていた時間(分)。T_end(12h 観測窓の終端、窓内 60 回転)より右の点は有人エピローグ期間中の完了で、刻みも margin も変わっていない。オレンジの 1 点がエピローグの意図的な fallback 発火(margin 10.1 分)— 本編の帯との落差がこの検証の要約になっている。
13 基準
| # | 基準 | 判定 |
|---|---|---|
| 1 | expired == 0 | PASS |
| 2 | success ≈5/h、計 ≥ 40 | PASS(12h 内 60) |
| 3 | 本編 forceful_fallback == 0 | PASS(静穏性) |
| 4 | short_lead_nodes == 0 | PASS(909 スクレイプ) |
| 5 | restart 0、seq 連続 | PASS |
| 6 | 負荷継続(全スナップショット) | PASS(desired=available=ready=5) |
| 7 | 検証対象構成の一致 | PASS(ゲージ 6 本 × 909) |
| 8 | 全回転 margin > 0 | PASS(min 68.3m) |
| 9 | 終了 census クリーン | PASS(全 5 claim < A) |
| 10 | Karpenter の予期しない disruption なし | PASS |
| 11 | epi: 凍結中 = 候補のみ | PASS(~2h 保持) |
| 12 | epi: 解凍で fallback 発火 | PASS(56 秒) |
| 13 | アボート則(解凍失期) | N/A |
基準の詳細観測 — クリックで展開
| # | 観測詳細 |
|---|---|
| 1 | 本編 + tail-follow + エピローグ通して 0。Karpenter Expiration イベントも 0 |
| 2 | [T0, T_end] 内 60(5.0/h); 記録終了までに 71(epi 込み 72); ~12m 間隔 |
| 3 | 全期間 0 — 静穏性の初実証 |
| 4 | 909 スクレイプで max 0 |
| 5 | controller restartCount=0(12h); gap 0、restart 0、SCRAPE_ERROR 0 |
| 6 | desired=available=ready=5 維持; Pending 滞留なし |
| 7 | 導出ゲージ 6 本 × 909 スクレイプで不一致 0; 両 policy Accepted; 期待 warn 1 件のみ |
| 8 | min 68.3 / 中央値 70.3 / max 71.2 分(n=71) |
| 9 | T_end の 5 claim は齢 11–59m(right-censored); 滞留 0、failed 0 |
| 10 | DisruptionBlocked/Unconsolidatable のみ(budgets + do-not-disrupt が抑止) |
| 11 | 凍結中 ~2h、candidates=1 / in_progress=0 維持 |
| 12 | 下記エピローグ参照(56 秒、placeholder ゼロ、本編無擾乱) |
| 13 | 正常リリースにより未行使; fail-closed は offline 検証のみ |
ノード入れ替えの解剖
71 回転はすべて同型のシーケンス:
- claim が齢 1h(= A)に達し候補化 → placeholder Pod 作成
- Karpenter が新ノードを provision → placeholder bind → surge ノード Ready(surgeWait 中央値 34 秒、23–54 秒)
- 両ノードに
do-not-disrupt→ 旧 NodeClaim 削除 → voluntary drain(drain 中央値 44 秒、18–83 秒) - 後始末 → total 中央値 81 秒(45–131 秒)。旧ノード残骸は
WhenEmpty/60s で回収
図 2 — 各回転の所要時間の内訳(surgeWait + drain)
縦軸: 秒。積み上げの下段が placeholder 作成から surge ノード Ready までの待ち、上段が旧ノードの drain。t_rot の設計上界は 720 秒 — 実測 total は 45〜131 秒(中央値 81 秒)で常にその 1/5 以下、この余裕がバックストップに勝ち続ける源泉になっている。
全回転台帳 — 72 行(本編 71 + epi 1)を開く
誕生・完了は UTC。margin = 誕生 + 2h12m − 完了。surge 先はすべて新規 provision の EC2 インスタンス。epi 行の「—」(placeholder/surge なし)が surge-less パスの証拠そのもの。
| # | claim | mode | 誕生 | 完了 | surgeWait | drain | total | margin(分) | surge 先ノード |
|---|---|---|---|---|---|---|---|---|---|
| 1 | fhx2b | surge | 07-14 13:18:50 | 07-14 14:20:29 | 40s | 44s | 83s | 70.3 | i-073168dd1b078525f |
| 2 | xdjh8 | surge | 07-14 13:30:53 | 07-14 14:32:30 | 40s | 39s | 79s | 70.4 | i-09dcbe1f5d6d88d7c |
| 3 | 9xfpf | surge | 07-14 13:42:57 | 07-14 14:44:31 | 41s | 35s | 76s | 70.4 | i-0ea97241df5cd15d2 |
| 4 | xl8fg | surge | 07-14 13:55:00 | 07-14 14:56:34 | 24s | 48s | 73s | 70.4 | i-0e2c826c7f0d82e1e |
| 5 | ksjxd | surge | 07-14 14:07:04 | 07-14 15:09:36 | 53s | 77s | 131s | 69.5 | i-07c3fd68b28570fb0 |
| 6 | s88b7 | surge | 07-14 14:19:08 | 07-14 15:20:46 | 38s | 38s | 76s | 70.4 | i-05701db7c4c26e742 |
| 7 | bvpc6 | surge | 07-14 14:31:13 | 07-14 15:32:27 | 25s | 28s | 52s | 70.8 | i-06610ad280717ecb4 |
| 8 | 4txxs | surge | 07-14 14:43:18 | 07-14 15:45:06 | 38s | 49s | 87s | 70.2 | i-0732ee95c07077219 |
| 9 | 7s7dt | surge | 07-14 14:55:23 | 07-14 15:57:11 | 27s | 59s | 86s | 70.2 | i-028415c25f2709a31 |
| 10 | wbsfb | surge | 07-14 15:07:27 | 07-14 16:09:07 | 36s | 42s | 78s | 70.3 | i-030b61343e3e0405d |
| 11 | m4jzw | surge | 07-14 15:19:32 | 07-14 16:21:26 | 37s | 55s | 92s | 70.1 | i-08665c82484969e3b |
| 12 | w4r59 | surge | 07-14 15:31:37 | 07-14 16:32:58 | 37s | 22s | 59s | 70.7 | i-0ac7d9f8c7de48fd5 |
| 13 | 8bhbp | surge | 07-14 15:43:42 | 07-14 16:45:26 | 24s | 58s | 82s | 70.3 | i-096f1d8ecdea85ea1 |
| 14 | vrtvs | surge | 07-14 15:55:47 | 07-14 16:57:47 | 23s | 74s | 97s | 70.0 | i-0c9f26564102a018e |
| 15 | zwlds | surge | 07-14 16:07:52 | 07-14 17:09:47 | 34s | 59s | 94s | 70.1 | i-01aebd4e635a7a1ef |
| 16 | sh6s8 | surge | 07-14 16:19:56 | 07-14 17:22:03 | 24s | 80s | 104s | 69.9 | i-01b49d56eb91cc36c |
| 17 | xhbsc | surge | 07-14 16:32:01 | 07-14 17:33:55 | 38s | 53s | 91s | 70.1 | i-054e29bf46d2ddb04 |
| 18 | hghwx | surge | 07-14 16:44:06 | 07-14 17:45:52 | 24s | 61s | 85s | 70.2 | i-02d851cb5b6421813 |
| 19 | r284b | surge | 07-14 16:56:11 | 07-14 17:57:52 | 27s | 53s | 79s | 70.3 | i-026b29c8cdf04f427 |
| 20 | q592n | surge | 07-14 17:08:15 | 07-14 18:09:48 | 27s | 44s | 71s | 70.5 | i-0299e5a36131f1a53 |
| 21 | rwqpb | surge | 07-14 17:20:21 | 07-14 18:22:29 | 24s | 81s | 105s | 69.9 | i-01362e558662da0a8 |
| 22 | 6gdtf | surge | 07-14 17:32:26 | 07-14 18:33:35 | 24s | 24s | 48s | 70.8 | i-01256b5baaa58ea6d |
| 23 | 76xn9 | surge | 07-14 17:44:30 | 07-14 18:46:28 | 38s | 57s | 95s | 70.0 | i-09cc4576bdb2f9954 |
| 24 | zw4gm | surge | 07-14 17:56:35 | 07-14 18:58:19 | 38s | 43s | 82s | 70.3 | i-0c8662b78afae7195 |
| 25 | 8bcgn | surge | 07-14 18:08:40 | 07-14 19:10:40 | 26s | 71s | 97s | 70.0 | i-09d5061451bc138b9 |
| 26 | 8hld7 | surge | 07-14 18:20:45 | 07-14 19:22:13 | 37s | 29s | 66s | 70.5 | i-0053ad9546c899cf6 |
| 27 | 656fc | surge | 07-14 18:32:50 | 07-14 19:34:25 | 38s | 34s | 73s | 70.4 | i-076c2254a9c78ebd7 |
| 28 | d4d5d | surge | 07-14 18:44:55 | 07-14 19:47:01 | 24s | 81s | 105s | 69.9 | i-0727f919a26d699b2 |
| 29 | 8nkrs | surge | 07-14 18:56:59 | 07-14 19:58:18 | 23s | 33s | 56s | 70.7 | i-0b563214cfb435736 |
| 30 | pmlpq | surge | 07-14 19:09:04 | 07-14 20:10:38 | 37s | 33s | 70s | 70.4 | i-07e64b99c9d32e18a |
| 31 | 6wvqx | surge | 07-14 19:21:09 | 07-14 20:22:30 | 34s | 23s | 58s | 70.7 | i-04a2ca7bb98062b91 |
| 32 | kmh4m | surge | 07-14 19:33:14 | 07-14 20:34:25 | 25s | 23s | 48s | 70.8 | i-01d64316889e57c5c |
| 33 | xdtc9 | surge | 07-14 19:45:20 | 07-14 20:46:56 | 33s | 39s | 73s | 70.4 | i-026b484c33d3d619c |
| 34 | jqmll | surge | 07-14 19:57:24 | 07-14 20:58:35 | 24s | 22s | 46s | 70.8 | i-084de2d8a4645fd56 |
| 35 | xsxbk | surge | 07-14 20:09:29 | 07-14 21:11:34 | 36s | 64s | 100s | 69.9 | i-00d71584622976033 |
| 36 | 8tp7t | surge | 07-14 20:21:35 | 07-14 21:24:00 | 39s | 83s | 122s | 69.6 | i-0b502fd445ad0c976 |
| 37 | pdcjt | surge | 07-14 20:33:40 | 07-14 21:35:18 | 35s | 39s | 74s | 70.4 | i-06e1f6f3806f1ec9c |
| 38 | jnrdw | surge | 07-14 20:45:45 | 07-14 21:47:21 | 24s | 49s | 73s | 70.4 | i-0ab92c05b557f1add |
| 39 | 8hbzb | surge | 07-14 20:57:50 | 07-14 22:01:34 | 42s | 38s | 81s | 68.3 | i-0837901c7b5fb0e3b |
| 40 | 7thv5 | surge | 07-14 21:09:55 | 07-14 22:10:56 | 37s | 23s | 60s | 71.0 | i-02f570d8c5fd7fcca |
| 41 | j8xsl | surge | 07-14 21:22:00 | 07-14 22:22:54 | 24s | 28s | 52s | 71.1 | i-003922ea46c3fb04f |
| 42 | fkwv7 | surge | 07-14 21:34:06 | 07-14 22:35:41 | 39s | 55s | 94s | 70.4 | i-084d041ac9a5866a7 |
| 43 | fjkq9 | surge | 07-14 21:46:11 | 07-14 22:47:10 | 25s | 22s | 47s | 71.0 | i-00d419f6d8287f8d8 |
| 44 | vsd8c | surge | 07-14 22:00:16 | 07-14 23:02:06 | 39s | 61s | 100s | 70.2 | i-09324bed1c04586d9 |
| 45 | z9dl8 | surge | 07-14 22:09:58 | 07-14 23:11:27 | 39s | 44s | 83s | 70.5 | i-0035175e53982ebb5 |
| 46 | p9qdf | surge | 07-14 22:22:03 | 07-14 23:23:18 | 30s | 38s | 68s | 70.8 | i-05532c42763792a49 |
| 47 | hwgp8 | surge | 07-14 22:34:08 | 07-14 23:35:10 | 32s | 22s | 54s | 71.0 | i-09fdd03b5776ec630 |
| 48 | kgkmw | surge | 07-14 22:46:24 | 07-14 23:49:06 | 52s | 54s | 106s | 69.3 | i-0fb0f63a1fa25c348 |
| 49 | mjwgw | surge | 07-14 23:00:29 | 07-15 00:02:01 | 40s | 48s | 88s | 70.5 | i-067d3e9a600db37df |
| 50 | jvjhr | surge | 07-14 23:10:08 | 07-15 00:10:59 | 24s | 22s | 46s | 71.2 | i-0027a501d3d908495 |
| 51 | b9b96 | surge | 07-14 23:22:13 | 07-15 00:23:11 | 31s | 23s | 54s | 71.0 | i-0c08f4d5cd021455d |
| 52 | rxf2c | surge | 07-14 23:34:17 | 07-15 00:36:09 | 25s | 80s | 105s | 70.1 | i-020c3d914bdd495cf |
| 53 | xv8m2 | surge | 07-14 23:47:22 | 07-15 00:48:49 | 54s | 28s | 81s | 70.5 | i-00564c34b6324ae46 |
| 54 | dxq8p | surge | 07-15 00:00:35 | 07-15 01:03:19 | 41s | 65s | 106s | 69.3 | i-06ca63b87ebb447dc |
| 55 | kbm2x | surge | 07-15 00:10:14 | 07-15 01:11:15 | 34s | 22s | 56s | 71.0 | i-002e0c626c9965415 |
| 56 | lnlxb | surge | 07-15 00:22:20 | 07-15 01:23:16 | 27s | 28s | 55s | 71.1 | i-07ed6412f48b14708 |
| 57 | kgj2d | surge | 07-15 00:34:25 | 07-15 01:36:04 | 25s | 73s | 97s | 70.3 | i-084bb7cca02775ea4 |
| 58 | tr854 | surge | 07-15 00:47:30 | 07-15 01:49:04 | 24s | 69s | 93s | 70.4 | i-0be5128cc2cb31ace |
| 59 | d94p8 | surge | 07-15 01:01:35 | 07-15 02:02:21 | 27s | 18s | 45s | 71.2 | i-01a1c010ed9bb8abc |
| 60 | btlwz | surge | 07-15 01:10:20 | 07-15 02:12:01 | 31s | 34s | 64s | 70.3 | i-0459aab876617973b |
| 61 | nssls | surge | 07-15 01:22:24 | 07-15 02:24:09 | 23s | 62s | 85s | 70.2 | i-019f5eed0b9c24fca |
| 62 | xwpp9 | surge | 07-15 01:34:29 | 07-15 02:36:18 | 42s | 48s | 90s | 70.2 | i-0449ff069770389cb |
| 63 | 2fmbr | surge | 07-15 01:47:34 | 07-15 02:49:30 | 38s | 58s | 96s | 70.1 | i-03b5247bae327d09a |
| 64 | 88rjv | surge | 07-15 02:01:38 | 07-15 03:02:57 | 26s | 33s | 59s | 70.7 | i-0adbdb142e012b81d |
| 65 | qdh2f | surge | 07-15 02:10:59 | 07-15 03:12:55 | 35s | 62s | 97s | 70.1 | i-07fa42782b1206b04 |
| 66 | 97mm5 | surge | 07-15 02:22:47 | 07-15 03:24:26 | 24s | 39s | 63s | 70.3 | i-0eb70b7e8fe3a72c5 |
| 67 | rbv8l | surge | 07-15 02:34:52 | 07-15 03:36:23 | 23s | 33s | 56s | 70.5 | i-0d032d5175f8d1b1b |
| 68 | 77m27 | surge | 07-15 02:47:56 | 07-15 03:49:49 | 35s | 42s | 77s | 70.1 | i-01b785c0e28e3d678 |
| 69 | 5qwn6 | surge | 07-15 03:02:01 | 07-15 04:04:16 | 36s | 62s | 98s | 69.8 | i-0651204cabf6506a8 |
| 70 | zv9gp | surge | 07-15 03:11:20 | 07-15 04:14:16 | 42s | 79s | 121s | 69.1 | i-07d0545749cf4c983 |
| 71 | epi-gtx42 | forceful-fallback | 07-15 02:22:50 | 07-15 04:24:46 | — | 48s | — | 10.1 | — |
| 72 | 49pqr | surge | 07-15 03:23:25 | 07-15 04:25:24 | 33s | 53s | 87s | 70.0 | i-0507f5b02688986aa |
エピローグ — fallback の決定的発火
別プールを使う理由
本編の「発動しない」だけでは半分。凍結した単一ノードプールで境界越えを人為的・決定的に作成。プール規模 1 の理由: 完了に最大 tGP + Buffer = 7m かかり得るため、複数台では 12 分帯内の完了を保証できない(複数台の実績は シナリオ O)。
| 時刻 | イベント |
|---|---|
| 02:22:50Z | epi claim gtx42 誕生(freeze 付きで作成)→ deadline d = 04:34:50Z |
| 03:22:50Z | 齢 1h で候補化; 凍結中 → candidates=1, in_progress=0 |
| 04:23:50Z | 区間探索で R を決定(d−12m < R < d−8m); freeze 解除。残余 11m < t_rot 12m |
| 04:24:46Z | 解凍から 56 秒後: claim 削除(drain 48 秒 < 7m 上界)。mode=forceful-fallback、surgeNode なし |
証拠:
forceful_fallback_total{nodepool-soak-epi}0→1- spec 文言どおりの
ForcefulFallbackWarning イベント - placeholder 台帳: epi claim の記録 ゼロ
expired終始 0- 本編無擾乱(解凍→発火ウィンドウで 70→71、完了間隔 ≤
P + t_rot= 42m)
アボート則(d−8m までに解凍不可なら凍結のまま撤去)は未行使。fail-closed は kubectl スタブ offline 検証で 3 系統 exit 3 確認済。
再試験するには
ランブック: test/e2e/eks-automode/SCENARIOS.md § Scenario P。
事前チェック:
go test ./internal/schedule/ -run TestDeriveScenarioPSoak -v # 導出ピン: A=1h / C=3 / G=2 / warn 1 件
test/e2e/eks-automode/scenarios/soak-analyze-fixture.sh # 解析系の自己検査運用ノート
- JSON ロギング必須(
logging.development: false): 解析器は zap JSON を読む。console 形式では台帳が空になる - 認証トークンの寿命を事前確認 — 途中で切れると二次記録が盲目化(一次のクラスタ内 scraper は無欠損)
expireAfterの live patch 禁止 — プールを作り直す(patch は Karpenter drift を誘発)- コスト: 約 $11(コントロールプレーン + NAT ~20h、5 × 2 vCPU ノード × 14h + エピローグ)。
terraform destroyを忘れないこと
本検証が確定させたこと
仕様書 §7.2 に validated 行が 2 つ追加:
- サブデイリーウィンドウ下で
leadTimeは genuine に競うexpireAfterに 12h 勝ち続け、fallback は装備されたまま静穏 - claim が graceful surge の組めない点を越えた瞬間、forceful fallback が決定的に発火
残る未検証項目: 同一 AZ の実キャパシティ不足(ICE)のみ — ロードマップ(§6.2) と §7.2 を参照。