NixOS 春节假期折腾之旅
春节在家刷到 ryan4yin 的博客,看完觉得 NixOS 挺有意思。折腾 Linux 这么久,/etc 下的配置文件改吐了,依赖冲突也踩了不少坑。系统一升级环境就炸,Docker 也只能临时救个急。
NixOS 让人强烈欲望入坑的特点
- 用一份
/etc/nixos/configuration.nix文件定义整个系统 - 每次系统配置更新,都会提供 Generation 防止错误配置无法回退
- 完全可复现的开发环境,只需要
git clone,不再需要pacman或apt
可复现特性基础
NixOS 中 /nix/store 目录是只读的,这个目录存储了所有通过 Nix 包管理器安装的软件包、库文件和配置文件。每个包都有一个唯一的哈希值路径,例如 /nix/store/l9i65cfgnxgrxlghxg792146w00kafcn-rocksdb-9.8.4/。
只读设计保证了软件包不会被乱改,同一个包的不同版本可以共存,系统更新出问题了也能直接回滚。这就是 NixOS 能做到完全可复现的关键。
安装 NixOS
我们可以从 NixOS 官方下载 Minimal ISO image,在一台全新的机器上安装 NixOS。或是通过 nixos-infect 脚本,一键将非 NixOS 的系统转换成 NixOS。后者在阿里云 ECS 上测试是可用的。
非常非常 Easy 的配置文件 /etc/nixos/configuration.nix,在下面的配置中,我们指定了 root 用户的 authorizedKeys 以及密码,配置了 ssh-server,配置了 vscode-server,主机名等信息都可以通过该配置文件完成。当把下面的文件写入后,只需要运行 nixos-rebuild switch,一切都完成了。
我的服务器通过手动划分磁盘,挂载 btrfs 后,执行 nixos-install 脚本安装。更详细的安装教程可以参考这篇博客:从 0 实现全集梦中情 OS。如果对于操作没有那么熟悉,可以现在虚拟机中尝试,后续想部署到服务器上,只需要复制配置文件到服务器,执行 nixos-rebuild switch,就都一模一样了。
# Edit this configuration file to define what should be installed on
# your system. Help is available in the configuration.nix(5) man page, on
# https://search.nixos.org/options and in the NixOS manual (`nixos-help`).
{ config, lib, pkgs, ... }:
{
imports =
[ # Include the results of the hardware scan.
./hardware-configuration.nix
# vscode server
(fetchTarball {
url = "https://github.com/nix-community/nixos-vscode-server/tarball/master";
sha256 = "09j4kvsxw1d5dvnhbsgih0icbrxqv90nzf0b589rb5z6gnzwjnqf";
})
];
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
# Pick only one of the below networking options.
# networking.wireless.enable = true;
networking.networkmanager.enable = true;
# 配置时区等机器信息
time.timeZone = "Asia/Shanghai";
i18n.defaultLocale = "en_US.UTF-8";
networking.hostName = "hostname";
nixpkgs.config.allowUnfree = true;
# 安装的软件
environment.systemPackages = with pkgs; [
openssl
wget
curl
nano
tailscale
];
# 设置默认编辑器
environment.variables = {
EDITOR = "nano";
};
# 配置 OpenSSH 和 Tailscale
services.openssh.settings = {
PermitRootLogin = "yes";
PasswordAuthentication = true;
};
# 启动系统服务,类似 systemctl enable --now xxx
services.openssh.enable=true;
services.tailscale.enable = true;
services.vscode-server.enable = true;
# Root 用户的配置
users.users.root = {
openssh.authorizedKeys.keys = [
"ssh-rsa AAAAB3NzaC......tTMFP5y8v8= therainisme@qq.com"
];
# 这密码我随便写的
password = "4355a46b19d348dc2f57c046f8ef63d4538ebb936000f3c9ee954a27460dd865";
};
system.stateVersion = "24.11";
}
开发环境配置(Devbox 与 Direnv)
在软件开发中,环境配置一直是令人头疼的难题。不同项目对编译器版本、依赖库、环境变量的差异化需求,常常导致"在我的机器上能运行"的经典问题。接下来将介绍基于 Devbox 和 Direnv 管理环境方案,通过两个典型 Go 项目案例,展示如何实现开发环境的精准控制。
实话说,这两个如果不是 NixOS 也能用,而且比在 NixOS 上还好用。
准备事项
在所有例子之前,必须安装 devbox 和 direnv,同时给 IDE 装上支持 direnv 的插件。如果使用的是 NixOS,添加如下配置文件,并 nixos-rebuild switch。
programs.direnv.enable = true;
environment.systemPackages = with pkgs; [
...
devbox
];
例子一:杂交的 Go 程序,开启 CGO 与 Rocksdb@v9.8.4 版本
需要编译使用 CGO 的 Go 程序,并链接 RocksDB v9.8.4 数据库。该场景的复杂性在于:
- 必须启用 CGO 支持
- 需要特定版本的 C 库(RocksDB)
- 依赖 pkg-config 查找头文件路径
首先进入项目的根目录,执行:
# 初始化 Devbox 项目
devbox init
# 添加精确版本依赖
devbox add rocksdb@9.8.4 # 指定 RocksDB 版本,不指定默认 @latest
devbox add pkg-config
devbox add gcc
# 生成 Direnv 配置
devbox generate direnv
此时打开 shell,输入 pkg-config --cflags --libs rocksdb 所有环境都已经配置好了,就这么简单。
$ pkg-config --cflags --libs rocksdb
-I/nix/store/l9i65cfgnxgrxlghxg792146w00kafcn-rocksdb-9.8.4/include -L/nix/store/l9i65cfgnxgrxlghxg792146w00kafcn-rocksdb-9.8.4/lib -lrocksdb
例子二:老旧的 Go 程序,必须使用 Go@1.20.3 版本
首先进入项目的根目录,执行:
devbox init
devbox add go@1.20.3 # 精确锁定 Go 版本
devbox generate direnv
此时打开 shell,执行以下步骤即可验证:
# 在项目目录内验证
$ go version
go version go1.20.3 linux/amd64
# 查看环境隔离效果
$ which go
/home/user/.devbox/nix/profile/default/bin/go
更多杂项配置
TODO
Nixpkg 相关资料
- https://search.nixos.org/options 搜索
/etc/nixos/configuration.nix文件的配置写法,例如安装 docker,根据它的描述,只需要添加一行即可:virtualisation.docker.enable = true; - https://search.nixos.org/packages 相关 nixpkgs 的搜索,只要包不是离奇的冷门,都能找到,不过找不到旧版本的包。比如说 Go 的 1.20 版本无法在 24.11 里找到。
- https://www.nixhub.io/ Devbox 的相关网站,可以找到最新的包和历史所有包。例如 Go 的 1.20 版本在这里。
Devbox 常用命令
devbox init: 为项目初始化 devbox, 运行后项目目录下会出现devbox.json文件,可以直接在文件中添加要使用的软件包,也可以像 npm 之类的包管理器一样使用devbox add添加软件包。devbox add: 添加软件包,后面可以跟有 @xxx 指定版本号,如devbox add rocksdb@9.8.4。dev search: 搜索软件包dev rm: 删除软件包devbox generate direnv: 自动为 direnv 生成.envrc文件,安装配置好后会自动载入环境。devbox shell: 手动进入环境终端devbox run: 手动运行命令(在devbox.json中shell.scripts字段下手动添加script,类似 node 的package.json中的 scripts)