OTA 更新

無線更新 (OTA) 是 Fuchsia 上的作業系統更新機制。本文件詳細說明 Fuchsia 中 OTA 更新的運作方式。

更新程序分為以下幾個階段:

正在檢查更新

作業系統更新程序的兩個進入點是 omaha-clientsystem-update-checker 元件。

omaha-clientsystem-update-checker 用途相同,可確認是否有作業系統更新並啟動更新。

一般來說,如果產品想使用 Omaha 來判斷更新的供應情形,則應使用 omaha-client。如果產品不想使用 Omaha,而想直接從套件存放區檢查更新,則應使用 system-update-checker

在任何指定的 Fuchsia 系統中,只能執行下列其中一項元件:

使用 omaha-client 更新檢查

在啟動過程中,omaha-client 會啟動並開始定期更新檢查。在這些檢查期間,omaha-client 會輪詢 Omaha 伺服器來檢查更新。

使用 Omaha 的好處如下:

  • 可讓在 Fauchsia 裝置上進行小規模的系統更新發布作業。例如,您可以設定只讓 10% 的裝置更新。這表示在輪詢 Omaha 時,只有 10% 的裝置會發現有可用的更新。其餘 90% 的裝置則不會看到可用的更新。
  • 因此有多種更新管道。例如,測試裝置可透過測試管道取得更新,並取得最新 (可能不穩定) 的軟體。如此一來,正式版裝置就能取得正式版裝置的更新,並取得最穩定的軟體。您可以選擇提供管道資訊給 Omaha 和產品和版本。

圖:使用 Omaha-client 檢查更新

圖 1:使用 omaha-client 的簡化版本更新檢查程序。有些政策可限制 omaha-client 是否可以檢查更新或套用更新。

omaha-client 從 Omaha 伺服器取得更新套件網址後,omaha-client 就會通知 system-updater 開始更新。

使用 system-update-checker 更新檢查

裝置未使用 omaha-client 時,也會使用「system-update-checker」。視其設定方式而定,system-update-checker 會定期輪詢更新套件。如未指定 auto_update,這些檢查會預設為停用。

如要檢查是否有可用的更新,system-update-checker 會檢查下列條件:

  • 目前執行中系統映像檔 (位於 /pkgfs/system/meta ) 的雜湊是否與更新套件中系統映像檔的雜湊 (位於 packages.json 中) 不同?
  • 如果系統映像檔不同,目前在系統上執行的 vbmeta 是否與更新套件的 vbmeta 不同?
  • 如果沒有 vbmeta,目前在系統上執行的 ZBI 是否與更新套件的 ZBI 不同?

如果這些答案為「是」,system-update-checker 就會知道更新套件已變更。system-update-checker 發現更新套件已變更後,system-update-checker 會觸發 system-updater,使用預設更新套件 (fuchsia-pkg://fuchsia.com/update) 來啟動更新作業。

圖:使用 system-update-checker 檢查更新

圖 2:使用 system-update-checker 的簡化版本更新檢查程序。

如果不需要更新,更新檢查工具會儲存伺服器上的最新已知更新套件。後續檢查更新時,系統會根據伺服器上的最後已知雜湊,檢查擷取的更新套件雜湊。如果最新更新套件的雜湊自上次檢查後有所變更,更新檢查工具會將執行中系統的 vbmeta 和 ZBI 與更新套件中對應的映像檔進行比較。如果執行中的映像檔與更新套件的 vbmeta 或 ZBI 不同,檢查工具會啟動系統更新。

監控

如果用戶端有意監控更新進度和狀態,可以實作 fuchsia.update.AttemptsMonitor 通訊協定,並將用戶端端提供給 fuchsia.update.Manager FIDL 通訊協定的 MonitorAllUpdateChecks() 方法。只有在透過其他方法開始更新或正在進行更新時,fuchsia.update.AttemptsMonitor 執行個體才會收到訊息。這不會觸發新的更新。

fuchsia.update.AttemptsMonitor 執行個體會接收 OnStart 訊息,其中包含伺服器對 fuchsia.update.Monitor 通訊協定。如此一來,用戶端就能接收及處理 OnState 訊息,通知狀態更新。

另一種做法是實作 fuchsia.update.Monitor,並將用戶端提供給 fuchsia.update.Manager 通訊協定的 CheckNow() 方法。系統隨即會開始檢查更新。這項功能只會監控目前正在執行的更新,並在更新完成後關閉控制代碼。

暫存更新

無論更新是否由 omaha-clientsystem-update-checker,甚至是強制更新檢查觸發,更新都必須寫入磁碟。

更新程序分為以下幾個步驟:

圖:起始狀態圖表

圖 3:裝置目前正在執行假設的 OS 版本 1 (在運算單元 A 上),並開始更新至假設 OS 版本 2 (從運算單元 B)。警告:這可能不是實際磁碟分區的方式。

擷取更新套件

system-updater 會使用提供的更新套件網址擷取更新套件。接著,動態索引會進行更新,以參照新的更新套件。以下為更新套件的範例:

/board
/epoch.json
/firmware
/fuchsia.vbmeta
/packages.json
/recovery.vbmeta
/version
/zbi.signed
/zedboot.signed
/meta/contents
/meta/package

如果因空間不足而擷取失敗,system-updater 會觸發垃圾收集,以刪除在靜態或動態索引或保留套件集中未參照的所有 BLOB。進行垃圾收集後,system-updater 將重試擷取。如果重試失敗,system-updater 將僅用要擷取的更新套件「替換」所設的保留套件。如果更新套件網址包含雜湊,就會清除保留的套件集,然後再觸發垃圾收集,然後重新擷取更新套件。

圖:擷取更新套件

圖 4 system-updater 會指示 pkg-resolver 解析第 2 版更新套件。我們假設 system-updater 因空間不足而無法擷取更新套件,觸發了垃圾收集來剔除運算單元 B 參照的版本 0 blob,然後重試以成功擷取版本 2 更新套件。

如有需要,更新套件可能包含 update-mode 檔案。這個檔案用於決定系統更新是「一般」或「強制復原」模式。如果沒有更新模式檔案,system-updater 將預設為一般模式。

模式為強制復原時,system-updater 會將映像檔寫入復原作業,將運算單元 A 和 B 標示為無法啟動,然後啟動復原。詳情請參閱強制復原的實作

驗證主面板相符

目前的執行系統中有一個主面板檔案位於 /config/build-info/boardsystem-updater 會驗證系統中的主面板檔案與更新套件中的主面板檔案是否相符。

圖:驗證主面板相符

圖 5system-updater 會驗證更新套件中的主面板與插槽 A 上的主機板是否相符。

確認支援週期

更新套件包含 Epoch 紀元檔案 (epoch.json)。如果更新套件的週期 (目標週期) 低於 system-updater (來源訓練週期) 的週期,則 OTA 會失敗。如要進一步瞭解相關資訊,請參閱 RFC-0071

圖:確認支援週期

圖 6system-updater 會將更新套件與當前 OS 的週期進行比較,藉此驗證更新套件中的週期。

取代保留的檔案組合

替換以目前的更新套件和之後在 OTA 程序中擷取的所有套件。

保留的套件組合是一組保護套件,它不會進行垃圾收集 (除了靜態和動態索引中的套件以外)。用於防止垃圾收集,刪除目前更新程序所需的 BLOB。舉例來說,假設某部裝置已擷取更新所需的某些套件,然後因不相關原因而重新啟動。裝置再次啟動 OTA 時,系統仍會要求裝置在重新啟動前擷取所擷取的套件,但這些套件不會受到動態索引保護 (例如保留的套件組合會在重新啟動時清除)。將這些套件新增至保留的套件集後,system-updater 就能觸發垃圾收集 (例如移除先前系統版本使用的 blob),不必復原先前的工作。

觸發垃圾收集作業

系統會觸發垃圾收集,刪除舊系統專用的所有 BLOB。 這個步驟可以釋出額外空間,供任何新包裹使用。

圖:垃圾收集

圖 7. system-updater 會指示 pkg-cache 將舊系統專屬的所有 BLOB 收集為垃圾。在這個範例中,這表示 pkg-cache 會剔除版本 1 更新套件只參照的 BLOB。

擷取其餘套件

System-updater 會剖析更新套件中的 packages.json 檔案。 packages.json 如下所示:

{
  "version": “1”,
  "content": [
    "fuchsia-pkg://fuchsia.com/sshd-host/0?hash=123..abc",
    "fuchsia-pkg://fuchsia.com/system-image/0?hash=456..def"
    ...
  ]
}

system-updater 會指示 pkg-resolver 解析所有套件網址。解析套件時,套件管理系統只會擷取更新所需的 BLOB,例如只擷取尚未存在的 BLOB。套件管理系統會擷取整個 BLOB,而不是目前系統中可能的差異。

擷取所有套件後,系統會觸發 BlobFS 同步處理作業,將 BLOB 清除至永久儲存空間。這個程序可確保 BlobFS 提供系統更新的所有必要的 BLOB。

圖:擷取剩餘的套件

圖 8. system-updater 會指示 pkg-Resolver 解析 packages.json 中參照的版本 2 套件。

撰寫圖片以封鎖裝置

system-updater 會決定要將哪些圖片寫入區塊裝置。映像檔、資產和韌體有兩種。

接著,system-updater 會指示分區映像檔安裝工具編寫系統啟動載入程式和韌體。 為避免閃光燈佩戴,只有在映像檔與區塊裝置上既有映像檔不同時,圖片才會寫入分區。

接著,system-updater 會指示分區映像檔安裝工具寫入 Fuchsia ZBI 及其 vbmeta。 否則,分區映像檔安裝工具體會同時寫入 A 和 B 分區 (如果 B 分區存在)。

最後,system-updater 會指示分區映像檔安裝工具寫入復原 ZBI 及其 vbmeta。

圖:撰寫圖片以區塊裝置

圖 9. system-updater 會透過分區映像檔安裝工具將第 2 版圖片寫入 B 槽。

將替代分區設為啟用

如果裝置支援 ABR,system-updater 會使用分區映像檔安裝工具將替代分區設為啟用。這樣一來,裝置就會在下次啟動時啟動至替代分區。

您可以透過幾種方式參照運算單元狀態。例如,內部分區映像檔安裝工具使用 Successful,而 FIDL 服務使用 Healthy,而其他情況則可能使用「Active」、「Active」、「Bootable」、「Unbootable」、「Current」、「Alternative」等...

重要的中繼資料是每個核心運算單元儲存的 3 項資訊。這項資訊有助於判斷每個核心運算單元的狀態。舉例來說,在運算單元 B 標示為有效前,中繼資料可能如下所示:

中繼資料 版位 A 版位 B
優先順序 15 0
剩餘點數 0 0
健康* 1 0

運算單元 B 標示為有效後,中繼資料看起來會像這樣:

中繼資料 版位 A 版位 B
優先順序 14 15**
剩餘點數 0 7**
健康料理 1 0

如果裝置不支援 ABR,由於沒有替代分割區,因此系統會略過這個檢查。而是會為每個更新寫入有效的分區。

圖:將替代分區設為啟用

圖 10. system-updater 會將運算單元 B 設為「Active」,以便在下次啟動時啟動,進入插槽 B。

Reboot

視更新設定而定,裝置不一定能重新啟動。裝置重新啟動後,裝置會啟動至新的插槽。

圖:重新啟動

圖 11. 裝置會重新啟動進入運算單元 B,並開始執行版本 2。

驗證更新

一旦系統驗證更新,系統就會提交更新。

系統會透過下列方式驗證更新:

正在重新啟動,以使用更新版本

下次啟動時,系統啟動載入程式必須決定要啟動的運算單元。 在這個範例中,系統啟動載入程式會判定運算單元 B 的優先順序較高,而且剩餘的嘗試次數超過 0 次,因此系統啟動載入程式會判斷要啟動至運算單元 B (請參閱「將替代分區設為啟用」)。接著,系統啟動載入程式會驗證 B 的 ZBI 與 B 的 vbmeta,最後再啟動到運算單元 B。

提早啟動後,fshost 會使用新的系統映像檔套件啟動 pkgfs。這是暫存更新時 packages.json 參照的系統映像檔套件。System-映像檔套件內含 static_packages 檔案,其中會列出新系統的基本套件。例如:

pkg-resolver/0 = new-version-hash-pkg-resolver
foo/0 = new-version-hash-foo
bar/0 = new-version-hash-bar
...
// Note the system-image package is not referenced in static_packages
// because it's impossible for it to refer to its own hash.

pkgfs 接著會將所有這些套件載入為基本套件。套件會顯示在 /pkgfs/{packages, versions} 中,表示已安裝或啟用套件。接著,系統會啟動 pkg-resolverpkg-cachenetstack 等...

提交更新

system-update-committer 元件會執行多項檢查,確認新的更新是否成功。例如,程式碼會指示 BlobF 任意讀取 1MiB 的資料。如果系統在啟動時已認可,就會略過這些檢查。如果檢查失敗且取決於系統的設定,system-update-committer 可能會觸發重新啟動作業。

驗證更新後,目前的分區 (運算單元 B) 會標示為 Healthy。以「將替代分區設為主動」中所述的範例進行操作,啟動中繼資料現在可能會像這樣:

中繼資料 版位 A 版位 B
優先順序 14 15
剩餘點數 7 0
健康料理 0 1

接著,替代分區 (運算單元 A) 就會標示為無法啟動。現在,啟動中繼資料可能如下所示:

中繼資料 版位 A 版位 B
優先順序 0 15
剩餘點數 0 0
健康料理 0 1

之後就視為已確認的更新。因此:

  • 系統一律會啟動至插槽 B,直到下一次系統更新。
  • 系統會放棄開機進入運算單元 A,直到下一次系統更新覆寫運算單元 A。
  • 運算單元 A 參照的 BLOB 現在可以進行垃圾收集作業。
  • 目前已允許後續的系統更新作業。當更新檢查工具發現新的更新時,整個更新程序就會再次啟動。