我對 Nix 的第一印象
原文由 Michael Lynch 于 發布,訂閱此部落格
Nix 是一套能依據原始碼檔案來設定軟體環境的工具。最近在 Hacker News 和 Twitter 上越來越常看到有人在討論 Nix。它的理念很吸引我,所以過去幾週我一直在摸索、試玩。
我與基礎設施即程式碼的淵源
十年前,我發現了 Salt,一套能用程式碼來定義電腦系統設定的工具。我很喜歡用一個 Git 儲存庫就能定義電腦和虛擬機上安裝了哪些服務的概念。就算把電腦整個清空重來,只要重新執行設定工具,就能回到一模一樣的狀態。
我玩了幾年 Salt,直到發現了 Ansible,我覺得它把同樣的理念實現得更好。
我所有的開發工作都在自家的 homelab 伺服器上的虛擬機裡進行。每個專案都有各自獨立的虛擬機,全部都用 Ansible 來管理。
Ansible 的問題
Ansible 最大的問題就是慢得讓人痛苦。在我其中一台虛擬機上跑一次 Ansible,通常要花上 10 到 15 分鐘。
假設我想安裝一個新的 apt 套件 foo。我是該直接執行 sudo apt install --yes foo,5 秒鐘就裝好?還是該打開我的 Ansible role,修改設定檔加上安裝 foo 的步驟,然後執行 playbook,再等上 15 分鐘?顯然,我大多會選擇前者,結果就是我的實際環境和本應代表它的 Ansible 檔案漸漸脫節、產生漂移。
另一個問題是 Ansible 的變更常常不向下相容。所以,如果我為了使用某個新功能而更新了一份 playbook,就得把所有的 playbook 都更新到相容的版本。但我從來提不起勁去重寫、重測全部的 playbook,結果就卡在舊版本上。我現在還在用三年前發布的 Ansible 2.9。
Nix 吸引我的地方
我看到越來越多人開始談論 Nix 和 NixOS。許多我覺得有趣的開發者也都在分享他們嘗試 Nix 的經驗。
其中影響我最深的推薦來自 Mitchell Hashimoto,他是 Hashicorp 的共同創辦人,這家公司打造了許多廣受使用的開源基礎設施工具,像是 Vagrant、Packer、Consul 和 Terraform。他稱 Nix 是「近年來我學過、最具正面影響力的第一名技術」。
Nix 的理念和 Ansible 很像。Nix 讓你用程式碼定義設定,然後它會把系統帶到那個狀態。
Nix 與 Ansible 的差異
Nix 和 Ansible 有幾個我覺得很有意思的重要差異。
Nix 比 Ansible 更快
Ansible 完全沒有「狀態」的概念。如果在我的某台虛擬機上跑一次 Ansible 要花 15 分鐘,那麼一分鐘後再跑一次同樣的 playbook,大概還是要花 10 分鐘。兩次執行之間大概不會有套件更新,所以能省下一點時間,但它基本上還是把所有工作重做一遍。
Ansible 永遠不會說:「喔,我剛剛才設定過這台機器,所以現在沒什麼事要做了。」因為從上次執行到現在,什麼事都可能發生,所以 Ansible 每次都得把所有設定重新執行一遍。
相反地,Nix 是有狀態概念的。如果你在 200 行的 Nix 設定檔中只改了一行,它不需要把另外 199 行的工作全部重做。它會對照設定檔評估系統的狀態,判斷出只需要套用那一行的變更。而那個變更通常幾秒鐘就能完成。
編按(2023-06-19):一位更有經驗的讀者澄清了 Nix 並非以我原本以為的方式擁有狀態:
Nix 之所以快,不是因為它是有狀態的,而是因為它是函數式且可重現的,這讓它能在不犧牲正確性的前提下進行快取。
我原本以為 Nix 會記錄是哪些任務讓系統到達哪個狀態。舉例來說,假設執行任務 X 和 Y 會到達狀態 A,而執行 X、Y、Z 則會到達狀態 B。我以為一個處於狀態 A 的 Nix 系統,會知道只要執行任務 Z 就能到達狀態 B。
我現在的理解是,如果你要求一個處於狀態 A 的 Nix 系統前往狀態 B,Nix 還是會執行任務 X、Y 和 Z,但因為 X 和 Y 的結果已經被快取起來,所以幾乎是瞬間就完成了。
Nix 為本地設定而最佳化
Ansible 的設計是用來透過網路去設定遠端系統的。你當然還是可以把目標指定為 localhost,但那並不是 Ansible 最擅長的情境。
Nix 則是為了設定它所在的環境本身而設計的。在 Nix 中,你定義好環境中應該有什麼,然後 Nix 就會在本地為你建立出那個環境。在 NixOS 中,你定義的是整個作業系統,而 NixOS 會讓作業系統進入那個狀態。你甚至可以更改檔案系統、Linux 核心或開機載入程式這類底層的東西。
這就解決了我在使用 Ansible 時遇到的問題:只要把其中一份 playbook 升級到較新的 Ansible 版本,就得把系統上所有的 playbook 都一起升級。你可以同時讓許多 Nix 系統各自跑不同版本的 Nix,彼此也不會衝突。但如果你有些 Ansible 檔案需要 2.9,有些需要 2.10,有些又需要 2.14,要同時兼顧就非常麻煩。
Nix 的變更具備原子性
使用 Ansible 時,很容易在設定到一半時失敗,讓系統停留在一個未定義的狀態。
而在 Nix 中,變更都是原子性的。Nix 要嘛把你的系統完整地帶到你想要的狀態,要嘛就 rollback 回到你嘗試變更之前的狀態。
對我有幫助的 Nix 資源
我最常看到大家抱怨 Nix 的一點,就是它的文件不足、寫錯或寫得很差。我的感覺是,那些文件像是寫給已經很熟悉 Nix 的老手看的。
我找到的很多 Nix 文件都會寫類似這樣的話:「只要加上這幾行就好!」
蛤?
到底是哪個檔案?又要加在檔案的哪個位置?
以下是我目前找到最有用的幾個資源:
- Zero to Nix:這是我找到最棒的 Nix 入門教學系列,由 Determinate Systems 撰寫,他們也寫了許多對新手友善、關於使用 Nix 的部落格文章。
- NixOS for the Impatient:在此之前我已經試過幾次安裝 NixOS,但這篇文章終於讓我相信其實比我想的簡單,也給了我最後一把推力,讓我把整個流程跑完。
- Julia Evans 的〈Some notes on using nix〉:Julia 也是 Nix 新手,所以看到她遇到了哪些障礙、又是怎麼繞過它們,對我很有幫助,即使生態系中還有很多對她來說仍是全新且陌生的東西。
第一次失敗嘗試:在虛擬機中安裝 NixOS
我在一台 Proxmox 虛擬機上照著「NixOS for the Impatient」教學操作,一開始一切都很順利。接著做到教學中要修改 hostname 的步驟,我重新開機讓設定生效。但虛擬機卻進入了一種詭異的狀態,看起來能開機,卻無法登入。在登入畫面輸入密碼後,整個畫面就凍住了。
NixOS 已成功安裝在我的 Proxmox 虛擬機伺服器上,但在第二次開機登入後就卡住了。
第二次失敗嘗試:在 Raspberry Pi 4 上安裝 NixOS
既然虛擬機行不通,我想說下一步就該試試實體機器了。手邊剛好有一台閒置的 Raspberry Pi 4,覺得拿 Pi 來實驗應該會很有趣。
我找到了兩份看起來很官方的、在 Raspberry Pi 4 上安裝 NixOS 的教學:
這兩份教學的問題在於,它們都假設你已經有一個正在運行的 Nix 環境。我是想在我的主力電腦上準備 microSD 卡,而那是一台 Win10 系統,所以我根本還沒有 Nix。
NixOS 下載頁面上列了一個 64 位元 ARM 映像檔。Raspberry Pi 4 支援 64 位元 ARM,所以我想說就試試看這個。
我打開平常燒錄 microSD 卡愛用的工具 Balena Etcher。第一個警訊馬上就來了,Etcher 基本上是在說:「嘿,你在想什麼?這根本不是可開機的映像檔。」
我還是硬著頭皮繼續燒錄!但當我嘗試用那張 microSD 開機時,Pi 也認定這不是可開機的映像檔,結果就直接卡住了。
我又試著用官方的 Raspberry Pi Imager 工具重燒一次同樣的映像檔,但結果還是一樣。
更新:我最後終於讓它成功跑起來了。
成功:在 Dell 迷你電腦上安裝 NixOS
我工作上的大多數測試都是在一台 Dell Optiplex 7040 上進行的。那是我手邊唯一一台可以隨便清空重灌的實體機器,所以我就拿它來試了。
一切都和〈NixOS for the Impatient〉寫的一模一樣。從頭到尾安裝只花了大約 10 分鐘,其中 7 分鐘只是在複製檔案。因為這只是台測試用的裝置,我跳過了加密,這樣每次重開機就不用多輸入一次密碼。
在 Dell Optiplex 7040 上安裝 NixOS(我把複製檔案的片段加速了)
最後,我得到了一個完整、可正常運作的 NixOS 安裝!
第三次失敗嘗試:再次在 Raspberry Pi 4 上安裝 NixOS
現在我已經有一台可運作的 NixOS 機器了,我想再給 Raspberry Pi 一次機會。之前卡關是因為沒有 Nix 環境可以準備 microSD 映像檔,現在我有了。
我照著 nix.dev 教學操作,這次比第一次進展得更遠,但還是無法開機。Pi 只會停在顯示彩色畫面的階段,然後就當在那裡:
當我從 NixOS 系統燒錄 NixOS Pi aarch64 的 microSD 映像檔並用它來啟動 Pi 時,它就卡在彩色畫面上。
更新:我最後終於讓它成功跑起來了。
任務一:取得 SSH 存取權限
好,回到我在 Dell Optiplex 上那個可運作的 NixOS 安裝。
我需要從主力電腦透過 SSH 連進這台 NixOS 系統。要做到這點,我得先把 SSH 金鑰放到系統上。
在 NixOS 上,我打開 Console 應用程式,然後輸入 sudo nano /etc/nixos/configuration.nix。接著,我找到 environment.systemPackages 並加入以下幾行:
environment.systemPackages = with pkgs; [
vim
curl
];要讓變更生效,我執行了:
sudo nixos-rebuild switch現在我已經有 vim 和 curl 可以用,所以可以從 GitHub 上抓下我的 SSH 公鑰:
sudo mkdir -p /etc/nixos/ssh
GITHUB_USERNAME='mtlynch'
curl "https://github.com/${GITHUB_USERNAME}.keys" | \
sudo tee --append /etc/nixos/ssh/authorized_keys接著,我執行 sudo vim /etc/nixos/configuration.nix 並加入以下幾行:
# Enable the OpenSSH daemon.
services.openssh.enable = true;
users.users.mike.openssh.authorizedKeys.keyFiles = [
/etc/nixos/ssh/authorized_keys
];最後,我重新建置並重新開機。我不確定重開機是否真的必要:
sudo nixos-rebuild switch && sudo reboot成功了!在那之後,我就能從主力電腦 SSH 連進我的 NixOS 系統了。
任務二:移除 Gnome 的雜亂內建程式
關於這個作業系統,最先讓我注意到的是裡面有好多雜亂的東西。有很多我根本不需要的內建應用程式,像是聯絡人清單和天氣 App:
我搜尋了要怎麼移除它們,發現它們是 Gnome Shell 默認包含的應用程式。你可以在 /etc/nixos/configuration.nix 中加入這一行來停用它們:
services.gnome.core-utilities.enable = false;或者,你也可以一個一個單獨移除:
environment.gnome.excludePackages = with pkgs.gnome; [
baobab # disk usage analyzer
cheese # photo booth
eog # image viewer
epiphany # web browser
gedit # text editor
simple-scan # document scanner
totem # video player
yelp # help viewer
evince # document viewer
file-roller # archive manager
geary # email client
seahorse # password manager
gnome-calculator
gnome-calendar
gnome-characters
gnome-clocks
gnome-contacts
gnome-font-viewer
gnome-logs
gnome-maps
gnome-music
gnome-screenshot
gnome-system-monitor
gnome-weather
gnome-disk-utility
pkgs.gnome-connections
];我選擇了「全部停用」的選項並重新建置:
sudo nixos-rebuild switch完成了!所有雜亂的東西都不見了:
任務三:把系統監控找回來(失敗)
唯一一個我覺得值得留下的 Gnome 工具是系統監控。我試著把它加到 environment.systemPackages 的清單中,但重新建置時卻失敗了:
$ sudo nixos-rebuild switch
building Nix...
building the system configuration...
error: undefined variable 'gnome-system-monitor'
at /etc/nixos/configuration.nix:130:5:
129| curl
130| gnome-system-monitor
| ^
131| ];
(use '--show-trace' to show detailed location information)我還試了其他可能的名字,像是 gnome-shell-system-monitor,但還是搞不清楚該怎麼安裝。
編按(2023-06-19):感謝讀者指出正確的套件名稱是 gnome.gnome-system-monitor。我當時漏掉的關鍵是,我可以在 search.nixos.org 上搜尋套件。
接下來想搞懂的事
我對這幾天使用 Nix 和 NixOS 的體驗很滿意。大致和我聽說的差不多。感覺只要用得好,它可以非常強大,但需要大量的前期投入,還得到處挖掘資訊。
我目前只是略懂皮毛,以下是接下來我想進一步了解 Nix 的幾個面向。
在 NixOS 系統上使用 VS Code Remote SSH
我所有的開發工作都是透過 VS Code 的 Remote SSH 進行的。當我嘗試從 VS Code 遠端連進 NixOS 系統時,安裝卻失敗了。VS Code 必須在目標系統上安裝某種伺服器元件,而 VS Code 大概不知道該怎麼在 NixOS 上安裝。
有一個 nixos-vscode-server 的 Git 儲存庫,那大概就是我需要的解決方案。我還沒試過。
Nix 的核心概念是如何串起來的
我看到像是「flakes」和「derivations」這類詞彙,但目前還不知道它們是什麼意思。我也不懂 Nix 語言的語法,不過它和 JavaScript、Python 夠像,讓我現在還能半矇半猜地應付過去。但要有效率地使用 Nix,顯然我還是得好好學這個語言。
決定性是在什麼時候發生的?
當我看到大家討論 Nix 時,最常被提到的特色之一就是 Nix 具有決定性。
到目前為止,我還是不太懂它怎麼算是決定性的。當我指定要安裝的套件時,我既沒指定完整性雜湊值,甚至連版本號都沒寫。如果一年後我再跑一次同樣的 Nix 設定檔,我猜我會得到一個不同的系統,因為它會去安裝不同版本的 vim 和 curl 套件。
我猜應該有更精確指定套件版本的方法,只是我還沒學到。
我到底在信任誰?
當我指定套件時,只是一份單純的套件名稱清單:
environment.systemPackages = with pkgs; [
vim
curl
];我能像上面這樣直接指定套件名稱,代表 Nix 一定是從某個預設的套件庫拉取套件。那是否有多個套件庫?我又該如何選擇要用哪一個?
隨機一篇部落格






留言
登入後參與討論