nix

503 阅读8分钟

Nix 是一个功能强大的包管理器,采用函数式的方式处理包管理,支持多版本共存、原子升级、回滚等功能。

安装 Nix

首先,你需要安装 Nix。可以在大多数 Linux 发行版和 macOS 上安装 Nix。执行以下命令来安装 Nix:

curl -L https://nixos.org/nix/install | sh

安装完成后,运行以下命令来启用 Nix 的环境变量:

. ~/.nix-profile/etc/profile.d/nix.sh

基本命令

  1. 安装包

    • 使用 nix-env 命令可以安装软件包。例如,安装 hello 包:

      nix-env -iA nixpkgs.hello
      

      这里的 nixpkgs 是 Nix 的软件包集合,hello 是包名。-iA 表示安装包,并通过 nixpkgs 的属性名称进行安装。

  2. 列出已安装的包

    • 要查看当前用户环境中安装的包:

      nix-env -q
      
  3. 删除包

    • 删除用户环境中的包:

      nix-env -e hello
      
  4. 升级包

    • 升级所有已安装的包:

      nix-env --upgrade
      
  5. 搜索包

    • 在 Nix 包集合中搜索包:

      nix-env -qaP hello
      

      -qaP 会列出所有包,并显示它们的全名和属性路径。

  6. 查看包信息

    • 查看某个包的详细信息:

      nix-env -qaP hello --description
      
  7. 回滚到先前的环境

    • Nix 支持回滚到以前的环境。执行以下命令可以回滚到上一个环境版本:

      nix-env --rollback
      

使用 Nix Shell

Nix 还支持创建临时的开发环境,不会影响系统的全局状态。通过 nix-shell,你可以进入一个包含特定软件包的临时环境。

  1. 进入临时环境

    • 使用 nix-shell 命令可以进入一个临时环境。例如:

      nix-shell -p hello
      

      这将在当前 shell 中安装 hello 包,退出 shell 后,环境恢复到之前的状态。

  2. 使用 shell.nix 定义开发环境

    • 你可以通过 shell.nix 文件定义一个开发环境。shell.nix 文件的内容可以是:

      { pkgs ? import <nixpkgs> {} }:
      pkgs.mkShell {
        buildInputs = [ pkgs.hello pkgs.gcc ];
      }
      

      然后,在该目录下运行 nix-shell,它将启动一个包含 hellogcc 的开发环境。

Nix 表达式

Nix 使用自己的配置语言来描述包和环境,称为 Nix 表达式 (.nix 文件)。Nix 表达式可以用于定义如何构建软件包、配置开发环境等。

一个简单的 Nix 表达式例子如下:

{ pkgs ? import <nixpkgs> {} }:

pkgs.stdenv.mkDerivation {
  name = "my-hello";
  src = pkgs.fetchurl {
    url = "http://ftp.gnu.org/gnu/hello/hello-2.10.tar.gz";
    sha256 = "0ssi1wpaf7plaswqqjwigcsiipfrq1rzl9mr6j4cqi5zcpr1c9la";
  };
  buildInputs = [ pkgs.gcc ];
}

这个表达式定义了如何构建一个 my-hello 软件包,它从指定的 URL 下载源码,使用 gcc 进行编译。

Nix Store 和垃圾回收

所有 Nix 安装的包都存储在 /nix/store 目录下,每个包有唯一的路径,包含了所有的依赖信息。Nix 不会自动删除旧的包或未使用的包,你可以通过垃圾回收来清理未使用的包。

  1. 查看当前系统状态和旧的快照

    nix-env --list-generations
    
  2. 删除旧的环境快照

    nix-env --delete-generations old
    
  3. 垃圾回收未使用的包

    nix-collect-garbage
    

    如果你想强制删除所有不再使用的包,可以使用:

    nix-collect-garbage -d
    

Nix Channels

Nix 使用 channels 来管理软件包的更新。默认情况下,Nix 使用 nixpkgs 的稳定版 channel。你可以切换到其他 channel 来获取不同版本的软件包。

  1. 查看当前的 channel

    nix-channel --list
    
  2. 添加新的 channel

    nix-channel --add https://nixos.org/channels/nixos-unstable nixpkgs
    
  3. 更新 channel

    nix-channel --update
    

Nix 是一个非常强大的包管理器,近年来引入了 flakes 以改进其在开发、部署和系统管理中的可复现性、可组合性和易用性。


Flakes 是什么?

Flakes 是 Nix 的一个新特性,旨在解决 Nix 生态系统中一些长期存在的问题,如依赖管理、版本锁定、项目可复现性等。它引入了一个标准化的配置格式和更好的依赖管理机制,使项目变得更加可复现和易于分享。

Flakes 的主要特点:

  1. 标准化的项目结构

    • Flakes 提供了一个标准的项目结构,通常包括 flake.nixflake.lock 文件。这种结构让项目变得更加可预测和一致,特别是在依赖管理和版本控制方面。
  2. 依赖锁定(Locking Dependencies)

    • flake.lock 文件锁定了所有依赖的版本,确保项目的构建和运行环境是一致的。无论在哪台机器上运行,只要使用相同的 flake.lock 文件,结果就会是相同的。
  3. 改进的依赖管理

    • Flakes 可以轻松管理 Nix 包、NixOS 系统配置、开发环境、文档等,并且支持从各种来源获取依赖(例如 GitHub 仓库、URL 等),类似于现代的包管理工具(如 Rust 的 Cargo、Node.js 的 npm)。
  4. 可组合性(Composability)

    • Flakes 提供了一个通用的接口,使得不同的 Nix 配置和包可以通过 Flakes 轻松组合。你可以轻松引入其他项目的 flakes,并将它们与自己的 flakes 进行组合和复用。
  5. 无状态

    • Flakes 是无状态的,不依赖于用户的 Nix 环境或 channels。这意味着 flakes 项目在任何机器上都有相同的行为,不受用户本地环境的影响。

Flakes 的基本使用方式

1. 创建 Flake 项目

一个典型的 Flake 项目需要一个 flake.nix 文件和一个 flake.lock 文件。

flake.nix 示例:

{
  description = "My First Flake";

  inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";

  outputs = { self, nixpkgs }: {
    packages.x86_64-linux.hello = nixpkgs.lib.mkShell {
      buildInputs = [ nixpkgs.hello ];
    };
  };
}
  • inputs 定义了项目的外部依赖,比如 nixpkgs
  • outputs 定义了 Flake 的输出内容,比如一个包含 hello 包的 shell 环境。

2. 使用 Flakes

  • 初始化项目:

    nix flake init
    

    这将生成一个简单的 flake.nix 文件。

  • 进入 Flake 定义的 shell 环境:

    nix develop
    

    这会启动一个包含 flake.nix 中定义依赖的开发环境。

  • 运行 Flake 中的包:

    nix run .#hello
    

    这会运行 flake.nix 中定义的 hello 包。


Nix 的优点

Nix 作为一个包管理器和系统配置管理工具,具有很多独特的优点,主要体现在以下几个方面:

1. 可复现性(Reproducibility)

Nix 强调系统和软件包的 可复现性。所有包都存储在 /nix/store 目录中,通过其所有依赖的哈希值命名,确保相同的输入总是会生成相同的输出。这种方式使得软件包的构建在不同的机器上都可以得到相同的结果,避免了传统包管理器中依赖冲突或版本不一致的问题。

2. 多版本共存(Multiple Versions Coexistence)

Nix 允许同时安装同一软件的多个版本,因为每个版本的包都存储在 /nix/store 中的独立位置,并且不会影响其他版本。这在需要同时使用不同版本的库或工具进行开发时非常有用。

3. 原子升级和回滚(Atomic Upgrades and Rollbacks)

Nix 的设计使得系统的升级和回滚都是原子的。每次更改系统配置或安装新包时,Nix 会创建一个新的系统快照。你可以轻松地回滚到之前的快照,保证系统的稳定性和安全性。

  • 查看系统的历史快照:

    nix-env --list-generations
    
  • 回滚到之前的快照:

    nix-env --rollback
    

4. 隔离性(Isolation)

Nix 的包管理是完全隔离的。每个包的依赖关系都在构建时明确指定,并且 Nix 在独立的环境中构建这些包,避免了由于环境污染(例如,环境变量、系统库版本等)导致的构建失败或不可复现的构建结果。

5. 用户级包管理

Nix 允许用户在自己的环境中安装和管理软件包,而不需要管理员权限。这对那些没有 root 权限的用户非常有用。用户可以使用 nix-env 命令来安装、升级或删除软件包,且不会影响系统的其他用户。

6. 灵活的开发环境

通过 nix-shell,Nix 可以为开发者提供一个临时的开发环境,包含所有项目所需的依赖,而不会污染系统环境。开发者可以为每个项目定义不同的开发环境,确保开发环境一致,减少 "works on my machine" 问题。

nix-shell -p python3

这会启动一个带有 python3 的 shell 环境,退出后不会影响系统的状态。

7. Declarative 配置管理

Nix 支持声明式系统配置,特别是在 NixOS 上,整个系统的配置(包括软件包、服务、用户等)都可以通过一个 .nix 文件来定义。你只需要修改配置文件并运行 nixos-rebuild switch,NixOS 就会自动将系统调整为你定义的状态。

8. 跨平台支持

Nix 不仅适用于 Linux 和 NixOS,还支持 macOS。这使得 Nix 成为在不同平台上管理软件包和构建环境的统一工具,特别适合那些需要在多个操作系统上开发和部署的项目。


Nix 和 Flakes 的核心优势总结

  • 高度可复现:Flakes 锁定了依赖版本,保证了构建和运行结果的一致性。
  • 无状态:Flakes 不依赖于用户本地的 Nix 环境,确保项目在不同机器上的行为一致。
  • 可组合:Flakes 提供了灵活的组合能力,可以在不同项目之间复用配置和依赖。
  • 多版本支持:Nix 的多版本共存和隔离特性使得它非常适合开发和测试不同版本的库和工具。
  • 强大的包管理和系统管理能力:Nix 提供了灵活的包管理工具,同时通过声明式配置语言使得系统管理变得简单高效。