前言: Nix: the package manager #
有使用過 Linux 的人應該都知道,無論是哪種 Distribution 都有自己的套件管理程式,譬如 Debian/Ubuntu 的 apt,或是 Arch 的 pacman。
macOS 的使用者也有 Homebrew,Windows 則有 chocolatey 與 scoop。這些套件管理程式讓我們可以很方便地安裝和更新常用的軟體。
但是你有沒有遇過一些情況?
同樣是通過 Homebrew 安裝,但是某些套件的版本互相不支援(例如 Python 3.10 與 3.11 的路徑衝突); 或是換了新電腦後,忘記自己曾經裝過哪些套件和 Library,導致在跟合作者討論專案時,對方無法複製你的環境,甚至引發更多「在我的電腦上明明可以跑」的問題。
當然,要讓你的程式開發環境獨立,一個常見的方式是使用 Docker。
但是 Docker 比較龐大,有時候我們只是想要簡單安裝一些小工具,譬如說 neovim、imagemagick、gnuplot 或是特定版本的 git,
為此起一個 Container 實在有點殺雞焉用牛刀。
Nix 作為一個程式語言,同時也是一個套件管理系統,它的設計哲學基本上就是想要克服上述這些痛點。 它透過獨特的架構設計,實現了三大核心目標:
- 可重現性 (Reproducibility)
- 可靠性 (Reliability)
- 宣告式 (Declarative)
重現性: 不僅僅是安裝,而是「精確複製」 #
在傳統的套件管理器中,當你執行 apt install python 時,
你得到的 Python 版本取決於你什麼時候執行這行指令。
今天的 Python 可能跟一個月後的 Python 版本不同,這就是環境不一致的元兇。
Nix 採用了一種極端的作法:Hash Everything。
Nix 會將一個套件所有的「輸入」(包含源碼、編譯參數、依賴套件的版本)計算出一個唯一的 Hash 值。
安裝路徑不再是 /usr/bin/python,而是看起來像這樣:
/nix/store/26qyq4819gk074kmjzcrsl0b4i9n9qlm-python3-3.11.11/bin/python
只要 Hash 值一樣,無論在你的電腦、我的電腦,還是 CI/CD 伺服器上,這個軟體的行為就是完全一致的。這保證了 100% 的可重現性。
可靠性: 向「Dependency Hell」說再見 #
你有沒有經歷過因為更新了一個軟體,結果把另一個軟體弄壞的慘劇?
這通常是因為它們共用了某個 Library(例如 libssl),但需要不同的版本。
Nix 的 Isolation (隔離) 機制完美解決了這個問題。
因為每個套件都住在自己唯一的 /nix/store/HASH-名稱 路徑下,
Nix 允許同一系統中同時存在多個不同版本的軟體或 Library,而且它們彼此互不干擾。
- 專案 A 需要 Node.js 14
- 專案 B 需要 Node.js 20
在 Nix 中,這兩者可以和平共存,完全不需要像 nvm 或 virtualenv 那樣頻繁切換全域環境。
此外,Nix 的操作是原子性 (Atomic) 的,升級失敗不會導致系統毀損,且隨時可以「瞬間回滾 (Rollback)」到上一個正常的狀態。
宣告式: 寫設定檔,而不是敲指令 #
這是 Nix 最強大也最現代的特性。
傳統的維護方式是指令式的:你必須記得敲過哪些指令 (apt install git, pip install numpy…)。
如果你忘了紀錄,下次重灌電腦就得憑記憶重來。
Nix 提倡宣告式的管理。你只需要寫一個設定檔(通常是 .nix 檔案),
告訴 Nix 你「想要」什麼,剩下的交給它。
範例一:基本 shell.nix
#
只要在你的專案資料夾下放入一個 shell.nix 檔案:
{ pkgs ? import <nixpkgs> {} }:
pkgs.mkShell {
packages = [
pkgs.python311
pkgs.poetry
pkgs.postgresql
];
}
你可以把這個設定檔放在 github,或是傳給別人,任何人只要安裝完 Nix 後,在資料夾內輸入:
nix-shell
Nix 就會自動下載並提供 Python 3.11、Poetry 和 PostgreSQL 的環境。
一旦離開這個 Shell (輸入:exit),這些工具就「消失」了,不會污染你的全域系統。
也可以使用
direnv,只要進入該路徑,就會自動啟用shell,離開該目錄就會自動退出。
範例二: 稍微複雜一些的 shell.nix
#
這個範例是拍拍君在使用 python時最常使用的基本環境。
let
nixpkgs = fetchTarball "https://github.com/NixOS/nixpkgs/tarball/nixos-24.11";
pkgs = import nixpkgs { config = {allowUnfree=true; }; overlays = []; };
myPython = pkgs.python312;
pythonWithPkgs = myPython.withPackages (pythonPkgs: with pythonPkgs; [
ipython
ipykernel
pip
setuptools
virtualenv
virtualenvwrapper
wheel
]);
lib-path = with pkgs; lib.makeLibraryPath [
libffi
openssl
stdenv.cc.cc
# If you want to use CUDA, you should uncomment this line.
# linuxPackages.nvidia_x11
];
in
pkgs.mkShell {
packages = [
pythonWithPkgs
pkgs.libgcc
pkgs.gcc
pkgs.python311
pkgs.lolcat
pkgs.vscode
pkgs.git
pkgs.git-lfs
pkgs.openssh
pkgs.rsync
];
GREETING = "Python Enviroment (default)!!!";
shellHook = ''
SOURCE_DATE_EPOCH=$(date +%s)
export "LD_LIBRARY_PATH=$LD_LIBRARY_PATH:${lib-path}"
VENV=.venv
if test ! -d $VENV; then
virtualenv $VENV
fi
source ./$VENV/bin/activate
export PYTHONPATH=`pwd`/$VENV/${myPython.sitePackages}/:$PYTHONPATH
echo $GREETING | lolcat
alias vi=nvim
'';
}
安裝 Nix #
Nix 支援 Linux, MacOS, Windows (WSL2), Docker 等環境。 可以參考官方安裝網頁來安裝。
以macOS來說,只需要打開終端機後輸入下面指令就可以安裝了:
sh <(curl --proto '=https' --tlsv1.2 -L https://nixos.org/nix/install)
結語 #
Nix 的學習曲線確實比其他套件管理軟體陡峭一些,因為它不僅是一個工具,更是一種管理軟體的思維轉變。
但如果你厭倦了:
- 每次換電腦都要花一整天設定開發環境。
- 因為升級某個套件導致專案跑不動。
- 團隊成員之間的環境版本永遠對不上。
那麼,Nix 絕對值得你投入時間學習。
它讓你的系統更乾淨、更可控,並且讓「環境設置」變成可以被版控的程式碼。
有時候不確定怎麼寫 .nix 檔案時,往往為LLM模型都可以輕易的解決你的問題。
預告 #
讀完這篇入門,你可能已經感受到 Nix 在套件管理上的威力,但這只是冰山一角。
如果不搭配生態系中的其他工具,每次都要手動輸入 nix-shell 其實還是有點麻煩的。
在後面文章中,我們將介紹 Nix 生態系中不可或缺的一些進階使用, 讓你的開發體驗從「手動排檔」升級到「自動駕駛」:
-
Home Manager (管好你的 Dotfiles)
還在手動複製貼上
.zshrc或.vimrc到新電腦嗎? Home Manager 讓你用 Nix 語言統一管理所有個人設定檔。 換了電腦,一行指令就能還原你熟悉的 Terminal、Git 設定與軟體清單。 -
direnv (自動化環境切換)
忘記輸入
nix-shell怎麼辦?搭配direnv, 當你 cd 進入專案目錄的瞬間,所有需要的開發工具(Node, Python, Go 等)會自動載入; 離開目錄後自動卸載。這才是真正的「無感」環境切換。 -
Nix Flakes (現代化的專案標準)
這是 Nix 的未來標準。它解決了舊版 Nix 頻道(Channels)版本不固定的問題, 提供了
flake.lock檔案(類似package-lock.json), 確保你的專案在十年後依然能構建出完全一模一樣的結果。
敬請期待下篇文章。