不可变性(Immutability)是 Nix 体系的基石之一。它确保了所有软件包、依赖和环境在安装后不会被修改,从而带来了安全性、可复现性、回滚能力和多版本共存的特性。
传统包管理 | Nix(不可变) |
---|---|
软件可以被修改,可能导致不可预测的行为 | 软件不可变,保证一致性 |
升级可能破坏系统,很难回滚 | 升级不会影响旧版本,可随时回滚 |
不同软件可能冲突(lib 版本问题) | 所有软件独立存储,不会相互干扰 |
依赖可能被其他软件意外更新 | 所有依赖固定,不会意外更改 |
安装影响全局系统 | 每个用户可以拥有自己的环境,不影响其他人 |
1. 为什么不可变性重要?
在传统的软件管理方式(如 apt
、yum
、brew
)中,软件安装在系统的全局目录(如 /usr/bin
、/lib
),这种方式带来了一些严重的问题:
- 版本冲突:如果 A 依赖
libX v1.2
,B 依赖libX v2.0
,你可能无法同时安装 A 和 B。 - 升级破坏系统:升级某个软件可能会影响甚至破坏其他程序,因为它修改了共享库。
- 难以回滚:如果更新后出问题,你无法轻松回到旧版本。
- 不可复现:即使你使用相同的安装命令,不同时间点的安装可能会导致不同的结果。
Nix 通过不可变性彻底解决了这些问题,所有软件都安装到一个唯一的、基于哈希的目录中,从而避免了冲突和意外修改。
2. 不可变性在 Nix 中如何实现?
Nix 采用内容寻址存储(Content-Addressable Storage),确保每个软件包的路径是唯一的、不可变的。
(1) Nix Store
所有软件包都被存储在 /nix/store
目录中,每个软件的路径包含一个基于内容的哈希值:
/nix/store/<hash>-<package-name>-<version>
例如:
/nix/store/7l5jybz5wv2pl6b1zfmj43jzpfvhw3m4-python-3.9.2
/nix/store/s97kf8hx1lzn6ajyq3zvbfzqflpzx9bv-openssl-1.1.1
这个 <hash>
是通过源码、依赖、构建环境计算得出的,确保:
- 相同输入 → 相同输出
- 不同的依赖或编译参数 → 生成不同的哈希
- 所有软件都是不可变的,一旦存入
/nix/store
,不会被修改
如果你重新构建 Python 但修改了 OpenSSL 依赖,它会生成一个新的 Python 版本:
/nix/store/abc123-python-3.9.2 # 旧的
/nix/store/xyz789-python-3.9.2 # 重新编译后的
旧版本不会被覆盖,确保不会破坏已有软件。
(2) 依赖的不可变性
传统 Linux 依赖:
libX.so -> /usr/lib/libX.so (可能被覆盖)
Nix 依赖:
/nix/store/xyz789-libX.so (永远不会被覆盖)
- 在 Nix 中,每个软件直接指向其依赖的
/nix/store
路径。 - 由于
/nix/store
下的文件不会修改,所有依赖关系是完全稳定的。 - 即使某个库升级,旧版本的库依然可用,不会影响已有软件。
(3) 用户级软件管理
传统 Linux:
sudo apt install firefox
- 需要管理员权限
- 会修改
/usr/bin/firefox
- 可能影响其他用户
Nix:
nix-env -i firefox
- 不需要管理员权限
- 不会修改全局系统,仅影响当前用户
- 不同用户可以安装不同版本的 Firefox,而不会互相影响
(4) 原子性 & 回滚
因为 Nix 不会直接修改软件,所以可以实现原子性操作和回滚:
-
升级时,旧版本仍然存在,如果新版本有问题,可以立即回滚:
nix-env --rollback
-
配置环境也是原子的,你可以定义完整的 Nix 环境:
nix-shell -p python3 git
退出
nix-shell
后,环境就会恢复,不影响全局系统。 -
升级软件不会导致系统崩溃:
-
传统升级:
apt upgrade glibc
可能会破坏整个系统。
-
Nix:
nix-env -u glibc
旧版本仍然可用,系统不会被破坏。
-
(5) 垃圾回收(Garbage Collection)
由于 Nix 不会主动删除旧版本,因此你可以在需要时手动清理:
nix-collect-garbage -d
-
只删除不再被引用的软件,确保回滚能力。
-
你可以随时恢复被删除的软件:
nix-env --restore
3. 真实场景中的不可变性
(1) 数据科学
问题:如果你的 Python 依赖 NumPy v1.20
,而新安装的软件要求 NumPy v1.22
,传统方式可能导致环境崩溃。
Nix 解决方案:
nix-shell -p python3 numpy
- 你可以创建一个独立的 Python 运行环境,不影响系统的其他 Python 版本。
(2) DevOps & CI/CD
问题:同一个 apt install
命令,不同时间可能安装不同版本的软件,导致 CI/CD 失败。
Nix 解决方案:
nix-build
- Nix 锁定所有依赖的哈希,即使过了一年,你的 CI/CD 仍然构建相同的软件。
(3) 服务器运维
问题:升级 glibc
可能导致服务器崩溃,传统方式很难回滚。
Nix 解决方案:
nix-env -u glibc
nix-env --rollback # 如果有问题,立即回滚
- 不会影响正在运行的进程,确保服务器稳定性。
5. 总结
- Nix 采用不可变的软件管理,所有软件存储在
/nix/store
,不会修改。 - 多版本共存,避免依赖冲突(
libX v1.2
和libX v2.0
可以同时存在)。 - 原子性升级/回滚,防止系统崩溃,支持随时回滚。
- 不同用户可以有不同的环境,互不影响。
- 垃圾回收确保空间利用率,只删除不再使用的软件。
你对不可变性有更深入的疑问吗?还是想看看更多的实际应用?