如果每次新建虚拟机都需要从头安装系统的话就太麻烦了,所以我决定先装一个虚拟机作为模板,以后的虚拟机从这个模板直接复制。作为 Arch 脑残粉,虚拟机当然也要装 Arch Linux

创建虚拟机

打开 Proxmox 的 shell,进入 /var/lib/vz/template/iso 目录,然后下载 Arch Linux 的安装镜像。完成后在 web 界面选择系统所在的 volume,应该可以在 Contents 中看到刚才下载的镜像文件,有了这个文件就可以创建虚拟机了。我的 Linode VPS 只有双核 1G 内存,上面开了很多服务,除了游戏服务器以外其他服务跑得都非常流畅(因为根本就没人访问⋯⋯),所以我决定先创建一个很小的模板。选择单核 512MB 内存,在光驱中挂载 Arch Linux 的镜像。在设置硬盘的时候系统会默认选择 SSD 上那个 150GB 的 volume,需要手动改成机械硬盘上的。其它选项一率默认。注意在设置内存的时候如果打开 advanced 选项,就会发现这个内存的大小其实是最大值,最小值可以单独设置,系统会根据需要动态分配内存。创建好以后启动虚拟机,会直接进入 Arch Linux 的安装 shell。这时基本就可以跟着 Arch 的官方文档一路走下来了。这个过程中有个坑,就是由于虚拟机默认使用 BIOS(而不是 UEFI),分区的时候分区表要选择 DOS MBR 格式,这样安装 Grub 的时候比较方便。选择 GUID 格式的话执行 grub-install 会报错。安装完成后重启。

后续设置

串口

由于所有的虚拟机都会从这个模板复制,规划好模板的初始设置可以给以后的工序节省很多时间。我做的第一件事是在这个虚拟机的设置里添加了一个串口设备,在 Linux 里会识别成 /dev/ttyS0。然后根据 ArchWiki 的指引,打开 /etc/default/grub,把 GRUB_CMDLINE_DEFAULT 这一行修改为

GRUB_CMDLINE_LINUX_DEFAULT="console=tty0 console=ttyS0,38400n8"

并且加入如下几行:

## Serial console
GRUB_TERMINAL=serial
GRUB_SERIAL_COMMAND="serial --speed=38400 --unit=0 --word=8 --parity=no --stop=1"

执行

grub-mkconfig -o /boot/grub/grub.cfg

然后重启,在 Proxmox 中选择这个虚拟机,点击 Console 按钮右面的下箭头,就会发现 xterm.js 这个选择以前是灰的,现在可以选了。这个实际上就相当于虚拟机的终端设备,可以像在一般的终端模拟器里一样选择文本以及复制粘贴。而之前的 console 都是 VNC 客户端,里面是图像。

AMD microcode

Pacman 直接安装 amd-ucode

Qemu guest agent

Qemu guest agent 可以用 pacman 直接安装。这个东西的好处是可以在 Proxmox 里直接看到虚拟机的所有 NIC 和 IP 地址,并且可以按 shutdown 按钮直接关机。我目前还没有发现其他的功能。注意如果没有这个的话在 web 界面里按 shutdown 按钮,这个关机过程(其实虚拟机并不知道你要关它)会一直持续,并且不能 stop,别问我怎么知道的。

网络设置

所有虚拟机都使用预先定好的固定 IP 和 hostname,这些显然不可能在模板里就预先设好,所以我写了一个简单的脚本来设置网络。

#!/usr/bin/env bash

USAGE="Usage: network-setup.sh HOSTNAME IP"

if [[ $# -ne 2 ]]; then
    echo ${USAGE}
    exit -1
fi

HOSTNAME="$1"
IP="$2"
GATEWAY=192.168.1.1
NIC="ens18"

echo "Setting hostname to ${HOSTNAME}..."

echo "${HOSTNAME}" > /etc/hostname
cat <<EOM > /etc/hosts
127.0.0.1 localhost
::1 localhost
127.0.1.1 ${HOSTNAME}.localdomain ${HOSTNAME}
EOM

echo "Setting IP to static ${IP}..."
systemctl disable --now dhcpcd.service

cat <<EOM > /etc/systemd/network/20-wired.network
[Match]
Name=${NIC}

[Network]
Address=${IP}/24
Gateway=${GATEWAY}
DNS=${GATEWAY}
#DNS=8.8.8.8
MulticastDNS=true
EOM

systemctl enable --now systemd-networkd.service systemd-resolved.service

这样当我新开一个虚拟机以后只要运行 network-setup.sh HOSTNAME IP 就可以设置好这个机器的网络了。注意在脚本里我打开了 multicast DNS,这就引出了下面一节⋯⋯

Multicast DNS (mDNS)

大家都见过有些设备一连上网你就可以在「网络邻居」里看到这个设备,而且还有正确的名字,这就是 mDNS 的功劳。它可以让其他设备再没有设置 DNS 的情况下正确解析域名,当然前提是两方都有 mDNS resolver 和 responder(Mac 和 Windows 自带)。在 Arch Linux 里,systemd 的 networkd 和 resolved 自带 mDNS 功能,但是默认不开启,需要手动设置,方法就是像上面的那个脚本里一样把 MulticastDNS 设为 true,然后开启 systemd-resolved 服务。Resolved 会自动把域名设为 HOSTNAME.local。比如我的文件服务器叫 adenine,那么从同一网络里的其他机器 ping adenine.local 应该可以收到回应。

创建用户

没什么好说的,总不能老是用 root 登录吧?

OpenSSH

Proxmox 的虚拟机控制台是不依赖 SSH 的,但是有两个大坑:那个默认 VNC 控制台由于是图像,不能选中文字,也不能复制粘贴;Xterm.js 控制台倒是文本的,但是经常有莫名其妙的 bug,比如如果一条命令太长折行的时候会把前面的部分覆盖掉,等等⋯⋯ 所以有时还是需要 SSH 进去的。鉴于这些虚拟机在初期配置好之后基本就不会去碰了,SSH 服务不默认开启。

Sudo

比较方便。

到此,虚拟机的模板就配置好了。需要创建虚拟机的时候只需从这个模板复制一份,可以做到即开即用。