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_manager
和 power_manager
之間建立依附元件週期。不過,兩者都位於 ZBI 中,因此沒有重大的分層問題。在任何情況下,重新啟動都不會避免某種程度的依附元件反轉,因為重新啟動會導致裝置的電源狀態發生變化,該狀態是驅動程式庫的責任。
如果呼叫 Admin/Reboot
失敗,component_manager
會改回平移,觸發不安全重新啟動。
這是一項敏感功能,我們不希望任意元件在終止時自行決定是否要重新啟動。因此,使用會受限於 component_manager
安全性政策中的許可清單,並在元件啟動時檢查這項政策。此外,我們可以使用 restricted_features
GN 許可清單,在未獲授權使用此功能的領域中設定子項時,產生建構時失敗的問題。
實作
on_terminate
選項
我們需要將 on_terminate
選項新增至資訊清單的 child
區段。這需要變更 cmc
、cmc_fidl_validator
和 cm_rust
,才能重設選項。由於這是特殊功能,因此我們可在 ComponentDecl
中將其設為 None
(預設為 on_terminate: none
)。
我們會為 on_terminate
新增 restricted_feature
至 cmc
。只有這份許可清單中的 CML 檔案才能在子項上設定 on_terminate: reboot
。首先,這個許可清單包含 core
和 network
運作範圍。
我們也會在 component_manager
的設定中加入 reboot_on_terminate_enabled
布林值,以便元件管理員的非根執行個體 (例如測試中的巢狀執行個體) 停用此功能。
偵測終止後重新啟動的元件是否終止
必須將邏輯新增至 component_manager
,才能偵測重新啟動的元件何時終止。在 Stop
動作中,component_manager
可以檢查 on_terminate
選項。如果設定了這個值,而且元件未關閉,component_manager
就會呼叫 Admin/Reboot
。關機表示元件會停止運作,且永遠不會重新啟動,這種情況會在下列情況中發生:
- 在系統關閉期間,這本身會由
Admin/Reboot
通訊協定觸發。在這種情況下,系統已關閉,因此無法再次觸發關閉程序。 - 元件遭到刪除時。可能的發生如下:(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: reboot
的cmc
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_manager
對power_manager
加入反轉依附元件。不過,這些範例都出現在 ZBI 中,因此沒有多層次違規行為。 - 由於涉及 CML 結構定義變更,因此需要在數個位置嵌入這個選項:
cmc
、cm_fidl_validator
、cm_rust
和cm_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
中管理。這個替代方法包含兩個部分。首先介紹「以元件為範圍」事件,讓取用端監控範圍限定為單一元件執行個體的事件 (尤其是 Started
和 Stopped
事件)。接著介紹名為「超級程序」的元件,這個元件會使用這些事件來監控異常終止或失敗的啟動,並重新啟動系統以回應。
元件範圍事件
「元件架構」團隊已討論一個概念,目的是提供一種方法,可將事件功能的範圍限定為單一元件執行個體,而非整個領域。這項設計提供了這個想法的具體應用方式。監督程序只需要監控特定元件,因此接收到這些元件的相關事件,而不是整個領域的相關事件。
為了加快速度,建議您採用最小的 CML 變更來啟用這項功能。日後,我們可能會建立更實質的語法修訂版本,以其他方式指定事件範圍 (請參閱元件事件 RFC)。我們會在 offer event
宣告中加入 scope
欄位,可指定 #child
或 realm
(預設)。
// 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",
},
],
由於日後可能會修訂語法,我們可以讓 cmc
將 scope
功能加入許可清單至 core.cml
和整合測試。
元件範圍事件不會在其酬載中傳送元件的身分相關資訊 (例如路徑名稱或網址)。一般而言,事件可能會在其酬載中儲存機密資訊 (例如元件元件或網址),我們我們希望僅在有知情必要時才公開這些資訊。由於執行程序不需要這項資訊,因此元件範圍事件不會提供產生事件的元件身分相關資訊。酬載中的其餘資訊是時間戳記和終止狀態,並不敏感。
監督者
監督程序本身相當簡單,這是 core
下的元件,可執行下列操作:
- 使用含有
Started
和Stopped
事件清單的靜態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
中的類似問題。如果我們延遲設計更一般的解決方案,屆時或許就能對問題空間有更瞭解。
未來的工作
basemgr
和 sessionmgr
會實作自己的當機復原策略,這個策略可配合監督者替代方案採取的做法。
fshost
和 archivist
目前使用 main_process_critical
。可以改用在重新啟動時終止。這樣一來,我們就能將 main_process_critical
限制在重新啟動程序中的元件 (driver_manager
和 power_manager
)。
部分路徑仍會觸發不安全重新啟動:
- 這項設計會在
power_manager
和間接driver_manager
上建立component_manager
的反轉依附元件。因此,這些元件無法使用重新啟動後重新啟動的元件,因此會改為標示為main_process_critical
,也就是說,只要任一元件發生當機情形,就會觸發不必要的重新啟動作業。 - 如果
Reboot
呼叫本身失敗,component_manager
恐慌也會觸發不安全重新啟動。
在這些情況下,我們有可能執行更正常的關閉作業;例如,Component_manager 可以執行正常系統關閉後再結束。另一方面,由於 power_manager
和 driver_manager
對系統運作非常重要,因此我們可能不希望在當機時讓系統繼續執行任意時間。
我們可能會重新審視電源管理責任的分配方式。例如,component_manager
或許能夠自行重新啟動 (仍需要依賴 driver_manager
設定電源狀態)。
系統完全遷移至 Components v2 後,元件管理員可運用對依附元件圖表的知識,支援更智慧的復原策略。
先前的圖片和參考資料
私人設計文件適用於事件 API 的 critical_components
功能和修訂版本。