2017年3月31日 星期五

在 zfs 裡面栽種很多個作業系統 (的根目錄!)

接續上一篇 zfs 檔案系統試水溫, 學會操作 zfs 之後, 下一個目標就是把 linux 的 root file system 放進 zfs 的沃土裡面。 網路上已有 純手工打造版 圖形介面版 兩種方式。 這裡介紹第三種方式: 半手工偷吃步, 雖然步驟也蠻多的, 但觀念上很簡單, 同時又保有很高的自由度。 我提出了這個構想, 結果原來 solaris + zfs 高手恆逸資訊的洪朝欽老師早就在這樣做, 然後就順手幫我把它裝到外接硬碟的 zfs 分割上了 :-)

[2020/01/11 Linus Torvalds建議不要使用ZFS (考量法律與 Oracle 興訟的習性)]

本文假設讀者已熟悉 linux 備份/還原/複製。 工作時需要用到三個 (每個至少 8G 的) 主分割區或邏輯分割區或 lvm 的 logical volume。 重點就是必須是 linux 原生支援的分割區加檔案系統, 不需要煩惱驅動程式問題。 其中兩個用來存放新安裝的 lubuntu (稱為暫存一號分割區跟暫存二號分割區好了); 另一個最好是主分割區或邏輯分割區, 用來建立 zfs 的 pool 並安裝 lubuntu。 (因為如果把 zfs 架在 lvm 上面就多此一舉、 沒效率了),

  1. 假設你的電腦已有現成的開機管理員 (例如 extlinux)。 像我都固定把 extlinux 獨立安裝在 /dev/sda1 的 ext4 (或 vfat 也可以) 檔案系統上, 並且順便裝一個 finnix, 這樣換作業系統甚至重切硬碟時, 都不需要去動到它, 而且永遠至少有 finnix 可以開機救急。 (以下稱之為開機分割區好了)
  2. 把新鮮的 64 bit lubuntu 安裝到暫存一號分割區去。 安裝時請 不要勾 「同時下載更新」, 因為可能會遇到 新版的 kernel 改變 API 令較舊的 dpl-dkms 編譯失敗 的狀況; 如果一開始就讓系統自動幫你升到新版 kernel, 後來再手動降級 kernel 以便配合舊版 dpl-dkms, 有可能會令 系統頭昏搞錯版本、 initrd 設定錯誤、 乃至進不了 X 或無法啟用網路。 (我的親身經歷) 開機管理員有沒有安裝都無所謂; 我用不慣 grub 2, 都習慣事後手動設定自己較熟悉的 extlinux
  3. 32 bit linux 不支援 zfs 但還是可以享用 snapshot 快照
  4. 用暫存一號分割區開機。 為了要讓 initrd 認得 zfs (跟 lvm), 請安裝 zfsutils-linux zfs-initramfs zfs-dkms (跟 lvm2) 這幾個套件。 這要花蠻久的時間, 因為 zfs-dkms 要重新編譯 kernel modules。 最後系統會自動產生新的 initrd。 (可能順便升級新的 kernel?) 這一步做完之後, 試試看 zfs list 指令, 如果出現 no datasets available 就成功了; 如果出現 The ZFS modules are not loaded. Try running '/sbin/modprobe zfs' as root to load them. 那麼你可能需要手動 dpkg-reconfigure spl-dkmsdpkg-reconfigure zfs-initramfs 並且尋找更詳盡的中間步驟錯誤訊息。
  5. 把新的 initrd (還有 kernel?) 複製到開機分割區去、 設定好開機管理員、 確認暫存一號分割區的 linux 可以用新鮮的 initrd 開機。
  6. 用別的 linux (例如 finnix 或其他開機光碟/開機隨身碟) 把暫存一號分割區再複製一份到暫存二號。 因為等一下要把 「認得 zfs 的 linux」 拷貝到 zfs 去, 所以需要兩個認得 zfs 的 linux。
  7. 重開機進入暫存二號。
  8. 在第三個工作分割區上面建立 zpool。 假設叫做 tank 好了, 類似這樣: zpool create tank /dev/sda99
  9. 在 tank 裡面建立 BOOT 檔案系統、 在它底下建立存放 lubuntu 的檔案系統 (叫做 lu1604z 好了)、 設定重要屬性:
    zfs create tank/ROOT
    zfs set canmount=off tank/ROOT
    zfs set mountpoint=legacy tank/ROOT
    zfs create tank/ROOT/lu1604z
    zfs set canmount=noauto tank/ROOT/lu1604z
    zfs set mountpoint=/ tank/ROOT/lu1604z
    
  10. 在 tank 裡面建立一個 swap 用的 volume
  11. 把暫存一號以唯讀的方式掛載起來、 把 lu1604z 也臨時掛載起來、 把暫存一號複製給它、 修改 /tank/ROOT/lu1604z/etc/fstab (刪掉 / 那一列、 更改 swap 裝置)、 卸載兩者、 還原 lu1604z 的屬性:
    mount -o ro ... /mnt/tmp1
    zfs set mountpoint=/mnt/target tank/ROOT/lu1604z
    zfs mount tank/ROOT/lu1604z
    cd /mnt/tmp1 ; rsync -aX . /mnt/target
    vim /mnt/target/etc/fstab
    cd
    umount /mnt/tmp1
    umount /mnt/target
    zfs set mountpoint=/ tank/ROOT/lu1604z
    
  12. 設定開機選單, 最重要的是這兩個選項: boot=zfs root=tank/ROOT/lu1604z 。 例如我用的是 extlinux, 所以加上這樣一段開機敘述:
    label zfs:lu1604z
            menu label lubuntu 16.04 on zfs
            kernel /boot/lu1604z/vmlinuz
            append initrd=/boot/lu1604z/initrd.img boot=zfs root=tank/ROOT/lu1604z
    
    [但若是 proxmox 4.4 的 zfs (zfsutils-linux 0.6.5.8-pve13~bpo80) 則要改成 boot=zfs root=ZFS=rpool/ROOT/pve-1] 如果你的 zfs 所在的硬碟/隨身碟的速度較慢, 可能還需要加上 rootdelay=1 延長開機等待時間。 如果你先前用別的版本開過這個 zpool 但用完後卻沒有正常 export, 那麼還需要加上 zfsforce=on, 不過我覺得這個選項有必要時直接打在 extlinux 開機命令列上就好, 不要寫進設定檔比較安全。
  13. 就這樣, 可以重開機測試一下了!

或者也可以不要重開機, 直接用 kvm 虛擬機 測試。 如同 「試水溫」 一文所說, 最好先讓 host 自廢武功:

zpool export tank
systemctl stop zed
rmmod zfs

然後這樣啟動虛擬機: kvm -m 2048 -kernel vmlinuz -initrd initrd.img -append 'boot=zfs root=tank/ROOT/lu1604z' /dev/sda99 就跟 kvm 啟動單一分割 一樣, 這裡把 kvm 命令列直接當成 boot loader 來用。 當然, vmlinuz 跟 initrd.img 要先拷貝出來。 (或是直接用暫存二號的, 反正一樣) 記憶體至少要給它 2G。

本來我在安裝 linux 時就有這樣的習慣: 每安裝到一個階段, 就 (原先用 tar; 最近才知道應該用 fsarchiver) 把乾淨的系統壓縮備份起來。 如果有四個階段, 大約會佔掉 2.5G * 4 = 10G 的空間。 現在有了 zfs, 就可以用輕巧的 snapshot 來取代階段備份 -- 在每一個 ZFS Filesystem 的 mountpoint 下有一個 .zfs 目錄, 裡面的 snapshots 子目錄存有 (唯讀的) 各個歷史階段快照。 至於備份, 則只需要備最新一份快照: zfs snapshot -r tank@frozen ; zfs send -R tank@frozen | gzip > /media/backup_device/.../tank@170331.zsnap.gz (要先遞迴建立 snapshots 才能遞迴備份。)

更好的是, 完全不需要關機離線請別的作業系統幫我備份/複製, 我站在正在運作的 zfs 上面, 自己就可以備份自己。 如果應用在開機隨身碟, 那就可以自己把自己複製給別顆隨身碟, 模仿單細胞生物的無性生殖完全達到一個新高的境界!

還有一個好處: 現在我若要裝好幾個不同版本的 linux, 不需要每個版本切一個固定大小的分割或 volunme 給它, 而可以把大家都收納在 tank/ROOT 底下, 分享可用空間, 卻又各自獨立。 所以切 20G 應該可以裝三個版本吧。 (還沒試。)

為了節省記憶體, 現在我的日常工作還是在 lvm volume 上面; 不過以後上課用的映像檔的製作過程可以在 zfs 虛擬機裡面進行, 這讓我的硬碟空間省很大, 時間也省很多啊! 早該開始學 zfs 的!

註: 第八步為什麼知道要設定那兩個屬性? 因為朝欽用的是 solaris, 他的檔案系統天生就是 zfs。 在他原先已可開機的系統下用這個指令: zfs get all xyzpool/ROOT | grep -Pv '(default|-)$' 就可以查出原先可開機的系統的 tank/ROOT 有哪些比較特別的屬性, 從而照著改新的 tank/ROOT。

10 則留言:

  1. 嗯~早該學ZFS可以少走很多路!有空要來好好來玩一下!extlinux與finnix真的都很重要grub之前有遇到不能從usb開機改用extlinux就可以開,為什麼我也不知先開的了機再說,謝謝老師分享!又讓我學到怪招了!

    回覆刪除
  2. 隨著硬碟越做越大,重裝系統變容易,好像漸漸不用切硬碟了,把整個系統放在同一個分割區。但這樣開機不會慢嗎?

    回覆刪除
    回覆
    1. 切分割跟開機速度無關哦。 主要是為了讓每個 OS 可以畫地為王,不會互相干擾。 切分割不會變慢,但會降低硬碟空間使用的靈活度,例如你在 /dev/sda1 還剩 20G 在 /dev/sda2 也還剩 20G, 但你有一個 21G 大小的檔案要存放... 那就 GG 了。 lvm 解決了這個問題。 zfs 的上層也提供了類似 lvm 的優點; 而它的下層則提供了類似 raid 的優點。

      刪除
    2. 所以其實切硬碟是不必要的,只是為了解決硬碟不夠大的問題?另外應該是考慮 crash 後如果根目錄過大, fsck 會跑很久,所以不想讓根目錄過大。只要 home 和其它分開即可。頻繁寫入的 var 也許也可以考慮,但這樣又變成都分開了 囧

      刪除
    3. 我有切一個 /mnt/worksp 分割, 從家目錄底下建一個 symlink 指到這裡的幾個子目錄。 凡是我自己建立的檔案(講義、投影片、...)還有上網到處亂抓的大檔案都放在這裡。 人工智慧的訓練圖片跟 git projects 也放這裡。 不同版本的 linux 開起來時,都可以使用。 其他就都沒切割了。 像 var/log, 根本還沒長多大, 可能這個 partition 就被我丟一邊了 -- 我可能一兩年內甚至幾個月內又灌了新版的 linux 或從 .fsa 還原一個乾淨版本(砍掉重練的概念)。

      刪除
  3. 這幾天也在試ZFS發現真的很方便。但遇到一個問題就是每個mount point都會有一個隱藏的目錄.zfs,即使ls -a不會顯示,但是還是可以cd進去的。問題就是這個目錄的權限是任何使用者都可以進入,變成任何這台機器上的user都可以看有哪些snapshot,不太安全,但找了好久就是找不到怎麼關掉它或限制它的權限(像把它設定成只有root可以進入之類的),除了目前只有發現用 chmod 500 .zfs 有用但重開機就失效了(難不成要我寫script讓它每次開機都chmod所有的.zfs資料夾嗎?XD)。不知道老師知道要怎麼做嗎?

    回覆刪除
    回覆
    1. 我是沒有這個需求啦。 安全問題的話, 如果每次 snapshot 時都有顧好, 讓用戶看到 snapshot 好像沒什麼壞處啊? 但總之我也搜尋不到符合你的需求的文章。 只有 man zfs 裡面的 snapdir 可以更開放直接讓 ls 看得見; 其他沒找到權限相關設定的文章 :-(

      刪除
  4. 老師您好,我想請問您,您比較過 zfs 與 btrfs 之後選擇 zfs 的原因嗎?

    回覆刪除
    回覆
    1. 其實沒玩過 btrfs 耶。 只是因為老弟推薦 zfs 才開始玩。 等 oracle 開始用授權騷擾 zfs 用戶 (我覺得是遲早的問題) 再來學 btrfs 吧。

      刪除

因為垃圾留言太多,現在改為審核後才發佈,請耐心等候一兩天。