監控系統的盲點:當三個健康檢查工作流自己掛了一週
事故概述
三個 n8n 排程工作流——負責監控網站健康狀態、API 可用性、App 評論資料——在 5/28 和 6/1 連續兩次執行中全部以 error 狀態結束。但因為這些工作流本身就是監控系統,沒有人(也沒有系統)在監控它們。問題在被發現之前已持續近一週。
這是一個經典的分散式系統盲點:監控鏈的最後一環沒有 meta-monitoring。
三個故障,三種根因
🔴 故障一:HTTP 節點輸出格式不相容
兩個健康檢查工作流依賴 n8n 的 HTTP Request 節點取得網頁狀態。原始 JavaScript Code 節點這樣寫:
// 錯誤寫法 — 舊版 n8n 格式
const ok = $input.all()[0].json.response?.statusCode === 200;
這個 .json.response?.statusCode 路徑是舊版 n8n HTTP 節點的輸出結構。n8n v2.x 改版後,回應 body 直接放在 .json,不再包一層 .response。路徑永遠是 undefined,工作流每次執行都報 TypeError。
修正:
// 正確寫法 — 判斷回應內容是否為有效 HTML
const item = $input.all()[0];
const ok = item && item.json && typeof item.json === "string" && item.json.length > 100;
🔴 故障二:外部依賴未自動重啟
第三個工作流依賴一個本機 HTTP wrapper(Python 腳本),接收 n8n 的 HTTP 請求後執行資料擷取。n8n 主程序在 5/29 重啟後,這個外部腳本 沒有跟著起來。n8n HTTP 節點每次連線都收到 ECONNREFUSED。
🔴 故障三:JS Code 的字串引號被 JSON 序列化吃掉
這是整個偵錯過程中最難排查的次生故障。修復 Code 節點時,透過 API PATCH 寫入的 JavaScript 程式碼中,typeof x === 'string' 的單引號經過 Shell 變數展開和 JSON 序列化後被剝離,變成:
typeof x === string // ReferenceError: string is not defined
n8n 的 VM sandbox 將裸 string 當作未定義變數,觸發 ReferenceError。解法:一律在傳輸 JS code 時使用雙引號 "string",或將 payload 寫入檔案以 @file 語法傳遞,繞過 Shell 轉義。
偵錯流程
- 透過 n8n REST API 拉取執行歷史,發現最近 6 次執行全部
error - 逐筆取得執行細節,提取每筆的錯誤訊息和堆疊追蹤
- 對照原始 Code 節點內容,識別出 HTTP 輸出路徑不相容
- 修復 → 觸發測試 → 出現新錯誤(
string is not defined)→ 識別出 JSON/Shell 轉義問題 - 最終修正 → 驗證四連 success → 恢復原始排程
學到的教訓
1. 監控系統需要自己的監控(Meta-Monitoring)
最核心的教訓:任何排程工作流的最後一環,必須有一個獨立的外部程序來檢查它是否正常執行。這個外部程序不能和被監控的對象共用基礎設施或依賴。
實作方式:在 Hermes Agent 層加一個 cron,每 24 小時透過 n8n REST API 檢查最新執行狀態。這層檢查完全獨立於 n8n 排程引擎之外。
2. 透過 API 傳輸 JS/Code 時要注意轉義層
當路徑是「Python 字串 → JSON → Shell → n8n API → n8n VM」時,每一層都可能吃掉或改變引號。最佳實踐:
- JS code 中的字串字面值使用雙引號
- 傳輸 payload 使用
@filename語法而非 inline string - 修改後立即 GET 驗證實際寫入的內容
3. 外部依賴需要 Process Supervisor
n8n 依賴的外部腳本(HTTP wrapper、資料擷取器等)不會自動跟隨 n8n 重啟。需要:
- 納入 systemd / s6 管理
- 或由 n8n 健康檢查 cron 一併驗證外部依賴的可用性
- 或將外部邏輯內嵌為 n8n Code 節點(犧牲靈活性換可靠性)
4. 漸進式測試:改一個 → 測一個 → 確認 → 再改下一個
三個工作流同時壞、每修一個又觸發次生故障。如果一口氣改三個而沒有中間驗證,debug 會變成噩夢。排程觸發的工作流無法直接手動執行,需要透過臨時改寫 cron 表達式來觸發測試——每次測試循環約 3 分鐘。逐個修、逐個測是最穩的方式。