↓ 跳过正文

NixOS(三):仓库架构——一个flake如何管理三台机器

锦李本鲤
作者
锦李本鲤
光锥之内,皆为命运。
NixOS系列 - 这篇文章属于一个选集。
§ 3: 本文

缘起
#

一台电脑的NixOS配置写好之后,如果还想把另一台电脑也换成NixOS,最容易想到的办法就是把配置复制过去,再改一下硬件相关的部分。

这种方式适合最初配置,但公共设置需要在多份文件中维护。例如,增加一个Shell设置或常用软件时,需要分别修改每台电脑的配置。如果遗漏了其中一份,配置就会出现差异。

所以我把几台电脑放进同一个flake里,公共配置写一份,每台电脑只保留自己的差异。这里用笔记本laptop、工作站workstation和服务器server三个示例说明,名字和路径都需要换成自己的。

这个系列最初从三台电脑的配置写起,现在同一套flake已经管理四台电脑。下面仍用三个虚构角色说明目录组织,实际配置并不限制主机数量。

如果还不熟悉flake,可以先看上一篇的概念介绍。

配置仓库的存放位置
#

安装NixOS之后,一般会在/etc/nixos里看到安装器生成的配置。不过日常使用时,配置仓库也可以放在其他位置,只要重建时指定它的路径:

1
sudo nixos-rebuild switch --flake /path/to/nixos-config#laptop

这样,这次构建使用的就是指定仓库里的flake。如果修改的是另一份配置,除非它被当前flake导入,否则不会影响这次构建。

我把配置单独放进Git仓库,方便查看历史、修改文件和同步到其他电脑。修改前需要确认重建命令使用的仓库路径。如果编辑的是/etc/nixos,重建时却指定了另一个目录,就需要检查这些文件是否由当前flake导入。

配置文件分类
#

一个简单的目录结构可以是:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
nixos-config/
├── flake.nix
├── flake.lock
├── modules/
│   └── common.nix
├── hosts/
│   ├── laptop/
│   │   ├── configuration.nix
│   │   └── hardware-configuration.nix
│   ├── workstation/
│   └── server/
├── home/
│   └── common.nix
└── docs/

其中flake.nix是入口,flake.lock记录依赖版本。其余的文件主要分成下面几类。

公共配置
#

几台电脑都会用到的设置,可以放在modules/common.nix。例如常用命令行工具、语言和时区、一些通用服务设置等。

公共模块只放几台电脑都需要的内容。例如,笔记本的桌面和电源设置不一定适合服务器,工作站的显卡驱动也应当保留在相应主机的配置中。

每台电脑的配置
#

每台电脑建立一个目录,例如hosts/laptop/。它的configuration.nix保存这台电脑自己的设置,后面会在flake里把它与公共模块组合起来。

显示器、笔记本合盖后的行为、特殊硬件支持等,通常放在这里。这样看到某台机器的配置时,就比较容易知道它和其他机器有什么区别。

硬件配置
#

hardware-configuration.nix是安装器生成的硬件描述,包含文件系统、swap和检测到的硬件模块等信息。这里可能有本机磁盘的UUID,不能把另一台电脑的文件直接拿来用。

如果存储布局发生了变化,可以在对应机器上重新生成一份,和仓库里的文件比较:

1
2
sudo nixos-generate-config --show-hardware-config > /tmp/hardware-candidate.nix
diff -u hosts/laptop/hardware-configuration.nix /tmp/hardware-candidate.nix

上面的做法先把结果写到临时文件,方便检查后再决定替换哪些内容。这里假设正在运行安装后的目标系统;如果还在安装U盘的环境中,就需要指定正确的目标根目录。

配置flake的主机输出
#

每台电脑都需要一个nixosConfigurations输出。由于创建输出的写法很相似,可以用一个函数省掉重复内容。例如,下面这段可以放在flake的outputs中(假设已经取得nixpkgs输入):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
let
  mkHost = hostModule: nixpkgs.lib.nixosSystem {
    system = "x86_64-linux";
    modules = [ ./modules/common.nix hostModule ];
  };
in {
  nixosConfigurations = {
    laptop = mkHost ./hosts/laptop/configuration.nix;
    workstation = mkHost ./hosts/workstation/configuration.nix;
    server = mkHost ./hosts/server/configuration.nix;
  };
}

这里的mkHost接收一个主机模块,再把它与公共模块一起交给nixosSystem。这是一个简化片段,不是完整的flake.nix。各主机模块还需要导入自己的硬件配置。

需要注意,上面的例子由mkHost统一导入公共模块,所以主机文件里不用再重复导入。也可以选择在主机文件中导入公共模块,只要整个仓库的写法保持清楚就行。

之后,在笔记本上就可以选择laptop输出:

1
sudo nixos-rebuild switch --flake .#laptop

在服务器上则选择server。修改公共模块会影响所有导入它的主机,所以只在一台电脑上重建成功,还不足以说明其他主机的配置也没有问题。

使用Home Manager管理用户配置
#

Shell、编辑器、终端等用户环境,可以交给Home Manager管理。它也可以像系统配置一样拆分:公共偏好放在一起,显示器布局等机器相关设置分别保存。

把Home Manager作为NixOS模块集成后,用户配置可以随nixos-rebuild一起应用,不需要再单独运行一次home-manager switch。

如果原来已经手动创建了配置文件,首次交给Home Manager管理时可能出现文件冲突。需要先备份已有文件,再检查如何处理;设置了自动备份扩展名时,也需要检查同名备份是否已经存在。后面介绍Home Manager时会详细说明这些操作,这里主要介绍用户配置的组织方式。

修改与检查配置
#

例如要修改笔记本的配置,可以先在仓库里看一下已有改动:

1
2
3
git status --short
git diff
git diff --cached

编辑相应文件之后,再检查并构建:

1
2
3
git diff --check
nix flake check --no-build --show-trace
nixos-rebuild build --flake .#laptop

如果新建了模块,记得把文件加入Git索引,否则本地Git flake可能看不到它。构建成功后,可以先用test试用,再根据结果执行switch:

1
2
3
sudo nixos-rebuild test --flake .#laptop
# 检查修改的功能是否正常,再执行下面的命令
sudo nixos-rebuild switch --flake .#laptop

内核和启动相关的修改,还需要重启验证。各条命令的具体区别见第五篇。

依赖更新可以单独做:运行nix flake update,看一下flake.lock的diff,再检查和构建。这样如果出了问题,比较容易分清是配置修改造成的,还是依赖版本发生了变化。

构建与运行记录
#

Git记录的是配置文件的修改,查看机器的实际运行状态还需要另外记录。例如,配置已经提交,构建仍然可能失败;执行了switch之后,如果尚未重启,也可能还在运行之前的内核。这些信息不能只通过Git日志判断。

因此,我会在构建和更新笔记中记录使用的提交、实际代次、是否激活、是否重启,以及仍需处理的问题。文件可以放在docs/里,按机器分别记录,方便以后查阅。

代次链接的时间只能作为线索,不能单凭它判断系统已经运行过。需要时还可以查看:

1
2
readlink -f /run/current-system
readlink -f /run/booted-system

旧代次被垃圾回收后,历史笔记仍然有用,不过笔记里存在的代次不一定还能回滚。当前可用的代次要另外检查。

这些记录自己以后会用到,交给AI继续维护时也有帮助。下一篇就介绍怎么把仓库里的约定告诉AI。

NixOS系列 - 这篇文章属于一个选集。
§ 3: 本文