快轉到主要內容
  1. 教學文章/

擺脫「在我的電腦上可以跑」的魔咒:Nix Package Manager 入門

·5 分鐘· loading · loading · ·
Nix
每日拍拍
作者
每日拍拍
科學家 X 科技宅宅
目錄
nix - 本文屬於一個選集。
§ 1: 本文

前言: Nix: the package manager
#

有使用過 Linux 的人應該都知道,無論是哪種 Distribution 都有自己的套件管理程式,譬如 Debian/Ubuntu 的 apt,或是 Arch 的 pacman。 macOS 的使用者也有 Homebrew,Windows 則有 chocolateyscoop。這些套件管理程式讓我們可以很方便地安裝和更新常用的軟體。

但是你有沒有遇過一些情況?

同樣是通過 Homebrew 安裝,但是某些套件的版本互相不支援(例如 Python 3.10 與 3.11 的路徑衝突); 或是換了新電腦後,忘記自己曾經裝過哪些套件和 Library,導致在跟合作者討論專案時,對方無法複製你的環境,甚至引發更多「在我的電腦上明明可以跑」的問題。

當然,要讓你的程式開發環境獨立,一個常見的方式是使用 Docker。 但是 Docker 比較龐大,有時候我們只是想要簡單安裝一些小工具,譬如說 neovimimagemagickgnuplot 或是特定版本的 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 中,這兩者可以和平共存,完全不需要像 nvmvirtualenv 那樣頻繁切換全域環境。 此外,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 生態系中不可或缺的一些進階使用, 讓你的開發體驗從「手動排檔」升級到「自動駕駛」:

  1. Home Manager (管好你的 Dotfiles)

    還在手動複製貼上 .zshrc.vimrc 到新電腦嗎? Home Manager 讓你用 Nix 語言統一管理所有個人設定檔。 換了電腦,一行指令就能還原你熟悉的 Terminal、Git 設定與軟體清單。

  2. direnv (自動化環境切換)

    忘記輸入 nix-shell 怎麼辦?搭配 direnv, 當你 cd 進入專案目錄的瞬間,所有需要的開發工具(Node, Python, Go 等)會自動載入; 離開目錄後自動卸載。這才是真正的「無感」環境切換。

  3. Nix Flakes (現代化的專案標準)

    這是 Nix 的未來標準。它解決了舊版 Nix 頻道(Channels)版本不固定的問題, 提供了 flake.lock 檔案(類似 package-lock.json), 確保你的專案在十年後依然能構建出完全一模一樣的結果。

敬請期待下篇文章。

nix - 本文屬於一個選集。
§ 1: 本文

相關文章

Github Actions: 自動化你的工作流
·5 分鐘· loading · loading
版本控制 Github Git
Apple Vision Pro with M5 搶先體驗!
·8 分鐘· loading · loading
搶先體驗 VisionOS VisionPro XR VR
Python: 我需要進度條! tqdm
·3 分鐘· loading · loading
Python Tqdm Data Science
超好用的本地LLM: Ollama
·6 分鐘· loading · loading
好玩軟體 LLM Ollama
讓你的終端機華麗變身:Rich 套件教學
·2 分鐘· loading · loading
Python Rich Cli
開發的好習慣 Unit Test
·5 分鐘· loading · loading
Python Pytest Ci Unittest