使用 Btrfs 快照方便升級 Fedora Linux 且易于回退
在 2018 年的一篇 早前的文章 中,我們介紹了在升級 Fedora Linux 前如何利用 LVM 復(fù)制根文件系統(tǒng),以便在可能出現(xiàn)錯誤的情況下能有一個回退機(jī)制。注意,本文不涉及會如何從其它文件系統(tǒng)轉(zhuǎn)換或遷移到 Btrfs 上。
確認(rèn)根文件系統(tǒng)是否為 Btrfs
本示例采用的是 Pinebook aarch64 筆記本電腦。在開始前,務(wù)必確認(rèn)你的根文件系統(tǒng)是否采用 Btrfs。要明確,不是所有的定制版或者鏡像文件默認(rèn)都使用 Btrfs。
$ df -T
Filesystem Type 1K-blocks Used Available Use% Mounted on
devtmpfs devtmpfs 4096 0 4096 0% /dev
tmpfs tmpfs 998992 0 998992 0% /dev/shm
tmpfs tmpfs 399600 6360 393240 2% /run
/dev/mmcblk2p3 btrfs 56929280 39796116 15058348 73% /
tmpfs tmpfs 998996 24 998972 1% /tmp
tmpfs tmpfs 5242880 0 5242880 0% /var/lib/mock
/dev/mmcblk2p3 btrfs 56929280 39796116 15058348 73% /f34
/dev/mmcblk2p3 btrfs 56929280 39796116 15058348 73% /home
/dev/mmcblk2p2 ext4 996780 551888 376080 60% /boot
/dev/mmcblk2p1 vfat 194348 31648 162700 17% /boot/efi
tmpfs tmpfs 199796 100 199696 1% /run/user/1000
tmpfs tmpfs 199796 84 199712 1% /run/user/0
列出當(dāng)前的 Btrfs 子卷
以上的示例輸出顯示掛載在 “根”(/
)的文件系統(tǒng)類型是 Btrfs。你會注意到,有三個掛載點(diǎn)顯示了相同的備份設(shè)備以及 已用 和 可用 的塊數(shù)。這是因?yàn)樗鼈兪菑耐?Btrfs 文件系統(tǒng)掛載的不同部分(子卷)。比如,/f34
子卷是我去年創(chuàng)建的那個可引導(dǎo)快照。
默認(rèn)的 Fedora Btrfs 安裝會創(chuàng)建一個 Btrfs 文件系統(tǒng),并在其上分別掛載兩個子卷,root
和 home
,掛載路徑分別為 /
和 /home
。讓我們一起來看看我還添加了哪些其它的子卷:
$ sudo btrfs subvol list /
ID 272 gen 110428 top level 5 path root
ID 273 gen 110426 top level 5 path home
ID 300 gen 109923 top level 5 path f34
ID 301 gen 95852 top level 5 path home.22Jul26
ID 302 gen 95854 top level 5 path f36.22Jul26
在這里,我們有一個來自最近一次系統(tǒng)升級的 f34
子卷,以及兩個只讀快照 home
和 f36
。要添加和刪除這些快照,最簡單的方法就是掛載 Btrfs 的根目錄。我會更新系統(tǒng)并創(chuàng)建當(dāng)前 f36 root
子卷的新快照。如果你已經(jīng)重命名了你的 root
子卷,我相信你知道該如何調(diào)整以下的示例以適應(yīng)你的系統(tǒng)。
創(chuàng)建 Btrfs 的回退快照
$ sudo dnf update --refresh
...更新了很多部分(如果升級了內(nèi)核還需要重啟)
$ sudo mkdir -p /mnt/root
$ sudo mount /dev/mmcblk2p3 /mnt/root
$ cd /mnt/root
$ ls
f34 f36.22Jul26 home home.22Jul26 root
$ sudo btrfs subvol snapshot root f36
Create a snapshot of 'root' in './f36'
因?yàn)?Btrfs 快照是以文件系統(tǒng)為基礎(chǔ)的,所以并不需要在創(chuàng)建快照之前進(jìn)行 “同步”,正如我在 LVM 中建議的。要從新的子卷引導(dǎo)為回退,你需要使用你喜歡的編輯器編輯 /mnt/root/f36/etc/fstab
。如果你是剛?cè)腴T的話,nano 就是一款十分簡單的文本編輯器,功能基本夠用。以下是我 fstab
文件中的一些行:
LABEL=PINE / btrfs subvol=root,compress=zstd:1 1 1
UUID=e31667fb-5b6f-48d9-aa90-f2fd6aa5f005 /boot ext4 defaults 1 2
UUID=75DB-5832 /boot/efi vfat umask=0077,shortname=winnt 0 2
LABEL=PINE /home btrfs subvol=home,compress=zstd:1 1 1
LABEL=SWAP swap swap discard=once 0 0
將 subvol=root
更改為 subvol=f36
。這個改動是作用在快照中的文件,而并非你實(shí)際運(yùn)行中的 fstab
文件。你可以通過 diff /etc/fstab /mnt/root/f36/etc/fstab
對比它們的區(qū)別。在我的情況下,我還使用了 sudo btrfs subvol delete f34
來刪除我去年的 f34
快照。
測試 Btrfs 的回退快照
你現(xiàn)在可以進(jìn)行回退的測試了。你可以使用 grubby 或在 /boot/loader/entries
中編輯一項(xiàng)來將 subvol=root
替換為 subvol=f36
。然而,為了初學(xué)者的安全起見,我們更建議你在啟動時編輯 GRUB 進(jìn)行操作。你可以參考 關(guān)于 GRUB 的這篇文章 了解如何進(jìn)入 GRUB 菜單。在你到達(dá)那里后,按下 e
鍵編輯默認(rèn)的內(nèi)核啟動項(xiàng)。放心 —— 你所做的更改都只存在于內(nèi)存中,如果你弄錯了,重啟電腦即可重新開始。就像編輯 fstab
文件一樣,找到 subvol=root
并將其更改為 subvol=f36
。然后,按 F10
或 Ctrl + X
來引導(dǎo)你修改過的項(xiàng)目。通過這些更改,你的系統(tǒng)應(yīng)該能夠引導(dǎo)進(jìn)入你的新快照。你可以查看 /etc/fstab
確保你正在引導(dǎo)至正確的子卷,或鍵入 mount | grep subvol
查看此時在 /
上掛載的子卷。
進(jìn)行 Fedora Linux 的系統(tǒng)升級
如果你的回退功能沒問題,重啟并返回你的正常根文件系統(tǒng)(并像上面所述,進(jìn)行確認(rèn))。然后,按照 維基頁面 上的指南進(jìn)行標(biāo)準(zhǔn)的系統(tǒng)升級。提示:在運(yùn)行 dnf system-upgrade reboot
之前,給 root
子卷創(chuàng)建另一個快照,你可以給它命名為 root.dl
。這樣,如果你發(fā)現(xiàn)硬盤空間不夠,你不必再下載五個 GB 的文件包。因?yàn)槌讼螺d的包以外,所有的內(nèi)容都將與 root
和 f36
共享,因此它不會占用任何額外的空間。然后說到磁盤塊的共享問題……
因?yàn)樵?nbsp;root
子卷中的 f36
文件和 f36
子卷中的相同文件都使用的是相同的磁盤位置,所以在處理 Btrfs 報(bào)告的可用空間時,dnf system-upgrade
會感到困惑。因此在升級過程中從 root
子卷中刪除它們并不會真正釋放任何空間。如果你耗盡了所有空間,并且決定重啟電腦,那么圖形用戶界面(GUI)將無法啟動。此時,你需要使用 Ctrl + Alt + F2
進(jìn)入文本控制臺并登錄,這將是你磨練命令行技能的好機(jī)會。如何清空空間或擴(kuò)展根文件系統(tǒng)在這里先不做討論(我的文件系統(tǒng)經(jīng)常在一個 LVM 的卷上,可以被擴(kuò)展)。通常,為升級保留超過 50% 的空閑空間是比較保險的。
恢復(fù)環(huán)節(jié)
如果不幸出現(xiàn)問題,你可以重啟電腦,并編輯 GRUB 條目以引導(dǎo)啟動回退快照。如果你是新手,一旦需要在磁盤上修改 GRUB 條目(這樣你就不需要每次啟動時都進(jìn)行編輯),可能需要一些指導(dǎo)。刪除或重命名損壞的 root
子卷是非常直接的。你可以為 f36
子卷(或 root.dl
快照)創(chuàng)建一個快照,然后嘗試再一次進(jìn)行系統(tǒng)升級。以下是在子卷 f36 上啟動到回退系統(tǒng)后重新開始的示例:
$ mount | grep subvol
$ sudo mount /dev/mmcblk2p3 /mnt/root
$ cd /mnt/root
$ sudo mv root root.failed
$ sudo btrfs subvol snapshot f36 root
Create a snapshot of 'f36' in './root'
Don't forget to edit /mnt/root/root/etc/fstab to change the subvol mounted on "/" to "root".
事實(shí)證明,新的 f38 版本的 kernel-6.2.11 在我的 Pinebook 上進(jìn)行系統(tǒng)升級后并未成功啟動?。ú贿^請不要擔(dān)心,ARM 只是 Fedora Linux 的可選 CPU 架構(gòu)——在主流設(shè)備上,你很少會遭遇此類問題。)的確,我成功地按照前文所述,在啟動時編輯了 GRUB 條目并恢復(fù)到了 f36 的 kernel-6.2.10。現(xiàn)在,我繼續(xù)使用著 f38,但同時搭載了 f36 的 kernel-6.2.10。
更新:kernel-6.2.12 已經(jīng)發(fā)布,且我已確認(rèn)它在 Pinebook 上運(yùn)行正常。
過期問題
隨著你不斷更新 f38 系統(tǒng),它最終可能會希望刪除 /boot
下的最后一個 f36 內(nèi)核。通常來說,這并不是問題,因?yàn)榈侥莻€時候,你已經(jīng)完全熟悉 f38,而 f36
快照只是一個存檔。然而,如果你希望你的復(fù)刻版本(即 f36
的快照)能無限期地啟動,那么你應(yīng)當(dāng)在 /boot
下保存一個能正常工作的 f36 內(nèi)核。最簡單的方法是在 /etc/dnf/dnf.conf
文件中設(shè)置 installonly_limit=0
并手動移除舊的內(nèi)核,這種方法既簡單又安全(盡管可能稍顯繁瑣)。
這里還有一個更復(fù)雜的解決方案(不適合新手):運(yùn)行 find /boot -name "*fc36*"
指令,列舉出所有在 f36
子卷快照中的內(nèi)核及 GRUB 文件(這些文件并未包含在快照之內(nèi))。將這些文件備份至安全位置(例如我會掛載 f36
子卷,并將文件備份至其下的一個目錄)。當(dāng) f38 系統(tǒng)啟動后,對于每一個 f36 內(nèi)核版本,都需使用 dnf
刪除特定版本的內(nèi)核(例如,使用 dnf remove kernel-core-5.19.11-200.fc36
)。千萬不要刪除 f38 的內(nèi)核!然后將你先前備份的 f36 內(nèi)核恢復(fù)至 /boot
下。此時,f38 系統(tǒng)已經(jīng)無法再識別 f36 的內(nèi)核,因此也不會將其從 /boot
中刪除。
然而,使用此方法有一個缺點(diǎn),那就是你可能會不小心刪除正在運(yùn)行的 f38 內(nèi)核。如果大家有更佳的解決辦法,歡迎在評論區(qū)分享。
展望未來
對于習(xí)慣于修改 GRUB 條目的用戶,他們可能會考慮創(chuàng)建一個命名為 f38
的快照子卷。將當(dāng)前的 GRUB 條目進(jìn)行修改,以便啟動到這個子卷,重啟后,在這個子卷中進(jìn)行系統(tǒng)升級。此后,總是根據(jù)它所包含的 Fedora Linux 版本來命名根文件系統(tǒng)的子卷。但在本文中,我沒有采用這種作法,原因有兩點(diǎn):
- 將當(dāng)前活動的子卷命名為
root
,這是遵循 Fedora Linux 的默認(rèn)設(shè)置。 - 堅(jiān)持使用
root
作為當(dāng)前子卷,這樣在進(jìn)行正常的系統(tǒng)升級操作之外,并不需要任何永久性的更改。
正如本文所展示的,針對重大系統(tǒng)更改(如系統(tǒng)版本升級)可能引發(fā)問題時,只讀快照作為本地恢復(fù)點(diǎn),非常有用。這些快照還可以通過 Btrfs 的 send
子命令,發(fā)送至遠(yuǎn)程備份。(如果遠(yuǎn)程備份設(shè)備已經(jīng)包含了先前的備份數(shù)據(jù),Btrfs
可以進(jìn)行增量發(fā)送,僅傳輸發(fā)生變更的文件,這樣就能節(jié)省時間和空間。)如果你打算長期存檔這些快照,保持清晰不混淆且能明確恢復(fù)順序的關(guān)鍵,就是要使用一套一致的命名規(guī)則。關(guān)于如何使用
Btrfs 的 send
命令來創(chuàng)建備份的更多信息,你可以參考關(guān)于 Btrfs 快照備份 的文章。