複製貼上式擴節點最常留下重複的 launchd Label同一連線埠上的幽靈守護行程;LB 權重再漂亮也救不了本機雙掛。請把 doctor --deep 當成服務發現掃描:先對帳、再清標籤、再以連線埠切片承載金絲雀,最後用Webhook 失敗摘要廣播把多節點狀態壓成一則可對帳的訊息。

《多可用區金絲雀、技能切片與探針合併》(流量比例、權重階梯)及《滾動升級與 npm 順序》互補:彼處談怎麼切流量,本文談本機控制面是否只有一份真實來源。片段合併與網關邊界見《模板合併與工作流金絲雀》;租戶探針與 Webhook 摘要模板見《租戶切片與 Webhook 失敗摘要》《多可用區網關與 Webhook》。完整索引在技術部落格首頁

為何需要 deep:與「只談金絲雀流量」的分工

金絲雀文假設每臺 Mac 只有一組權威程序監聽約定連線埠;實務上 CI、手動 bootstrap 與舊版 plist 常疊出雙註冊openclaw doctor --deep(旗標名稱請以實際二進位為準)把設定語意、監聽套接字、launchd 單元與磁碟路徑交叉比對,輸出可機器讀的 JSON,讓「哪一顆 plist 在說謊」變成可排序清單,而不是 grep 大賽。

doctor --deep:最小對帳欄位

演練前約定三欄寫進稽核:label_authority(plist 絕對路徑與 SHA256)、listeners(PID、位址、連線埠)、canary_slice(金絲雀環境變數或次要連線埠)。三台 clustervps Mac 各自產出 doctor-deep-${HOST}.json,用 jq 做集合差分;若僅一臺出現重複 Label,優先懷疑手動拷貝的 LaunchDaemons而非應用程式碼。

set -euo pipefail
/usr/local/bin/openclaw doctor --deep --json >"/var/db/openclaw/audit/doctor-deep-${HOSTNAME}-$(date -u +%Y%m%dT%H%M%SZ).json"
/usr/bin/lsof -nP -iTCP -sTCP:LISTEN | /usr/bin/grep -E 'openclaw|gateway' || true

清理重複 launchd 標籤(最小可復現)

原則:同一 Label 只允許一個權威 plist;其餘一律 launchctl bootout 後移出 /Library/LaunchDaemons(或團隊約定目錄)到冷儲存,再 bootstrap 單一檔案。禁止在不明狀態下連續 kickstart -k 兩次而不比對 plist 指紋,否則容易把競態藏進「偶發成功」。

  • ① 列出衝突:launchctl print system | grep -A2 com.openclaw(Label 前綴請替換為實際值)與 deep JSON 並列。
  • ② 凍結寫入:暫停合併器與自動 reload,避免清標籤途中寫入半截設定。
  • ③ bootout 非權威單元:保留 tar 備份路徑與操作者於工單。
  • ④ bootstrap 權威 plist:再跑一輪 doctor --deep,確認 listeners 唯一。

金絲雀連線埠切片:與 LB 權重的第二道閘門

在網關行程啟動前注入 OPENCLAW_GATEWAY_PORT(名稱示意)或設定片段覆寫,使金絲雀綁定次要連線埠;本機反代或 sidecar 只把小比例上游轉到該埠。這樣即使 launchd 曾重複註冊,也能用連線埠隔離把爆炸半徑壓在單一 slice。合併探針 JSON 應同時帶 primary_portcanary_port,避免監控只看預設埠而誤判全綠。

export OPENCLAW_GATEWAY_PORT="${CANARY_PORT:-18088}"
export OPENCLAW_PROBE_TAGS="canary_slice=port:${OPENCLAW_GATEWAY_PORT}"
/usr/local/bin/openclaw doctor --deep --json | /usr/bin/tee /tmp/deep.json

Webhook 失敗摘要廣播:帶上 deep 與連線埠欄位

延續租戶文的五分鐘視窗去重,在摘要第一行 JSON 增加 duplicate_labels 計數、canary_portdoctor_deep_ok。值班用手機即可判斷「要回滾權重」還是「要登入某臺清 plist」,而不必翻完整堆疊。完整事件仍寫本機 JSONL 供單筆重播。

演練步驟清單(三台 clustervps Mac)

  1. R0:各節點備份 LaunchDaemons 目錄與 LB/反代權重 JSON;記錄當前監聽表。
  2. 全節點跑 doctor --deep --json,匯總重複 Label 與連線埠衝突清單。
  3. 金絲雀節點依序 bootout→冷儲→bootstrap 權威 plist;再 deep 驗證 listeners 唯一。
  4. 啟用連線埠切片與小比例轉發;跑合併探針兩個週期。
  5. 人為製造一次出站 Webhook 4xx,確認通知器只廣播單則摘要且含 deep/連線埠欄位。
  6. 其餘節點一次一臺重複 ②—④;每臺結束寫入 promotions.jsonl 一行指紋。

回滾矩陣(R1/R2)

觸發條件影響範圍動作
僅金絲雀 deep 仍報重複 Label 或連線埠衝突單節點+切片流量R1:bootout 實驗 plist、還原 R0 tar、取消切片環境變數、權重回到凍結快照。
已推廣至多節點且探針大面積紅燈全叢集R2:每台依序還原 plist、bootstrap 舊單元、還原監聽環境;廣播「R2 已啟動」含主機清單。
Webhook 摘要風暴或誤報通知通道關閉聚合器五分鐘、降級為「每節點一行心跳」直至 deep 全綠。
回滾驗收:R1/R2 結束後必再跑一輪 doctor --deep,並以 jq 斷言 duplicate_labels == 0;否則視為回滾失敗,禁止重新加權。

FAQ

deep 會不會太慢拖垮探針? 為 deep 設硬逾時與快取欄位;超時標記 degraded,但重複 Label仍應硬失敗。

使用者域與系統域 plist 衝突怎麼辦? 先凍結使用者登入項目,再以 deep 輸出決定哪一側為權威;禁止兩側同 Label 並存。

連線埠切片要開防火牆洞嗎? 金絲雀埠預設只給本機反代;對外仍只有單一入口,邊界與網關文一致。

本文為維運指引。doctor --deep、環境變數名稱與 Label 前綴請以貴團隊安裝包為準;殼層與 jq 片段為示意,非可直接貼上的機密。
免登入即可瀏覽

把 deep 掃描與連線埠切片寫進專用 Mac runbook

公開頁面即可對齊預算與流程:請先瀏覽方案與價格、閱讀說明中心,或回到網站首頁—無需登入主控台即可與團隊共享連結。

瀏覽 Mac 方案 開啟說明中心