第 22 章

效能、記憶體、Safe Mode、升級與故障排除

從有限指標與第一個錯誤定位問題,分清 queue(佇列)、buffer(緩衝區)、latency(延遲)、rollback(回復)與 escalation(升級通報),並在 Safe Mode、備份與版本邊界內完成可回復的排錯。本章只做離線檢查,不連線、不部署。

為何需要本章

「Node-RED 變慢」可能是編輯器顯示大量 Debug、訊息物件太大、序列緩衝區持續累積、外部 I/O 卡住,或 V8 正在頻繁回收 old space;它不等於「記憶體一定不夠」。同樣地,「升級後不能開」可能發生在 Add-on 初始化、套件安裝、runtime、反向代理、HA 連線或 flow schema 任一層。

本章固定在 Add-on 22.0.1 內嵌 Node-RED 5.0.2、HA WebSocket nodes 0.80.3 的行為,不拿 rolling 文件或其他版本猜測。你會從症狀、時間線與有限指標開始,逐層縮小範圍;所有網路探測、HA 呼叫、寫入、套件變更與 Deploy 都維持停用或不執行。

安全原則:先觀察、再隔離、一次只改一項、每項都有回復點。先完成第 18 章的 Debug/Catch/Status 測試與第 21 章的備份還原演練再處理正式環境。

從層級與時間線定位

層級症狀例先看什麼不要先做什麼
Supervisor/Add-on啟動迴圈、初始化中止啟動時間、第一個 error、options 最近差異不要清空設定或重複重啟掩蓋第一個錯誤
Node.js/runtimeGC 變頻繁、event loop 延遲、process 離開記憶體趨勢、訊息速率、old-space 設定不要直接把 heap 設到接近主機 RAM
Flow/節點重複訊息、緩衝區增長、unknown node節點 status、Catch、訊息 shape、Deploy 時間不要刪 unknown node 或 Full Deploy
外部 I/Otimeout、reconnect、資料延遲目的端狀態、請求時間、重試率、佇列深度不要做未授權探測或取消 TLS 驗證
反向代理/認證Ingress 可開但 endpoint 失敗Ingress/direct、port、path、TLS、auth 分層不要開啟 leave_front_door_open

Add-on manifest 只建置 aarch64 與 amd64,且 init=false;這個 init manifest flag 不是 runtime Safe Mode。映像直接固定 [email protected]、[email protected],並含啟動支援依賴 [email protected]、[email protected]。診斷時先確認層級與版本,不把 Add-on 版本寫成 Node-RED 版本。

逐步且可回復的排錯順序

  1. 界定單一症狀與時間窗。

    記錄「何時開始、影響哪一條 flow/route、持續或間歇、最近一次已知正常時間」。使用 placeholder 取代 URL、entity、device、area、server、MQTT topic/client ID;不要收集 payload 全文。

  2. 保存基線,不做變更。

    取得 Add-on/Node-RED/HA WebSocket 版本、啟動時間、CPU 與記憶體趨勢、訊息速率、錯誤率、佇列或緩衝區深度、外部 I/O 延遲。先截取錯誤前後有限行數並去識別,避免把 credentials、headers、Cookie 或位置資料帶入工單。

  3. 把外部副作用隔離。

    停用 Action、API、Fire Event、Update Config、HTTP Request、MQTT Out、File、email、Cast、InfluxDB、Modbus、serial、TCP/UDP/WebSocket output 與排程觸發。若 flow 會在啟動時立即動作,改用 Add-on safe_mode 抑制初次啟動;在第一次 Deploy 前就要停用或斷開所有副作用路徑,因為 Deploy 仍會啟動已部署 flows。

  4. 從最小唯讀路徑重現。

    使用手動 Inject 與小型合成訊息,只接 Change/Switch/Function 等純資料處理和限定欄位的 Debug。一次加入一個節點,確認是哪一段開始出現延遲、heap 或佇列成長;不連正式 HA 或網路。

  5. 一次只變更一個受控因素。

    例如先限制 Debug,再調整緩衝區/timeout,再評估 old-space;每次記錄前後指標與回復值。選擇 Modified Nodes 或 Modified Flows 前先確認重啟範圍,不以 Full Deploy 當一般排錯手段。

  6. 復原並觀察。

    若指標沒有改善,回到先前值,不疊加其他變更。若改善,仍先在隔離環境做長度足夠的負載觀察;正式恢復網路、HA 或寫入需另行核准,本章不執行。

章末離線驗收

產物預期內容失敗即停止
事故工作表一組去識別基線、一個可反駁假設、一個變更因素、前後結果與明確回復值;外部 I/O 與 Deploy 均為未執行沒有最近正常時間或第一個 error、同時改多項、缺少回復值、證據含秘密/環境 ID,或必須連線、部署、安裝才能判讀

訊息、緩衝區、Context 與外部 I/O

先把症狀轉成可比較的指標

症狀至少記錄優先檢查
編輯器卡頓Debug 每秒筆數、每筆顯示長度、sidebar 是否持續開啟停用不必要 Debug,將 output 改為單一經清理欄位
延遲逐漸上升輸入/輸出速率、處理時間、佇列深度Delay rate limit、Trigger pending timers、Join 等待 sequence
記憶體呈階梯上升process/container 記憶體趨勢、訊息大小、pending 數大物件 clone、context 累積、未完成 sequence、重試佇列
週期性尖峰尖峰週期、排程、Poll State/Get History 時間同時觸發的 flows、查詢區間與結果量、外部 timeout
訊息遺失或重複_msgid、msg.parts、重試次數Split/Join 邊界、timeout 路徑、部署重啟範圍

Debug 與訊息大小

Debug node 應只顯示一個已清理欄位,不要選完整 message object。Add-on settings template 的 debugMaxLength 是 1000 字元,但截斷顯示不會讓上游大物件消失,也不保證完整物件沒有被 clone 或保留。圖片、音訊、巨型 JSON、msg.req/msg.res live objects 都不應送入一般 Debug 或 context。

Node-RED 會在訊息分支等情況處理 message clone;大型 nested object、Buffer 或多分支會放大成本。先只傳下游需要的欄位,避免在 Function 中無界複製歷史陣列。Function、Switch、Change、Range、Template、Exec、RBE 都應以明確輸入 shape 與錯誤路徑使用;Exec 屬外部命令能力,本章保持未執行。

RBE(Palette 顯示 Filter)可依值是否改變,或 numeric deadband/narrowband 規則決定是否放行;它會按設定的 topic 邊界保留前次比較狀態,不是無狀態的格式轉換。必須明確選擇比較的 message property(例如 msg.payload)、topic property,並把 deadband 契約的預期資料型別固定為 number;runtime 會使用 parseFloat,所以要先拒絕字串/object,不能讓寬鬆解析偶然通過。驗收要包含相同值、越過與恰在門檻、不同 topic、msg.reset,以及 node restart/Deploy 後比較狀態重建;reset 與 restart 都可能改變下一筆是否輸出,不能讓下游副作用依賴未測的前次狀態。

Delay、Trigger、Join 必須有上限

  • Delay:rate limit 與佇列策略要能承受峰值;不要讓輸入永遠高於輸出。丟棄或保留中間值是產品語意,需先核准。
  • Trigger:每個 topic/stream 可能保留 timer;輸入 key 無界時,pending timer 也會增長。設定可驗證的 timeout 與 key allowlist。
  • Split/Join/Batch/Sort:保留正確的 msg.parts,並限制等待中的 sequence 數、每組筆數與等待時間。Add-on settings 提供 nodeMaxMessageBufferLength 的可選 runtime 上限;0 代表不限制,不適合拿來宣稱已設防。
  • HA Wait Until:每則等待訊息都需要明確 timeout 與 timeout 分支;不要建立無界等待。Time trigger 要避免大量同時觸發。

Context 與外部 I/O

node/flow/global context 可使用 memory 或 localfilesystem named store。不要把無界陣列、完整事件或 binary payload 放進 context;磁碟 store 也不是每次 assignment 立刻耐久寫入。先限制 key、大小、保存期限與寫入頻率。

HTTP、MQTT、WebSocket、TCP、UDP、serial、檔案、資料庫,以及 HA Get History/Poll State 都是外部 I/O。為請求設定 timeout、結果大小、重試上限與退避;Get History 限制區間和結果數,Poll State 避免取代事件訂閱。所有 host、URL、broker、topic、server 與 IDs 使用完整 placeholder,診斷期間不執行連線。MQTT 邊界見第 20 章,HTTP 路由見第 19 章。

max_old_space_size 只限制 V8 old space

Add-on option max_old_space_size 是 MB 整數。啟動 script 只有在 option 存在時才匯出:

NODE_OPTIONS=--max_old_space_size=PLACEHOLDER_MB

這行只作精確 runtime 對照,不是在本章執行的 shell command。它限制 Node.js V8 heap 的 old memory section,不是 Node.js process 總記憶體、container 上限或主機 RAM。process 還有 young generation、code、native addon、Buffer/external memory 與其他 overhead;把值設得太高可能壓迫 Home Assistant 主機,太低則可能增加 garbage collection 或導致 heap failure。

調整前後都要有證據

  1. 先記錄一段有代表性的 process/container 記憶體、訊息速率、延遲、restart 與佇列趨勢。
  2. 先修無界 Debug、message、context、Delay/Trigger/Join 與外部 I/O;提高 old space 不會修掉 leak 或無界緩衝區。
  3. 若仍有合理需求,在隔離環境選擇保守值,只改這一項並重跑相同負載。
  4. 觀察 GC、延遲、峰值及主機餘裕;無改善就回復,不持續加大。
沒有通用數字:可用值取決於主機、其他 Add-ons、flow 與負載。沒有量測就指定「最佳 MB」會是虛構建議。

Safe Mode、Deploy 範圍與隱私安全 Log

Add-on option safe_mode: true 會讓啟動 script 加入 --safe;Node-RED runtime 啟動,但只抑制初次 flow startup,供你修復。任何 Deploy 都可啟動該次部署的 flows,即使 safe_mode 仍保持 true。它不是 Home Assistant Safe Mode、不是停止 Add-on,也不是存取控制。編輯器與管理面仍必須遵守原有 Ingress/direct auth 邊界。

# Add-on 22.0.1 的確切 option;本章不套用或重新啟動
safe_mode: true

這個示意不代表 flow 已安全;在第一次 Deploy 前,網路、HA、寫入、排程等副作用節點就必須停用,或把連往它們的 wires 斷開。不能先 Deploy 再補救,因為 post-Deploy 檢查無法撤銷已發生的動作。

安全處理順序是:先有可還原備份,再由有權限者於維護窗口設定 Safe Mode 並重啟 Add-on;在不 Deploy 的情況下先修復或停用 unknown node、無界緩衝區與所有副作用路徑;第一次 Deploy 視為會啟動已部署 flows 並另行核准。取消 Safe Mode 也可能在下次啟動立即執行 flow,所以不能把「能開編輯器」當成完成。

Inject、Debug、Catch、Status、Complete、Link In/Out/Call、Comment 與 Junction 的角色不同:Catch 收錯誤、Status 收節點狀態,Complete 表示受監看節點處理完輸入,不保證所有 downstream 已完成;Link 節點改變訊息路由,Comment 與 Junction 不應被誤當成 runtime 量測點。診斷時只啟用必要觀測節點,避免觀測本身造成負載。

Deploy 範圍不是效能開關

Node-RED 5.0.2 編輯器提供 Full、Modified Flows、Modified Nodes。範圍決定哪些 flow/nodes 被重啟;重啟會影響 timers、連線、context 生命週期及在途訊息。先理解變更相依性,再選最小正確範圍。若你不確定 shared config node 的影響,不要為了「比較快」任選 Modified Nodes。

Log 只收必要資訊

log_level schema 接受 trace、debug、info、notice、warning、error、fatal,且可省略;官方文件建議平時 info。wrapper 會把 warning 映射成 Node-RED 的 warn。提高詳細度可能暴露 payload、URL、headers、entity/device/area IDs、MQTT topic、檔案路徑或 stack;只在限定時間內使用,排錯後回復必要等級。

提供紀錄時只保留版本、時間戳、node type、錯誤類型與 correlation placeholder。移除 token、password、Authorization、Cookie、credential ciphertext、私鑰、webhook、位置資料、真實 hostname/IP、完整 payload 與截圖中的環境資訊。[email protected] 可改善 stack trace,但不會自動清理敏感值。

升級、初始化與可回復邊界

升級前

  • 完成 Add-on 備份與隔離還原演練,另保存 credential_secret/Project secret;確認 node_modules 被排除並保存 dependency 清冊。
  • 匯出已 scrub 的 flow 清冊,記錄 Add-on、Node-RED、HA WebSocket、額外 npm/system packages,以及選裝且非內建的 @flowfuse/[email protected]。該 Dashboard metadata 宣告 Node >=14、Node-RED >=3.0.0;相容範圍不取代目標環境測試。
  • 閱讀目標版 release notes 與 breaking changes,確認 HA、Node.js、architecture 與節點相容性。本章基線只證明 22.0.1/5.0.2/0.80.3,不保證其他版本。
  • 停用 HA、網路、寫入與排程節點;指定停止條件、回復版本與備份。升級動作需另行核准,本章不執行。

22.0.1 的確切啟動鏈

階段同版行為失敗時安全處置
資料初始化若新 /config 沒有 settings,可由 /homeassistant/node-red 遷移;否則建立 settings、flows 與 nodes 目錄。先保存兩邊目錄清冊,不手動搬移或覆蓋;找第一個 migration error。
Theme migration舊 dark 會更新為 dark-modern。辨認為名稱遷移,不把外觀問題誤判成 flow 損毀。
衝突套件處理wrapper 會嘗試移除 node-red-contrib-home-assistant、node-red-contrib-home-assistant-llat、node-red-contrib-home-assistant-ws。這是 runtime script 行為;不要另下刪除命令。若失敗,保留 log 與 package 清冊。
自訂 packages先 Alpine system_packages,再 npm npm_packages;任一失敗即中止。找第一個 package/repository/architecture error;回復最近新增的受控 option,不清空資料目錄。
自訂命令init_commands 每次啟動逐行 eval,任一失敗即中止。將最近新增命令維持不執行並回復到已知設定;不得在 log 或命令放秘密。
Runtime flagssafe_mode 轉成 --safe;max_old_space_size 轉成 NODE_OPTIONS。核對 options 是否存在與型別,勿把 manifest init=false 當 Safe Mode。

升級後與回復

先在 Safe Mode 且不 Deploy 的情況下確認 editor、節點 type、credentials 解密、settings 與 package 載入;所有副作用節點停用或斷線後,才另行核准 Deploy 純資料最小 flow,因為這次 Deploy 會啟動已部署內容。unknown nodes、schema migration warning、route 或 HA connection error 未釐清前,不啟動正式 flows。

回復不是只降版映像。先停止進一步變更,保存失敗版本的已去識別 log 與狀態,依事先核准的 Home Assistant Add-on restore 方法回到匹配備份、版本和 secret。不要拿新版已遷移資料直接餵給舊 runtime,也不要手動修改 flow JSON 的 node type/version 欄位。

症狀導向的安全處置

  • Add-on 啟動停在自訂 package 或 command:從 log 找第一個 system_packages、npm_packages 或 init_commands error;比對最近 option 差異與 architecture。回復單一最近變更,不執行臨時 shell、不刪 /config、不把秘密貼入命令。
  • HA 節點顯示 disconnected 或 Unauthorized WebSocket:先確認 Add-on 仍為預先配置的連線模式;官方 known issue 要求 HA Server 設定中的「I use the Home Assistant Add-on」模式正確。核對 0.80.3 prerequisites:HA 2024.3+、Node-RED >=3.1.1、Node >=18.2.0;這不同於 Add-on manifest 的 HA 2023.3.0 安裝門檻。不要複製 token、改成未加密 URL 或發送測試 Action。
  • HTTP node/Dashboard route 找不到:先分辨 Ingress 與 direct access。Add-on 文件指出 HTTP nodes 需要另行映射 network port,路徑位於 /endpoint/;檢查 direct port、TLS cert/key、path 與 http_node auth。不要關閉 TLS 或啟用 leave_front_door_open。
  • TLS handshake 或憑證錯誤:只做檔名、有效期、主體名稱、chain 與 key/certificate 配對的受控檢查;檔案必須位於 /ssl。不要輸出私鑰、不停用驗證、不把 Ingress TLS 與 direct ssl option 混為一談。
  • 匯入/升級後出現 unknown nodes 或 schema warning:保持 Safe Mode,不 Deploy、不刪節點、不手改 type。從備份清冊找出固定 package;先在隔離環境恢復相容版本。舊 HA Entity node 是 deprecated,只按同版 migration 處理,不新建。
  • Join/Wait Until 不輸出或記憶體持續增長:抽樣檢查 msg.parts、每組 sequence 大小、timeout、pending key 與輸入/輸出速率。先停止新輸入並保存最小證據;不要注入更多測試訊息或直接清空正式 context。
  • 需要升級回報或升級通報:停止反覆變更,再向對應的 Add-on/node package 維護者提供固定版本、architecture、重現時間線、第一個 error、停用後是否重現、最小 scrub flow 與已嘗試的單一變更。不得附 token、credentials、private key、真實 endpoint/IDs、完整 payload、瀏覽器 storage 或未遮罩截圖。

固定版本來源

本章的 options、scripts、節點與相容性只引用下列精確提交及同版官方文件:

常見問題

max_old_space_size 可以填主機可用 RAM 嗎?
不可以這樣推算。它只限制 V8 old space,不含整個 process、container 與主機其他服務。先找無界訊息/緩衝區/context,再以隔離負載和主機餘裕評估保守值。
Safe Mode 會停掉整個 Add-on 嗎?
不會。它讓 Node-RED 以 --safe 啟動,只抑制初次 flow startup;任何 Deploy 都可啟動已部署 flows,即使 safe_mode 仍為 true。第一次 Deploy 前就要停用或斷開所有副作用路徑。
Debug sidebar 截斷訊息就表示記憶體成本變小嗎?
不表示。顯示長度上限只處理顯示,無法證明上游物件沒有被建立、clone 或放入緩衝區。應在來源縮小 message shape,並限制 Debug 頻率與欄位。
unknown node 可以直接刪除再重畫嗎?
不要。刪除會失去原設定,Deploy 也可能啟動其他 flow。保持 Safe Mode,從固定版本清冊識別 package,在隔離環境恢復相容節點或依官方 migration 處理。
反覆重啟後第一個 error 消失,只剩後續 timeout,應以哪份證據判斷?
停止重啟,把最早一次啟動的第一個 error、最近正常時間與 options 差異列為主要證據;後續 timeout 只能標成次生症狀。若原始紀錄已遺失,不要猜根因或疊加修復,先維持隔離並依既定升級通報路徑補齊可重現的最小時間線。