我在 NixOS 踩坑 tmpfs on root 的記錄
我在 NixOS 轉向 tmpfs on root 的過程中碰到了很多坑, 有些是 NixOS 自己的問題,而有些是我本身的許多需求耦合在一起的結果。
為什麼要 tmpfs on root?
先說 tmpfs on root 的好處:tmpfs 讓我的根目錄和家目錄僅用內存裝載, 就能保證每次都能有一個相對乾淨的根目錄和家目錄, 不會因為一些東西的緣故讓系統的狀態積重難返。
所有系統的狀態和需要保留的文件都可以額外通過 impermanence 模組指定硬盤的位置存放, 以持久化的方式保存下來,在運行的時候通過 bindfs 的方式硬連接到指定的位置。
如何做到?
很好,現在目標定下來了。接下來就是把大象裝進冰箱了。
簡單地說,切換到 tmpfs 需要我僅僅把系統狀態依賴於硬盤的持久化目錄。 也就是說,我的需求是:
-
保存 NixOS 必要的運行文件,即原有系統裡的
/nix/store
和/nix/var
。 -
保存 Linux 和 systemd 和一些依賴家目錄配置的應用程序的必要的運行時文件, 例如
/etc/adjtime
、/var/lib/systemd/timers
等等。 這些必要的系統狀態我選擇放到/nix/persist
中去。 -
保存一些我需要使用的文件,比如
動漫高手看的高手動漫, 這些文件我選擇放到/nix/storage
裡去。
需要注意的一點是,持久化不代表這些文件是只能被讀取而不能寫的。 相反,持久化意味著所有系統的狀態都會於 NixOS 運行時在上述的指定位置修改。
總結一下現在的需求的實現目標,我希望我的硬盤是以下這個樣子:
/dev/nvme1n1
|- /dev/nvme1n1p1 # 存放 /boot 信息
|- /dev/nvme1n1p3 # SWAP 預留分區
|- /dev/nvme1n1p2 # 系統啟動時掛載為 /nix 目錄
| # 第一部分: NixOS 必要的運行文件
|- store # - 原來的 /nix/store
|- var # - 原來的 /nix/var
| # 第二部分: Linux, systemd, 以及
|- persist # 一些依賴家目錄的應用必要的運行文件
| |- var/lib # - Linux, systemd 以及一些系統級服務
| | # 運行時需要的數據. 例如: bluetooth
| | # 會在這裡記錄已連接的藍牙設備.
| |- var/log # - 日誌文件
| |- etc/ # - 上述系統相關程序的配置. 例如:
| | # NetworkManager 會在此存放配置.
| |- home/shinri/.local
| |- home/shinri/.config
| # - 一些應用依賴家目錄來存放配置
|- storage # 第三部分: 放一些我想放的東西
|- OneDrive
|- Collections
...
這樣,當根目錄是 tmpfs 時,系統就會形成以下的樣子(=>
表示掛載關係)
/ => tmpfs
|- nix => /dev/nvme1n1p2
| |- store
| |- var
| |- persist
| |- storage
|- home/shinri
| |- .local => /nix/persist/home/shinri/.local
| |- .config => /nix/persist/home/shinri/.config
| |- Collections => /nix/storage/Collections
| |- OneDrive => /nix/storage/OneDrive
| ...
|- var
| |- lib/bluetooth => /nix/persist/var/lib/bluetooth
| |- log => /nix/persist/var/log
| ...
|- etc
| |- NetworkManager/system-connections
| | => /nix/persist/etc/NetworkManager/system-connections
| ...
...
但實際上,在動工之前,我的硬盤是這個樣子:
/ => /dev/nvme1n1p2
|- nix
| |- store
| |- var
|- home
|- etc
|- var
|- ...
於是,為了讓硬盤達到我希望的狀態,我需要做的就是: 先愚公移山,把當前的硬盤改造成上面我想要的 layout。
愚公移山
在備份了一些小且重要的配置文件之後,
我需要做的就是把需要持久化的文件拷貝到 /persist
和 /storage
下,
如果類似 100G 以上的動漫,太大的話就移動過去。
結合 impermanence 模組的聲明式配置,
我可以在不掛 tmpfs 的情況下在當前系統調試這樣做是否符合我的想法。
沒錯,持久化這些目錄比掛載 tmpfs 要先開始,
因為我要不停地觀察 mount
、lsblk
是否給出符合我需求的掛載。
雖然這一步說起來很簡單,但是做起來很複雜,花了我兩到三個整天的時間。 選擇細粒度地梳理哪些需要被持久化是很痛苦的, 有時候可能會發現既有的 NixOS 或 home-manager 的 options 可以優先調用, 因此我花費了大量的時間。
再次提醒,備份,備份,備份。
在這個過程中我有好幾次膽戰心驚地誤刪了目標的文件,都是靠備份救回來的。
比如我一開始不太熟悉 impermanence,
直接 nixos-rebuild
把空目錄掛載到目標上去,
目標目錄直接變空;
或者有好幾次我直接刪掉了本來應該放在 /persist 和 /storage 的文件。
備份,備份,備份。
利用 NixOS LiveCD 切換根目錄文件系統為 tmpfs
等硬盤的 layout 符合我的預期以後,接下來就是把冰箱門關起來了。
然而因為我用了很多 flake 模組,
導致 nixos-rebuild
(以下簡稱 rebuild
)
的過程本身就經歷了一個疊床架屋的運行過程:
-
rebuild
會調用我本地 Git 倉庫的 flake -
然後將我 sops-nix 模組配置的密碼利用 放在家目錄的私鑰 解密到
/run/secrets-rendered
-
最後當構建完之後 lanzaboote 模組調用
/etc/secureboot
對新的啟動項進行簽名,以符合 secureboot 的需求。
以下是一些在 LiveCD 環境下,從 non-tmpfs 切換到 tmpfs 的踩坑的記錄, 不包括前置的作業流程,比如挑選需要保留的文件, 這個挑選過程請看上文的「愚公移山」一節。
-
在僅僅有
home
和nix
目錄的情況下nixos-enter
(以下簡稱enter
)沒有辦法在/mnt
成功, 提示 not a NixOS installation。解決方式是在
/mnt/etc/NIXOS
創建一個空文件。 -
rebuild
本身的問題是會用到$SUDO_USER
然後提示說無法使用 PAM authentication。解決方式是把這個環境變量刪掉。
-
基於 flake 的
rebuild
會因為找不到 git 報錯。解決方式是直接開一個有 git 的 nix-shell 。
nix-shell -p git
-
因為
enter
給的環境的 hostname 變成了nixos
, 所以直接調用rebuild
會提示找不到 flake。解決方式是要么改 hostname,要么改 flake。
hostname <your_name>
-
使用 impermanence 模組的時候要特別注意一點,如果原來規定的目錄 (比如
/persist
)要改成其他目錄(比如/nix/persist
)的時候, 由於enter
調用的是我既有的系統構建,所以 impermanence 的掛載會失敗, 在rebuild
過程中必要的文件需要手動複製或者掛載。 -
在
rebuild
過程配置 secureboot 的時候,會因為前述第 5 點無法裝載 lanzaboote 模組對目標內核簽名的文件, 導致沒有辦法構建成功並灌到 bootloader 裡去[1]。 解決辦法是cp -ar /nix/persist/etc/secureboot/ /etc/. # -a 代表保留權限信息
順帶一提,這個花了我快一個小時。 報錯根本屁用沒有,提示文件找不到卻不告訴我哪個文件找不到, 我只能用 strace 一個個去看,修的過程 LiveCD 還炸了, 於是前面第 1-6 點我還要再弄一遍。
-
系統在掛載
tmpfs
的時候無法接受default
選項, 會直接卡死在 NixOS stage 1。解決方法是把該死的
default
從 disko 模組的配置裡刪掉。順帶一提,這個也花了我一個多小時,因為要進 LiveCD 改配置的前置作業很麻煩, 前面 1-6 點都要解決,而且 LiveCD 裝載得慢得要死。
-
在
enter
時由於我還有 sops-nix 模組用來加載加密信息, 而又由於前述第 5 點無法裝載~/.ssh
,會導致報錯。 雖然還是可以進enter
的chroot
環境, 但我把nix.conf
的 GitHub API Token 也給 sops-nix 管理了, 如果需要互聯網可能就比較麻煩,這個時候解決這個問題就需要手動複製密鑰過去。cp -ar /nix/persist/home/shinri/.ssh/ /home/shinri
後記
這是我最 nix 頭腦體操的一次經歷了。
從發想到完整實行,時間跨度竟然有一個多月,
再加上這週集中排查 /var
/etc
~/.config
~/.local
,
再調了一下沒怎麼用過的 plasma-manager 模組,
簡直是一石激起千層浪。
不過好在終於把我既有的東西都搬到這裡了,也避免了重裝系統。 在這個過程中找資料的時候沒有看到解決類似問題的文章, 因此我把這個折騰的過程寫在這裡供想要轉向 tmpfs on root 同時又不想重裝系統的讀者參考。
如果你有問題,歡迎通過 Matrix 與我聯繫:
@shinri:zince.net
。
如果只是想打個招呼、混個臉熟也非常歡迎!
感謝
感謝 @olmo:chillin.gs
和 @2xsaiko:tchncs.de
這兩位
在 NixOS 的 Matrix 群組幫助我解答部分疑惑。
I want to thank @olmo:chillin.gs
and @2xsaiko:tchncs.de
for helping me initiating the idea and solving my questions.
感謝「gyara」向我推薦 tmpfs-on-root 的做法, 並持續幫助我, 在這個過程中為我答疑解惑。 我的NixOS 配置有很多 參考了他的配置。
评论
登录后方可评论
登录/注册