在 jq 同学的一再安利下,我终于也入坑 NixOS 了)))
什么是 NixOS
NixOS 是独立开发的 GNU/Linux 发行,它旨在改进系统配置管理的现状。在 NixOS 中,整个操作系统,包括内核、应用程序、系统软件包、配置文件,统统都由Nix包管理器来创建。Nix 将所有软件包以彼此分离的方式进行存储,因此就不存在/bin
、/sbin
、/lib
、/usr
之类的目录,而是保存在/nix/store
中。NixOS 的其他创新特色包括可靠升级、回滚、可重现的系统配置、二进制代码基于源文件的管理模型、多用户包管理。
对于我来说,用 NixOS 的好处就是:
- 体验一把用程序写配置文件(?)
- 方便进行 VPS 的迁移和一键部署,使用 NixOS 只需要根据配置代码进行生成部署即可。
可以复制粘贴 jq 写的配置代码
安装 NixOS 至 ESXi 中
虽然可以直接使用其它发行版的 Linux 到 NixOS 的转换脚本,但是我的 ESXi 里也没装正经 Linux,所以不如直接全新安装。
- 下载 NixOS Minimal ISO image (64bit),上传到 ESXi 的 datastore 里。我下载的是21.11版本。
- 在 ESXi 中创建一个新的虚拟机,CD/DVD Drive 选择刚才 NixOS ISO 镜像,Boot Options 选择 BIOS(省事),并配置网络,开启虚拟机。
- 切换为root用户
1
sudo -i
- 检查网络配置。ESXi 会虚拟一个有线网络,使用
ifconfig
命令查看是否拿到 IP 地址。 - 创建分区表、格式化分区、挂载,创建一个 19G 的基本分区和一个 1G 的 Swap 分区。
1
2
3
4
5
6
7[root@nixos:~]# parted /dev/sda -- mklabel msdos
[root@nixos:~]# parted /dev/sda -- mkpart primary 1MiB -19GiB
[root@nixos:~]# parted /dev/sda -- mkpart primary linux-swap -1GiB 100%
[root@nixos:~]# mkfs.ext4 -L nixos /dev/sda1
[root@nixos:~]# mkswap -L swap /dev/sda2
[root@nixos:~]# mount /dev/disk/by-label/nixos /mnt
[root@nixos:~]# swapon /dev/sda2 - 如果需要配置静态IP,使用如下命令。
1
2
3
4
5
6[root@nixos:~]# ifconfig <interface> <my-static-ip> netmask <my-netmask>
[root@nixos:~]# ifconfig <interface> down
[root@nixos:~]# ifconfig <interface> up
[root@nixos:~]# route del -net 0.0.0.0
[root@nixos:~]# route add default gw <gateway-ip>
[root@nixos:~]# echo "nameserver 8.8.8.8" >> /etc/resolv.conf - 开始安装。在安装的最后一步,会提示设置 root 密码。
1
2
3[root@nixos:~]# nixos-generate-config --root /mnt
[root@nixos:~]# sed -i 's#\# boot.loader.grub.device = "/dev/sda"#boot.loader.grub.device = "/dev/sda"#g' /mnt/etc/nixos/configuration.nix
[root@nixos:~]# cd /mnt/etc/nixos/ && nixos-install 重启虚拟机(断开 CD/DVD Drive),完成安装
如需在 GPT 磁盘中使用 UEFI 引导安装,
或者解锁各种花里胡哨的安装方法,请参考文档
Nix 基本语法
举例 | 描述 |
---|---|
基本类型 | |
"Hello world" |
字符串 |
"${pkgs.bash}/bin/sh" |
包含表达式的字符串(展开为"/nix/store/hash-bash-version/bin/sh" ) |
true , false |
布尔类型 |
123 |
整数 |
./foo.png |
路径 |
复合类型 | |
{ x = 1; y = 2; } |
集合,有 x 和 y 两个属性 |
{ foo.bar = 1; } |
嵌套集合,等价于{ foo = { bar = 1; }; } |
rec { x = "foo"; y = x + "bar"; } |
集合中可以使用运算,等价于{ x = "foo"; y = "foobar"; } |
[ "foo" "bar" ] |
列表,有两个元素 |
运算符 | |
"foo" + "bar" |
字符串连接 |
1 + 2 |
整数相加 |
"foo" == "f" + "oo" |
判断相等 |
"foo" != "bar" |
判断不相等 |
!true |
逻辑取反 |
{ x = 1; y = 2; }.x |
选取属性 |
{ x = 1; y = 2; }.z or 3 |
选取属性,缺省值为3 |
{ x = 1; y = 2; } // { z = 3; } |
集合合并(右侧会覆盖左侧) |
控制结构 | |
if 1 + 1 == 2 then "yes!" else "no!" |
条件表达式 |
assert 1 + 1 == 2; "yes!" |
断言检查,如果断言成立,则返回分号后的内容 |
let x = "foo"; y = "bar"; in x + y |
变量定义 |
with pkgs.lib; head [ 1 2 3 ] |
将给定集合中的所有属性添加到当前域 |
函数 (lambdas) | |
x: x + 1 |
传入整数x ,返回x+1 的函数 |
(x: x + 1) 100 |
函数调用,传入x=100 ,返回101 |
let inc = x: x + 1; in inc (inc (inc 100)) |
函数绑定到变量名,并嵌套使用 |
{ x, y }: x + y |
传入一个仅包含属性x 和y 的集合,返回x +y 的函数 |
{ x, y ? "bar" }: x + y |
传入一个仅包含属性x 和y 的集合(y 的缺省值为"bar" ),返回x +y 的函数 |
{ x, y, ... }: x + y |
传入一个包含属性x 和y 的集合(其他属性被忽略),返回x +y 的函数 |
{ x, y } @ args: x + y |
传入一个仅包含属性x 和y 的集合(绑定集合至变量args ),返回x +y 的函数 |
内置函数 | |
import ./foo.nix |
加载和返回 Nix 文件 |
map (x: x + x) [ 1 2 3 ] |
将函数应用到列表中的每一个元素 (返回值为 [ 2 4 6 ] ) |
开启 SSH 访问
通过 ESXi 的 Console 进行访问也不方便,毕竟 NixOS 也是 Linux,为什么不用简单好用的 SSH 来访问呢?其实 NixOS 自带了 SSH,但自动生成的配置也没有开启 SSH 访问,需要手动修改 configuration.nix
来开启 SSH 访问功能。1
2[root@nixos:~]# cd /etc/nixos
[root@nixos:/etc/nixos]# nano configuration.nix
其中有一行1
# services.openssh.enable = true
将其改为1
2services.openssh.enable = true
services.openssh.permitRootLogin = "yes";
保存文件。然后使用以下命令重新构建系统。1
[root@nixos:/etc/nixos]# nixos-rebuild switch
关于nixos-rebuild
命令的一些常见用法如下:
命令 | 描述 |
---|---|
nixos-rebuild switch |
根据新配置构建系统,使其成为 GRUB 启动时默认 profile,并尝试在当前系统中应用新的配置 |
nixos-rebuild switch -p test |
根据新配置构建系统,创建一个新的 profile,在 GRUB 启动页面显示为NixOS - Profile 'test' |
nixos-rebuild test |
根据新配置构建系统,并尝试在当前系统中应用新的配置,但不使其成为 GRUB 启动时默认 profile |
nixos-rebuild boot |
根据新配置构建系统,使其成为 GRUB 启动时默认 profile,但不并尝试在当前系统中应用新的配置 |
nixos-rebuild build |
根据新配置构建系统,但什么也不做,通常用来看是否编译正常 |
现在,可以使用ssh root@[虚拟机的IP]
命令,输入密码进行登录。
但是,如果我想使用证书方式登录,应该怎么配置呢?首先,得找个地方存储我们的authorized_keys
。1
2
3[root@nixos:~]# cd /etc/nixos
[root@nixos:/etc/nixos]# mkdir -p ssh
[root@nixos:/etc/nixos]# echo 'ssh-rsa AAAAB3NzaC...E= jogle@home' > ssh/authorized_keys
然后修改configuration.nix
文件,添加以下内容1
2
3users.users."root".openssh.authorizedKeys.keyFiles = [
/etc/nixos/ssh/authorized_keys
];
最后当然是重新构建系统。1
[root@nixos:/etc/nixos]# nixos-rebuild switch
如果证书配置正确的话,重新使用ssh root@[虚拟机的IP]
命令进行连接即可直接登录。
参考链接:NixOS Wiki - SSH public key authentication
配置静态 IP
有时候需要配置静态 IP,要不然没有网络连接,甚至都没法进行 nixos-rebuild
。配置静态 IP 仅需要编辑 configuration.nix
。1
2
3
4
5
6networking.interfaces.<interface>.ipv4.addresses = [ {
address = "192.168.1.2";
prefixLength = 24;
} ];
networking.defaultGateway = "192.168.1.1";
networking.nameservers = [ "8.8.8.8" ];
安装 Vim
系统里只自带了一个 Nano 文本编辑器,为了方便以后的 Nix 配置修改,所以我要安装一个 Vim。没有 Vim 用我快要死了)
在 configuration.nix
中可以找到:1
2
3
4
5
6
7# List packages installed in system profile. To search, run:
# $ nix search wget
# environment.systemPackages = with pkgs; [
# vim # Do not forget to add an editor to edit configuration.nix! The Nano editor is also installed by default.
# wget
# firefox
# ];
可以在environment.systemPackages
中声明要安装的包名,NixOS 就会自动为我们安装。这种方式称为声明式包管理(Declarative Package Management)。就像 apt-get
一样简单)
所以我们顺手把environment.systemPackages
改成1
2
3environment.systemPackages = with pkgs; [
nano vim wget curl
];
执行nixos-rebuild switch
后就可以在命令行里使用 Vim 了。
装完了 Vim,自然少不了配置.vimrc
,以及配置插件,那么如何配置呢?这里给出一种方法:
在/etc/nixos/
下创建一个vim.nix
文件,内容填写为1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18with import <nixpkgs> {};
vim_configurable.customize {
# Specifies the vim binary name.
# E.g. set this to "my-vim" and you need to type "my-vim" to open this vim
# This allows to have multiple vim packages installed (e.g. with a different set of plugins)
name = "vim";
vimrcConfig.customRC = ''
syntax on
set nu!
set autoindent
'';
vimrcConfig.packages.myVimPackage = with pkgs.vimPlugins; {
# loaded on launch
start = [ vim-airline fugitive ];
# manually loadable by calling `:packadd $plugin-name`
};
}
然后在configuration.nix
中,修改刚才的environment.systemPackages
为1
2
3environment.systemPackages = with pkgs; [
nano (import ./vim.nix) wget curl
];
使用 Fish Shell
在 configuration.nix
中添加一行,为 root 修改默认 shell 为 fish。1
users.users.root.shell = pkgs.fish;
执行nixos-rebuild switch
后,还是使用默认的 bash shell。这是因为nixos-rebuild
命令不会自动开启/关闭用户服务,而是为每一个开启了用户服务的用户执行daemon-reload
。所以,nixos-rebuild
无法直接打开一个新的 fish shell 来替换当前使用的 bash shell。
解决方法是:重启!
关于 fish shell 的插件配置,可以参考 NixOS Wiki - fish。
开启 Flakes
在刚才的配置中,我们只配置了软件包开启,但是没有指定软件包的版本。为了能对软件包的版本进行控制,需要开启 Nix Flakes。由于官方担心 Nix 2.4 功能变化过大,尤其是会与旧版 Nix 的行为不兼容,NixOS 21.11 仍使用 Nix 2.3,且默认禁用 Flake 功能,所以需要手动进行开启。
在 configuration.nix
中添加一下内容:1
2
3
4
5
6
7
8
9
10
11
12{ config, pkgs, ... }:
{
# ...
nix = {
package = pkgs.nixUnstable;
extraOptions = ''
experimental-features = nix-command flakes
'';
};
# ...
}
修改之后需要运行 nixos-rebuild switch
命令,将 Nix 包管理器升级到支持 Flake 的测试版。
然后在 /etc/nixos
里创建一个 flake.nix
文件。其中定义了一个软件源(input),是 NixPkgs 的 unstable 分支(也就是 master 分支)。文件内容如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18{
# 输入配置,即软件源
inputs = {
# Nixpkgs,即 NixOS 官方软件源
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
};
# 输出配置,即 NixOS 系统配置
outputs = { self, nixpkgs, ... }@inputs: {
# 定义一个名为 nixos 的系统
nixosConfigurations."nixos" = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
./configuration.nix
];
};
};
}
运行 nix flake update
,会生成一个 flake.lock
文件。在下一次运行 nixos-rebuild switch
命令时,NixOS 会自动优先读取 flake.nix
而非 configuration.nix
,把系统里的所有软件包升级(或降级)到这个特定的版本。但因为我们已经把 configuration.nix
加入至 flake.nix
中,所以系统配置还是保持不变。
远程部署
有些软件包在安装的时候需要编译,在低配置机器上执行 nixos-rebuild
会编译失败而出错。Flakes 支持远程部署,即先在本地进行编译,生成二进制文件,然后传输到目标机器上。仅需要在 flakes.nix
文件中增加相关的连接信息:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
# 新增下面几行
deploy-rs = {
url = "github:serokell/deploy-rs";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = { self, nixpkgs, ... }@inputs: {
nixosConfigurations."nixos" = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
./configuration.nix
];
};
# 新增下面几行
deploy = {
sshUser = "root"; # SSH 登录用户名
user = "root"; # 远程操作的用户
sshOpts = [ "-p" "2222" ]; # SSH 参数,这里是指定端口 2222
# 部署失败自动回滚,建议关闭
# 因为 NixOS(尤其是 Unstable 分支)部署不太稳定,有时需要部署两次才成功
# 如果自动回滚了,反而适得其反,导致连续部署失败
autoRollback = false;
# 断网自动回滚,建议关闭
# 在你配置防火墙或 IP 出错把网络干掉时,自动回滚,这样你就不用去主机商控制面板连 VNC 或 IPMI 了
# 但如果你就是在调整防火墙或者 IP 配置,会有当时断网、但重启机器就可以应用新配置恢复正常的情况
# 自动回滚反而适得其反,因此建议关闭
magicRollback = false;
nodes = {
"nixos" = {
# 目标机器的地址,IP 或域名或 .ssh/config 中配置的别名均可
hostname = "192.168.56.105";
profiles.system = {
# 调用上面的 nixosConfigurations."nixos"
path = deploy-rs.lib.x86_64-linux.activate.nixos self.nixosConfigurations."nixos";
};
};
};
};
};
}
参考资料:https://lantian.pub/article/modify-website/nixos-initial-config-flake-deploy.lantian/