方案1

  • System, frameworks, applications 各自獨立, 任何一者的更新不會影響其他單元的可用性
  • System 及 user data 可以放在如 ext4 等更成熟的 file system
  • Frameworks 及 applications 集中放置於 btrfs image file 中
    • 每一個 framework/application 都放置於獨立的 sub-volume
    • 所有的 sub-volume 都是 read-only 的
    • 在任一 framework 及 application 更新時, 會產生新的 sub-volume, 不影響目前執行中的版本
    • 更新失敗時 (例如 download 不全), 不影響現有 sub-volume
    • 新版有可用性問題時, 可以 rollback 回舊版
  • 更新 sub-volume 時, 可以 btrfs 提供的 send/receive 完成, 配合壓縮, 最小化資料傳輸
  • 安裝 application 時, 會先產生新的 sub-volume, 並在其中將 framework 及 application 的內容以 hardlink 合成在一塊
  • 此設計為的是在 xdg-app 的設計還未被廣泛接受時, 減少 build environment 遷移帶來的工作量, 暫時的方案
  • Host file system 可見度問題還需要其他解決方案

btrfs Image 內容放置方式

目錄結構設計如下, leaf 都是 sub-volumn, 其他為 directory

  • frameworks/
    • a/
    • .../
    • g/
      • gnome-/
        • 2.0/
        • 3.0/
          • 3.16/
          • 3.18/
    • q/
      • qt/
        • 4/
          • 4.8/
        • 5/
          • 5.1/
          • 5.4/
          • 5.5/
  • applications/
    • gimp/
      • 2.8.14/
  • exports/
  • containers
    • gimp/
      • 2.8.14/

解說如下

  • frameworks 中放置 download 下來的 framework
  • applications 中放置 download 下來的 application
  • exports 中放置 container 中要讓 container 看到的內容, 如 icon, dbus .service file, systemd unit files, ...
  • containers 中放置 framework 及 application 合成後的結果

Framework 與 Application 的合成

之所以合成 framework 及 application, 目的是

  • application 如果有依賴的 framework, 那在 application 執行時, 相應的 framework 必須要能被系統看見並載入
  • framework 會被多個應用所共享, 所以 application 不能變動原始 framework 內容
  • 目前 linux 上的 application 安裝基本散落在 /usr 下各子目錄中
  • 如果以 unionfs/aufs/overlayfs 的方式可以疊加目錄結構, 但會降低整體檔案存取的效率
  • 如果在 runtime 以 bind mount 的方式完成, 會相當耗時及耗 kernel 內存
  • 一個解法是, 在 install-time 就將 framework copy 一份 (以 btrfs 的 snapshot 完成), 並將 application 的內容 hardlink 進這份新的 framework 中, 省時高效

以下以實際說明過程. 假設 framework 內容如下 (先忽略版本)

  • frameworks/
    • myfw
      • a/
        • b
      • c/
        • d

Application 內容如下

  • applications/
    • myapp
      • c/
        • d
      • e/
        • f
        • g

首先, 將 myfw 做一份 snapshot, 並更名為目標 application 的名字, 成為一個新的 container

$ btrfs subvol snap frameworks/myfw containers/myapp

接著

  • 遞規的將 application 中的內容 hardlink 到 container 中
  • 如果來源目錄已存在於 container 中, 跳過不做 hardlink
    • 但對其下的內容做 hardlink
  • 如果來源目錄不存在於 container 中, 建立一個新的目錄
  • 如果來源檔案已存在於 container 中, 就以 hardlink 的方式 overwrite 掉 container 中的檔案
$ ln -f applications/myapp/c/d containers/myapp/c/d
$ mkdir containers/myapp/e
$ ln -f applications/myapp/e/f containers/myapp/e/f
$ ln -f applications/myapp/e/g containers/myapp/e/g

最終目錄內容會長成這樣

  • containers/
    • myapp
      • a/
        • b
      • c/
        • d
      • e/
        • f
        • g

然後, 再將這個 container 設為 read-only

$ sudo btrfs property set containers/myapp ro true

缺點

  • Application 下檔案多時建立 hardlink 的時間會比較長 (但 apt-get install 時解壓檔案的安裝動作更花時間)
  • 每次 framework 更新時, 要
    • 刪了 container 下相關的應用
    • 重新合成 container
  • 如果 framework 是一個大而全的集合時, 更新的可能性會很大
  • 一個應用如果相要關聯多個 framework 但它們並不在同一個集合中時, 就只能以自帶的方式包含進來
  • 用到了 mount 指令, 就無法做法完全以 non-privilege 身份完成安裝

方案2

在這個方案中, framework 不是大而全的集合, 而是最小單元. 跟方案1 的差異只在於, 安裝時, 先產生新的 sub-volume (空的), 然後將所有用到的 frameworks hardlink 進去後, 再將 application hardlink 進去

方案3

類似方案2, 但丟掉 btrfs. 好處是, 可以消除 btrfs 帶來的不穩定可能性 (其實 4.2 及之後的版本穩定性相當高了), 另外因為沒有使用 image file, 不需要 mount 或 btrfs 的操作, 所以可以做到以 non-privilege user 的身份安裝. 但要自行管理的東西就相對多.

方案4

類似方案3, 但配合 ostree 作內容的管理, 安裝時也會進行 frameworks 及 application 的合成.