diff --git a/content/blog/cluster-filesystem.md b/content/blog/cluster-filesystem.md new file mode 100644 index 0000000..4081265 --- /dev/null +++ b/content/blog/cluster-filesystem.md @@ -0,0 +1,96 @@ +--- +title: 集群的文件系统应该怎么做? +date: 2025-01-14T12:14:06+08:00 +summary: 让我捋一捋 +--- + +NixOS 的文件系统本来就比较复杂(加上 impermanence 之后)。 +集群又需要共享家目录。 +又考虑到集群里机器的 CPU 不一样(home-manager 等工具生成的配置文件指向的二进制在不同机器上不一定能用,因为编译时 `-march` 不同), + 搞得就很麻烦。 +前几天把一直分开运行的两个机器合并到一起了,但在合并前并没有非常仔细地思考文件系统怎么设计, + 于是成功地自己把自己绕进去了,出了一些自己也不理解的 bug。 +impermanence 的配置文件,自从我一年前把它写好之后就没有大动过,遇到需要的时候就得过且过修修补补,现在它已经很混乱了,是时候重新设计一下了。 + +按照我的设想,`/home` 中的文件主要来自两个地方。 +一个是 `/nix/persistent/home`,这些文件在每次重启后依然会保留;一个是 `/nix/rootfs/current/home`,这些文件在每次重启后都会丢失。 +只有少量的文件挂载或软链接自别的地方。 + +## 需求 + +明确需求才能提出合理的解决方案。 +最后解决方案需要满足所有的需求,也不需要过度设计,不要创造需求。 + +1. 只需要考虑 `/home` 挂载的问题。其它目录的挂载运行良好,不需要更改。 +2. 集群上,一部分文件需要共享,另一部分则不能共享: + 在集群中,`/home` 下的大多数文件需要在所有节点上共享(即,从机通过 NFS 来获得大部分文件)。 + 但是有一些文件和目录是不应该共享的,包括例如 `.config` `.zshrc` 等。 + 这些目录中包含许多 home-manager 生成的文件,其中指向的二进制文件在不同机器上不兼容。 +3. 可复现与留存状态的平衡: + 桌面用途下的 `chn` 用户是一个特例,`/home` 下的大部分文件应该在重启后丢失,只有少数留存。 + 除了这个特例以外,`/home` 下的大部分文件应该留存状态(即,在重启后不丢失,也即来自于 `/nix/persistent`), + 少部分文件(例如 `.cache`)应该为了确保可复现而丢失状态(即,在重启后丢失,也即来自于 `/nix/rootfs/current`)。 +4. 设置正确的权限和所有者。 + +基本上就这些。 + +## 解决方案 + +1. `/home` 以外的挂载代码不动。 +2. `/home` 本身不挂载。 +3. 在所有挂载开始之前,确认 `/nix/persistent/home/user` 和 `/nix/rootfs/current/home/user` 存在并设置合理的权限。 + 这通过 `system.activationScripts` 来实现。 + 这个设置会在两个情况下起作用:一个是在启动时,在挂载好了根目录但还没有 switch root 前进行;另一个是 rebuild 时进行。 + 这里已经有了一些内容,包括: + * `users` 是 nixpkgs 设置用户的一些内容(例如创建家目录)。 + * `createPersistentStorageDirs` 是 impermanence 生成的, + 用于在挂载前用正确的权限创建需要的目录(指 target,不是挂载点所在的目录),如果 target 已经存在则不检查权限; + 并根据 target 的权限调整挂载点及其父目录的权限。 + 在前者之后运行。 + * `persist-files` 也是 impermanence 生成的,用于挂载需要的文件(不涉及目录)。 + 仔细阅读之后,确认现在的代码应该已经足够,不再需要手动在这里添加代码。 +4. 挂载 `/home/user`。这分为几种情况: + * 在集群的主节点上,导出 `/nix/persistent/home`;再其它节点上,通过 NFS 挂载。 + 这个挂载需要在 `activationScripts` 之前完成(`neededForBoot = true`)。 + * 对于桌面用途的 `chn` 用户,不需要挂载。 + * 对于其它情况,挂载 `/nix/persistent/home/user` 到 `/home/user`。 + 这通过 nixos 的 impermanence 模块实现(以设置正确的权限)。 +5. 挂载更详细的目录。大部分使用 nixos 的 impermanence 中,`users.user` 来实现,有一个例外需要直接写 `systemd.mounts`。 + * 对于所有用户,`.cache` 需要在重启后丢失,它应该挂载自 `/nix/rootfs/current/home/user/.cache`。 + * 对于桌面的 `chn` 用户,有一些额外的目录需要挂载自 `/nix/persistent/home/chn` 或 `/nix/rootfs/current/home/chn`。 + * 对于集群的非主节点,需要采取额外的措施来避免覆写主节点的文件,包括: + * 禁用 home-manager 在家目录的根目录中创建的一些符号链接,包括 `.zshrc` 等;改而使用挂载。 + * 额外从 `/nix/rootfs/current/home/user` 挂载一些目录过来,例如 `.ssh`,home-manager 将需要覆写这些目录中的文件。 + * 由于 impermanence 会将 target 及其父目录的权限复制给挂载点,这会导致一个问题: + 挂载来自 nix store 的文件(即 home-manager 生成的那些文件)时,家目录会被改写为 `root:root 555`。 + 这些文件改为直接用 `systemd.mounts` 来挂载。 + +最终代码在[这里](https://github.com/CHN-beta/nixos/tree/741b6185a4412f0e45fc5afd13bcfadb91c5de7e/modules/system/fileSystems)。 + +## 其中的坑 + +我遇到了一些问题。 + +第一个问题是,如果 home-manager 生成的文件也用 impermanence 来挂载(最开始我就是这样做的), + 那么家目录的权限会在从机启动后被改写为 `root:root 555`。 +出现这个状况的原因在之前已经解释过了。 +通过将这些文件直接用 `systemd.mounts` 来挂载,这个问题可以被避免——然后我就发现家目录权限被改成了 `root:root 755`。 +为什么会出现这个状况,我想了很久才想通。这个过程是这样的: +1. 在 NFS 挂载之前:`/nix/rootfs/current` 已经被挂载到了 `/`,而 `/home/user` 和 `/nix/rootfs/current/home/user` 都不存在。 +2. 在 NFS 挂载时,会自动创建不存在的挂载点然后再挂载。 + 这样,在 NFS 挂载 `192.168.178.1:/nix/persistent/home/user` 到 `/home/user` 之后, + `/home/user` 的权限是远程文件系统的 `user:user 700`, + 但 `/nix/rootfs/current/home/user` 的权限是被自动创建的挂载点在挂载前的权限,也就是 `root:root 755`。 +3. 之后 `system.activationScripts` 中 impermanence 中的脚本会运行, + 它发现 `/nix/rootfs/current/home/user` 已经存在了,就不会再创建或者修改它的权限; + 然后它将 `/nix/rootfs/current/home/user` 的权限复制给 `/home/user`,这个行为也一并把远程的权限覆盖掉了。 + +直觉来说,只要告诉 systemd 创建挂载点时用某个用户、设定某个权限就好了; + 但我翻了一圈资料,似乎没有设定所有者的办法(权限是可以设定的,通过 `X-mount.mkdir` 选项)。 +所以最终解决办法是: +1. 先将 `192.168.178.1:/nix/persistent/home` 挂载到 `/remote/home`。 + `/remote/home` 的权限无所谓,随他便,这只是为了避免创建 `/nix/rootfs/current/home/user`。 +2. 再将 `/remote/home/user` 挂载到 `/home/user`,这一步通过 impermanence 进行, + 以设定正确的权限(`/home/user` 和 `/nix/rootfs/current/home/user` 的权限都会被设定为 `user:user 700`)。 +3. 之后,impermanence 再挂载其它来自 `/nix/rootfs/current/home/user` 的目录或文件,都不会出问题了。 +