给VPS安装Arch Linux

最近重新给VPS折腾安装Arch Linux的时候遇到了一些很有意思的问题。解决问题的过程中我也收获了很多知识,在这里给大家分享一下。

补充

有两个VPS安装系统时上场率比较高的脚本。一个是VPS2Arch,另一个是menhera。这两个脚本都可以实现安装系统的功能,但是其实现方法有点不太一致。

VPS2Arch脚本使用的是bootstrap+chroot的方法,严格遵照了ArchWiki所描述的方法。特点是直接覆盖原有的系统,过程中并不能重新分区,比较符合VPS的环境。

而menhera的实现方法就有所不同。它使用了完整的安装镜像,在内存中制作了一个临时系统并将根目录和当前的运行环境无缝转移到内存中,从而释放对硬盘的控制。

这个脚本只是制作了一个临时内存系统,并不具备自动安装系统的功能,但这个脚本为安装系统提供了一个很好的思路——即使用内存系统作为安装环境。这样做的好处就是可以重新分区,当然在VPS的环境下分区是一个很少会需要去做的一个操作,但是它至少能这么做。代价也是巨大的——在内存中制作系统环境,需要非常大的内存空间。

脚本中使用的airootfs镜像,单个大小就在660MB左右,这一限制将512M内存的小机直接拒之门外。1G内存的机器也未必能幸免,因为一个内存系统实际占用的内存空间可能需要700MB左右,加上系统运行本身需要占用部分内存,能留给脚本或用户支配的内存空间非常的稀少。如果你的服务商比较坑,1G内存缩水比较严重,实际内存只有800MB左右大小,那也可能会导致内存不足的情况发生,甚至会产生pacman无法正常运行的情况。

当然可能有读者会想到使用客制化镜像的方法,在镜像中删去一些不必要且占用空间极大的包,比如linux-firmware,这个驱动包在系统已经启动的情况下根本使用不到,而且占用空间超100MB。不过即使删去了这些不必要的软件包,镜像系统也有400MB多的大小,只能确保在1G内存的机器中正常运行。512M内存机依旧是一个不可逾越的鸿沟。这种情况下显然使用vps2Arch脚本是最好的选择,但是这样的话使用内存系统安装系统的方法就失去了使用的意义——效果是一样的,但vps2arch可以全环境通吃,为什么不用它呢?

下面的内容是对第二种方法的简单介绍,修复了之前存在的部分问题。仅供参考。

准备安装镜像

首先下载最新Arch Linux的airootfs。

wget https://mirrors.qcloud.com/Arch Linux/iso/latest/arch/x86_64/airootfs.sfs

这里我们使用的是腾讯云公共镜像的源,如果是阿里云服务器可以直接将域名更改为阿里云,教育网服务器可以直接更改为教育网源。因为这里我们选用的版本是“latest”的,所以这个文件的相对路径并不会随源的不同或版本的不同而不同。

这是Arch Linux的系统软件包,而不是完整ISO文件。它提供了ArchISO中的大部分系统软件,不包含了内核与驱动等关键运行组件。这部分内容需要我们VPS原有的系统提供。

挂载镜像文件

挂载镜像

首先需要创建一个内存盘来存放所有的文件。这里选择在/tmp中创建/tmp/arch文件夹,因为/tmp本来就是系统默认的内存盘位置,所以挂载在这里不会有问题。

mkdir /tmp/arch
mount tmpfs /tmp/arch -t tmpfs -o size=100%
cd /tmp/arch

创建完后需要创建必需的目录,给之后的镜像挂载使用。这里假设已经进入刚才创建的内存盘中。

mkdir sfs rootfs work space
mount -t squashfs "/tmp/arch/airootfs.sfs" "/tmp/arch/sfs"
mount overlay -t overlay -o rw,lowerdir="/tmp/arch/sfs",upperdir="/tmp/arch/space",workdir="/tmp/arch/work" "/tmp/arch/rootfs"

因为airootfs是squashfs类型的只读压缩镜像文件,如果直接挂载并chroot的话会导致之后的系统不可用。为了让它具备写入的功能,我们需要使用另一个有意思的文件系统overlay

Overlayfs允许将一个通常为可读写的目录树覆盖到另一个只读目录树上。所有修改都转到上层可写层。体验相当于复制了一份sfs中的文件然后直接修改,但由于Overlay的优势,实际上并没有复制,只保存了额外修改后的结果。这项功能可以让操作简单,同时节省内存空间。

配置域名解析

复制系统原有的resolv.conf文件。如果没有这个文件,chroot后将无法解析域名。

# squashfs里的resolv.conf是一个指向/run/systemd/resolve/stub-resolv.conf的符号链接,可能复制失败,这里可以先删除后复制
rm /tmp/arch/rootfs/etc/resolv.conf
cp -axL "/etc/resolv.conf" "/tmp/arch/rootfs/etc"

进入内存系统

切换根目录

接着就可以进行chroot操作了。这里经Oldherl大佬的提醒,使用了systemd提供的switch-root功能而不是pivot_root。

mount --make-rprivate /
systemctl switch-root /tmp/arch/rootfs

如果采用了systemctl提供的switch-root指令,静候片刻后屏幕就会显示出ArchISO风格的命令行界面,等候时间可能有些长并出现假死的现象,耐心等待即可。切换成功之后就可以像正常用U盘一样直接安装系统了,不需要执行下面的步骤。


下面的这些是脚本中使用的指令。在我测试的时候可以正常chroot,但是后续mkfs的时候会报错,只能采用保留原有分区覆盖原有文件的方式安装。

#mkdir -p /tmp/arch/rootfs/mnt/old
#pivot_root "/tmp/arch/rootfs" "/tmp/arch/rootfs/mnt/old"
#mount --move /mnt/old/sys /sys
#mount --move /mnt/old/run /run
#mount --move /mnt/old/dev /dev
#mount --move /mnt/old/proc /proc
#mount -t tmpfs tmpfs -o size=100% /tmp

现在已经成功chroot到内存系统中了,即原来的/tmp/arch/rootfs。先挂载原来的系统盘并将内存盘移动到现在的根目录下。

#mount /dev/vda1 /mnt # 注意这里替换为你自己的硬盘位置
#mkdir -p /tmp/arch
#mount --move /mnt/tmp/arch /tmp/arch

清理旧进程

这时还没有完全切换到内存系统中,因为系统中还存在很多来自与旧系统的进程。现在对它们进行清理

swapoff -a
systemctl daemon-reexec
fuser -kvm "/mnt" -15

清理完成后就会切换到内存系统中,这时我们就会看到熟悉的ArchISO界面。

到此就已经完全进入内存系统了。下面就可以开始系统安装。

正式安装系统

准备阶段

这里可以自由地选择系统安装的方式,可以利用原有分区安装新系统,也可以重新分区。

这里我选择重新分区。如果要重新分区,记得先卸载上一步挂载的旧系统盘。

umount /mnt
mkfs.ext4 /dev/vda1
mount /dev/vda1 /mnt
pacman-key --init
pacman-key --populate Arch Linux

安装软件包

这里省去对内存系统的设置过程,直接给出我安装的软件包。

pacstrap /mnt base base-devel linux-lts dkms linux-lts-headers linux-firmware intel-ucode amd-ucode cloud-init Arch Linux-keyring Arch Linuxcn-keyring
pacstrap /mnt vi vim nano sudo screen openssh arch-install-scripts yay grub git fish gnupg wget curl
pacstrap /mnt polkit

如果要使用Archlinuxcn源记得一定要安装钥匙环。

这里选用了LTS的内核,因为不想老是滚系统,可以减少服务器重启的次数。

polkit包提供poweroffreboot指令的直接执行,不然需要添加systemctl的前缀才能执行。

安装引导

给新系统安装引导,这里使用的是BIOS+MBR的引导方式。

grub-install --target=i386-pc /dev/vda
grub-mkconfig -o /boot/grub/grub.cfg

配置网络

别忘了给新系统配置resolv.conf,将内存系统中的resolv复制到新系统中。

cp /etc/resolv.conf /mnt/etc/

新系统的网络连接有两种配置方式,第一种是使用cloud-init自动配置,第二种是手动配置,两种方式都不麻烦,这里都说一下。

  • Cloud-init

    安装cloud-init包,然后执行

    cloud-init init

    就好了。

    优点是很方便,缺点是如果需要对网络特殊设置就会很麻烦。

  • 手动配置

    和正常给电脑配置网络一样,我们需要选择一个网络管理器。出于方便配置的目的,我选择使用系统自带的systemd-networkd来管理我的网络。

    需要创建一个网卡配置。在/etc/systemd/network/下创建一个配置文件,注意后缀名一定要是.network

    [Match]
    Name=ens5
    
    [Network]
    DHCP=yes

    注意替换为自己的网卡名称。

    然后启动systemd-networkd服务。

    systemctl enable systemd-networkd

    就可以了。不需要设置systemd-resolved,因为我们已经手动设置好了resolv配置。

参考资料

https://github.com/pexcn/vps2arch/blob/master/vps2arch

https://github.com/Jamesits/menhera.sh/blob/master/menhera.sh