我对 Nix 的初印象
Nix 是一款根据源文件来配置软件环境的工具。最近,我在 Hacker News 和 Twitter 上越来越频繁地看到关于 Nix 的讨论。它的理念很吸引我,所以在过去几周里我一直在摆弄它。
我与 Infrastructure as Code(基础设施即代码) 的渊源
十年前,我发现了 Salt,这是一款可以用源代码来定义计算机系统配置的工具。我很喜欢用一个 Git 仓库来定义我的电脑和虚拟机上安装了哪些服务这个想法。我可以清空电脑,重新运行配置工具,就能恢复到之前相同的状态。
我摆弄了几年 Salt,直到发现了 Ansible,我觉得它把同样的理念执行得更好。
我所有的开发工作都是在家用实验室服务器上的虚拟机里完成的。我为每个项目都创建了一个独立的虚拟机,并用 Ansible 来统一管理它们。
Ansible 的问题
Ansible 最大的问题是速度慢得令人痛苦。在我的一台虚拟机上运行一次 Ansible 通常需要 10 到 15 分钟。
假设我想安装一个新的 apt 软件包 foo。我是直接运行 sudo apt install --yes foo 在 5 秒内装好?还是打开我的 Ansible 角色,修改配置以添加安装 foo 的步骤,然后运行 playbook,再等待 15 分钟?显然,我更多时候会选择前者,所以我的环境会逐渐偏离本应描述它们的 Ansible 文件。
另一个问题是 Ansible 的变更不向后兼容。所以,如果我更新某个 playbook 以使用 Ansible 的新特性,就必须把所有 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,但 Nix 缓存了任务 X 和 Y 的结果,所以它们几乎是瞬间完成的。
Nix 针对本地配置进行了优化
Ansible 旨在通过网络配置系统。你仍然可以把 localhost 指定为目标,但那并不是 Ansible 优化的场景。
Nix 则是为配置其所在的环境而设计的。使用 Nix,你定义环境中应该包含什么,然后 Nix 会就地为你创建该环境。使用 NixOS,你定义整个操作系统,NixOS 会让操作系统进入该状态。你可以更改文件系统、Linux 内核或引导加载程序等底层内容。
这解决了我在使用 Ansible 时遇到的问题:把一个 Ansible playbook 升级到更新版本的 Ansible,就要求我升级系统上的所有 playbook。你可以让许多运行着不同版本 Nix 的 Nix 系统共存,且都能正常工作。如果我有一些需要 Ansible 2.9、一些需要 2.10、还有一些需要 2.14 的 Ansible 文件,那么要同时兼顾它们会非常麻烦。
Nix 的变更是原子性的
使用 Ansible 时,很容易在配置进行到一半时失败,使系统处于未定义的状态。
而使用 Nix,变更是原子性的。Nix 要么让你的系统进入期望的状态,要么回滚到你尝试更改配置之前的状态。
对我有帮助的 Nix 资源
我看到的关于 Nix 最大的抱怨之一是它文档不足、错误或质量低下。我的体会是,现有文档感觉像是面向有经验的 Nix 用户的。
我找到的很多 Nix 文档都会说这样的话:“只需添加这几行!”
哈?
哪个文件?在文件的哪个位置添加这几行?
以下是我目前找到的最好的资源:
- Zero to Nix:这是我找到的最好的 Nix 入门教程合集。它由 Determinate Systems 编写,该公司还撰写了对初学者友好的博客文章来介绍 Nix 的使用。
- NixOS for the Impatient:在此之前我曾尝试过几次安装 NixOS,但这篇文章终于让我相信这比我想象的要容易,并给了我最后的推动力来完成整个过程。
- “Some notes on using nix” by Julia Evans(朱莉娅·埃文斯):朱莉娅·埃文斯也是一位 Nix 新手,所以看到她遇到的障碍以及她如何在生态系统中仍有许多新奇陌生的情况下绕过这些障碍,对我很有帮助。
失败的尝试 #1:在虚拟机中安装 NixOS
我在 Proxmox 虚拟机上按照“NixOS for the Impatient”教程操作,一开始一切正常。然后,我按照教程修改了主机名并重启以使其生效。但虚拟机进入了一种奇怪的状态,看起来能启动却无法登录。在登录界面输入密码后,屏幕就直接卡住了。
NixOS 已在我的 Proxmox VM 服务器上成功安装,但在第二次启动登录后卡住了。
失败的尝试 #2:在 Raspberry Pi 4 上安装 NixOS
既然虚拟机行不通,我觉得裸机就是下一个合乎逻辑的选择。我手头正好有一台备用的 Raspberry Pi 4,我觉得在 Pi 上做实验会很有趣。
我找到了两份看起来很官方的在 Raspberry Pi 4 上安装 NixOS 的教程:
这两份教程的问题在于,它们都假设你已经在运行 Nix 环境了。我正试图用我的主力电脑来准备 microSD 卡,而那是一台 Win10 系统,所以我还没有安装 Nix。
NixOS 下载页面列出了一个 64 位 ARM 镜像。Raspberry Pi 4 支持 64 位 ARM,所以我想试试这个。
我打开了 Balena Etcher,这是我首选的 microSD 烧录工具。第一个危险信号是 Etcher 基本上在说:“嘿,你在想什么?那根本不是一个可启动镜像。”
我还是继续了!但当我尝试从 microSD 启动时,Pi 也认为这不是一个可启动镜像,所以就直接卡住了。
我尝试使用官方的 Raspberry Pi Imager 工具烧录同一镜像,但结果相同。
更新:我最终让它成功运行了。
成功:在 Dell Mini 电脑上安装 NixOS
我工作中大部分测试都是在一台 Dell Optiplex 7040 上进行的。这是我手头唯一一台可以清空重装的裸机,所以我就在它上面试了试。
一切都和“NixOS for the Impatient”中描述的完全一样。整个安装从开始到结束大约花了 10 分钟。其中 7 分钟只是在复制文件。由于这只是一台测试设备,我想省去每次重启都要输入密码的步骤,所以跳过了加密。
在 Dell Optiplex 7040 上安装 NixOS(我加速了文件复制部分)
最后,我得到了一个完整、可用的 NixOS 安装!
失败的尝试 #3:再次在 Raspberry Pi 4 上安装 NixOS
既然现在我有了一台可用的 NixOS 机器,我想再给 Raspberry Pi 一次机会。之前的阻碍是没有可用于准备 microSD 镜像的 Nix 环境,但现在我有了。
我按照 nix.dev 教程操作,这次比第一次进展更远,但仍然无法启动。Pi 只会显示一个多彩的屏幕,然后就卡住了:
当我从 NixOS 系统烧录 NixOS Pi aarch64 microSD 镜像并用它启动 Pi 时,它卡在了多彩屏幕上。
更新:我最终让它成功运行了。
任务 1:获取 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 系统了。
任务 2:移除 Gnome 杂乱组件
我对这个操作系统第一印象是里面有很多杂乱的东西。它内置了许多我不想要的应用,比如通讯录和天气应用:
我搜索了如何摆脱它们,发现它们是作为 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瞧!所有杂乱的东西都消失了:
任务 3:恢复 System Monitor(失败)
我觉得唯一值得保留的 Gnome 工具是 System Monitor。我尝试把它添加到我的 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
我所有的开发都是通过远程 SSH 在 VS Code 中完成的。当我尝试从 VS Code 远程连接到我的 NixOS 系统时,安装失败了。VS Code 必须在目标系统上安装某种服务器,而 VS Code 可能不知道如何在 NixOS 上安装。
有一个 nixos-vscode-server 的 Git 仓库,那可能就是我需要的解决方案。我还没有尝试过。
Nix 的主要概念如何协同工作
我看到像“flakes(flake)”和“derivations(derivation)”这样的词,目前还不知道它们是什么意思。我还不理解 Nix 的语言语法,但它和 JavaScript 和 Python 足够相似,让我目前还能勉强应付。但要有效地使用 Nix,我显然需要学习这门语言。
确定性何时发生?
当我看到关于 Nix 的讨论时,其中最常被提及的特性之一就是 Nix 具有确定性。
到目前为止,我还不明白它是如何确定性的。当我指定要安装的软件包时,我既没有指定完整性哈希,甚至连版本号都没有指定。如果一年后我运行同样的 Nix 配置,我猜我会得到一个不同的系统,因为它会安装不同版本的我指定的 vim 和 curl 软件包。
我想一定有更精确地指定软件包版本的方法,但我还没有学到。
我在信任谁?
当我指定软件包时,只是一个普通的软件包名称列表:
environment.systemPackages = with pkgs; [
vim
curl
];要让我能够像上面那样指定软件包,Nix 肯定是从默认仓库拉取软件包的。是否存在多个仓库?我该如何选择使用哪个仓库?
随机一篇博客





