初来乍到的NIX术语入门读物

2020-06-03 02:40:11

也许我只是在此之前没有注意到,但在过去的一年左右,围绕NIX包管理器的讨论似乎有所增加。这也是让我最终尝试的原因,我对结果非常满意,尽管道路有点颠簸。

我不相信我是唯一一个提到文档缺少一点完成的人。我个人发现,尼克斯允许你做的事情太多了,没有两个人做事情的方式是一样的。最重要的是,官方的Nix手册可能会尝试教你一大堆你在实践中不会用到的东西。

这篇帖子是一种尝试,试图帮助平整一下道路。我将尝试做的是自下而上地检查NIX术语,并希望以这种方式解释概念。但这在很大程度上是从我的角度来看的,也会包含一些观点。

设置承载可公开访问的Portier项目应用程序的NixOS服务器,其中包括打包Rust和Python工具和应用程序。此配置是公共的,可以在GitHub上的Portier/public-Infrastructure存储库中找到。

使用nix-darwinand home-manager设置我自己的MacBook,替换我的私有dotfile repo、vimlug、oh-my-zsh,以及我以前使用Homebrew所做的几乎所有事情。(我仍然使用自制的木桶。)。

但是,我仍然认为自己是个新手,所以一定要记住这里可能会有错误和不准确的地方!

NIX通常被称为包管理器,因为与其他包管理器一样,它的目的是管理您需要的所有工具和应用程序的安装。您会发现Nix稍微扩展了这个定义,它不仅用于管理软件包安装,还用于更小和更大的任务。

NIX本身的一部分是NIX表达式语言和一组命令行工具。

这是用来描述包构建的语言,它包含一些独特的语言结构来实现这一点。同样重要的是要知道,语言中的所有值都是不变的,并且语言本身不允许副作用(所有代码和函数都是纯的)。

我个人认为,作为用户使用NIX,80%是用NIX表达式语言做有趣的事情,20%是包管理。

字面意思是NIX语言中的一个表达,但这个术语经常在传递时使用。

在NIX语言中,表达式是顶级语言结构。(例如,与JavaScript/Python中的语句或C和Friends中的定义相反。)。

术语NIX表达式有时也用于描述包含它们的文件,并带有.nix后缀。值得注意的是,.nix文件不是指';库或';模块,而是更多地指用于加载它们的内置函数之后的';导入';。(例如:";从foo.nix.";导入Nix表达式)。

一组名称/值对。非常类似于Python中的字典或JavaScript中的对象。

虽然这是一个令人难以置信的基本概念,但我觉得我需要特别强调它的术语,因为在浏览手册时很容易遗漏它。

NIX语言中的一种特殊类型的值,本质上用于描述构建步骤。

派生是属性集的子类型,并使用语言内置函数派生创建。(不过,您最终通常会对其使用包装器。)。

派生接受一些输入并产生一些输出。由于延迟求值,仅当用NIX语言求值其中一个输出时才实际构建派生。当然,这也会导致输入被求值,而这又可能是更多的派生。这就是包在NIX中相互依赖的方式。

为NIX打包时,最常见的场景是派生有一个名为out的输出(缺省设置),并将其用作要打包的任何工具的安装前缀。因此,例如,可执行文件将以$out/bin结束。

同样重要的是要注意,派生不仅用于构建包,还用于构建任何东西,例如配置文件。

派生所需的输入之一是构建器,它负责实际生成输出。这通常只是一个shell脚本。

派生{=";hello-world";;=";x86_64-linux";;=[";out";];#默认,可以省略。=";${pkgs.bash}/bin/bash";;=[";-c";";echo';Hello World!';>;$out";];}。

为构建器分配了文件系统上的一些输出路径,每个声明的输出对应一个路径。在这里,我们只有out,然后该输出的路径在$out环境变量中可用。构建器可以在那里创建目录,但也可以只写入单个文件。

通过内容散列提前知道其输出的派生。

如果打包的基本步骤是下载您要打包的任何工具的源代码,那就是一种。在NIX中,这些步骤只是更多的派生,由FetChurl等包装器函数创建。

在没有任何验证的情况下简单地获取URL不是一个好主意,因为URL的内容可能会更改,从而破坏Nix试图实现的不可变的构建。更不用说:不验证下载的包管理器将是一个巨大的安全风险。

NIX通过添加内容散列来解决这个问题,这就是固定输出所指的内容散列。这看起来像是:

(这将调用函数FETCHULL,并将属性设置为其参数。FETFLULL的返回值是派生的。)。

因此,我们已经确定派生在文件系统上产生输出,但是这些输出在NIX语言中也是不变的值。这些值只是以下格式的字符串路径:

NIX在评估完所有输入之后,甚至在构建器开始运行之前就确定了这些路径。事实上,上面的构建器脚本中的$out变量已经是最终的/nix/store路径。

一旦评估,路径中的散列就是所有输入的内容散列。这意味着散列不仅包含所使用的依赖项和版本,还包含所有这些依赖项的特定构建。例如,即使只有用于构建包的编译器有较小的版本提升,它也会发生变化。

只要有可能,包就使用完整的/nix/store路径引用依赖项。NIX实际上不遗余力地修补上游工具的各个部分,以便将其应用到所有地方。例如,通过在NixOS系统上运行诸如ldd$(其中bash)之类的命令,您可以在共享库中看到这一点:

linux-vdso.so.1(0x00007fffc547e000)libreadline.so.7=>;/nix/store/ms1ris36xzyx9rzyss4h7pir759adc2d-readline-7.0p5/lib/libreadline.so.7(0x000071e760ba4000)libhistory.so.7=>;/nix/store/ms1ris36xzyx9rzyss4h7pir759adc2d-readline-7.0p5/lib/libhistory.so.7(0x000071e760b97000)libncursesw.so.6=>;/nix/store/kpw4kmc74djprg3bjc5rxblij46jdmnf-ncurses-6.1-20190112/lib/libncursesw.so.6(0x000071e760b26000)libdl.so.2=>;/nix/store/9hy6c2hv8lcwc6clnc1p2jf09cs5q9dp-glibc-2.30/lib/libdl.so.2(0x000071e760b21000)libc.so.6=>;/nix/store/9hy6c2hv8lcwc6clnc1p2jf09cs5q9dp-glibc-2.30/lib/libc.so.6(0x000071e760962000)/nix/store/9hy6c2hv8lcwc6clnc1p2jf09cs5q9dp-glibc-2.30/lib/ld-linux-x86-64.so.2=>;/nix/store/9hy6c2hv8lcwc6clnc1p2jf09cs5q9dp-glibc-2.30/lib64/ld-linux-x86-64.so.2(0x000071e760bf4000)。

语言中的不可变值和/nix/store中的不可变文件之间的这种匹配使NIX能够精确地描述包的非常具体的构建,甚至整个系统,包括它的所有依赖项。它还自然地提供了可重现的构建。

这些只是托管Nix可以下载并直接复制到/nix/store的预置派生输出。对于像glibc或GCC这样构建成本高昂的大型软件包来说尤其有用,但是您会发现官方打包的包中可能有90%以上都有可用的二进制文件。

一组可供用户使用的软件包,以便可以在shell中正常使用。有时也称为用户环境。

配置文件可以使用NIX附带的nix-env工具进行管理,但是许多NIX用户选择不同的解决方案。(像NixOS、nix-Darwin或home-manager,因为它们允许您在配置文件中声明性地写出所需的包。)。

用户的shell路径中有$HOME/.nix-profile/bin。然后,最终的用户环境目录包含所有安装的软件包的组合目录树。

编号符号链接编码';代';。这样,只需切换符号链接,就可以很容易地切换回以前的状态。(NIX-ENV-G<;NUM>;)。

同样,在NixOS上,您会发现有一个系统配置文件,其中安装了所有软件包,并且可以在系统范围内/对所有用户使用。

当你继续使用Nix时,它将继续在商店中建立文件,并继续建立老一代的配置文件。这些需要通过nix-store--gc或其包装器nix-Collect-垃圾清理。这可以通过简单地以一定的间隔(通常是每天)运行其中一个来实现自动化。

这个过程实际上非常简单:NIX扫描/nix/var/nix中的符号链接,并删除不再可以通过这些符号链接访问的任何存储项。该目录包含Profiles子目录,但也包含一个gcroots子目录,该目录可由构建在Nix之上的工具使用。所有这些符号链接都称为垃圾收集根或GC根。

从外部源获取的NIX表达式的集合。可与其他包管理器中的包注册表/存储库相媲美。

NIX通道由NIX附带的NIX通道工具管理。此工具只需下载并解压缩tarball,它必须包含要导入的default.nix。

通常,您会发现频道是直接从GitHub存储库中提取的,使用特殊的GitHub URL从主分支下载tarball。即使是官方的Nixpkgs频道也可以这样使用,尽管标准的Nix安装使用的是nixos.org镜像。

NIX-CHANNEL工具重复使用轮廓功能。每个用户的符号链接链又一次出现了:

在最终的用户环境目录中,您将按名称找到每个频道的符号链接,指向它们提取的内容。

类似于shell中的路径,但用于Nix语言导入。以分号分隔的搜索路径列表。

默认情况下,NIX_PATH至少包含/nix/var/nix/profile/per-user/root/channel,这意味着所有人都可以使用root注册的频道。(这样,root用户负责管理系统上的nixpkgs的主副本。)。

然后搜索nix_path中的每个目录以查找nixpkgs,并使用第一个命中。

当然,这可以用于任何目的,但最常被像NixOS这样的系统用来对配置位置进行编码:

NIX包的官方集合。Fresh Nix安装将有一个针对Nixpkgs的通道设置,通常位于root用户帐户中。

手动导入时,您将获得一个工厂函数,该函数接受包含可选配置的属性集。例如,此配置用于打开/关闭非免费套餐。该函数的返回值是包含所有可用包的属性集。(懒惰的评估让这变得很便宜。)。

但通常情况下,您不必手动导入Nixpkg,因为它是在其他地方配置的。查找名为pkgs的参数或变量,它将包含已配置的Nixpkgs。

Nixpkgs还用作Nix语言的标准库。Nix语言本身有一些内置函数,但是Nixpkgs在lib属性中提供了许多有用的扩展。

这些是自定义Nixpkgs的方法。覆盖是当前使用的方法,而FLACKS是一种新方法,其实现在撰写本文时仍处于实验阶段。

通常,这些允许添加新包或自定义现有包。要使用的覆盖集是提供给Nixpkgs工厂函数的配置的一部分,但有更多方法可以设置它。

此守护程序以root用户身份运行,但实际上并不计算Nix语言代码。但是,它确实执行实际的派生构建。即使构建被沙箱保护,通常只有一组选定的用户被允许在系统上使用NIX。

NixOS接受configation.nix,并使用派生为完整的Linux系统生成实际配置。过度简化后,可以将其视为/etc的大型构建步骤。此构建步骤会自动引入实现系统配置所需的任何包,就像任何其他依赖项一样。

最好将configation.nix的处理看作是在Nix包构建之上添加一个全新的层,这引入了所有新概念。但是这个新的层可以使用和修改包装构建的较低层。

这里看不到,因为这个链跳过了一个步骤,但是/nix/store中的这个内置实际上是系统配置文件的一部分。因为它使用下面的配置文件,所以它有可以回滚到的层代。(NIXOS-REBUILD开关--回滚)。

NixOS还可以为系统上的单个用户管理软件包。这些配置文件的工作原理有点类似于nix-env配置文件,但是它们分别位于/etc/profile中。

至关重要的是,Nixpkgs中的软件包只是软件的构建版本,并且不包含systemd单元文件。这就是模块的用武之地。

模块是在处理NixOS configuration ation.nix时加载的NIX表达式,并提供新的选项。NixOS预加载的标准模块通常有一个启用选项来激活它们,并且可以使用Imports=[.]加载自定义模块。在配置中列出。(不是语言级导入。)

启用后,模块可以修改NixOS配置的各个部分以及您自己的configation.nix,例如创建/etc文件和添加新的systemd单元。因此,这些通常会拉进包裹。例如,Nginx模块拉入Nginx包,并创建必要的systemd服务单元。

(您会发现home-manager和nix-darwin(稍后讨论)也使用模块概念,但该代码不在Nixpkgs中。)。

到目前为止,我们只讨论了构建时概念。例如,Nix之上的NixOS层还添加了一个运行时元素,因为服务需要启动/停止。NixOS使用激活这个术语来表示nixos-rebuild开关在切换配置时所做的所有额外的运行时工作。

其中一部分是名为逐字激活的脚本,可以在系统配置文件的TopLevel目录中找到该脚本。该脚本由NixOS和通过system.activate ationScriptsconfiguration选项启用的各种模块组成。(查找该脚本的一种快捷方法是使用/run/current-system符号链接,该符号链接还指向当前的systemprofile生成。)。

不过,实际上启动/停止服务是由一些Perl代码作为NixOS工具的一部分来处理的。

nix-Darwin的结构类似于NixOS:使用Darwin配置.nix,由模块组成,使用系统配置文件,并且有一个激活步骤。(达尔文-重建交换机)。

nix-darwin中的模块允许您有选择地接管部分MacOS配置,添加新的启动服务,当然还有安装软件包。

请注意,要启动并运行nix-Darwin,您首先需要按照手册中的说明进行多用户安装。但是NIX-达尔文之后将接管这个装置的管理,它的效果要好得多。不过,您可能会在MacOS上设置NIX时出错。快速提示:了解如何为/nix设置APFS卷,关闭该卷的Spotlight索引,并注意安装程序发出的有关/etc中未触及的文件的警告。(这些需要特别注意。)

同样,其结构类似于NixOS:使用home.nix,由模块组成,使用主管理器配置文件,并且有一个激活步骤。(家庭管理员交换机)。

home-manager中的模块允许您有选择地接管git、zsh、vim、Firefox等工具的配置,仅举几例,但列表很长。Home-Manager还可以为您设置systemd用户服务,当然还可以安装软件包。

Home-Manager同时支持Linux和MacOS,尽管在撰写本文时对MacOS的支持还不完全。值得注意的是,许多启动用户服务的模块还不知道启动。

在单用户系统上真正有用的是,您可以将您的家庭管理器配置嵌入到您的NixOS或nix-Darwin配置中。这也结合了激活,这意味着您根本不必触摸主管理器命令行工具。

我想要有一个章节来讨论NIX官方手册中提到的(非NixOS)安装方法,因为它们是一件事,但它们是不可靠的。在MacOS上,您必须经过这些步骤才能升级到nix-Darwin,我相信在其他Linux发行版上,它们可能是唯一受支持的方法。

其中一个选项是单用户nix安装,这意味着没有nix守护进程,并且/nix不属于root用户,而属于普通用户。手册本身已经建议不要这样做,因为Nix可以在此设置中构建沙箱。

另一种选择是多用户安装,它以root身份正确运行nix-daemon。不过,安装程序做这件事的方式有点一次性,至少在MacOS上是这样。一旦安装,实际上升级Nix本身就变成了一个非常手动且令人困惑的过程。

这里有很多东西我根本没碰过,比如NixOps或远程构建。主要是因为我自己还没有用过它们,我想知道我会不会用!但希望这也意味着这个列表是新用户感兴趣的一个更舒适的子集。

我个人发现真正强大的是将NixOS用于具有自定义应用程序的单个服务器。我从来没有想过在Debian上打包,但是NixOS打包相对容易,尽管有语言学习曲线。绕过Debian上的打包总是让我感到疑惑:";如果无人参与的升级推出了重要依赖项(如OpenSSL或Node.js)的新版本,我如何自动重新启动我的服务?此外,NixOS中的systemd集成令人惊叹,使自定义chroot变得轻而易举。

我认为对我来说,NIX最大的障碍是MacOS的安装过程。也许在其他Linux发行版上试用NIX也存在同样的问题?我觉得在NixOS之外的第一次很棒的体验可能真的有助于培养更多的兴趣。

具体地说,我认为Nix-Darwin应该有一个组合安装程序,独立安装方法应该注销为高级。

我的另一个抱怨是针对每个用户的频道管理。这很好,但是默认设置应该为只有一个用户的系统量身定做,因为我觉得这将是大多数安装?具体地说,我在Mac上将Nixpkgs频道移到了我自己的用户,而不是root用户。不得不切换到root来升级nix和nixpkgs真的很烦人。现在,我根本不需要sudo,而且只有达尔文-rebuild开关会向我索要密码。

尽管如此,我真诚地希望这一切都不会让你远离尼克斯,因为我被炒作了。就软件包和系统管理而言,它是未来主义的。在我看来,花在学习上的时间是值得的!