前一篇,AI 完成第一次 Ingest,並從程式碼與部署設定找出第一批弱點。後續用相同流程掃描其他 Service 後,Wiki 已經累積更多實驗候選。
今天要驗證 AI 能不能從 Wiki 選出一條弱點,提出假設、設計混沌實驗,再根據 metrics 寫出觀察報告?
實驗只能在測試環境執行,PostgreSQL、Kafka 與 Loki 禁止成為攻擊目標。人類會在實驗前確認設計,遇到異常時接管;其餘流程由 AI 主導。
接下來會從實驗報告中,挑出幾段比較完整、也比較有趣的內容來說明。
AI 從 Wiki 選中 teller-service 呼叫 account-service 的 API 路徑。這條路徑剛好有兩個弱點:TEL-W001 是沒有 Circuit Breaker,TEL-W002 是 HTTP client 沒有設定 timeout。
它提出的假設是:如果 teller-service 呼叫 account-service 時多出 10 秒網路延遲,teller-service 的 thread 會持續等待;並發量夠高時,連健康檢查都可能拿不到 thread。
AI 因此設計 NetworkChaos,讓 teller-service 呼叫 account-service 的流量延遲 10 秒。核心設定如下:
spec:
action: delay
selector:
labelSelectors: { app.kubernetes.io/name: teller-service }
direction: to
target:
selector:
labelSelectors: { app.kubernetes.io/name: account-service }
delay: { latency: "10s" }
這份設定只會在 teller-service 發往 account-service 的封包加上延遲。AI 也會觀察 account-service 本身,以及其他 Service 呼叫 account-service 的結果。只有 teller-service 到 account-service 的延遲上升,其他流量維持正常,才能確認故障沒有超出預定範圍。
AI 會把假設、Chaos YAML、觀測指標與停止條件整理進實驗文件,執行前再由人類確認設計。
接下來會看到兩份文件。EXP-002.md 是實驗紀錄,包含事前假設、設計與執行後回填的結果;EXP-002-report.md 是 AI 在實驗完成後整理的觀察報告,讀起來比較像給人看的版本。
先來看看 AI 在 EXP-002.md 寫下哪些預期現象。

圖:EXP-002 實驗設計文件中的假設,內容在正式注入前完成。
簡單地說,AI 預測 10 秒延遲會一路傳到使用者,造成業務成功率下降,也會讓越來越多 teller-service thread 卡在等待 account-service。同時,account-service 本身與其他 Service 呼叫 account-service 的流量應該維持正常。後面的觀察會逐項回來比對。
正式執行時,AI 先用 k6 維持 60 個 VUs(Virtual Users,k6 用來模擬同時操作系統的虛擬使用者),跑 45 秒正常流量,收集故障注入前的基準值(Baseline)。當時 teller-service → account-service 平均延遲約 16ms,teller-service 處理一筆請求平均約 41ms,轉帳、存款與提款成功率都是 100%。
實驗完成後,AI 將這些數字整理進 EXP-002-report.md 的「穩態」章節,再和故障注入期間的結果比較。

圖:AI 將故障注入前量到的 Baseline 整理成報告中的穩態。
穩態確認完成後,AI 維持 60 個 VUs,開始注入 10 秒網路延遲。NetworkChaos 持續 240 秒,期間持續送出轉帳、存款與提款請求,並記錄服務狀態與 metrics。
實驗結束後,AI 將 metrics、trace 與 k6 結果整理成五個觀察現象。以下先看現象一到三:

圖:AI 寫進 EXP-002 報告的現象一到三,保留原始內容。
簡單地說,AI 確認 teller-service 呼叫 account-service 時,確實多出完整的 10 秒延遲。一筆轉帳會連續呼叫 account-service 兩次,所以使用者大約要等 20 秒。從使用者端的 k6 測試結果來看,轉帳成功率降到 75%;但從 trace 整理出的 teller-service SERVER spanmetrics 顯示,teller-service 完成的請求幾乎都成功。這個差異代表只看服務端的 metrics,會低估使用者實際遇到的失敗。
接著是現象四、實驗期間數據、圖表與現象五:

圖:AI 寫進 EXP-002 報告的現象四、原始時間順序數據、圖表與現象五。
這張圖確認 account-service 本身維持正常,故障範圍符合原本設計。NetworkChaos 在 T+240 秒停止後,teller-service 的延遲仍未完全恢復,錯誤率在恢復尾段反而上升。堆積的請求在故障解除後繼續逾時,讓影響又延續約 60 秒。
不過,AI 回頭比對假設四時,發現「200 條 thread 全部被卡住」還缺少證據。teller-service 全程維持 Ready,也沒有重啟。60 個 VUs 中只有約 27% 的操作會經過被延遲的路徑,還不足以把 200 條 thread 全部卡住。
因此,AI 將假設四標成 load-limited,代表這次負載不足,還不能確認 200 條 thread 全部被卡住後,健康檢查真的會失敗。
Run 1 沒有讓 200 條 thread 全部被卡住,所以 AI 設計了 Run 2。這次將負載提高到 280 個 VUs,而且全部只打轉帳,讓每個請求都經過被延遲的路徑。
AI 預期 10 秒延遲會讓 teller-service 的 thread 長時間無法釋放。當 200 條 thread 全部被卡住,Prometheus 與健康檢查也會拿不到 thread,最後由 Kubernetes 重啟 teller-service。
NetworkChaos 的注入時間則從 240 秒縮短為 120 秒。AI 預期這段時間足以觀察一次重啟,也能避免 teller-service 持續進入 CrashLoop。
以下是 AI 寫進報告的完整 Run 2 時間順序:

圖:AI 根據 Run 2 完整觀測資料整理出的事件時間順序。
正式注入前,280 個 VUs 已經讓 tomcat_threads_busy_threads 到達 199,但 teller-service 仍然維持 Ready,Prometheus 也抓得到 metrics。這代表高負載已經用掉大部分 thread,但請求仍能快速完成並釋放資源,服務還撐得住。
注入延遲後,情況開始改變:
T+2s busy 到達 200
T+39s Prometheus 抓不到 metrics,teller-service 變成 NotReady
T+75s liveness 失敗,Kubernetes 第一次重啟 teller-service
T+122s NetworkChaos 完成恢復
T+194s Kubernetes 第二次重啟 teller-service
T+240s busy 回到 1,Prometheus 與 readiness 恢復
T+358s teller-service 持續維持正常
所以看到 busy=200,還不能直接判斷 teller-service 已經撐不住。這次真正的失效訊號依序是 Prometheus 抓不到 metrics、teller-service 變成 NotReady,最後由 liveness 觸發重啟。Pod UID 全程沒有改變,代表 Kubernetes 重啟的是同一個 Pod 裡的 teller-service container。
AI 原本預期 120 秒的注入時間只會造成一次重啟。實際執行時,第一次重啟發生在 T+75 秒;NetworkChaos 在 T+122 秒顯示 AllRecovered=True,代表 10 秒延遲已經解除。
不過,高負載仍在持續,teller-service 最後在 T+194 秒發生第二次重啟。從 busy 再次到達 200 來看,持續送入的 280 個 VUs,加上前面還沒處理完的請求,讓服務在恢復期間又一次到達上限。原本預期的單次重啟沒有成立,這也是這次實驗多觀察到的結果。
第二次重啟後,teller-service 在 T+240 秒恢復成 busy=1、up=1 與 Ready。後續高負載一度再次讓 busy 到達 200,readiness 也短暫波動,但沒有發生第三次重啟。負載在 T+325 秒結束,觀測持續到 T+358 秒,teller-service 全程維持正常。
恢復後重新送出一筆轉帳,API 回傳 201,耗時約 0.066 秒。account-service、exchange-service 全程維持 Ready,PostgreSQL、Kafka 與 Loki 的 restart count 也沒有增加,故障範圍仍符合原本設計。
回到假設四,Run 2 完整確認了這條因果鏈:10 秒延遲讓 teller-service 的 thread 長時間無法釋放,接著影響 Prometheus 與健康檢查,最後觸發 Kubernetes 重啟 teller-service。AI 猜錯的是重啟次數,核心假設仍有完整證據支持。
兩輪測試完成後,AI 根據觀測證據排出修復順序:

圖:AI 根據 EXP-002 結果整理出的修復優先順序與驗收條件。
AI 將 Circuit Breaker 與 HTTP client timeout 列為 P0。對下一篇最重要的是 P2:修復後必須重跑相同實驗,確認 Circuit Breaker 能在 100ms 內 fast-fail,16 秒延遲與複合效應也要消失。
這篇確認了 AI 能主導一場混沌實驗:從 Wiki 選擇弱點、提出假設、產生 Chaos YAML,到整理觀測證據與更新報告。
Run 1 的負載不足,AI 將假設標記為 load-limited,再調整條件執行 Run 2。Run 2 補齊了 thread 被占滿、健康檢查失敗與 container 重啟的完整因果鏈,也觀察到原先沒有預期的第二次重啟。對於第二次重啟的原因,AI 清楚標示為根據 metrics 與事件順序做出的推論。
人類負責設定測試環境、安全紅線、實驗前 review,以及發生異常時接管。AI 則主導實驗設計、觀測、判讀、報告與 Wiki 更新。
AI 最後更新 EXP-002-report.md,並將實驗狀態更新為 reflected。TEL-W001 也從程式碼掃描出的弱點,補上 executed-confirmed 的實驗證據。
下一篇會看看 AI 如何把這份修復建議變成驗收實驗。
今天就先寫到這,我們明天見!