獨立博客閱讀: https://thiscute.world/posts/nixos-and-flake-basics/
長文警告??
(資料圖)
目錄零、為什么選擇 Nix一、Nix 簡介Nix 的優(yōu)點Nix 的缺點簡單總結(jié)下二、安裝三、Nix Flakes 與舊的 Nix四、NixOS 的 Flakes 包倉庫五、Nix 語言基礎1. 基礎數(shù)據(jù)類型一覽2. let ... in ...3. attribute set 說明4. with 語句5. 繼承 inherit ...6. ${ ... } 字符串插值7. 文件系統(tǒng)路徑8. 搜索路徑9. 多行字符串10. 函數(shù)內(nèi)置函數(shù)import 表達式pkgs.lib 函數(shù)包11. 不純(Impurities)12. Fetchers13. Derivations六、以聲明式的方式管理系統(tǒng)1. 使用本文的目標 NixOS 版本為 22.11,Nix 版本為 2.13.3,在此環(huán)境下 Nix Flakes 仍然為實驗性功能。
/etc/nixos/configuration.nix
配置系統(tǒng)2. 啟用 NixOS 的 Flakes 支持3. 將系統(tǒng)配置切換到 flake.nix4. 通過 Flakes 來管理系統(tǒng)軟件5. 為 Flake 添加國內(nèi) cache 源6. 安裝 home-manager7. 模塊化 NixOS 配置8. 更新系統(tǒng)9. 回退個別軟件包的版本10. 使用 Git 管理 NixOS 配置11. 查看與清理歷史數(shù)據(jù)七、Nix Flakes 的使用1. Flake 的 inputs2. Flake 的 outputs3. Flake 命令行的使用八、Nixpkgs 的高級用法OverridingOverlays模塊化 overlays 配置進階玩法總結(jié)參考零、為什么選擇 Nix好幾年前就聽說過 Nix 包管理器,它用 Nix 語言編寫配置來管理系統(tǒng)依賴,此外基于 Nix 包管理器設計的 Linux 發(fā)行版 NixOS,還能隨時回滾到任一歷史狀態(tài)。雖然聽著很牛,但是不僅要多學一門語言,裝個包還得寫代碼,當時覺得太麻煩就沒研究。但是最近搞系統(tǒng)遷移遇到兩件麻煩事,使我決定嘗試下 Nix.
第一件事是在新組裝的 PC 主機上安裝 EndeavourOS(Arch Linux 的一個衍生發(fā)行版),因為舊系統(tǒng)也是 EndeavourOS 系統(tǒng),安裝完為了省事,我就直接把舊電腦的 Home 目錄 rsync 同步到了新 PC 上。這一同步就出了問題,所有功能都工作正常,但是視頻播放老是卡住,firefox/chrome/mpv 都會卡住,網(wǎng)上找各種資料都沒解決,還是我靈光一閃想到是不是 Home 目錄同步的鍋,清空了 Home 目錄,問題立馬就解決了...后面又花好長時間從舊電腦一點點恢復 Home 目錄下的東西。
第二件事是,我最近想嘗鮮 wayland,把桌面從 i3wm 換成了 sway,但是因為用起來區(qū)別不明顯,再加上諸多不便(hidpi、sway 配置調(diào)優(yōu)都要花時間精力),嫌麻煩就還是回退到了 i3wm。結(jié)果回退后,每次系統(tǒng)剛啟動時,有一段時間 firefox/thunar 等 GUI 程序會一直卡著,要大概 1 分鐘后才能正常啟動...
發(fā)生第二件事時我就懶得折騰了,想到歸根結(jié)底還是系統(tǒng)沒有版本控制跟回滾機制,導致出了問題不能還原,裝新系統(tǒng)時各種軟件包也全靠自己手工從舊機器導出軟件包清單,再在新機器安裝恢復。就打算干脆換成 NixOS.
我折騰的第一步是在我 Homelab 上開了臺 NixOS 虛擬機,在這臺虛擬機里一步步調(diào)試,把我物理機的 EndeavourOS i3 配置遷移到 NixOS + Flakes,還原出了整個桌面環(huán)境。在虛擬機里搞定后問題就不大了,直接備份好我辦公電腦的 Home 目錄、軟件清單,然后將系統(tǒng)重裝為 NixOS,再 git clone 我調(diào)試好的 NixOS 配置,改一改硬盤掛載相關的參數(shù),額外補充下 Nvidia 顯卡相關的 NixOS 配置,最后一行命令部署配置。幾行命令就在我全新的 NixOS 系統(tǒng)上還原出了整個 i3 桌面環(huán)境跟我的常用軟件,那一刻真的很有成就感!
NixOS 的回滾能力給了我非常大的底氣——再也不怕把系統(tǒng)搞掛了,于是我前幾天我又進一步遷移到了 hyprland 桌面,確實比 i3 香多了,它的動畫效果我吹爆!(在以前 EndeavourOS 上我肯定是不太敢做這樣的切換的,原因前面已經(jīng)解釋過了——萬一把系統(tǒng)搞出問題,會非常麻煩。)
補充:v2ex 上有 v 友反饋 btrfs 文件系統(tǒng)的快照功能,也能提供類似的回滾能力,而且簡單很多。我研究了下發(fā)現(xiàn)確實如此,btrfs 甚至也可以像 NixOS 一樣配置 grub 從快照啟動。所以如果你只是想要系統(tǒng)回滾能力,那么基于 btrfs 快照功能的 btrbk 也是一個不錯的選擇。當然如果你仍然對 Nix 感興趣,那學一學也絕對不虧,畢竟 Nix 的能力遠不止于此,系統(tǒng)快照只是它能力的一部分而已~
在學了大半個月的 NixOS 與 Nix Flakes 后,我終于將我的 PC 從 EndeavouOS 系統(tǒng)切換到了 NixOS,這篇文章就脫胎于我這段時間的折騰筆記,希望能對你有所幫助~
前因后果交代完畢,那么下面開始正文!
一、Nix 簡介Nix 包管理器,跟 DevOps 領域當前流行的 pulumi/terraform/kubernetes 類似,都是聲明式配置管理工具,用戶需要在某些配置文件中聲明好期望的系統(tǒng)狀態(tài),而 Nix 負責達成目標。區(qū)別在于 Nix 的管理目標是軟件包,而 pulumi/terraform 的管理目標是云上資源。
簡單解釋下什么是「聲明式配置」,它是指用戶只需要聲明好自己想要的結(jié)果——比如說希望將 i3 桌面替換成 sway 桌面,Nix 就會幫用戶達成這個目標。用戶不需要關心底層細節(jié)(比如說 sway 需要安裝哪些軟件包,哪些 i3 相關的軟件包需要卸載掉,哪些系統(tǒng)配置或環(huán)境變量需要針對 sway 做調(diào)整、如果使用了 Nvidia 顯卡 Sway 參數(shù)要做什么調(diào)整才能正常運行等等),Nix 會自動幫用戶處理這些細節(jié)。
基于 Nix 構建的 Linux 發(fā)行版 NixOS,可以簡單用 OS as Code 來形容,它通過聲明式的 Nix 配置文件來描述整個操作系統(tǒng)的狀態(tài)。
NixOS 的配置只負責管理系統(tǒng)層面的狀態(tài),用戶目錄不受它管轄。有另一個重要的社區(qū)項目 home-manager 專門用于管理用戶目錄,將 home-manager 與 NixOS、Git 結(jié)合使用,就可以得到一個完全可復現(xiàn)、可回滾的系統(tǒng)環(huán)境。
因為 Nix 聲明式、可復現(xiàn)的特性,Nix 不僅可用于管理桌面電腦的環(huán)境,也有很多人用它管理開發(fā)編譯環(huán)境、云上虛擬機、容器鏡像構建,Nix 官方的 NixOps 與社區(qū)的 deploy-rs 都是基于 Nix 實現(xiàn)的運維工具。
Nix 的優(yōu)點聲明式配置,Environment as Code,可以直接用 Git 管理配置,只要配置文件不丟,系統(tǒng)就可以隨時還原到任一歷史狀態(tài)(理想情況下)。這跟一些編程語言中 cargo.lock/go.mod 等文件鎖定依賴庫版本以確保構建結(jié)果可復現(xiàn)的思路是一致的。與 Docker 相比,Dockerfile 實際是命令式的配置,而且也不存在版本鎖這樣的東西,所以 Docker 的可復現(xiàn)能力遠不如 Nix.高度便捷的系統(tǒng)自定義能力通過改幾行配置,就可以簡單地更換系統(tǒng)的各種組件。這是因為 Nix 將底層的復雜操作全部封裝在了 nix package 包中,只給用戶提供了簡潔且必要的聲明式參數(shù)。而且這種修改非常安全,例證之一是有 v 友表示「nixos 切換不同桌面非常簡單干凈,而且很安全,我就經(jīng)常 gnome/kde/sway 來回換著用。」可回滾:可以隨時回滾到任一歷史環(huán)境,NixOS 甚至默認將所有舊版本都加入到了啟動項,確保系統(tǒng)滾掛了也能隨時回退。所以 Nix 也被認為是最穩(wěn)定的包管理方式。沒有依賴沖突問題:因為 Nix 中每個軟件包都擁有唯一的 hash,其安裝路徑中也會包含這個 hash 值,因此可以多版本共存。社區(qū)很活躍,第三方項目也挺豐富,官方包倉庫 nixpkgs 貢獻者眾多,也有很多人分享自己的 Nix 配置,一遍瀏覽下來,整個生態(tài)給我一種發(fā)現(xiàn)新大陸的興奮感。Nix 的缺點學習成本高:如果你希望系統(tǒng)完全可復現(xiàn),并且避免各種不當使用導致的坑,那就需要學習了解 Nix 的整個設計,并以聲明式的方式管理系統(tǒng),不能無腦Home 目錄下文件眾多,行為也不一,因此不可能對其中的所有文件進行版本控制,代價太高。一般僅使用 home-manager 管理一些重要的配置文件,而其他需要備份的文件可以用 rsync/synthing 等手段做備份同步,或者用 btrbk 之類的工具對 home 目錄做快照。
nix-env -i
(這類似 apt-get install
)。文檔混亂:首先 Nix Flakes 目前仍然是實驗性特性,介紹它本身的文檔目前比較匱乏。 其次 Nix 社區(qū)絕大多數(shù)文檔都只介紹了舊的 nix-env
/nix-channel
,想直接從 Nix Flakes 開始學習的話,需要參考大量舊文檔,從中提取出自己需要的內(nèi)容。另外一些 Nix 當前的核心功能,官方文檔都語焉不詳(比如 imports
跟 Nixpkgs Module System),想搞明白基本只能看源碼了...nix-collect-garbage
來手動清理舊的歷史環(huán)境,也還是建議配置個更大的硬盤...報錯信息比較隱晦:一般的報錯提示還是比較清楚的,但是遇到好幾次依賴版本有問題或者傳參錯誤提示不出原因,--show-trace
直接輸出一堆的內(nèi)部堆棧,都花了很長時間才定位到,通過升級依賴版本或者修正參數(shù)后問題解決。猜測導致這個問題的原因有兩個,一是 Nix 是動態(tài)語言,各種參數(shù)都是運行時才確定類型。二是我用到的 flake 包的錯誤處理邏輯寫得不太好,錯誤提示不清晰,一些隱晦的錯誤甚至通過錯誤堆棧也定位不到原因。簡單總結(jié)下總的來說,我覺得 NixOS 適合那些有一定 Linux 使用經(jīng)驗與編程經(jīng)驗,并且希望對自己的系統(tǒng)擁有更強掌控力的開發(fā)者。
另外一條信息:在開發(fā)環(huán)境搭建方面 Nix 與相對流行的 Dev Containers 也有些競爭關系,它們的具體區(qū)別還有待我發(fā)掘~
二、安裝Nix 有多種安裝方式,支持以包管理器的形式安裝到 MacOS/Linux/WSL 三種系統(tǒng)上,Nix 還額外提供了 NixOS ——一個使用 Nix 管理整個系統(tǒng)環(huán)境的 Linux 發(fā)行版。
我選擇了直接使用 NixOS 的 ISO 鏡像安裝 NixOS 系統(tǒng),從而最大程度上通過 Nix 管理整個系統(tǒng)的環(huán)境。
安裝很簡單,這里不多介紹,僅列一下我覺得比較有用的參考資料:
國內(nèi)鏡像源說明:https://mirrors.bfsu.edu.cn/help/nix/Nix 的官方安裝方式: 使用 bash 腳本編寫, 目前(2023-04-23)為止nix-command
& flakes
仍然是實驗性特性,需要手動開啟。你需要參照 Enable flakes - NixOS Wiki 的說明啟用 nix-command
& flakes
官方不提供任何卸載手段,要在 Linux/MacOS 上卸載 Nix,你需要手動刪除所有相關的文件、用戶以及用戶組The Determinate Nix Installer: 第三方使用 Rust 編寫的 installer, 默認啟用 nix-command
& flakes
,并且提供了卸載命令。三、Nix Flakes 與舊的 NixNix 于 2020 年推出了 nix-command
& flakes
兩個新特性,它們提供了全新的命令行工具、標準的 Nix 包結(jié)構定義、類似 cargo/npm 的 flake.lock
版本鎖文件等等。這兩個特性極大地增強了 Nix 的能力,因此雖然至今(2023/5/5)它們?nèi)匀皇菍嶒炐蕴匦裕且呀?jīng)被 Nix 社區(qū)廣泛使用,是強烈推薦使用的功能。
目前 Nix 社區(qū)的絕大多數(shù)文檔仍然只介紹了傳統(tǒng) Nix,不包含 Nix Flakes 相關的內(nèi)容,但是從可復現(xiàn)、易于管理維護的角度講,舊的 Nix 包結(jié)構與命令行工具已經(jīng)不推薦使用了,因此本文檔也不會介紹舊的 Nix 包結(jié)構與命令行工具的使用方法,也建議新手直接忽略掉這些舊的內(nèi)容,從 nix-command
& flakes
學起。
這里列舉下在 nix-command
& flakes
中已經(jīng)不需要用到的舊的 Nix 命令行工具與相關概念,在查找資料時,如果看到它們直接忽略掉就行:
nix-channel
: nix-channel 與 apt/yum/pacman 等其他 Linux 發(fā)行版的包管理工具類似,通過 stable/unstable/test 等 channel 來管理軟件包的版本。Nix Flakes 在 flake.nix 中通過 inputs 聲明依賴包的數(shù)據(jù)源,通過 flake.lock 鎖定依賴版本,完全取代掉了 nix-channel 的功能。nix-env
: 用于管理用戶環(huán)境的軟件包,是傳統(tǒng) Nix 的核心命令行工具。它從 nix-channel 定義的數(shù)據(jù)源中安裝軟件包,所以安裝的軟件包版本受 channel 影響。通過 nix-env
安裝的包不會被自動記錄到 Nix 的聲明式配置中,是完全脫離掌控的,無法在其他主機上復現(xiàn),因此不推薦使用。在 Nix Flakes 中對應的命令為 nix profile
nix-shell
: nix-shell 用于創(chuàng)建一個臨時的 shell 環(huán)境在 Nix Flakes 中它被 nix develop
與 nix shell
取代了。nix-build
: 用于構建 Nix 包,它會將構建結(jié)果放到 /nix/store
路徑下,但是不會記錄到 Nix 的聲明式配置中。在 Nix Flakes 中對應的命令為 nix build
...四、NixOS 的 Flakes 包倉庫跟 Arch Linux 類似,Nix 也有官方與社區(qū)的軟件包倉庫:
nixpkgs 是一個包含了所有 Nix 包與 NixOS 模塊/配置的 Git 倉庫,其 master 分支包含最新的 Nix 包與 NixOS 模塊/配置。比如 qq 就直接包含在 nixpkgs 中了NUR: 類似 Arch Linux 的 AUR,NUR 是 Nix 的一個第三方的 Nix 包倉庫,算是 nixpkgs 的一個增補包倉庫。這些常用國產(chǎn)軟件,都可以通過 NUR 安裝:qqmusicwechat-uosdingtalk更多程序,可以在這里搜索:Nix User RepositoriesNix Flakes 也可直接從 Git 倉庫中安裝軟件包,這種方式可以用于安裝任何人提供的 Flakes 包此外一些沒有 Nix 支持或者支持不佳的軟件,也可以考慮通過 Flatpak 或者 AppImage 的方式安裝使用,這兩個都是在所有 Linux 發(fā)行版上可用的軟件打包與安裝手段,詳情請自行搜索,這里就不介紹細節(jié)了。
五、Nix 語言基礎https://nix.dev/tutorials/first-steps/nix-language
Nix 語言是 Nix 的基礎,要想玩得轉(zhuǎn) NixOS 與 Nix Flakes,享受到它們帶來的諸多好處,就必須學會這門語言。
Nix 是一門比較簡單的函數(shù)式語言,在已有一定編程基礎的情況下,過一遍這些語法用時應該在 2 個小時以內(nèi),本文假設你具有一定編程基礎(也就是說寫得不會很細)。
這一節(jié)主要包含如下內(nèi)容:
數(shù)據(jù)類型let...in... with inherit 等特殊語法函數(shù)的聲明與調(diào)用語法內(nèi)置函數(shù)與庫函數(shù)inputs 的不純性(Impurities)用于描述 Build Task 的 DerivationOverriding 與 Overlays...先把語法過一遍,有個大概的印象就行,后面需要用到時再根據(jù)右側(cè)目錄回來復習。
1. 基礎數(shù)據(jù)類型一覽下面通過一個 attribute set (這類似 json 或者其他語言中的 map/dict)來簡要說明所有基礎數(shù)據(jù)類型:
{ string = "hello"; integer = 1; float = 3.141; bool = true; null = null; list = [ 1 "two" false ]; attribute-set = { a = "hello"; b = 2; c = 2.718; d = false; }; # comments are supported}
以及一些基礎操作符(普通的算術運算、布爾運算就跳過不介紹了):
# 列表拼接[ 1 2 3 ] ++ [ 4 5 6 ] # [ 1 2 3 4 5 6 ]# 將 // 后面的 attribut set 中的內(nèi)容,全部更新到 // 前面的 attribute set 中{ a = 1; b = 2; } // { b = 3; c = 4; } # 結(jié)果為 { a = 1; b = 3; c = 4; }# 邏輯隱含,等同于 !b1 || b2.bool -> bool
2. let ... in ...Nix 的 let ... in ...
語法被稱作「let 表達式」或者「let 綁定」,它用于創(chuàng)建臨時使用的局部變量:
let a = 1;ina + a # 結(jié)果是 2
let 表達式中的變量只能在 in
之后的表達式中使用,理解成臨時變量就行。
花括號 {}
用于創(chuàng)建 attribute set,也就是 key-value 對的集合,類似于 JSON 中的對象。
attribute set 默認不支持遞歸引用,如下內(nèi)容會報錯:
{ a = 1; b = a + 1; # error: undefined variable "a"}
不過 Nix 提供了 rec
關鍵字(recursive attribute set),可用于創(chuàng)建遞歸引用的 attribute set:
rec { a = 1; b = a + 1; # ok}
在遞歸引用的情況下,Nix 會按照聲明的順序進行求值,所以如果 a
在 b
之后聲明,那么 b
會報錯。
可以使用 .
操作符來訪問 attribute set 的成員:
let a = { b = { c = 1; }; };ina.b.c # 結(jié)果是 1
.
操作符也可直接用于賦值:
{ a.b.c = 1; }
此外 attribute set 還支持一個 has attribute 操作符,它可用于檢測 attribute set 中是否包含某個屬性,返回 bool 值:
let a = { b = { c = 1; }; };ina?b # 結(jié)果是 true,因為 a.b 這個屬性確實存在
has attribute 操作符在 nixpkgs 庫中常被用于檢測處理 args?system
等參數(shù),以 (args?system)
或 (! args?system)
的形式作為函數(shù)參數(shù)使用(嘆號表示對 bool 值取反,是常見 bool 值運算符)。
with 語句的語法如下:
with ;
with
語句會將
中的所有成員添加到當前作用域中,這樣在
中就可以直接使用
中的成員了,簡化 attribute set 的訪問語法,比如:
let a = { x = 1; y = 2; z = 3; };inwith a; [ x y z ] # 結(jié)果是 [ 1 2 3 ], 等價于 [ a.x a.y a.z ]
5. 繼承 inherit ...inherit
語句用于從 attribute set 中繼承成員,同樣是一個簡化代碼的語法糖,比如:
let x = 1; y = 2;in{ inherit x y;} # 結(jié)果是 { x = 1; y = 2; }
inherit 還能直接從某個 attribute set 中繼承成員,語法為 inherit (
,比如:
let a = { x = 1; y = 2; z = 3; };in{ inherit (a) x y;} # 結(jié)果是 { x = 1; y = 2; }
6. ${ ... } 字符串插值${ ... }
用于字符串插值,懂點編程的應該都很容易理解這個,比如:
let a = 1;in"the value of a is ${a}" # 結(jié)果是 "the value of a is 1"
7. 文件系統(tǒng)路徑Nix 中不帶引號的字符串會被解析為文件系統(tǒng)路徑,路徑的語法與 Unix 系統(tǒng)相同。
8. 搜索路徑請不要使用這個功能,它會導致不可預期的行為。
Nix 會在看到
這類三角括號語法時,會在 NIX_PATH
環(huán)境變量中指定的路徑中搜索該路徑。
因為環(huán)境變量 NIX_PATH
是可變更的值,所以這個功能是不純的,會導致不可預期的行為。
在這里做個介紹,只是為了讓你在看到別人使用類似的語法時不至于抓瞎。
9. 多行字符串多行字符串的語法為 ""
,比如:
"" this is a multi-line string""
10. 函數(shù)函數(shù)的聲明語法為:
:
舉幾個常見的例子:
# 單參數(shù)函數(shù)a: a + a# 嵌套函數(shù)a: b: a + b# 雙參數(shù)函數(shù){ a, b }: a + b# 雙參數(shù)函數(shù),帶默認值。問號后面的是參數(shù)的默認值{ a ? 1, b ? 2 }: a + b# 帶有命名 attribute set 作為參數(shù)的函數(shù),并且使用 ... 收集其他可選參數(shù)# 命名 args 與 ... 可選參數(shù)通常被一起作為函數(shù)的參數(shù)定義使用args@{ a, b, ... }: a + b + args.c# 如下內(nèi)容等價于上面的內(nèi)容,{ a, b, ... }@args: a + b + args.c# 但是要注意命名參數(shù)僅綁定了輸入的 attribute set,默認參數(shù)不在其中,舉例let f = { a ? 1, b ? 2, ... }@args: argsin f {} # 結(jié)果是 {},也就說明了 args 中包含默認值# 函數(shù)的調(diào)用方式就是把參數(shù)放在后面,比如下面的 2 就是前面這個函數(shù)的參數(shù)a: a + a 2 # 結(jié)果是 4# 還可以給函數(shù)命名,不過必須使用 let 表達式let f = a: a + a;in f 2 # 結(jié)果是 4
內(nèi)置函數(shù)Nix 內(nèi)置了一些函數(shù),可通過 builtins.
來調(diào)用,比如:
builtins.add 1 2 # 結(jié)果是 3
詳細的內(nèi)置函數(shù)列表參見 Built-in Functions - Nix Reference Mannual
import 表達式import
表達式以其他 Nix 文件的路徑作為參數(shù),返回該 Nix 文件的執(zhí)行結(jié)果。
import
的參數(shù)如果為文件夾路徑,那么會返回該文件夾下的 default.nix
文件的執(zhí)行結(jié)果。
舉個例子,首先創(chuàng)建一個 file.nix
文件:
$ echo "x: x + 1" > file.nix
然后使用 import 執(zhí)行它:
import ./file.nix 1 # 結(jié)果是 2
pkgs.lib 函數(shù)包除了 builtins 之外,Nix 的 nixpkgs 倉庫還提供了一個名為 lib
的 attribute set,它包含了一些常用的函數(shù),它通常被以如下的形式被使用:
let pkgs = import {};inpkgs.lib.strings.toUpper "search paths considered harmful" # 結(jié)果是 "SEARCH PATHS CONSIDERED HARMFUL"
可以通過 Nixpkgs Library Functions - Nixpkgs Manual 查看 lib 函數(shù)包的詳細內(nèi)容。
11. 不純(Impurities)Nix 語言本身是純函數(shù)式的,是純的,「純」是指它就跟數(shù)學中的函數(shù)一樣,同樣的輸入永遠得到同樣的輸出。
Nix 有兩種構建輸入,一種是從文件系統(tǒng)路徑等輸入源中讀取文件,另一種是將其他函數(shù)作為輸入。
Nix 唯一的不純之處在這里:從文件系統(tǒng)路徑或者其他輸入源中讀取文件作為構建任務的輸入,這些輸入源參數(shù)可能沒變化,但是文件內(nèi)容或數(shù)據(jù)源的返回內(nèi)容可能會變化,這就會導致輸入相同,Nix 函數(shù)的輸出卻可能不同——函數(shù)變得不純了。
12. FetchersNix 中的搜索路徑與
builtins.currentSystem
也是不純的,但是這兩個功能都不建議使用,所以這里略過了。
構建輸入除了直接來自文件系統(tǒng)路徑之外,還可以通過 Fetchers 來獲取,F(xiàn)etcher 是一種特殊的函數(shù),它的輸入是一個 attribute set,輸出是 Nix Store 中的一個系統(tǒng)路徑。
Nix 提供了四個內(nèi)置的 Fetcher,分別是:
builtins.fetchurl
:從 url 中下載文件builtins.fetchTarball
:從 url 中下載 tarball 文件builtins.fetchGit
:從 git 倉庫中下載文件builtins.fetchClosure
:從 Nix Store 中獲取 Derivation舉例:
builtins.fetchurl "https://github.com/NixOS/nix/archive/7c3ab5751568a0bc63430b33a5169c5e4784a0ff.tar.gz"# result example => "/nix/store/7dhgs330clj36384akg86140fqkgh8zf-7c3ab5751568a0bc63430b33a5169c5e4784a0ff.tar.gz"builtins.fetchTarball "https://github.com/NixOS/nix/archive/7c3ab5751568a0bc63430b33a5169c5e4784a0ff.tar.gz"# result example(auto unzip the tarball) => "/nix/store/d59llm96vgis5fy231x6m7nrijs0ww36-source"
13. Derivations一個構建動作的 Nix 語言描述被稱做一個 Derivation,它描述了如何構建一個軟件包,它的構建結(jié)果是一個 Store Object
Store Object 的存放路徑格式為 /nix/store/
,其中
是構建結(jié)果的 hash 值,
是它的名字。路徑 hash 值確保了每個構建結(jié)果都是唯一的,因此可以多版本共存,而且不會出現(xiàn)依賴沖突的問題。
/nix/store
被稱為 Store,存放所有的 Store Objects,這個路徑被設置為只讀,只有 Nix 本身才能修改這個路徑下的內(nèi)容,以保證系統(tǒng)的可復現(xiàn)性。
在 Nix 語言的最底層,一個構建任務就是使用 builtins 中的不純函數(shù) derivation
創(chuàng)建的,我們實際使用的 stdenv.mkDerivation
就是它的一個 wrapper,屏蔽了底層的細節(jié),簡化了用法。
https://nixos.wiki/wiki/Overview_of_the_NixOS_Linux_distribution
了解了 Nix 語言的基本用法之后,我們就可以開始使用 Nix 語言來配置 NixOS 系統(tǒng)了。NixOS 的系統(tǒng)配置路徑為 /etc/nixos/configuration.nix
,它包含系統(tǒng)的所有聲明式配置,如時區(qū)、語言、鍵盤布局、網(wǎng)絡、用戶、文件系統(tǒng)、啟動項等。
如果想要以可復現(xiàn)的方式修改系統(tǒng)的狀態(tài)(這也是最推薦的方式),就需要手工修改 /etc/nixos/configuration.nix
文件,然后執(zhí)行 sudo nixos-rebuild switch
命令來應用配置,此命令會根據(jù)配置文件生成一個新的系統(tǒng)環(huán)境,并將新的環(huán)境設為默認環(huán)境。同時上一個系統(tǒng)環(huán)境會被保留,而且會被加入到 grub 的啟動項中,這確保了即使新的環(huán)境不能啟動,也能隨時回退到舊環(huán)境。
另一方面,/etc/nixos/configuration.nix
是傳統(tǒng)的 Nix 配置方式,它依賴 nix-channel
配置的數(shù)據(jù)源,也沒有任何版本鎖定機制,實際無法確保系統(tǒng)的可復現(xiàn)性。更推薦使用的是 Nix Flakes,它可以確保系統(tǒng)的可復現(xiàn)性,同時也可以很方便地管理系統(tǒng)的配置。
我們下面首先介紹下通過 NixOS 默認的配置方式來管理系統(tǒng),然后再過渡到更先進的 Nix Flakes.
1. 使用/etc/nixos/configuration.nix
配置系統(tǒng)前面提過了這是傳統(tǒng)的 Nix 配置方式,也是當前 NixOS 默認使用的配置方式,它依賴 nix-channel
配置的數(shù)據(jù)源,也沒有任何版本鎖定機制,實際無法確保系統(tǒng)的可復現(xiàn)性。
簡單起見我們先使用這種方式來配置系統(tǒng),后面會介紹 Flake 的使用。
比如要啟用 ssh 并添加一個用戶 ryan,只需要在 /etc/nixos/configuration.nix
中添加如下配置:
# Edit this configuration file to define what should be installed on# your system. Help is available in the configuration.nix(5) man page# and in the NixOS manual (accessible by running ‘nixos-help’).{ config, pkgs, ... }:{ imports = [ # Include the results of the hardware scan. ./hardware-configuration.nix ]; # 省略掉前面的配置...... # 新增用戶 ryan users.users.ryan = { isNormalUser = true; description = "ryan"; extraGroups = [ "networkmanager" "wheel" ]; openssh.authorizedKeys.keys = [ # replace with your own public key "ssh-ed25519 ryan@ryan-pc" ]; packages = with pkgs; [ firefox # thunderbird ]; }; # 啟用 OpenSSH 后臺服務 services.openssh = { enable = true; permitRootLogin = "no"; # disable root login passwordAuthentication = false; # disable password login openFirewall = true; forwardX11 = true; # enable X11 forwarding }; # 省略其他配置......}
這里我啟用了 openssh 服務,為 ryan 用戶添加了 ssh 公鑰,并禁用了密碼登錄。
現(xiàn)在運行 sudo nixos-rebuild switch
部署修改后的配置,之后就可以通過 ssh 密鑰遠程登錄到我的這臺主機了。
這就是 NixOS 默認的聲明式系統(tǒng)配置,要對系統(tǒng)做任何可復現(xiàn)的變更,都只需要修改 /etc/nixos/configuration.nix
文件,然后運行 sudo nixos-rebuild switch
部署變更即可。
/etc/nixos/configuration.nix
的所有配置項,可以在這幾個地方查到:
Chrome NixOS
就能找到 Chrome 相關的配置項,一般 NixOS Wiki 或 nixpkgs 倉庫源碼的排名會比較靠前。在 NixOS Options Search 中搜索關鍵字系統(tǒng)級別的配置,可以考慮在 Configuration - NixOS Manual 找找相關文檔直接在 nixpkgs 倉庫中搜索關鍵字,讀相關的源碼。2. 啟用 NixOS 的 Flakes 支持與 NixOS 默認的配置方式相比,Nix Flakes 提供了更好的可復現(xiàn)性,同時它清晰的包結(jié)構定義原生支持了以其他 Git 倉庫為依賴,便于代碼分享,因此更建議使用 Nix Flakes 來管理系統(tǒng)配置。
但是目前 Nix Flakes 作為一個實驗性的功能,仍未被默認啟用。所以我們需要手動啟用它,修改 /etc/nixos/configuration.nix
文件,在函數(shù)塊中啟用 flakes 與 nix-command 功能:
# Edit this configuration file to define what should be installed on# your system. Help is available in the configuration.nix(5) man page# and in the NixOS manual (accessible by running ‘nixos-help’).{ config, pkgs, ... }:{ imports = [ # Include the results of the hardware scan. ./hardware-configuration.nix ]; # 省略掉前面的配置...... # 啟用 Nix Flakes 功能,以及配套的新 nix-command 命令行工具 nix.settings.experimental-features = [ "nix-command" "flakes" ]; environment.systemPackages = with pkgs; [ git # Nix Flakes 通過 git 命令從數(shù)據(jù)源拉取依賴,所以必須先安裝好 git vim wget ]; # 省略其他配置......}
然后運行 sudo nixos-rebuild switch
應用修改后,即可使用 Nix Flakes 來管理系統(tǒng)配置。
額外還有個好處就是,現(xiàn)在你可以通過 nix repl
打開一個 nix 交互式環(huán)境,有興趣的話,可以使用它復習測試一遍前面學過的所有 Nix 語法。
在啟用了 Nix Flakes 特性后,sudo nixos-rebuild switch
命令會優(yōu)先讀取 /etc/nixos/flake.nix
文件,如果找不到再嘗試使用 /etc/nixos/configuration.nix
。
可以首先使用官方提供的模板來學習 flake 的編寫,先查下有哪些模板:
nix flake show templates
其中有個 templates#full
模板展示了所有可能的用法,可以看看它的內(nèi)容:
nix flake init -t templates#fullcat flake.nix
我們參照該模板創(chuàng)建文件 /etc/nixos/flake.nix
并編寫好配置內(nèi)容,后續(xù)系統(tǒng)的所有修改都將全部由 Nix Flakes 接管,示例內(nèi)容如下:
{ description = "Ryan"s NixOS Flake"; # 這是 flake.nix 的標準格式,inputs 是 flake 的依賴,outputs 是 flake 的輸出 # inputs 中的每一項依賴都會在被拉取、構建后,作為參數(shù)傳遞給 outputs 函數(shù) inputs = { # flake inputs 有很多種引用方式,應用最廣泛的是 github:owner/name/reference,即 github 倉庫地址 + branch/commit-id/tag # NixOS 官方軟件源,這里使用 nixos-unstable 分支 nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; # home-manager,用于管理用戶配置 home-manager = { url = "github:nix-community/home-manager/release-22.11"; # `follows` 是 inputs 中的繼承語法 # 這里使 sops-nix 的 `inputs.nixpkgs` 與當前 flake 的 `inputs.nixpkgs` 保持一致, # 避免依賴的 nixpkgs 版本不一致導致問題 inputs.nixpkgs.follows = "nixpkgs"; }; }; # outputs 即 flake 的所有輸出,其中的 nixosConfigurations 即 NixOS 系統(tǒng)配置 # 一個 flake 可以有很多用途,也可以有很多種不同的輸出,nixosConfigurations 只是其中一種 # # outputs 的參數(shù)都是 inputs 中定義的依賴項,可以通過它們的名稱來引用。 # 不過 self 是個例外,這個特殊參數(shù)指向 outputs 自身(自引用),以及 flake 根目錄 # 這里的 @ 語法將函數(shù)的參數(shù) attribute set 取了個別名,方便在內(nèi)部使用 outputs = { self, nixpkgs, ... }@inputs: { # 名為 nixosConfigurations 的 outputs 會在執(zhí)行 `sudo nixos-rebuild switch` 時被使用 # 默認情況下上述命令會使用與主機 hostname 同名的 nixosConfigurations # 但是也可以通過 `--flake /path/to/flake/direcotry#nixos-test` 來指定 # 在 flakes 配置文件夾中執(zhí)行 `sudo nixos-rebuild switch --flake .#nixos-test` 即可部署此配置 # 其中 `.` 表示使用當前文件夾的 Flakes 配置,`#` 后面的內(nèi)容則是 nixosConfigurations 的名稱 nixosConfigurations = { # hostname 為 nixos-test 的主機會使用這個配置 # 這里使用了 nixpkgs.lib.nixosSystem 函數(shù)來構建配置,后面的 attributes set 是它的參數(shù) # 在 nixos 系統(tǒng)上使用如下命令即可部署此配置:`nixos-rebuild switch --flake .#nixos-test` "nixos-test" = nixpkgs.lib.nixosSystem { system = "x86_64-linux"; # Nix 模塊系統(tǒng)可將配置模塊化,提升配置的可維護性 # # modules 中每個參數(shù),都是一個 Nix Module,nixpkgs manual 中有半份介紹它的文檔: # # 說半份是因為它的文檔不全,只有一些簡單的介紹(Nix 文檔現(xiàn)狀...) # Nix Module 可以是一個 attribute set,也可以是一個返回 attribute set 的函數(shù) # 如果是函數(shù),那么它的參數(shù)就是當前的 NixOS Module 的參數(shù). # 根據(jù) Nix Wiki 對 Nix modules 的描述,Nix modules 函數(shù)的參數(shù)可以有這幾個: # # lib: nixpkgs 自帶的函數(shù)庫,提供了許多操作 Nix 表達式的實用函數(shù) # 詳見 https://nixos.org/manual/nixpkgs/stable/#id-1.4 # config: 當前 flake 的所有 config 參數(shù)的集何 # options: 當前 flake 中所有 NixOS Modules 中定義的所有參數(shù)的集合 # pkgs: 一個包含所有 nixpkgs 包的集合 # 入門階段可以認為它的默認值為 `nixpkgs.legacyPackages."${system}"` # 可通過 `nixpkgs.pkgs` 這個 option 來自定義 pkgs 的值 # modulesPath: 默認 nixpkgs 的內(nèi)置 Modules 文件夾路徑,常用于從 nixpkgs 中導入一些額外的模塊 # 這個參數(shù)通常都用不到,我只在制作 iso 鏡像時用到過 # # 默認只能傳上面這幾個參數(shù),如果需要傳其他參數(shù),必須使用 specialArgs,你可以取消注釋如下這行來啟用該參數(shù) # specialArgs = inputs # 將 inputs 中的參數(shù)傳入所有子模塊 modules = [ # 導入之前我們使用的 configuration.nix,這樣舊的配置文件仍然能生效 # 注: /etc/nixos/configuration.nix 本身也是一個 Nix Module,因此可以直接在這里導入 ./configuration.nix ]; }; }; };}
這里我們定義了一個名為 nixos-test
的系統(tǒng),它的配置文件為 ./configuration.nix
,這個文件就是我們之前的配置文件,這樣我們?nèi)匀豢梢匝赜门f的配置。
現(xiàn)在執(zhí)行 sudo nixos-rebuild switch
應用配置,系統(tǒng)應該沒有任何變化,因為我們僅僅是切換到了 Nix Flakes,配置內(nèi)容與之前還是一致的。
切換完畢后,我們就可以通過 Flakes 來管理系統(tǒng)了。管系統(tǒng)最常見的需求就是裝軟件,我們在前面已經(jīng)見識過如何通過 environment.systemPackages
來安裝 pkgs
中的包,這些包都來自官方的 nixpkgs 倉庫。
現(xiàn)在我們學習下如何通過 Flakes 安裝其他來源的軟件包,這比直接安裝 nixpkgs 要靈活很多,最顯而易見的好處是你可以很方便地設定軟件的版本。以 helix 編輯器為例,我們首先需要在 flake.nix
中添加 helix 這個 inputs 數(shù)據(jù)源:
{ description = "NixOS configuration of Ryan Yin"; # ...... inputs = { # ...... # helix editor, use tag 23.05 helix.url = "github:helix-editor/helix/23.05" }; outputs = inputs@{ self, nixpkgs, ... }: { nixosConfigurations = { nixos-test = nixpkgs.lib.nixosSystem { system = "x86_64-linux"; # 將所有 inputs 參數(shù)設為所有子模塊的特殊參數(shù),這樣就能在子模塊中使用 helix 這個 inputs 了 specialArgs = inputs; modules = [ ./configuration.nix ]; }; }; };}
接下來在 configuration.nix
中就能引用這個 flake input 數(shù)據(jù)源了:
# Edit this configuration file to define what should be installed on# your system. Help is available in the configuration.nix(5) man page# and in the NixOS manual (accessible by running ‘nixos-help’).# Nix 會通過名稱匹配,自動將 specialArgs 中的 helix 注入到此函數(shù)的第三個參數(shù){ config, pkgs, helix, ... }:{ # 省略掉前面的配置...... environment.systemPackages = with pkgs; [ git # Nix Flakes 通過 git 命令從數(shù)據(jù)源拉取依賴,所以必須先安裝好 git vim wget # 這里從 helix 這個 inputs 數(shù)據(jù)源安裝了 helix 程序 helix."${pkgs.system}".packages.helix ]; # 省略其他配置......}
改好后再 sudo nixos-rebuild switch
部署,就能安裝好 helix 程序了,可直接在終端使用 helix
命令測試驗證。
Nix 為了加快包構建速度,提供了 https://cache.nixos.org 提前緩存構建結(jié)果提供給用戶,但是在國內(nèi)訪問這個 cache 地址非常地慢,如果沒有全局代理的話,基本上是無法使用的。另外 Flakes 的數(shù)據(jù)源基本都是某個 Github 倉庫,在國內(nèi)從 Github 下載 Flakes 數(shù)據(jù)源也同樣非常非常慢。
在舊的 NixOS 配置方式中,可以通過 nix-channel
命令添加國內(nèi)的 cache 鏡像源以提升下載速度,但是 Nix Flakes 會盡可能地避免使用任何系統(tǒng)級別的配置跟環(huán)境變量,以確保其構建結(jié)果不受環(huán)境的影響,因此在使用了 Flakes 后 nix-channel
命令就失效了。
為了自定義 cache 鏡像源,我們必須在 flake.nix
中添加相關配置,這就是 nixConfig
參數(shù),示例如下:
{ description = "NixOS configuration of Ryan Yin"; # 為了確保夠純,F(xiàn)lake 不依賴系統(tǒng)自身的 /etc/nix/nix.conf,而是在 flake.nix 中通過 nixConfig 設置 # 但是為了確保安全性,flake 默認僅允許直接設置少數(shù) nixConfig 參數(shù),其他參數(shù)都需要在執(zhí)行 nix 命令時指定 `--accept-flake-config`,否則會被忽略 # # 注意:即使添加了國內(nèi) cache 鏡像,如果有些包國內(nèi)鏡像下載不到,它仍然會走國外。 # 我的解法是使用 openwrt 旁路由 + openclash 加速下載。 # 臨時修改系統(tǒng)默認網(wǎng)關為我的旁路由 IP: # sudo ip route add default via 192.168.5.201 # 還原路由規(guī)則: # sudo ip route del default via 192.168.5.201 nixConfig = { experimental-features = [ "nix-command" "flakes" ]; substituters = [ # replace official cache with a mirror located in China "https://mirrors.bfsu.edu.cn/nix-channels/store" "https://cache.nixos.org/" ]; # nix community"s cache server extra-substituters = [ "https://nix-community.cachix.org" ]; extra-trusted-public-keys = [ "nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=" ]; }; inputs = { # 省略若干配置... }; outputs = { # 省略若干配置... };}
改完后使用 sudo nixos-rebuild switch
應用配置即可生效,后續(xù)所有的包都會優(yōu)先從國內(nèi)鏡像源查找緩存。
6. 安裝 home-manager注:上述手段只能加速部分包的下載,許多 inputs 數(shù)據(jù)源仍然會從 Github 拉取,另外如果找不到緩存,會執(zhí)行本地構建,這通常仍然需要從國外下載源碼與構建依賴,因此仍然會很慢。為了完全解決速度問題,仍然建議使用旁路由等局域網(wǎng)全局代理方案。
前面簡單提過,NixOS 自身的配置文件只能管理系統(tǒng)級別的配置,而用戶級別的配置則需要使用 home-manager 來管理。
根據(jù)官方文檔 Home Manager Manual,要將 home manager 作為 NixOS 模塊安裝,首先需要創(chuàng)建 /etc/nixos/home.nix
,配置方法如下:
{ config, pkgs, ... }:{ # 注意修改這里的用戶名與用戶目錄 home.username = "ryan"; home.homeDirectory = "/home/ryan"; # 直接將當前文件夾的配置文件,鏈接到 Home 目錄下的指定位置 # home.file.".config/i3/wallpaper.jpg".source = ./wallpaper.jpg; # 遞歸將某個文件夾中的文件,鏈接到 Home 目錄下的指定位置 # home.file.".config/i3/scripts" = { # source = ./scripts; # recursive = true; # 遞歸整個文件夾 # executable = true; # 將其中所有文件添加「執(zhí)行」權限 # }; # 直接以 text 的方式,在 nix 配置文件中硬編碼文件內(nèi)容 # home.file.".xxx".text = "" # xxx # ""; # set cursor size and dpi for 4k monitor xresources.properties = { "Xcursor.size" = 16; "Xft.dpi" = 172; }; # git 相關配置 programs.git = { enable = true; userName = "Ryan Yin"; userEmail = "[email protected]"; }; # Packages that should be installed to the user profile. home.packages = [ pkgs.htop pkgs.btop ]; # 啟用 starship,這是一個漂亮的 shell 提示符 programs.starship = { enable = true; settings = { add_newline = false; aws.disabled = true; gcloud.disabled = true; line_break.disabled = true; }; }; # alacritty 終端配置 programs.alacritty = { enable = true; env.TERM = "xterm-256color"; font = { size = 12; draw_bold_text_with_bright_colors = true; }; scrolling.multiplier = 5; selection.save_to_clipboard = true; }; # This value determines the Home Manager release that your # configuration is compatible with. This helps avoid breakage # when a new Home Manager release introduces backwards # incompatible changes. # # You can update Home Manager without changing this value. See # the Home Manager release notes for a list of state version # changes in each release. home.stateVersion = "22.11"; # Let Home Manager install and manage itself. programs.home-manager.enable = true;}
添加好 /etc/nixos/home.nix
后,還需要在 /etc/nixos/flake.nix
中導入該配置,它才能生效,可以使用如下命令,在當前文件夾中生成一個示例配置以供參考:
nix flake new example -t github:nix-community/home-manager#nixos
調(diào)整好參數(shù)后的 /etc/nixos/flake.nix
內(nèi)容示例如下:
{ description = "NixOS configuration"; inputs = { nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; home-manager.url = "github:nix-community/home-manager"; home-manager.inputs.nixpkgs.follows = "nixpkgs"; }; outputs = inputs@{ nixpkgs, home-manager, ... }: { nixosConfigurations = { # 這里的 nixos-test 替換成你的主機名稱 nixos-test = nixpkgs.lib.nixosSystem { system = "x86_64-linux"; modules = [ ./configuration.nix # 將 home-manager 配置為 nixos 的一個 module # 這樣在 nixos-rebuild switch 時,home-manager 配置也會被自動部署 home-manager.nixosModules.home-manager { home-manager.useGlobalPkgs = true; home-manager.useUserPackages = true; # 這里的 ryan 也得替換成你的用戶名 # 這里的 import 函數(shù)在前面 Nix 語法中介紹過了,不再贅述 home-manager.users.ryan = import ./home.nix; # 使用 home-manager.extraSpecialArgs 自定義傳遞給 ./home.nix 的參數(shù) # 取消注釋下面這一行,就可以在 home.nix 中使用 flake 的所有 inputs 參數(shù)了 # home-manager.extraSpecialArgs = inputs; } ]; }; }; };}
然后執(zhí)行 sudo nixos-rebuild switch
應用配置,即可完成 home-manager 的安裝。
安裝完成后,所有用戶級別的程序、配置,都可以通過 /etc/nixos/home.nix
管理,并且執(zhí)行 sudo nixos-rebuild switch
時也會自動應用 home-manager 的配置。
home.nix
中 Home Manager 的配置項有這幾種查找方式:
到這里整個系統(tǒng)的骨架基本就配置完成了,當前我們 /etc/nixos
中的系統(tǒng)配置結(jié)構應該如下:
$ tree.├── flake.lock├── flake.nix├── home.nix└── configuration.nix
下面分別說明下這四個文件的功能:
flake.lock
: 自動生成的版本鎖文件,它記錄了整個 flake 所有輸入的數(shù)據(jù)源、hash 值、版本號,確保系統(tǒng)可復現(xiàn)。flake.nix
: 入口文件,執(zhí)行 sudo nixos-rebuild switch
時會識別并部署它。configuration.nix
: 在 flake.nix 中被作為系統(tǒng)模塊導入,目前所有系統(tǒng)級別的配置都寫在此文件中。此配置文件中的所有配置項,參見官方文檔 Configuration - NixOS Manualhome.nix
: 在 flake.nix 中被 home-manager 作為 ryan 用戶的配置導入,也就是說它包含了 ryan 這個用戶的所有 Home Manager 配置,負責管理其 Home 文件夾。此配置文件的所有配置項,參見 Appendix A. Configuration Options - Home Manager通過修改上面幾個配置文件就可以實現(xiàn)對系統(tǒng)與 Home 目錄狀態(tài)的修改。但是隨著配置的增多,單純依靠 configuration.nix
跟 home.nix
會導致配置文件臃腫,難以維護,因此更好的解決方案是通過 Nix 的模塊機制,將配置文件拆分成多個模塊,分門別類地編寫維護。
在前面的 Nix 語法一節(jié)有介紹過:「import
的參數(shù)如果為文件夾路徑,那么會返回該文件夾下的 default.nix
文件的執(zhí)行結(jié)果」,實際 Nix 還提供了一個 imports
參數(shù),它可以接受一個 .nix
文件列表,并將該列表中的所有配置合并(Merge)到當前的 attribute set 中。注意這里的用詞是「合并」,它表明 imports
如果遇到重復的配置項,不會簡單地按執(zhí)行順序互相覆蓋,而是更合理地處理。比如說我在多個 modules 中都定義了 program.packages = [...]
,那么 imports
會將所有 modules 中的 program.packages
這個 list 合并。不僅 list 能被正確合并,attribute set 也能被正確合并,具體行為各位看官可以自行探索。
我只在 nixpkgs-unstable 官方手冊 - evalModules parameters 中找到一句關于
imports
的描述:A list of modules. These are merged together to form the final configuration.
,可以意會一下...(Nix 的文檔真的一言難盡...這么核心的參數(shù)文檔就這么一句...)
我們可以借助 imports
參數(shù),將 home.nix
與 configuration.nix
拆分成多個 .nix
文件。
比如我之前的 i3wm 系統(tǒng)配置 ryan4yin/nix-config/v0.0.2,結(jié)構如下:
├── flake.lock├── flake.nix├── home│ ├── default.nix # 在這里通過 imports = [...] 導入所有子模塊│ ├── fcitx5 # fcitx5 中文輸入法設置,我使用了自定義的小鶴音形輸入法│ │ ├── default.nix│ │ └── rime-data-flypy│ ├── i3 # i3wm 桌面配置│ │ ├── config│ │ ├── default.nix│ │ ├── i3blocks.conf│ │ ├── keybindings│ │ └── scripts│ ├── programs│ │ ├── browsers.nix│ │ ├── common.nix│ │ ├── default.nix # 在這里通過 imports = [...] 導入 programs 目錄下的所有 nix 文件│ │ ├── git.nix│ │ ├── media.nix│ │ ├── vscode.nix│ │ └── xdg.nix│ ├── rofi # rofi 應用啟動器配置,通過 i3wm 中配置的快捷鍵觸發(fā)│ │ ├── configs│ │ │ ├── arc_dark_colors.rasi│ │ │ ├── arc_dark_transparent_colors.rasi│ │ │ ├── power-profiles.rasi│ │ │ ├── powermenu.rasi│ │ │ ├── rofidmenu.rasi│ │ │ └── rofikeyhint.rasi│ │ └── default.nix│ └── shell # shell 終端相關配置│ ├── common.nix│ ├── default.nix│ ├── nushell│ │ ├── config.nu│ │ ├── default.nix│ │ └── env.nu│ ├── starship.nix│ └── terminals.nix├── hosts│ ├── msi-rtx4090 # PC 主機的配置│ │ ├── default.nix # 這就是之前的 configuration.nix,不過大部分內(nèi)容都拆出到 modules 了│ │ └── hardware-configuration.nix # 與系統(tǒng)硬件相關的配置,安裝 nixos 時自動生成的│ └── nixos-test # 測試用的虛擬機配置│ ├── default.nix│ └── hardware-configuration.nix├── modules # 從 configuration.nix 中拆分出的一些通用配置│ ├── i3.nix│ └── system.nix└── wallpaper.jpg # 桌面壁紙,在 i3wm 配置中被引用
詳細結(jié)構與內(nèi)容,請移步前面提供的 github 倉庫鏈接,這里就不多介紹了。
8. 更新系統(tǒng)在使用了 Nix Flakes 后,要更新系統(tǒng)也很簡單,先更新 flake.lock 文件,然后部署即可。在配置文件夾中執(zhí)行如下命令:
# 更新 flake.locknix flake update# 部署系統(tǒng)sudo nixos-rebuild switch
另外有時候安裝新的包,跑 sudo nixos-rebuild switch
時可能會遇到 sha256 不匹配的報錯,也可以嘗試通過 nix flake update
更新 flake.lock 來解決(原理暫時不太清楚)。
在使用 Nix Flakes 后,目前大家用得比較多的都是 nixos-unstable
分支的 nixpkgs,有時候就會遇到一些 bug,比如我最近(2023/5/6)就遇到了 chrome/vscode 閃退的問題。
這時候就需要退回到之前的版本,在 Nix Flakes 中,所有的包版本與 hash 值與其 input 數(shù)據(jù)源的 git commit 是一一對應的關系,因此回退某個包的到歷史版本,就需要鎖定其 input 數(shù)據(jù)源的 git commit.
為了實現(xiàn)上述需求,首先修改 /etc/nixos/flake.nix
,示例內(nèi)容如下(主要是利用 specialArgs
參數(shù)):
{ description = "NixOS configuration of Ryan Yin" inputs = { # 默認使用 nixos-unstable 分支 nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; # 最新 stable 分支的 nixpkgs,用于回退個別軟件包的版本,當前最新版本為 22.11 nixpkgs-stable.url = "github:nixos/nixpkgs/nixos-22.11"; # 另外也可以使用 git commit hash 來鎖定版本,這是最徹底的鎖定方式 nixpkgs-fd40cef8d.url = "github:nixos/nixpkgs/fd40cef8d797670e203a27a91e4b8e6decf0b90c"; }; outputs = inputs@{ self, nixpkgs, nixpkgs-stable, nixpkgs-fd40cef8d, ... }: { nixosConfigurations = { nixos-test = nixpkgs.lib.nixosSystem rec { system = "x86_64-linux"; # 核心參數(shù)是這個,將非默認的 nixpkgs 數(shù)據(jù)源傳到其他 modules 中 specialArgs = { # 注意每次 import 都會生成一個新的 nixpkgs 實例 # 這里我們直接在 flake.nix 中創(chuàng)建實例, 再傳遞到其他子 modules 中使用 # 這樣能有效重用 nixpkgs 實例,避免 nixpkgs 實例泛濫。 pkgs-stable = import nixpkgs-stable { system = system; # 這里遞歸引用了外部的 system 屬性 # 為了拉取 chrome 等軟件包,需要允許安裝非自由軟件 config.allowUnfree = true; }; pkgs-fd40cef8d = import nixpkgs-fd40cef8d { system = system; config.allowUnfree = true; }; }; modules = [ ./hosts/nixos-test # 省略其他模塊配置... ]; }; }; };}
然后在你對應的 module 中使用該數(shù)據(jù)源中的包,一個 Home Manager 的子模塊示例:
{ pkgs, config, # nix 會從 flake.nix 的 specialArgs 查找并注入此參數(shù) pkgs-stable, # pkgs-fd40cef8d, # 也可以使用固定 hash 的 nixpkgs 數(shù)據(jù)源 ...}:{ # 這里從 pkg-stable 中引用包 home.packages = with pkgs-stable; [ firefox-wayland # chrome wayland support was broken on nixos-unstable branch, so fallback to stable branch for now # https://github.com/swaywm/sway/issues/7562 google-chrome ]; programs.vscode = { enable = true; package = pkgs-stable.vscode; # 這里也一樣,從 pkgs-stable 中引用包 };}
配置完成后,通過 sudo nixos-rebuild switch
部署即可將 firefox/chrome/vscode 三個軟件包回退到 stable 分支的版本。
10. 使用 Git 管理 NixOS 配置根據(jù) @fbewivpjsbsby 補充的文章 1000 instances of nixpkgs,在子模塊中用
import
來定制nixpkgs
不是一個好的習慣,因為每次import
都會重新求值并產(chǎn)生一個新的 nixpkgs 實例,在配置越來越多時會導致構建時間變長、內(nèi)存占用變大。所以這里改為了在flake.nix
中創(chuàng)建所有 nixpkgs 實例。
NixOS 的配置文件是純文本,因此跟普通的 dotfiles 一樣可以使用 Git 管理。
此外 Nix Flakes 配置也不一定需要放在 /etc/nixos
目錄下,可以放在任意目錄下,只要在部署時指定正確的路徑即可。
我們在前面第 3 小節(jié)的代碼注釋中有說明過,可以通過
sudo nixos-rebuild switch --flake .#xxx
的--flake
參數(shù)指定 Flakes 配置的文件夾路徑,并通過#
后面的值來指定使用的 outputs 名稱。
比如我的使用方式是將 Nix Flakes 配置放在 ~/nixos-config
目錄下,然后在 /etc/nixos
目錄下創(chuàng)建一個軟鏈接:
sudo mv /etc/nixos /etc/nixos.bak # 備份原來的配置sudo ln -s ~/nixos-config/ /etc/nixos
然后就可以在 ~/nixos-config
目錄下使用 Git 管理配置了,配置使用普通的用戶級別權限即可,不要求 owner 為 root.
另一種方法是直接刪除掉 /etc/nixos
,并在每次部署時指定配置文件路徑:
sudo mv /etc/nixos /etc/nixos.bak # 備份原來的配置cd ~/nixos-config# 通過 --flake .#nixos-test 參數(shù),指定使用當前文件夾的 flake.nix,使用的 nixosConfiguraitons 名稱為 nixos-testsudo nixos-rebuild switch --flake .#nixos-test
兩種方式都可以,看個人喜好。
11. 查看與清理歷史數(shù)據(jù)如前所述,NixOS 的每次部署都會生成一個新的版本,所有版本都會被添加到系統(tǒng)啟動項中,除了重啟電腦外,我們也可以通過如下命令查詢當前可用的所有歷史版本:
nix profile history --profile /nix/var/nix/profiles/system
以及清理歷史版本釋放存儲空間的命令:
# 清理 7 天之前的所有歷史版本sudo nix profile wipe-history --profile /nix/var/nix/profiles/system --older-than 7d# 清理歷史版本并不會刪除數(shù)據(jù),還需要手動 gc 下sudo nix store gc --debug
以及查看系統(tǒng)層面安裝的所有軟件包(這個貌似只能用 nix-env
):
nix-env -qa
七、Nix Flakes 的使用到這里我們已經(jīng)寫了不少 Nix Flakes 配置來管理 NixOS 系統(tǒng)了,這里再簡單介紹下 Nix Flakes 更細節(jié)的內(nèi)容,以及常用的 nix flake 命令。
1. Flake 的 inputsflake.nix
中的 inputs
是一個 attribute set,用來指定當前 Flake 的依賴,inputs 有很多種類型,舉例如下:
{ inputs = { # 以 GitHub 倉庫為數(shù)據(jù)源,指定使用 master 分支,這是最常見的 input 格式 nixpkgs.url = "github:Mic92/nixpkgs/master"; # Git URL,可用于任何基于 https/ssh 協(xié)議的 Git 倉庫 git-example.url = "git+https://git.somehost.tld/user/path?ref=branch&rev=fdc8ef970de2b4634e1b3dca296e1ed918459a9e"; # 上面的例子會復制 .git 到本地, 如果數(shù)據(jù)量較大,建議使用 shallow=1 參數(shù)避免復制 .git git-directory-example.url = "git+file:/path/to/repo?shallow=1"; # 本地文件夾 (如果使用絕對路徑,可省略掉前綴 "path:") directory-example.url = "path:/path/to/repo"; # 如果數(shù)據(jù)源不是一個 flake,則需要設置 flake=false bar = { url = "github:foo/bar/branch"; flake = false; }; sops-nix = { url = "github:Mic92/sops-nix"; # `follows` 是 inputs 中的繼承語法 # 這里使 sops-nix 的 `inputs.nixpkgs` 與當前 flake 的 inputs.nixpkgs 保持一致, # 避免依賴的 nixpkgs 版本不一致導致問題 inputs.nixpkgs.follows = "nixpkgs"; }; # 將 flake 鎖定在某個 commit 上 nix-doom-emacs = { url = "github:vlaci/nix-doom-emacs?rev=238b18d7b2c8239f676358634bfb32693d3706f3"; flake = false; }; # 使用 `dir` 參數(shù)指定某個子目錄 nixpkgs.url = "github:foo/bar?dir=shu"; }}
2. Flake 的 outputsflake.nix
中的 outputs
是一個 attribute set,是整個 Flake 的構建結(jié)果,每個 Flake 都可以有許多不同的 outputs。
一些特定名稱的 outputs 有特殊用途,會被某些 Nix 命令識別處理,比如:
Nix packages: 名稱為apps..
, packages..
或 legacyPackages..
的 outputs,都是 Nix 包,通常都是一個個應用程序。可以通過 nix build .#name
來構建某個 nix 包Nix Helper Functions: 名稱為 lib
的 outputs 是 Flake 函數(shù)庫,可以被其他 Flake 作為 inputs 導入使用。Nix development environments: 名稱為 devShells
的 outputs 是 Nix 開發(fā)環(huán)境可以通過 nix develop
命令來使用該 Output 創(chuàng)建開發(fā)環(huán)境NixOS configurations: 名稱為 nixosConfigurations.
的 outputs,是 NixOS 的系統(tǒng)配置。nixos-rebuild switch .#
可以使用該 Output 來部署 NixOS 系統(tǒng)Nix templates: 名稱為 templates
的 outputs 是 flake 模板可以通過執(zhí)行命令 nix flake init --template
使用模板初始化一個 Flake 包其他用戶自定義的 outputs,可能被其他 Nix 相關的工具使用NixOS Wiki 中給出的使用案例:
{ self, ... }@inputs:{ # Executed by `nix flake check` checks.""."" = derivation; # Executed by `nix build .#` packages.""."" = derivation; # Executed by `nix build .` packages."".default = derivation; # Executed by `nix run .#` apps.""."" = { type = "app"; program = ""; }; # Executed by `nix run . -- ` apps."".default = { type = "app"; program = "..."; }; # Formatter (alejandra, nixfmt or nixpkgs-fmt) formatter."" = derivation; # Used for nixpkgs packages, also accessible via `nix build .#` legacyPackages.""."" = derivation; # Overlay, consumed by other flakes overlays."" = final: prev: { }; # Default overlay overlays.default = {}; # Nixos module, consumed by other flakes nixosModules."" = { config }: { options = {}; config = {}; }; # Default module nixosModules.default = {}; # Used with `nixos-rebuild --flake .#` # nixosConfigurations."".config.system.build.toplevel must be a derivation nixosConfigurations."" = {}; # Used by `nix develop .#` devShells.""."" = derivation; # Used by `nix develop` devShells."".default = derivation; # Hydra build jobs hydraJobs.""."" = derivation; # Used by `nix flake init -t #` templates."" = { path = ""; description = "template description goes here?"; }; # Used by `nix flake init -t ` templates.default = { path = ""; description = ""; };}
3. Flake 命令行的使用在啟用了 nix-command
& flakes
功能后,我們就可以使用 Nix 提供的新一代 Nix 命令行工具 New Nix Commands 了,下面列舉下其中常用命令的用法:
# 解釋下這條指令涉及的參數(shù):# `nixpkgs#ponysay` 意思是 `nixpkgs` 這個 flake 中的 `ponysay` 包。# `nixpkgs` 是一個 flakeregistry ida,# nix 會從 中# 找到這個 id 對應的 github 倉庫地址# 所以這個命令的意思是創(chuàng)建一個新環(huán)境,安裝并運行 `nixpkgs` 這個 flake 提供的 `ponysay` 包。# 注:前面已經(jīng)介紹過了,nix 包 是 flake outputs 中的一種。echo "Hello Nix" | nix run "nixpkgs#ponysay"# 這條命令和上面的命令作用是一樣的,只是使用了完整的 flake URI,而不是 flakeregistry id。echo "Hello Nix" | nix run "github:NixOS/nixpkgs/nixos-unstable#ponysay"# 這條命令的作用是使用 zero-to-nix 這個 flake 中名 `devShells.example` 的 outptus 來創(chuàng)建一個開發(fā)環(huán)境,# 然后在這個環(huán)境中打開一個 bash shell。nix develop "github:DeterminateSystems/zero-to-nix#example"# 除了使用遠程 flake uri 之外,你也可以使用當前目錄下的 flake 來創(chuàng)建一個開發(fā)環(huán)境。mkdir my-flake && cd my-flake## 通過模板初始化一個 flakenix flake init --template "github:DeterminateSystems/zero-to-nix#javascript-dev"## 使用當前目錄下的 flake 創(chuàng)建一個開發(fā)環(huán)境,并打開一個 bash shellnix develop# 或者如果你的 flake 有多個 devShell 輸出,你可以指定使用名為 example 的那個nix develop .#example# 構建 `nixpkgs` flake 中的 `bat` 這個包# 并在當前目錄下創(chuàng)建一個名為 `result` 的符號鏈接,鏈接到該構建結(jié)果文件夾。mkdir build-nix-package && cd build-nix-packagenix build "nixpkgs#bat"# 構建一個本地 flake 和 nix develop 是一樣的,不再贅述
此外 Zero to Nix - Determinate Systems 是一份全新的 Nix & Flake 新手入門文檔,寫得比較淺顯易懂,適合新手用來入門。
八、Nixpkgs 的高級用法callPackage、Overriding 與 Overlays 是在使用 Nix 時偶爾會用到的技術,它們都是用來自定義 Nix 包的構建方法的。
我們知道許多程序都有大量構建參數(shù)需要配置,不同的用戶會希望使用不同的構建參數(shù),這時候就需要 Overriding 與 Overlays 來實現(xiàn)。我舉幾個我遇到過的例子:
fcitx5-rime.nix: fcitx5-rime 的rimeDataPkgs
默認使用 rime-data
包,但是也可以通過 override 來自定義該參數(shù)的值,以加載自定義的 rime 配置(比如加載小鶴音形輸入法配置)。vscode/with-extensions.nix: vscode 的這個包也可以通過 override 來自定義 vscodeExtensions
參數(shù)的值來安裝自定義插件。nix-vscode-extensions: 就是利用該參數(shù)實現(xiàn)的 vscode 插件管理firefox/common.nix: firefox 同樣有許多可自定義的參數(shù)等等總之如果需要自定義上述這類 Nix 包的構建參數(shù),或者實施某些比較底層的修改,我們就得用到 Overriding 跟 Overlays。
OverridingChapter 4. Overriding - nixpkgs Manual
簡單的說,所有 nixpkgs 中的 Nix 包都可以通過
來自定義某些構建參數(shù),它返回一個使用了自定義參數(shù)的新 Derivation. 舉個例子:
pkgs.fcitx5-rime.override {rimeDataPkgs = [ ./rime-data-flypy];}
上面這個 Nix 表達式的執(zhí)行結(jié)果就是一個新的 Derivation,它的 rimeDataPkgs
參數(shù)被覆蓋為 [./rime-data-flypy]
,而其他參數(shù)則沿用原來的值。
除了覆寫參數(shù),還可以通過 overrideAttrs
來覆寫使用 stdenv.mkDerivation
構建的 Derivation 的屬性,比如:
helloWithDebug = pkgs.hello.overrideAttrs (finalAttrs: previousAttrs: { separateDebugInfo = true;});
上面這個例子中,helloWithDebug
就是一個新的 Derivation,它的 separateDebugInfo
參數(shù)被覆蓋為 true
,而其他參數(shù)則沿用原來的值。
Chapter 3. Overlays - nixpkgs Manual
前面介紹的 override 函數(shù)都會生成新的 Derivation,不影響 pkgs 中原有的 Derivation,只適合作為局部參數(shù)使用。但如果你需要覆寫的 Derivation 還被其他 Nix 包所依賴,那其他 Nix 包使用的仍然會是原有的 Derivation.
為了解決這個問題,Nix 提供了 overlays 能力。簡單的說,Overlays 可以全局修改 pkgs 中的 Derivation。
在舊的 Nix 環(huán)境中,Nix 默認會自動應用 ~/.config/nixpkgs/overlays.nix
~/.config/nixpkgs/overlays/*.nix
這類路徑下的所有 overlays 配置。
但是在 Flakes 中,為了確保系統(tǒng)的可復現(xiàn)性,它不能依賴任何 Git 倉庫之外的配置,所以這種舊的方法就不能用了。
在使用 Nix Flakes 編寫 NixOS 配置時,Home Manager 與 NixOS 都提供了 nixpkgs.overlays
這個 option 來引入 overlays, 相關文檔:
nixpkgs.overlays
nixpkgs source code - nixpkgs.overlays
舉個例子,如下內(nèi)容就是一個加載 Overlays 的 Module,它既可以用做 Home Manager Module,也可以用做 NixOS Module,因為這倆定義完全是一致的:
不過我使用發(fā)現(xiàn),Home Manager 畢竟是個外部組件,而且現(xiàn)在全都用的 unstable 分支,這導致 Home Manager Module 有時候會有點小毛病,因此更建議以 NixOS Module 的形式引入 overlays
{ config, pkgs, lib, ... }:{ nixpkgs.overlays = [ # overlayer1 - 參數(shù)名用 self 與 super,表達繼承關系 (self: super: { google-chrome = super.google-chrome.override { commandLineArgs = "--proxy-server="https=127.0.0.1:3128;http=127.0.0.1:3128""; }; }) # overlayer2 - 還可以使用 extend 來繼承其他 overlay # 這里改用 final 與 prev,表達新舊關系 (final: prev: { steam = prev.steam.override { extraPkgs = pkgs: with pkgs; [ keyutils libkrb5 libpng libpulseaudio libvorbis stdenv.cc.cc.lib xorg.libXcursor xorg.libXi xorg.libXinerama xorg.libXScrnSaver ]; extraProfile = "export GDK_SCALE=2"; }; }) # overlay3 - 也可以將 overlay 定義在其他文件中 (import ./overlays/overlay3.nix) ];}
這里只是個示例配置,參照此格式編寫你自己的 overlays 配置,將該配置作為 NixOS Module 或者 Home Manager Module 引入,然后部署就可以看到效果了。
模塊化 overlays 配置上面的例子說明了如何編寫 overlays,但是所有 overlays 都一股腦兒寫在一起,就有點難以維護了,寫得多了自然就希望模塊化管理這些 overlays.
這里介紹下我找到的一個 overlays 模塊化管理的最佳實踐。
首先在 Git 倉庫中創(chuàng)建 overlays
文件夾用于存放所有 overlays 配置,然后創(chuàng)建 overlays/default.nix
,其內(nèi)容如下:
args: # import 當前文件夾下所有的 nix 文件,并以 args 為參數(shù)執(zhí)行它們 # 返回值是一個所有執(zhí)行結(jié)果的列表,也就是 overlays 的列表 builtins.map (f: (import (./. + "/${f}") args)) # map 的第一個參數(shù),是一個 import 并執(zhí)行 nix 文件的函數(shù) (builtins.filter # map 的第二個參數(shù),它返回一個當前文件夾下除 default.nix 外所有 nix 文件的列表 (f: f != "default.nix") (builtins.attrNames (builtins.readDir ./.)))
后續(xù)所有 overlays 配置都添加到 overlays
文件夾中,一個示例配置 overlays/fcitx5/default.nix
內(nèi)容如下:
# 為了不使用默認的 rime-data,改用我自定義的小鶴音形數(shù)據(jù),這里需要 override# 參考 https://github.com/NixOS/nixpkgs/blob/e4246ae1e7f78b7087dce9c9da10d28d3725025f/pkgs/tools/inputmethods/fcitx5/fcitx5-rime.nix{pkgs, config, lib, ...}:(self: super: { # 小鶴音形配置,配置來自 flypy.com 官方網(wǎng)盤的鼠須管配置壓縮包「小鶴音形“鼠須管”for macOS.zip」 rime-data = ./rime-data-flypy; fcitx5-rime = super.fcitx5-rime.override { rimeDataPkgs = [ ./rime-data-flypy ]; };})
我通過上面這個 overlays 修改了 fcitx5-rime 輸入法的默認數(shù)據(jù),加載了我自定義的小鶴音形輸入法。
最后,還需要通過 nixpkgs.overlays
這個 option 加載 overlays/default.nix
返回的所有 overlays 配置,在任一 NixOS Module 中添加如下參數(shù)即可:
{ config, pkgs, lib, ... } @ args:{ # ...... # 添加此參數(shù) nixpkgs.overlays = import /path/to/overlays/dir; # ......}
比如說直接寫 flake.nix
里:
{ description = "NixOS configuration of Ryan Yin"; # ...... inputs = { # ...... }; outputs = inputs@{ self, nixpkgs, ... }: { nixosConfigurations = { nixos-test = nixpkgs.lib.nixosSystem { system = "x86_64-linux"; specialArgs = inputs; modules = [ ./hosts/nixos-test # 添加如下內(nèi)嵌 module 定義 # 這里將 modules 的所有參數(shù) args 都傳遞到了 overlays 中 (args: { nixpkgs.overlays = import ./overlays args; }) # ...... ]; }; }; };}
按照上述方法進行配置,就可以很方便地模塊化管理所有 overlays 配置了,以我的配置為例,overlays 文件夾的結(jié)構大致如下:
.├── flake.lock├── flake.nix├── home├── hosts├── modules├── ......├── overlays│ ├── default.nix # 它返回一個所有 overlays 的列表│ └── fcitx5 # fcitx5 overlay│ ├── default.nix│ ├── README.md│ └── rime-data-flypy # 自定義的 rime-data,需要遵循它的文件夾格式│ └── share│ └── rime-data│ ├── ...... # rime-data 文件└── README.md
你可以在我的配置倉庫 ryan4yin/nix-config/v0.0.4 查看更詳細的內(nèi)容,獲取些靈感。
進階玩法逐漸熟悉 Nix 這一套工具鏈后,可以進一步讀一讀 Nix 的三本手冊,挖掘更多的玩法:
Nix Reference Manual: Nix 包管理器使用手冊,主要包含 Nix 包管理器的設計、命令行使用說明。nixpkgs Manual: 主要介紹 Nixpkgs 的參數(shù)、Nix 包的使用、修改、打包方法。NixOS Manual: NixOS 系統(tǒng)使用手冊,主要包含 Wayland/X11, GPU 等系統(tǒng)級別的配置說明。nix-pills: Nix Pills 對如何使用 Nix 構建軟件包進行了深入的闡述,寫得比官方文檔清晰易懂,而且也足夠深入,值得一讀。在對 Nix Flakes 熟悉到一定程度后,你可以嘗試一些 Flakes 的進階玩法,如下是一些比較流行的社區(qū)項目,可以試用:
flake-parts: 通過 Module 模塊系統(tǒng)簡化配置的編寫與維護。flake-utils-plus:同樣是用于簡化 Flake 配置的第三方包,不過貌似更強大些digga: 一個大而全的 Flake 模板,揉合了各種實用 Nix 工具包的功能,不過結(jié)構比較復雜,需要一定經(jīng)驗才能玩得轉(zhuǎn)。......以及其他許多實用的社區(qū)項目可探索,我比較關注的有這幾個:
devenv: 開發(fā)環(huán)境管理agenix: secrets 管理nixos-generator: 鏡像生成工具,從 nixos 配置生成 iso/qcow2 等格式的鏡像lanzaboote: 啟用 secure bootimpermanence: 用于配置無狀態(tài)系統(tǒng)。可用它持久化你指定的文件或文件夾,同時再將 /home 目錄掛載為 tmpfs 或者每次啟動時用工具擦除一遍。這樣所有不受 impermanence 管理的數(shù)據(jù)都將成為臨時數(shù)據(jù),如果它們導致了任何問題,重啟下系統(tǒng)這些數(shù)據(jù)就全部還原到初始狀態(tài)了!colmena: NixOS 主機部署工具總結(jié)這是本系列文章的第一篇,介紹了使用 Nix Flakes 配置 NixOS 系統(tǒng)的基礎知識,跟著這篇文章把系統(tǒng)配置好,就算是入門了。
我會在后續(xù)文章中介紹 NixOS & Nix Flakes 的進階知識:開發(fā)環(huán)境管理、secrets 管理、軟件打包、遠程主機管理等等,盡請期待。
參考如下是我參考過的比較有用的 Nix 相關資料:
Zero to Nix - Determinate Systems: 淺顯易懂的 Nix Flakes 新手入門文檔,值得一讀。NixOS 系列: 這是 LanTian 大佬的 NixOS 系列文章,寫得非常清晰明了,新手必讀。Nix Flakes Series: 官方的 Nix Flakes 系列文章,介紹得比較詳細,作為新手入門比較 OKNix Flakes - Wiki: Nix Flakes 的官方 Wiki,此文介紹得比較粗略。ryan4yin/nix-config: 我的 NixOS 配置倉庫,README 中也列出了我參考過的其他配置倉庫X 關閉
X 關閉