FR-172 · 說明 · 循序圖

升級執行流程

主系統升級、主系統發動 Agent 升級、Agent 主控台發動

三張循序圖加兩張逐步表,說明按下更新後每一步誰在跑、跑哪支程式、畫面看到什麼。與 FR-175 機制問答「線上升級執行流程」同一份內容;那邊另有補問「升級觸發機制:更新單與主機監看服務」。

主系統 1.28.3/Agent 1.5.5 3 張循序圖
§1

設計原則

  • 容器不動 docker,主機才動:主系統後端與 Agent 都只寫一張「升級單」交給自己那台主機,停容器、換版、退回全部由主機上的 systemd 單元以 root 執行。容器在升級中會被換掉,不能當執行者。
  • 狀態只有一個來源:主機每步改寫進度檔,畫面、維護頁、開機檢查都讀這一份,所以容器停了進度照樣會動。
  • Agent 不連外網:Agent 的包一律由主系統向更新站下載、驗章,再交給 Agent。
  • 不靠心跳:主系統直接連 Agent 的 8443 推指令;Agent 主動送事件回報進度。心跳只表示「活著」。
§2

主系統升級

%%{init: {'theme':'base','themeVariables':{'primaryColor':'#E2F0F1','primaryTextColor':'#14201F','primaryBorderColor':'#0E7C86','secondaryColor':'#EEF2F3','secondaryTextColor':'#14201F','tertiaryColor':'#FBFCFC','tertiaryTextColor':'#14201F','lineColor':'#4A5A5C','textColor':'#14201F','mainBkg':'#E2F0F1','nodeBorder':'#0E7C86','nodeTextColor':'#14201F','edgeLabelBackground':'#FBFCFC','titleColor':'#14201F','clusterBkg':'#FBFCFC','clusterBorder':'#E4EAEB','actorBkg':'#E2F0F1','actorTextColor':'#14201F','actorBorder':'#0E7C86','actorLineColor':'#C7D1D2','signalColor':'#4A5A5C','signalTextColor':'#14201F','labelBoxBkgColor':'#EEF2F3','labelBoxBorderColor':'#C7D1D2','labelTextColor':'#14201F','loopTextColor':'#14201F','noteBkgColor':'#F6EBD5','noteTextColor':'#14201F','noteBorderColor':'#9C6B12','activationBkgColor':'#EEF2F3','activationBorderColor':'#C7D1D2','sequenceNumberColor':'#FFFFFF'}}}%%
sequenceDiagram
    autonumber
    participant B as 瀏覽器(更新中心)
    participant N as 前門 guidant-fe(nginx)
    participant M as 後端 guidant-api/worker
    participant S as 更新站+R2
    participant H as 主機 systemd(root)
    participant P as 進度檔 upgrade-progress.json
    B->>M: 立即檢查(另有每日排程)
    M->>S: /v1/check product=host
    S-->>M: 新版、簽好的清單、三段說明
    B->>M: POST …/apply「更新到 X」
    Note over B: 全頁遮罩,只顯示進度
    M->>S: /v1/download-url(15 分鐘預簽網址)
    M->>S: 下載整包與 ops 小檔(背景工作、可續傳)
    M->>M: 本機公鑰驗章、比對雜湊
    M->>H: 寫更新單 upgrade/orders/<job>.json
    H->>H: guidant-upgrade-order.path 看到檔 → .service → guidantai upgrade --from-order
    H->>H: 重驗包與章(容器寫的單不全信)
    H->>P: 第 0 步 換新版維運指令與維護頁
    H->>P: 第 1 步 預檢、第 2 步 放 draining 旗標等工作歸零
    Note over M: worker 不撿新單、不推新工作給 Agent;API 照常
    rect rgb(236,236,236)
    Note over B,P: 第 3~6 步:後端與資料庫停機,畫面由前門維護頁提供
    H->>H: 第 3 步 停 api/worker/socketio/db,複製資料庫
    B->>N: 輪詢拿到 502 → 整頁 reload
    N-->>B: 探後端不通 → 送維護頁
    B->>N: 每 3 秒 GET /upgrade-progress.json
    N->>P: 直接讀唯讀掛載的檔(不經後端)
    H->>H: 第 4 步 在複本 load 映像、跑 migration
    H->>H: 第 5 步 用複本起新版,打 version/login/三支 API
    H->>H: 第 6 步 改 .env、換目錄、依序起服務,最後重建 fe
    end
    H->>P: 第 7 步 清理舊映像、舊快照;status=succeeded
    N-->>B: 探後端通了 → 跳回更新中心 ?upgraded=1
    M->>P: 排程每分鐘讀:同步單終態、補寫系統日誌、回報更新站
    M->>M: agent-bundles 有相容新版 → 自動建 Agent 推送列(接圖 2)
    rect rgb(253,236,236)
    Note over B,P: 任一步失敗或逾時
    H->>H: 第 8 步 rollback:.env 寫回舊值、起舊版
    H->>P: rolled_back(退回也失敗寫 rollback_failed)
    end
圖 1 — 主系統升級。編號為時間順序;灰色區段後端與資料庫停機,畫面由前門 nginx 的維護頁提供,維護頁直接讀進度檔。190 實測 1.28.2→1.28.3 整次 6 分 42 秒,停機約 4.5 分鐘。
步驟 誰在跑、跑哪支 做什麼 使用者看到
按下「更新到 X」 後端 product_update_flow.py、package_downloader.py、package_verifier.py、upgrade_order.py 沒包就先下載(背景工作),用本機公鑰驗章與雜湊,寫更新單 upgrade/orders/<job>.json。後端到此為止,不碰 docker 全頁遮罩、八步清單,每 3 秒問 GET …/status
單落地 主機 guidant-upgrade-order.path → .service → lib/upgrade_order.sh → lib/upgrade_runner.sh 撿單就刪單,重驗包與章、確認版號比現行新(測試機開同版開關時同版也放行) 「已送出」
0 換維運指令 主機 self_replace.sh 從 ops 小檔取新版 guidantai、lib/、維護頁;exec 新版續跑 照常
1 預檢 主機 upgrade_preflight.sh 簽章、雜湊、docker、資料庫、磁碟等;不過就停,系統沒動 照常
2 停接新工作 主機 upgrade_drain.sh;後端 core/maintenance_gate.py 放 draining 旗標:worker 不撿新單、不推新工作給 Agent;等進行中的歸零 照常操作,新工作先排隊
3 停服務、複製資料庫 主機 upgrade_db_copy.sh 停 api/worker/socketio/db(fe、redis、seaweedfs 不停),複製 pgdata → pgdata-next 維護頁(前門 nginx 直接讀進度檔)
4 在複本上升級 主機 upgrade_apply_on_copy.sh 解包、load 映像並核雜湊、db 指到複本、init 容器跑 migration、補設定 維護頁第 4 步
5 健康檢查 主機 upgrade_health.sh 用複本起新版 api,打 version、login、三支 API 維護頁第 5 步
6 換接 主機 upgrade_switch.sh 改 .env 版號與資料庫目錄、搬目錄、依序起服務,最後重建 fe;寫 last-switch.json fe 重建那幾秒看不到畫面,之後跳回更新中心
7 清理 主機 upgrade_cleanup.sh 刪舊映像、只留一代資料庫快照;升級成功後 incoming/ 只留清單 新版畫面
換接後一分鐘內 後端排程 core/scheduler_update.py 同步單終態、補寫系統日誌 6239~6248、回報更新站;有相容 Agent 新版就自動建推送列 現行版與「上次更新完成時間」
失敗或逾時 主機 upgrade_rollback.sh 停新版、.env 寫回、起舊版;第 0~2 步失敗系統沒動只記 skipped 維護頁「已退回」或「需主機管理員」
§3

主系統發動 Agent 升級

%%{init: {'theme':'base','themeVariables':{'primaryColor':'#E2F0F1','primaryTextColor':'#14201F','primaryBorderColor':'#0E7C86','secondaryColor':'#EEF2F3','secondaryTextColor':'#14201F','tertiaryColor':'#FBFCFC','tertiaryTextColor':'#14201F','lineColor':'#4A5A5C','textColor':'#14201F','mainBkg':'#E2F0F1','nodeBorder':'#0E7C86','nodeTextColor':'#14201F','edgeLabelBackground':'#FBFCFC','titleColor':'#14201F','clusterBkg':'#FBFCFC','clusterBorder':'#E4EAEB','actorBkg':'#E2F0F1','actorTextColor':'#14201F','actorBorder':'#0E7C86','actorLineColor':'#C7D1D2','signalColor':'#4A5A5C','signalTextColor':'#14201F','labelBoxBkgColor':'#EEF2F3','labelBoxBorderColor':'#C7D1D2','labelTextColor':'#14201F','loopTextColor':'#14201F','noteBkgColor':'#F6EBD5','noteTextColor':'#14201F','noteBorderColor':'#9C6B12','activationBkgColor':'#EEF2F3','activationBorderColor':'#C7D1D2','sequenceNumberColor':'#FFFFFF'}}}%%
sequenceDiagram
    autonumber
    participant B as 瀏覽器(更新中心 Agent 區)
    participant M as 主系統後端
    participant S as 更新站+R2
    participant A as Agent 容器(8443)
    participant H as Agent 主機 systemd(root)
    participant C as Agent 主控台(8444)
    B->>M: 勾選 N 台,「更新選取的 N 台到 X」
    alt 手上沒有包,或手上的包比更新站查到的舊
    M->>S: 下載 Agent X(背景工作,畫面有進度、可取消)
    M->>M: 驗 agent_release_manifest 章與雜湊 → agent-bundles/X/
    Note over M: 下載中再有人按更新,併入待推清單(各記操作者)
    end
    M->>M: 建推送列:先推 1 台,成功再推其餘
    M->>A: 直推 POST /update {版號, sha256, 大小}(mTLS+60 秒 JWT)
    Note over A: 進入維護模式:停收新工作
    C->>A: 每 30 秒問狀態 → 拿到 in_progress → 整頁鎖住
    A->>M: GET /agents/update/bundle 下載包(可續傳)
    A-->>M: 事件 update_download
    A->>A: 核大小與雜湊,寫升級單 agent-upgrade-order.json
    H->>H: guidant-agent-upgrade-order.path 看到單 → upgrade-host order
    H->>H: 從現行 Agent 映像讀出 upgrade_order.sh,用舊版 Agent 驗新版程式樹的章
    H->>H: 預檢 → 等工作做完 → 換上新版設定 → 重建容器 → 健康檢查 → 清理
    A-->>M: 事件 update_step(每一步,失敗帶錯誤輸出末 20 行)
    A-->>M: 新容器起來:事件 update_result+上傳升級紀錄
    M-->>B: 每台八步進度與結果;整批成功後清用不到的包
    C->>A: 版本換了 → 整頁重新載入回原頁(不必重登)
圖 2 — 主系統發動 Agent 升級。按更新不必先預先下載;Agent 當時離線,主系統會在它下次心跳回來時補推。開著的主控台最慢 30 秒內鎖住(平常輪詢間隔),鎖住後每 3 秒更新進度。
畫面那一格 誰在跑、跑哪支 做什麼
等 Agent 接手 主系統 agent_rollout_service.py、core/plugins/_agent_commands.py;Agent sync/api/cmd_update.py 主系統直推 POST /update;Agent 收下即進入維護模式(停收新工作)
下載更新包 Agent sync/app/update_sync.py;主系統 GET /agents/update/bundle 向主系統下載包、核大小與雜湊,送 update_download 事件
解開更新包 Agent 主機 upgrade_order.sh(從現行 Agent 映像讀出來跑) 撿升級單、預檢版號(同版只有開關開才放行)、磁碟、解包、用舊版 Agent 驗新版程式樹的章
預檢 Agent 主機 upgrade_runner.sh 主系統位址必須 https、工具差異提醒、空間
停接新工作,等進行中的做完 同上、upgrade_guard.sh 等執行中的掃描做完
換上新版 同上 先備份 .env 與 compose 檔,再換 compose 檔與映像版本
啟動新版與健康檢查 同上 重建容器、確認版本與資料
清理 同上 維運指令換新;只記警告,不影響結果
(結果) Agent update_events.py、update_log_upload.py 新容器起來送 update_result、上傳升級紀錄;換版後失敗自動還原 .env 與 compose 檔
§4

Agent 主控台發動

%%{init: {'theme':'base','themeVariables':{'primaryColor':'#E2F0F1','primaryTextColor':'#14201F','primaryBorderColor':'#0E7C86','secondaryColor':'#EEF2F3','secondaryTextColor':'#14201F','tertiaryColor':'#FBFCFC','tertiaryTextColor':'#14201F','lineColor':'#4A5A5C','textColor':'#14201F','mainBkg':'#E2F0F1','nodeBorder':'#0E7C86','nodeTextColor':'#14201F','edgeLabelBackground':'#FBFCFC','titleColor':'#14201F','clusterBkg':'#FBFCFC','clusterBorder':'#E4EAEB','actorBkg':'#E2F0F1','actorTextColor':'#14201F','actorBorder':'#0E7C86','actorLineColor':'#C7D1D2','signalColor':'#4A5A5C','signalTextColor':'#14201F','labelBoxBkgColor':'#EEF2F3','labelBoxBorderColor':'#C7D1D2','labelTextColor':'#14201F','loopTextColor':'#14201F','noteBkgColor':'#F6EBD5','noteTextColor':'#14201F','noteBorderColor':'#9C6B12','activationBkgColor':'#EEF2F3','activationBorderColor':'#C7D1D2','sequenceNumberColor':'#FFFFFF'}}}%%
sequenceDiagram
    autonumber
    participant O as 操作者(Agent 主控台 8444)
    participant A as Agent
    participant M as 主系統後端
    participant S as 更新站
    O->>A: 立即檢查
    A->>M: POST /agents/update/check(通行證+DPoP)
    M->>S: 查 Agent 版本線(全機 5 分鐘節流)
    A->>M: GET /agents/update/available
    M-->>A: 有新版 X(主系統手上還沒有包也照報)
    O->>A: 更新到 X
    Note over O: 按下當下整頁鎖住
    A->>M: POST /agents/update/request(操作者名稱)
    M->>M: 手上不是最新 → 排下載,推送人記「agent-console:操作者」
    M->>A: 下載完直推 POST /update(之後同圖 2 第 7 步起)
    rect rgb(236,236,236)
    Note over O,S: 與主系統斷線時
    O->>A: 離線上傳 .offline.tar.gz
    A->>A: 用現行版公鑰驗章 → 雜湊 → 版本 → 磁碟 → 寫升級單(同圖 2 第 12 步起)
    end
圖 3 — Agent 主控台發動。主控台的「更新到 X」只是請主系統替這一台做圖 2 的事;主系統斷線時只能離線上傳。
§5

常見問題

  • 主系統停機時,畫面是誰在服務?前門 guidant-fe(nginx)從不停。它探到後端不通就改送維護頁,維護頁每 3 秒直接讀進度檔,不經後端、不經資料庫。
  • Agent 升級時還能派掃描嗎?不能。從收到 /update 起 Agent 就停收新工作,直到換版完成。
  • Agent 主控台為什麼有時慢一點才鎖?主控台平常每 30 秒問一次狀態;由主系統發動時最慢 30 秒才鎖,在主控台自己按的當下就鎖。這段時間 Agent 已經停收新工作。
  • Agent 不連外網,包怎麼來?一律由主系統向更新站下載、驗章後交給 Agent;離線時才由現場人員上傳離線包,驗章用 Agent 現行版的公鑰,不用包裡附的。
  • 升級中有人又按更新?主系統更新單同時只跑一張;Agent 下載中再有人按,會併入同一份待推清單,不覆蓋先按的人。
  • 測試機想重看升級流程?兩個環境變數都開才出現「重新套用目前版本」:主系統 PRODUCT_UPDATE_ALLOW_SAME_VERSION=1、Agent AGENT_UPDATE_ALLOW_SAME_VERSION=1。出貨範本不帶這兩個鍵,正式環境不會出現。
§6

相關文件

想看 去哪
升級觸發機制(更新單、主機 systemd 監看服務) FR-175 機制問答 · 線上升級執行流程 補問
程式清單、優化項目、交付物現況 FINAL-SPEC.md
PM 常問的 16 題 for-pm-qa.md