效能、記憶體、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 都維持停用或不執行。
從層級與時間線定位
| 層級 | 症狀例 | 先看什麼 | 不要先做什麼 |
|---|---|---|---|
| Supervisor/Add-on | 啟動迴圈、初始化中止 | 啟動時間、第一個 error、options 最近差異 | 不要清空設定或重複重啟掩蓋第一個錯誤 |
| Node.js/runtime | GC 變頻繁、event loop 延遲、process 離開 | 記憶體趨勢、訊息速率、old-space 設定 | 不要直接把 heap 設到接近主機 RAM |
| Flow/節點 | 重複訊息、緩衝區增長、unknown node | 節點 status、Catch、訊息 shape、Deploy 時間 | 不要刪 unknown node 或 Full Deploy |
| 外部 I/O | timeout、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 版本。
逐步且可回復的排錯順序
- 界定單一症狀與時間窗。
記錄「何時開始、影響哪一條 flow/route、持續或間歇、最近一次已知正常時間」。使用 placeholder 取代 URL、entity、device、area、server、MQTT topic/client ID;不要收集 payload 全文。
- 保存基線,不做變更。
取得 Add-on/Node-RED/HA WebSocket 版本、啟動時間、CPU 與記憶體趨勢、訊息速率、錯誤率、佇列或緩衝區深度、外部 I/O 延遲。先截取錯誤前後有限行數並去識別,避免把 credentials、headers、Cookie 或位置資料帶入工單。
- 把外部副作用隔離。
停用 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。 - 從最小唯讀路徑重現。
使用手動 Inject 與小型合成訊息,只接 Change/Switch/Function 等純資料處理和限定欄位的 Debug。一次加入一個節點,確認是哪一段開始出現延遲、heap 或佇列成長;不連正式 HA 或網路。
- 一次只變更一個受控因素。
例如先限制 Debug,再調整緩衝區/timeout,再評估 old-space;每次記錄前後指標與回復值。選擇 Modified Nodes 或 Modified Flows 前先確認重啟範圍,不以 Full Deploy 當一般排錯手段。
- 復原並觀察。
若指標沒有改善,回到先前值,不疊加其他變更。若改善,仍先在隔離環境做長度足夠的負載觀察;正式恢復網路、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。
調整前後都要有證據
- 先記錄一段有代表性的 process/container 記憶體、訊息速率、延遲、restart 與佇列趨勢。
- 先修無界 Debug、message、context、Delay/Trigger/Join 與外部 I/O;提高 old space 不會修掉 leak 或無界緩衝區。
- 若仍有合理需求,在隔離環境選擇保守值,只改這一項並重跑相同負載。
- 觀察 GC、延遲、峰值及主機餘裕;無改善就回復,不持續加大。
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 flags | safe_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_commandserror;比對最近 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_nodeauth。不要關閉 TLS 或啟用leave_front_door_open。 - TLS handshake 或憑證錯誤:只做檔名、有效期、主體名稱、chain 與 key/certificate 配對的受控檢查;檔案必須位於
/ssl。不要輸出私鑰、不停用驗證、不把 Ingress TLS 與 directssloption 混為一談。 - 匯入/升級後出現 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 嗎?
Safe Mode 會停掉整個 Add-on 嗎?
--safe 啟動,只抑制初次 flow startup;任何 Deploy 都可啟動已部署 flows,即使 safe_mode 仍為 true。第一次 Deploy 前就要停用或斷開所有副作用路徑。