鴻蒙pc的融合開發(fā)引擎技術(shù)分析

最近鴻蒙pc終于上線了“融合開發(fā)引擎”開發(fā)工具,可以在命令行終端里執(zhí)行大部分的linux命令了(openEuler 24.03 LTS-SP1 ),系統(tǒng)可用性大幅提升。但是在日常使用中,發(fā)現(xiàn)這個linux環(huán)境還是殘缺的,相較于完整虛擬機(jī)很多功能都無法使用,例如docker無法運(yùn)行,系統(tǒng)沒有systemd進(jìn)程等等。經(jīng)過對其運(yùn)行環(huán)境的分析,發(fā)現(xiàn)其既不是傳統(tǒng)意義上的虛擬機(jī),也不是標(biāo)準(zhǔn)的容器,這引起了我的興趣,現(xiàn)將分析結(jié)果記錄如下。

總體架構(gòu)

要理解這個融合開發(fā)引擎,核心是要理解stratovirt的架構(gòu),參考官方介紹文檔:https://www.openeuler.org/zh/blog/wangzhigang/2020-09-25-stratovirt-intro.html

image.png

環(huán)境檢測

進(jìn)入融合開發(fā)引擎創(chuàng)建的 Linux 環(huán)境,搜集以下信息:

層1 - 輕量虛擬機(jī):

  • 設(shè)備樹顯示 linux,dummy-virt,這是 virt machine type 的標(biāo)志
  • 中斷控制器是 GICv3(ARM 虛擬機(jī)標(biāo)配)
  • 所有存儲設(shè)備都是 virtio(vda/vdb/vdc/vdd),沒有真實(shí)物理磁盤
  • 網(wǎng)卡 MAC 地址是 52:54:00:xx:xx:xx,這是 輕量虛擬機(jī)默認(rèn) MAC 前綴
  • BogoMIPS 只有 2000,典型虛擬 CPU 特征

層2 - OzoneC 容器:

  • 根文件系統(tǒng)是 overlay,掛載路徑明顯看到 OzoneC(華為自研容器運(yùn)行時)
  • /mnt/linux_share 通過 virtiofs 掛載(VM 和宿主機(jī)共享文件系統(tǒng))
  • sysfs 是只讀掛載(ro),容器內(nèi)無法修改內(nèi)核參數(shù)
  • 大量 /proc/sys/ 路徑被 tmpfs 覆蓋(容器隔離手段)

整體環(huán)境架構(gòu):

物理宿主機(jī)(鴻蒙PC aarch64,HarmonyOS)
    └── StratoVirt 輕量虛擬機(jī)(aarch64,Guest Linux Kernel 6.6.0)
            └── OzoneC 容器(openEuler 24.03 LTS-SP1 鏡像)

源碼分析

源碼基于官方代碼倉庫:https://atomgit.com/openeuler/stratovirt
將其中代碼結(jié)合本地實(shí)際運(yùn)行環(huán)境進(jìn)行分析:

1. 設(shè)備樹 compatible 字符串

當(dāng)前環(huán)境:linux,dummy-virt
StratoVirt 源碼 fdt.rs:
  fdt.set_property_string("compatible", "linux,dummy-virt")

這個字符串是 StratoVirt aarch64 標(biāo)準(zhǔn)/輕量機(jī)型共用的 FDT 根節(jié)點(diǎn)標(biāo)識,不是 QEMU 獨(dú)有的。

2. 網(wǎng)卡 MAC 地址完全匹配

當(dāng)前環(huán)境 eth0:52:54:00:12:34:56
當(dāng)前環(huán)境 eth1:52:54:00:12:34:57

StratoVirt 源碼 virtio/src/device/net.rs:
  let mac = "52:54:00:12:34:56";   ← eth0
  let mac = "52:54:00:12:34:57";   ← eth1(測試用默認(rèn)值)

這是 StratoVirt 硬編碼的默認(rèn) MAC,直接對上了。

3. virtio-mmio 而非 virtio-pci

當(dāng)前環(huán)境中斷:virtio0 ~ virtio10(全部走 MMIO 總線)
lsblk:vda/vdb/vdc/vdd(virtio-blk-device 命名風(fēng)格)

StratoVirt microvm 用 virtio-mmio,標(biāo)準(zhǔn)機(jī)型(virt)才用 virtio-pci。當(dāng)前是 virtio-mmio,確認(rèn)是輕量模式。

4. GICv3 + PL031 RTC 匹配 microvm 內(nèi)存布局

StratoVirt aarch64/micro.rs 定義:
  GicDist  @ 0x0800_0000
  GicCpu   @ 0x0801_0000
  GicRedist @ 0x080A_0000
  Rtc(PL031) @ 0x0901_0000

當(dāng)前 /proc/interrupts:
GICv3 70 Level  rtc-pl031   ← 完全對應(yīng)

5. seccomp 白名單(<55個syscall)

microvm 模式下 StratoVirt 用 seccomp 嚴(yán)格限制自身的 syscall,運(yùn)行時只允許約 55 個系統(tǒng)調(diào)用,這是其安全性的核心設(shè)計。

microvm vs 標(biāo)準(zhǔn) virt 機(jī)型的本質(zhì)區(qū)別(源碼層面)

特性              microvm(輕量)         standard virt(標(biāo)準(zhǔn))
設(shè)備總線          virtio-mmio             virtio-pci + PCIe
啟動固件          簡化 BootLoader         UEFI (edk2)
ACPI             無                      有
熱插拔            不支持                  支持
設(shè)備枚舉          靜態(tài),啟動時確定         動態(tài),ACPI 驅(qū)動
啟動時間          ~50ms                   慢(固件初始化)
內(nèi)存開銷          ~4MB                    更高
攻擊面            最小化                  較大
適用場景          容器/Serverless          傳統(tǒng)虛機(jī)替代

啟動流程

融合開發(fā)引擎 app(harmonyos app)
      ↓ 調(diào)用OzoneC 接口創(chuàng)建虛擬機(jī)
OzoneC(Rust)宿主機(jī)側(cè)
      │ 讀取 config.json 發(fā)現(xiàn)有 vm 字段
      ↓ 啟動 StratoVirt microvm
StratoVirt(Rust)→ KVM → aarch64 VM
      ↓ VM 內(nèi)核啟動,執(zhí)行 hsl_init
OzoneC(Rust)VM 內(nèi)側(cè)
      │  pivot_root → overlay rootfs
      │  namespace 隔離
      │  seccomp + capabilities
      ↓
openEuler 24.03 容器環(huán)境
      ↓
Linux App 進(jìn)程

這里面比較有意思的點(diǎn)是OzoneC分別在宿主機(jī)和虛擬機(jī)內(nèi)承擔(dān)了不同的角色,一共調(diào)用了兩次。

OzoneC介紹

OzoneC到底是什么?
從 Cargo.toml 定義一目了然:

description = "An OCI runtime implemented by Rust"

OzoneC 是一個 OCI 標(biāo)準(zhǔn)容器運(yùn)行時,類比 runc / crun,但是用 Rust 寫的,專門為 StratoVirt VM 內(nèi)部設(shè)計。

它實(shí)現(xiàn)了 OCI Runtime Spec 標(biāo)準(zhǔn)接口:

ozonec create   → 創(chuàng)建容器
ozonec start    → 啟動容器
ozonec state    → 查詢狀態(tài)
ozonec kill     → 發(fā)送信號
ozonec delete   → 刪除容器
ozonec exec     → 在容器內(nèi)執(zhí)行命令

StratoVirt 和 OzoneC 各自負(fù)責(zé)什么

這是問題的核心。兩者解決的是完全不同層面的問題:

StratoVirt microvm 負(fù)責(zé):

  • KVM 硬件虛擬化隔離(VM 邊界)
  • 虛擬硬件(virtio 設(shè)備、GICv3)
  • 提供一個完整的 Linux 內(nèi)核運(yùn)行環(huán)境
  • 把物理機(jī)上的多個 VM 彼此隔離

OzoneC 負(fù)責(zé)(在 VM 內(nèi)部):

  • OCI 標(biāo)準(zhǔn)接口(讓 iSulad/Docker 能調(diào)用它)
  • 每個容器的 Linux namespace 隔離
  • 每個容器的文件系統(tǒng)(overlay rootfs + pivot_root)
  • 每個容器的 seccomp syscall 白名單
  • 每個容器的 capability 降權(quán)
  • 每個容器的 cgroup 資源配額
  • 每個容器的 sysctl 參數(shù)

StratoVirt 創(chuàng)造了一個"干凈的 Linux 環(huán)境",但它完全不知道這個環(huán)境里跑著什么應(yīng)用,也不管應(yīng)用之間的隔離。OzoneC 在這個環(huán)境里負(fù)責(zé)應(yīng)用級別的精細(xì)管理。

到這里其實(shí)可以看出來了,OzoneC 的設(shè)計初衷就是安全容器場景(和 katacontainer 非常相似,只是用了rust 編寫),業(yè)界既想用到vm的隔離性,也想用到容器的輕量性,搞出來的一個中間方案,這也很好理解,StratoVirt 本身就是華為云團(tuán)隊設(shè)計出來用于云計算場景做多租戶隔離的,設(shè)計完全是數(shù)據(jù)中心導(dǎo)向的,而并非用于個人電腦。

業(yè)界做安全容器的方案也不少,這邊做一個比較:

方案             隔離手段              內(nèi)核來源          語言
─────────────────────────────────────────────────────────
Kata Containers  輕量 VM(QEMU/FC)    Guest Linux 內(nèi)核   Go
gVisor           用戶態(tài)內(nèi)核攔截         自實(shí)現(xiàn) syscall     Go
Firecracker      microVM              Guest Linux 內(nèi)核  Rust
OzoneC           microVM(StratoVirt) Guest Linux 內(nèi)核  Rust

OzoneC + StratoVirt 和 Firecracker 是最接近的對標(biāo),都是 Rust 寫的 microVM 方案,但有幾個關(guān)鍵設(shè)計差異。


OzoneC 的獨(dú)特設(shè)計:同一個二進(jìn)制,兩個角色

這是 OzoneC 最與眾不同的地方。

Kata Containers 需要兩個獨(dú)立組件:

宿主機(jī)側(cè):kata-shim(Go)  ← 接收 OCI 調(diào)用,管理 VM 生命周期
VM 內(nèi)側(cè):kata-agent(Go)  ← 在 VM 內(nèi)執(zhí)行容器操作
兩個進(jìn)程,兩套代碼,gRPC 通信

OzoneC 是同一個 Rust 二進(jìn)制,根據(jù)上下文切換角色:

// oci_spec/src/runtime.rs
pub struct RuntimeConfig {
    pub vm: Option<VmPlatform>,      // 有這個字段 → 宿主機(jī)角色:啟動 VM
    pub linux: Option<LinuxPlatform>, // 有這個字段 → VM內(nèi)角色:建容器
}
宿主機(jī)側(cè):OzoneC 讀到 vm 字段 → 調(diào) StratoVirt 啟動 microvm
VM 內(nèi)側(cè):OzoneC(hsl_init 觸發(fā))讀到 linux 字段 → 建 namespace/rootfs

同一個二進(jìn)制,6127 行 Rust,替換掉 kata-shim + kata-agent 兩個 Go 組件

兩階段容器初始化(源碼里最精密的部分)

從 linux/container.rs 的 create() 函數(shù):

主進(jìn)程(OzoneC 宿主)
  │
  ├─ fork → Stage 1 進(jìn)程 [ozonec:1:CHILD]
  │           ├── set_user_namespace()    用戶命名空間
  │           ├── set_pid_namespace()     PID 命名空間
  │           └─ fork → Stage 2 進(jìn)程 [ozonec:2:INIT]
  │                       ├── set_rest_namespaces()   其余 ns
  │                       ├── rootfs.prepare_rootfs() overlay 掛載
  │                       ├── pivot_root()            切換根文件系統(tǒng)
  │                       ├── set_seccomp()           syscall 過濾
  │                       ├── drop_capabilities()     降權(quán)
  │                       └── exec_program()          執(zhí)行應(yīng)用
  │
  └─ 通過 Channel<Message> 同步各階段狀態(tài)

為什么要兩階段?User namespace 的 uid/gid mapping 必須由父進(jìn)程寫入(/proc/pid/uid_map),但寫入時子進(jìn)程必須已經(jīng)進(jìn)入新 namespace 且處于 dumpable 狀態(tài)。兩階段設(shè)計用 Unix pipe 做同步,精確控制這個時序,避免競爭條件。


安全縱深:四層疊加

層次        機(jī)制                          在哪里實(shí)現(xiàn)
──────────────────────────────────────────────────────────
第一層  KVM 硬件虛擬化隔離              StratoVirt + CPU VT/Kunpeng-V
第二層  StratoVirt 進(jìn)程 seccomp         micro_common/syscall.rs
        (VM 進(jìn)程本身限制 <55 syscall)
第三層  Linux namespace 隔離            OzoneC linux/namespace.rs
        (容器內(nèi) pid/mnt/uts/net/ipc)
第四層  容器級 seccomp                  OzoneC linux/seccomp.rs
        (per-container syscall 白名單)

和 Kata Containers 的對比

維度              Kata Containers           OzoneC + StratoVirt
────────────────────────────────────────────────────────────────
VMM 后端          QEMU / Firecracker 可選   只有 StratoVirt
宿主側(cè)組件        kata-shim(Go)           OzoneC(Rust)
VM 內(nèi) agent       kata-agent(Go)          OzoneC(Rust)
通信協(xié)議          TTRpc over vsock          OCI spec 直接實(shí)現(xiàn)
語言              Go(運(yùn)行時開銷,GC)       Rust(無 GC,內(nèi)存安全)
代碼規(guī)模          shim+agent 合計 ~8萬行    OzoneC 6127 行
外部依賴          OpenStack 基金會項目      華為完全自主

Kata 最大的問題是架構(gòu)復(fù)雜度——shim、agent、TTRpc、各種 VMM 后端,組件多、版本管理復(fù)雜、Go 的 GC 在低延遲場景有抖動。OzoneC 用 6000 行 Rust 做了 Kata 兩個核心組件的事,是典型的"少即是多"策略。

結(jié)論

經(jīng)過以上分析,現(xiàn)在基本可以下一個結(jié)論了,即這個所謂的“融合開發(fā)引擎”就是使用 OzoneC 工具創(chuàng)建的一個“安全容器”。 具體來說,stratovirt 虛擬化工具創(chuàng)建了一個輕量化虛擬機(jī)(microvm,帶完整的 guest kernel),虛擬機(jī)里面使用 hsl_init 拉起了一套 OzoneC 管理的容器環(huán)境。

之前介紹過,這套安全容器架構(gòu)本身是華為云計算 openEuler 團(tuán)隊用于云計算場景下的多租戶隔離,這個本身沒有任何問題,工程方面的實(shí)現(xiàn)也非常優(yōu)秀。問題在于現(xiàn)在把這套技術(shù)用于鴻蒙pc,用于模擬 Linux 開發(fā)環(huán)境就產(chǎn)生了一種非常擰巴的效果:guest kernel 理論上是完整的,但是由于ozone這個安全沙盒的存在,導(dǎo)致很多內(nèi)核能力不可用(iptables、ipvs、cgroups等等),也導(dǎo)致了沒有systemd(1號進(jìn)程是hsl_init),同時也無法在容器內(nèi)再去掛載overlay,因?yàn)樗约罕旧砭鸵呀?jīng)是overlay了,屬于套娃了,導(dǎo)致docker也不可用。

華為使用這套基于云計算場景的架構(gòu)做pc 端 Linux 環(huán)境模擬的初衷目前暫未可知,對于融合開發(fā)引擎這個場景,完全可以用最簡單的方式:

stratovirt \
    -machine microvm \
    -kernel openeuler-6.6.bin \
    -append "root=/dev/vda init=/sbin/init console=ttyAMA0" \
    -drive file=linux-gnu-rootfs.ext4,id=rootfs \
    -device virtio-blk-device,drive=rootfs \
    ...

VM 啟動,systemd 拉起,用戶得到一個完整的 Linux 環(huán)境,root 權(quán)限,Docker 正常工作,和使用真實(shí) Linux 機(jī)器沒有區(qū)別。OzoneC 這一層是云平臺加的多租戶隔離機(jī)制,在單用戶的桌面場景里沒有存在的必要。個人認(rèn)為微軟的 WSL2 是一個比較好的參考實(shí)現(xiàn),同樣也是使用了輕量化虛擬機(jī) Hyper-V vm,但其內(nèi)核能力是完整的,沒有安全沙盒這一層,所以安裝docker,systemd這些都完全沒有問題,希望后續(xù)融合開發(fā)引擎也往這個方向發(fā)展。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容