LWN:Nix和Guix简介!

关注了就能看到更多这么棒的文章哦~

A look at Nix and Guix

By Daroc Alden
February 27, 2024
Gemini translation
https://lwn.net/Articles/962788/

Nix 和 Guix 是基于声明式配置(declarative configuration)理念的两个不太寻常的软件包管理器。它们相应的 Linux 发行版是 NixOS 和 Guix System,更进一步地允许用户定义一个独立的集中式配置文件来描述整个系统状态。两者此前都已在 LWN 上提及过(https://lwn.net/Articles/891752),但没有进行很深入的报道。它们对于将软件包实现成不可变方式的核心思想提供了不同的方案。

在正常的 Linux 发行版中,系统上安装的一组软件包、服务和配置文件是使用一些不需要任何编程知识的工具来逐渐构建出来的。这种方法可能很方便,因为它可以让用户只需安装缺失的软件包就可以随时使用该软件包。这种方法的缺点在于,当需要迁移到新的主机时,使用相同的工具、服务和调整重建新系统会变得非常困难。大多数 Linux 发行版缺少集中式的记录,来介绍为什么每个软件包需要被安装上来或者每个配置选项为什么需要更改。

NixOS 和 Guix 提供了一种替代方案:在可编程的配置文件中指定你的设置,然后让包管理器来管理系统上可用的软件以实现目标。这些配置可以包含注释、版本控制和提取针对不同机器的通用部分的能力,这样以来就更容易追踪某个软件被配置或安装的原因是什么。

核心思想

Nix 是一个“”纯粹的函数式包管理器“”。跟其他包管理器很不一样的是,Nix 使用了名为 Nix 的通用编程语言进行配置。该编程语言经过优化,适用于描述软件包的任务。Nix 语言从 ML 系列 语言中汲取灵感,尤其以 Haskell 为主。与 Haskell 类似,Nix 程序利用不可变值(immutable values)来调用组合函数((“functional”部分)),并且这些函数不会产生副作用(所谓的“pure”),最终生成不可变值。执行 Nix 程序会生成一组供包管理器运行时的构建操作,但不会自行构建或安装任何内容。Nix 也是惰性的(lazy),只计算最终真有需求的数值。这使得 Nix 程序可以跳过那些最终不会被使用的可选依赖项的相关计算。

每个 Nix 程序描述了一个 derivation(制定在完全定义的输入文件上运行可执行程序,以可重复的方式在确定惟一的文件系统路径处生成输出文件所需的规范)。derivation 描述了要执行的具体构建任务,包括以必需依赖项、构建工具、源代码等形式存在的针对构建任务的所有必需输入信息。Nix 不允许构建任务之间存在隐式依赖项,要求必须显式指定所有先决条件。

使用这种方式的好处在于,系统的整体状态都在相同的位置使用相同的语言来指定。该配置不仅包括已安装的软件包,还包括预定用于 /etc 的配置文件、systemd 服务文件、文件系统 mount 点、用户、内核配置选项、驱动程序等。例如,这里有一个用于运行 nginx 的配置模块,它会构建 nginx,设置其配置文件,为 nginx 本身设置 systemd 服务,并创建用于使用 LetsEncrypt 更新 HTTPS 证书的 systemd timer 定时任务:

# This whole file evaluates to a single function from a set of
# arguments (config, lib, and pkgs are normally used, but there
# can be more which are ignored, thus "...") to a set of configuration
# values that themselves are used in calculating the build action for
# the overall system.
{ config, lib, pkgs, ... }:

{
  # ACME is the protocol that LetsEncrypt uses to give out certificates
  security.acme.acceptTerms = true;
  security.acme.defaults.email = "example@example.com";

  services.nginx = {
    enable = true;

    # Options can enable a bundle of related, more detailed settings
    recommendedTlsSettings = true;
    recommendedOptimisation = true;
    recommendedGzipSettings = true;
    recommendedProxySettings = true;
  };

  # nginx can serve multiple virtual hosts with their own configurations
  services.nginx.virtualHosts."www.example.com" = {
    http2 = true;
    enableACME = true;
    forceSSL = true;

    locations."/" = { root = "/var/www/example.com"; };
  };
}

实现这些选项的代码在 NixOS 的软件包存储库 - nixpkgs 中定义,与其它配置动作使用相同的语言。要真正熟练掌握 Nix 可能需要一些时间,但大多数配置比较简单,而且NixOS 手册提供了常见设置的示例。还可以使用选项搜索或 nix search 命令行工具来搜索配置选项和可用软件包。

导出物几乎全部是可再现的(reproducible),相同的输入会生成相同的输出。有些软件包仍然包含随机性或关于构建时间的的信息,这会破坏可再现性。人们正在致力于为这些软件包打补丁,以实现项目完全可再现构建的目标。这与 derivation 记录了构建过程的所有输入这个特点相结合,于是 derivation 的产出结果被缓存下来。Nix 将先前执行过的 derivation 的缓存保存在 /nix/store/ 下,按照 derivation 的输入数据的哈希值来进行索引。

Nixpkgs 包含了超过 80,000 个软件包的定义。这些软件包由 NixOS 的构建服务器 - Hydra 进行构建、缓存和共享。因此,尽管 NixOS 配置描述了如何从头获取和构建所有选定软件,但如果用户依赖 nixpkgs 中的软件包定义,他们很可能可以直接从 Hydra 下载 derivation 的缓存的输出内容。因此,NixOS 将自身称为源代码和二进制混合发行版 — 用户可以从头构建软件(包括像 Gentoo 用户一样调整构建flag),但大多数用户只需下载他们大多数软件包的二进制版本,并且知道他们获得的结果是跟从头构建的结果完全相同的。

多个版本

通过这种方式构建系统,另一个好处是可以依赖同一个软件包的多个版本。由于每个 derivation 明确引用了其构建输入,因此不需要同时存在一个单一的全局版本编译器或共享库来满足每个依赖包的需求。这使得保持满足特定需求的旧软件平稳运行就更容易了。这样一来,在公共依赖项目发生重大更新时,可以减轻一次性更新生态系统中每个软件包的负担,并且这意味着维护人员不必解决版本冲突。然而,这是一把双刃剑,因为它确实意味着很容易无意中依赖了软件包的多个版本,这会增大 NixOS 安装环境的空间占用。

能够保留软件包的多个版本实际上扩展一下,就实现了保留系统整个配置的多个不同版本。在使用 Nix 重新构建系统时,先前配置的 derivation 仍然存在,并且可以使用引导加载程序切换到它。因此,用户可以放心进行升级,因为在尝试新配置时引发的任何问题都可以通过重新启动来解决。通过同样的原理,对于那些只需在短时间内使用的软件(例如尝试新程序或获取一个很少使用的命令行工具)就可以将其安装到临时环境中,而不会干扰到整个系统配置。

人们经常可以通过做一些不同寻常的事情,使 Linux 系统变得无法使用了。因此这个特性就非常重要了。虽然可以从解决这些问题中学到很多东西,但在需要一个可运行的系统时出现问题的话,会非常麻烦。

像 NixOS 那样直接引用依赖项会导致对未针对该发行版打包的软件出现问题。与典型的发行版不同,NixOS 完全只使用 /usr/bin 来进行env 动作,目的是允许支持从 #!/usr/bin/env 开始的 Shell 脚本。对于那些假设系统二进制文件存在于 /usr/bin 或其他标准路径 下的 Shell 脚本或其他程序来说,通常需要调整才能在 NixOS 上运行。然而,有一些工具可用来在预期的路径中放置二进制文件的 chroot 环境,因此这个麻烦并不大。

Guix

Nix 包管理方法有其优点,但 Nix 语言也有其奇怪的地方。学习使用一种新的编程语言和一种新的包管理理念,这可能会显得难度很大。Guix 项目致力于采用 Nix 方式来管理软件包,但使用一种更广泛使用且通用一致的编程语言:Scheme。Guix 是一个 GNU 项目,因此仅打包自由软件(与 Nix 相反,Nix 可以通过配置选项来打包非自由软件)。它拥有的贡献者也比 Nix 少得多,也是一个相对年轻的项目,它是在 2012 年开始了第一笔提交,而 Nix 可以追溯到 2003 年。因此在 Guix 中可用的软件包要少一些。

尽管如此,Guix 自身也取得了一些有趣的进展,包括完全可用的 自举构建(bootstrappable builds)。现在,Guix 几乎可以完全 从源代码 在 x86_64 上完成自身构建,这是该项目的一个重要里程碑。Ekaitz Zárraga 在 FOSDEM 2024 上发表了演讲,讨论了 扩展该支持到 RISC-V。Guix 还具备一些 NixOS 没有的特性,例如将安全更新“合并”到包中的能力。可以把一个程序包的运行时依赖的版本更新到新版本上(大概是为了修复一些安全问题),而无需重新编译所依赖的程序包。这样以来用户就可以在不等待构建服务器重新便宜 Guix 的所有程序包的情况下,对一些基础库进行安全更新。

与上面给出的 Nix 配置类似,Guix 配置的一部分看起来像下面这样:

; Guix defines types and helper functions in their own Scheme modules
(require (gnu services web)
         (gnu services certbot))

; This hook sends a signal to nginx to make it re-load its configuration,
; including the updated HTTPS certificates
(define %nginx-deploy-hook
  (program-file
   "nginx-deploy-hook"
   #~(let ((pid (call-with-input-file "/var/run/nginx/pid" read)))
       (kill pid SIGHUP))))

(define %my-nginx-service
  (service nginx-service-type
     (nginx-configuration
       (server-blocks
         (list (nginx-server-configuration
                 (server-name '("www.example.com"))
                 (root "/srv/http/www.example.com")
                 (ssl-certificate
                   "/etc/letsencrypt/live/www.example.com/fullchain.pem")
                 (ssl-certificate-key
                   "/etc/letsencrypt/live/www.example.com/privkey.pem")))))))

(define %my-certbot-service
  (service certbot-service-type
           (certbot-configuration
            (email "example@example.com")
            (certificates
             (list
              (certificate-configuration
               (domains '("www.example.com"))
               (deploy-hook %nginx-deploy-hook)))))))

此配置有一些不同之处。首先,Guix 使用 GNU Shepherd 作为 init 系统,而不是 systemd。另外,此处显示的代码不是由主配置文件导入的独立文件。相反,用户需要导入此文件,然后手动将其系统配置的服务列表中添加 %my-nginx-service 和 %my-certbot-service 。

如何尝试

Nix 和 Guix(包管理器)可以在其他发行版上使用。某些发行版(例如 Ubuntu 和 Debian)提供了这两者的软件包。对于其他发行版的用户,Nix 提供了 安装说明 和 卸载说明。而 Guix 只提供 安装说明,但维护者 Tobias Geerinckx-Rice 提供了非常有价值的信息: 他人能直接阅读安装程序脚本,就可以知道应该移除什么内容。

无论哪种情况,在其他发行版上使用包管理器对于创建临时环境非常有用,即使它们不能提供全系统配置。各个编程语言通常为临时开发环境提供了一个解决方案(例如,Python 的 虚拟环境)。现在许多项目都将一个 shell.nix 文件作为一个与语言无关的替代方案,它可以安装不是由这种语言的包管理器来打包的依赖项。为了相同的目的来提供 guix.scm 文件的就比较少了,但确实还有几个 GNU 项目在这样做。

通过重新思考包管理的实现方式,NixOS 和 Guix 为传统发行版提供了引人注目的的替代方案。它们的设计使得可以支持难以置信的灵活性,简化了发行版维护者的包管理,并使用二进制包给用户更多对其计算机配置的控制权。尽管如此,这两者似乎都不太可能取代传统包管理器,特别是当其他发行版使用 其他技术 获得了可复制性所代表的部分好处时。

全文完
LWN 文章遵循 CC BY-SA 4.0 许可协议。

欢迎分享、转载及基于现有协议再创作~

长按下面二维码关注,关注 LWN 深度文章以及开源社区的各种新近言论~

d98c991b4d65bb1c19688e57a33c4899.jpeg

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值