RFC-0110:終止重要元件並重新啟動

RFC-0110:因重要元件終止而重新啟動
狀態已接受
區域
  • 元件架構
說明

第 2 版元件功能提供與 v1 important_components 的一致性

變更
作者
審查人員
提交日期 (年/月)2021-05-26
審查日期 (年/月)2021-07-21

摘要

提議在元件資訊清單的子項宣告中加入「重新啟動」選項,提供與 sysmgr 功能一致的critical_components功能。

提振精神

在元件 v1 中,sysmgr 支援名為 critical_components 的功能,可讓系統服務元件自行標示為「重要」。也就是說,如果元件基於任何原因 (包括一般結束) 終止,sysmgr 會觸發系統重新啟動。這次重新啟動是由 power_manager 驅動的安全重新啟動,導致元件拓撲中斷。安全重新啟動會以一致的方式關閉系統,並讓元件有機會徹底關閉,以便保留診斷資訊,並徹底關閉檔案系統。

如果用戶端不確定在元件故障時可以繼續進行正常系統行為,通常就會在元件上設定此選項。意外地,若元件的服務在系統作業中扮演著重要的角色,通常就會採用這個選項,例如:

  • netstack
  • wlanstack
  • omaha-client-service
  • system-update-checker

除了 critical_components 實作的簡單策略之外,還有許多可能的當機復原策略。此設計著重於解決該使用情境。當機復原程序超出 critical_components 提供的範圍 (但請參閱「未來工作」)。

相關規定

主要規定是提供與 critical_components 對等的功能。這表示 core 以下的元件或 core 的子類別,應該在元件終止時選擇觸發安全重新啟動。

為何現在發行?

動機中使用 critical_components 的元件會禁止遷移至 Components v2,直到對等功能可供使用為止。

設計

我們會將 on_terminate 列舉新增至 ChildDecl (等同於元件資訊清單children 區段),提供語意相當於 critical_component。選項有兩種:none (預設) 或 reboot。當具有 on_terminate: reboot 的子項元件基於任何原因終止 (包括一般結束) 時,component_manager 會從 power_manager 公開的 fuchsia.hardware.power.statecontrol.Admin 通訊協定中叫用 Admin/Reboot 方法,藉此觸發安全系統重新啟動。

這需要在 component_managerpower_manager 之間建立依附元件週期。不過,兩者都位於 ZBI 中,因此沒有重大的分層問題。在任何情況下,重新啟動都不會避免某種程度的依附元件反轉,因為重新啟動會導致裝置的電源狀態發生變化,該狀態是驅動程式庫的責任。

如果呼叫 Admin/Reboot 失敗,component_manager 會改回平移,觸發不安全重新啟動。

這是一項敏感功能,我們不希望任意元件在終止時自行決定是否要重新啟動。因此,使用會受限於 component_manager 安全性政策中的許可清單,並在元件啟動時檢查這項政策。此外,我們可以使用 restricted_features GN 許可清單,在未獲授權使用此功能的領域中設定子項時,產生建構時失敗的問題。

實作

on_terminate選項

我們需要將 on_terminate 選項新增至資訊清單child 區段。這需要變更 cmccmc_fidl_validatorcm_rust,才能重設選項。由於這是特殊功能,因此我們可在 ComponentDecl 中將其設為 None (預設為 on_terminate: none)。

我們會為 on_terminate 新增 restricted_featurecmc。只有這份許可清單中的 CML 檔案才能在子項上設定 on_terminate: reboot。首先,這個許可清單包含 corenetwork 運作範圍。

我們也會在 component_manager 的設定中加入 reboot_on_terminate_enabled 布林值,以便元件管理員的非根執行個體 (例如測試中的巢狀執行個體) 停用此功能。

偵測終止後重新啟動的元件是否終止

必須將邏輯新增至 component_manager,才能偵測重新啟動的元件何時終止。在 Stop 動作中,component_manager 可以檢查 on_terminate 選項。如果設定了這個值,而且元件未關閉,component_manager 就會呼叫 Admin/Reboot。關機表示元件會停止運作,且永遠不會重新啟動,這種情況會在下列情況中發生:

  1. 系統關閉期間,這本身會由 Admin/Reboot 通訊協定觸發。在這種情況下,系統已關閉,因此無法再次觸發關閉程序。
  2. 元件遭到刪除時。可能的發生如下:(a) 明確呼叫 DestroyChild、(b) transient 集合的父項停止,或 (c) single-run 集合中的元件結束。如果是 (a) 和 (b) 的情況,未觸發重新啟動的情況似乎是正確的決定,因為這是元件外部的動作,而不是導致該元件停止的元件中終止。在此情況下,我們仍然可以在元件終止後觸發刪除程序,確保元件退出後會重新啟動,而在元件終止後觸發刪除程序,以確保元件退出程序會重新啟動。

呼叫 fuchsia.hardware.power.statecontrol.Admin 通訊協定

如要觸發安全重新啟動,其中一個會連線至通訊協定 fuchsia.hardware.power.statecontrol.Admin,並呼叫 Admin/Reboot。這個通訊協定是由 power_manager 元件實作。(基於歷史原因,這個伺服器實際上是由 shutdown_shim 進行 Proxy)。由於這個通訊協定是透過元件實作,因此 component_manager 如何存取這個通訊協定?為此,我們可以讓 root#bootstrap 向其父項公開通訊協定。這意味著根是將通訊協定公開給根層級「上方」的節點,即 component_manager。如需此反版本的詳細說明,請參閱「設計」。

製作原型

您可以在這裡找到原型。

效能

這項設計沒有任何效能考量。只有在 on_terminate: reboot 元件實際終止時,component_manager 才會開啟與 fuchsia.hardware.power.statecontrol.Admin 的連線。

人體工學

這個設計有簡單的人體工學:如要設定元件上重新啟動後重新啟動的機制,您需要執行下列操作:

  • 在父項的 ChildDecl 中設定 on_terminate: reboot (CML 中的 children 宣告)。
  • 如果尚未加入父項的 CML,請將父項的 CML 新增至 on_terminate: rebootcmc restricted_features 許可清單。
  • 將元件的路徑名稱加入政策許可清單,以便在重新啟動後終止。

on_terminate 選項是由父項 (而非元件本身) 設定,因此可在實際工作環境中觸發重新啟動的元件,不必修改 CML 即可進行測試。此外,這樣一來,您不必變更元件,就能將元件加入不同產品設定中,希望以不同方式設定選項。

回溯相容性

這項變更並不影響相容性。用戶端必須明確選擇在終止後重新啟動。

安全性考量

假設使用者將元件標示為無效時重新啟動後,即可不當觸發這項功能,藉此濫用這項功能。不過,由於安全性政策許可清單的使用限制,因此新的用途必須獲得明確核准。請注意,不受信任的元件無法藉由嵌入已加入許可清單的元件,誘騙 component_manager 授予其重新啟動權限,因為該元件已由其路徑名稱 (拓撲路徑) 列入許可清單,而非網址。

隱私權注意事項

本提案不提出新的隱私權注意事項。

測試

我們可以透過模擬 fuchsia.hardware.power.statecontrol.Admin 通訊協定,輕鬆整合測試此功能。請務必測試不滿的路徑,例如缺少通訊協定或通訊協定失敗時。

在理想情況下,建議您為終止後重新啟動的元件新增 E2E 測試涵蓋率,驗證其終止的確實會觸發安全重新啟動。

說明文件

您必須變更下列說明文件:

  • on_terminate 選項新增文件,以平行 critical components
  • 更新遷移指南,說明如何遷移 critical_component

缺點、替代方案和未知

優點和缺點

福利

  • 設定方式很簡單,
  • 與 v1 的直接一致性,可簡化遷移程序。
  • 這項功能完全位於 component_manager 中,因此可輕鬆實作,而且不會大幅降低例如遺失事件等故障模式的風險。
  • 我們可以將 main_process_critical 的部分用途替換為 on_terminate: reboot,效果相當出色。
  • 允許用戶端在未經修改的情況下,運用在實際工作環境中設定的 on_terminate: reboot 元件。

缺點

  • 非功能型,這種模式與東正教的架構模型不同。
  • 這個外掛程式能直接在 component_manager 中對部分當機復原政策進行編碼。雖然一般來說不建議這麼做,但在本例中,這項政策雖然簡單,因此成本較少,但看起來並不為零。
  • 透過 component_managerpower_manager 加入反轉依附元件。不過,這些範例都出現在 ZBI 中,因此沒有多層次違規行為。
  • 由於涉及 CML 結構定義變更,因此需要在數個位置嵌入這個選項:cmccm_fidl_validatorcm_rustcm_rust 的用戶端 (即使這是小眾功能)。

替代版本:program 上的 system_critical 位元

我們可以將其新增至元件資訊清單program 區段,而不是將選項新增至 ChildDecl。此方法的主要差異在於選項是在元件本身設定,而不是父項中的子項宣告。

將位元放在 program 中,可以將特定功能從 ComponentDecl 中正確出來。由於 ComponentDecl 的角度來看,program 具有任意形式語法,因此不需要變更 cmc、驗證工具或 rust 繫結來因應新選項。您只需在 component_manager 中加入邏輯,以便在元件停止時從 program 擷取選項 (藉此判斷是否需要重新啟動)。

不過,這種做法有一項明顯的缺點:如果測試中使用 system_critical 元件,就必須修改 CML 以移除 system_critical 位元 (因為位元無法在測試領域中設定,而我們不希望測試觸發系統重新啟動)。這樣會增加處理使用該元件的整合測試的用戶端造成的維護負擔。

替代版本:使用 main_process_critical

ELF 執行元件支援名為 main_process_critical 的功能,會在元件以非零狀態結束或遭終止時,終止 component_manager 的根工作。這將會造成不安全重新啟動的效果。由於重新啟動不會很安全,這會導致系統無力關閉,且不會提供系統保存診斷或指標的機會。

main_process_critical 應只在無法觸發安全重新啟動的位置使用。例如,power_manager 本身標示為 main_process_critical。由於情況並非任何重要元件,因此這個選項並非可行的替代選項,而是列於此處,以便提供完整體驗。

替代選項:主管

我們可在 core 領域中管理當機復原,而無需在 component_manager 中管理。這個替代方法包含兩個部分。首先介紹「以元件為範圍」事件,讓取用端監控範圍限定為單一元件執行個體的事件 (尤其是 StartedStopped 事件)。接著介紹名為「超級程序」的元件,這個元件會使用這些事件來監控異常終止或失敗的啟動,並重新啟動系統以回應。

元件範圍事件

「元件架構」團隊已討論一個概念,目的是提供一種方法,可將事件功能的範圍限定為單一元件執行個體,而非整個領域。這項設計提供了這個想法的具體應用方式。監督程序只需要監控特定元件,因此接收到這些元件的相關事件,而不是整個領域的相關事件。

為了加快速度,建議您採用最小的 CML 變更來啟用這項功能。日後,我們可能會建立更實質的語法修訂版本,以其他方式指定事件範圍 (請參閱元件事件 RFC)。我們會在 offer event 宣告中加入 scope 欄位,可指定 #childrealm (預設)。

// core.cml
offer: [
    {
        event: "started",
        from: "framework",
        scope: "#wlanstack",
        to: "#supervisor",
        as: "started-wlanstack",
    },
    {
        event: "stopped",
        from: "framework",
        scope: "#wlanstack",
        to: "#supervisor",
        as: "stopped-wlanstack",
    },
],

由於日後可能會修訂語法,我們可以讓 cmcscope 功能加入許可清單至 core.cml 和整合測試。

元件範圍事件不會在其酬載中傳送元件的身分相關資訊 (例如路徑名稱或網址)。一般而言,事件可能會在其酬載中儲存機密資訊 (例如元件元件或網址),我們我們希望僅在有知情必要時才公開這些資訊。由於執行程序不需要這項資訊,因此元件範圍事件不會提供產生事件的元件身分相關資訊。酬載中的其餘資訊是時間戳記和終止狀態,並不敏感。

監督者

監督程序本身相當簡單,這是 core 下的元件,可執行下列操作:

  • 使用含有 StartedStopped 事件清單的靜態 event_stream
  • 如果這個 event_stream 收到含有錯誤的 Started 事件,或含有酬載包含非 ok 狀態的 Stopped 事件,請呼叫 fuchsia.hardware.power.statecontrol/Admin.Restart 來觸發安全重新啟動。

這是 critical_components 功能的簡易實作目標。日後,監督程序可能會進化以支援更多用途,或者可能會有多個監管程序,請參閱未來工作一節。

將事件轉送至主管

元件範圍的事件必須從每個重要元件轉送至監督程序。對於是 core 子項的重要元件,此要求必須進行兩項變更:

  • 修改 core.cml,以便將「啟動」和「已停止」事件從元件轉送至監督程序 (請參閱元件範圍事件)
  • 修改監管程序的 CML,以便使用靜態事件串流中的事件。

如果重要元件是巢狀配置於 core 的子領域下,則需要執行另一個步驟:

  • 修改每個中繼元件,以向父項公開事件。

舉例來說,這可能是 netstack 所發生的情況,因為我們計畫讓 netstack 位於 core 下的 network 子領域中。

以下是主管的 CML 範例:

// supervisor.cml
use: [
    {
        events: [
            "netstack-started",
            "netstack-stopped",
            "wlan-started",
            "wlan-stopped",
        ],
    },
    // The supervisor will trigger reboot under the following conditions:
    // - It receives a `started` event with an error.
    // - It receives a `stopped` event with a non-ok status.
    {
        event_stream: "EventStream",
        subscriptions: [
            {
                event: [
                    "netstack-started",
                    "netstack-stopped",
                    "wlan-started",
                    "wlan-stopped",
                ],
                on_receive: "start",
            },
        ],
    },
],
...

請注意,目前查看的元件不需要修改。這是刻意設定:監督是運作領域管理其元件的方式,而非元件本身。換句話說,元件不負責決定是否監督或如何監督。

正在啟動監督程序

我們必須確保主管一律準時啟動,才能收到事件。 為此,建議您為 event_stream 訂閱項目新增名為 on_receive: "start" 的選項。on_receive: "start" 會導致 component_manager 在收到該事件時自動啟動元件。如此一來,component_manager 可確保事件絕不會遺失。預設選項 "dispatch_if_started" 只會將事件分派到元件執行中 (預設行為)。

這需要變更事件分派系統。具體來說,分派事件時,component_manager 必須遵循所有已轉送的事件功能,以免靜態事件串流使用事件。否則,即使元件已標示為 on_receive: "start",但尚未解決,仍可能會錯過事件。

您可能有引數可將 on_receive: "start" 設為靜態事件串流的預設行為,但這不在本提案的範圍之內。

優點和缺點

福利

  • 避免 component_manager 中的當機當機復原政策編碼。這項做法促進了更明確的關注點分離,因為一般而言,我們沒有充分瞭解哪種當機復原政策,所以無法合理判斷應該在 component_manager 中提供直接支援。
  • 這個方法比 recovery 選項更易於調整。在 Basemgr 和 sessionmgr 中,有必要針對代理程式和工作階段本身實作不同的當機復原政策,與重新啟動復原政策不同。

缺點

  • 需要建構事件系統的支援。這會讓事件系統更加複雜,且相較於直接在 component_manager 中實作解決方案,可能需要更多時間和心力。
  • 需要比 recovery 的樣板多。每個重要元件的事件都必須從每個重要元件轉送到監督程序。
  • 我們最終會需要解決 basemgr/sessionmgr 中的類似問題。如果我們延遲設計更一般的解決方案,屆時或許就能對問題空間有更瞭解。

未來的工作

basemgrsessionmgr 會實作自己的當機復原策略,這個策略可配合監督者替代方案採取的做法。

fshostarchivist 目前使用 main_process_critical。可以改用在重新啟動時終止。這樣一來,我們就能將 main_process_critical 限制在重新啟動程序中的元件 (driver_managerpower_manager)。

部分路徑仍會觸發不安全重新啟動:

  • 這項設計會在 power_manager 和間接 driver_manager 上建立 component_manager 的反轉依附元件。因此,這些元件無法使用重新啟動後重新啟動的元件,因此會改為標示為 main_process_critical,也就是說,只要任一元件發生當機情形,就會觸發不必要的重新啟動作業。
  • 如果 Reboot 呼叫本身失敗,component_manager 恐慌也會觸發不安全重新啟動。

在這些情況下,我們有可能執行更正常的關閉作業;例如,Component_manager 可以執行正常系統關閉後再結束。另一方面,由於 power_managerdriver_manager 對系統運作非常重要,因此我們可能不希望在當機時讓系統繼續執行任意時間。

我們可能會重新審視電源管理責任的分配方式。例如,component_manager 或許能夠自行重新啟動 (仍需要依賴 driver_manager 設定電源狀態)。

系統完全遷移至 Components v2 後,元件管理員可運用對依附元件圖表的知識,支援更智慧的復原策略。

先前的圖片和參考資料

私人設計文件適用於事件 API 的 critical_components 功能和修訂版本。