前言
众所周知,给云服务器安装操作系统可以使用服务商提供的预装镜像,也可以使用服务商额外的挂载 ISO 镜像功能安装。对于一些没有提供我们想要的操作系统镜像,又不提供挂载镜像功能的服务商,比如 QQ云、阿里云的轻量服务器等,可以使用 vps2arch 等第三方脚本(或手动操作,原理一致)覆盖原有的系统安装新的操作系统。但是这种方法有个致命的缺点,就是无法对原有的文件系统进行修改,无法重新分区。这种情况下,哪怕只是想从系统盘空闲空间中划分一块建立SWAP分区也会十分困难,因为预装镜像中常见的ext4
文件系统不支持在线 Resize ,必须先 umount 才能进行操作(xfs
、btrfs
支持在线扩容的特性)。
在之前给 VPS 安装 Archlinux 的时候,我尝试过一个可以脱离系统盘工作的内存系统脚本menhera.sh
结果以失败告终。最近,由于在研究给 VPS 安装 NixOS ,还尝试了 Root on tmpfs 的玩法,必须对系统盘进行重新分区,我不得不重拾这个脚本继续探索。
脚本分析
首先我们阅读这个脚本的源码,可以发现这个脚本做的工作其实很简单,大致分为以下几个部分:
下载
rootfs
镜像部署镜像
复制系统环境
安装额外的所需软件
转移系统
menhera
脚本使用的镜像是 Debian 构建的 rootfs ,格式是squashfs
,包含完整的系统内核,是一个可以独立工作的 rootfs 。但是它缺少很多安装系统会用到的工具,而且体积很大,包含完整的内核——只能使用旧系统的内核,不需要自备内核,所以这个镜像并不是很适合用来构建安装系统的内存环境。之前的尝试中,我使用过 ArchISO 的 airootfs 镜像来替代 Debian 的镜像,但是效果不尽人意。虽然 airootfs 中会包含更多的工具,但是同是包含内核的 rootfs ,这个镜像的体积也非常的大(600MB+)。不过经过我的测试,Archlinux 的bootstrap
在这个环境中也可以工作,它不包含内核,包含绝大多数需要的工具,而且体积不大(100MB+),很适合用于内存环境的构建。
部署镜像指在 tmpfs 中挂载这个镜像,需要使用到 tmpfs 和镜像对应文件系统需要的工具。
需要复制的系统环境主要包括含ROOT
密码的/etc/passwd
和/etc/shadow
文件,以及包含系统 DNS 的/etc/resolv.conf
文件,可以直接在内存环境中使用旧系统的密码和 DNS ,省去自行配置的过程。
安装额外的软件指给内存环境安装需要但不包含的软件,比如 SSH 服务器程序。
这一切都做好之后就可以将系统根目录转移到内存中去,进入内存环境。
开始工作
由于清理占用系统盘的进程需要进行多次,这种情况下无法运行脚本,所以这里我们需要手动执行命令,并且不能使用 SSH 连接服务器,必须使用服务商提供的 VNC 连接或串口终端(Azure)。为了确保内存环境中的软件能正常工作,需要原有系统的内核版本尽可能的高,这里推荐新装服务器直接使用 Debian 11 的系统。准备好后就可以开始了。
下载镜像
首先下载archlinux-bootstrap
镜像。
latest
版本的镜像名称是与当下最新的版本号有关,在自己操作的时候版本号可能并不一致,操作时请自行获取最新的镜像下载地址。
国内公网可以使用阿里云、腾讯云等公共云服务的镜像:
1 | curl https://mirrors.aliyun.com/archlinux/iso/latest/archlinux-bootstrap-XXXX.XX.XX-x86_64.tar.gz -O |
教育网建议使用教育网源的镜像,比如清华源:
1 | curl https://mirrors.tuna.tsinghua.edu.cn/archlinux/iso/latest/archlinux-bootstrap-XXXX.XX.XX-x86_64.tar.gz -O |
海外主机可以使用海外源:
1 | curl https://mirror.rackspace.com/archlinux/iso/latest/archlinux-bootstrap-XXXX.XX.XX-x86_64.tar.gz -O |
部署内存环境
下载好的镜像我们需要部署到内存中。
首先开辟一块内存盘用于存储环境,这里我们将这个环境挂载到/tmp/menhera
下:
1 | mkdir /tmp/menhera |
解压镜像到这个内存盘中:
1 | tar zxf archlinux-bootstrap-2022.03.01-x86_64.tar.gz -C /tmp/menhera |
由于原版镜像外层套有一层名为root.x86_64
的文件夹,需要将 root 从这个文件夹中移动到内存盘的根下,否则后面的pivot_root
命令无法执行。
这里不能使用常规的mv
指令,需要额外指定参数:
1 | mv /tmp/menhera/root.x86_64/* -t /tmp/menhera |
复制系统必需文件
直接从系统根目录复制到内存系统的根目录下:
1 | cp -r /etc/resolv.conf /tmp/menhera/etc |
安装需要的额外软件包
这里需要chroot
到内存系统中安装需要的软件包,比如btrfs-progs
、vim
,都不包含在bootstrap
中。
首先 bind 系统目录,使这个内存系统可以运转:
1 | mount -t proc /proc /tmp/menhera/proc |
然后设置软件源:
1 | echo 'Server = http://mirror.rackspace.com/archlinux/$repo/os/$arch' > /tmp/menhera/etc/pacman.d/mirrorlist |
记得替换为适合自己的软件源。
初始化pacman-keyring
并更新软件仓库:
1 | chroot /tmp/menhera pacman-key --init |
也可以安装软件包的同时更新软件仓库:
1 | chroot /tmp/menhera pacman -Sy vim --noconfirm |
注意如果是小内存(如1G,更小内存的机型尚未测试)的服务器在这一步可能会因为内存不足更新失败,可以一个一个安装软件包。如果不幸中断了,可以删除锁后重新执行指令。
1 | rm /tmp/menhera/var/lib/pacman/db.lck |
过于庞大的软件包(如 Nix )建议进入内存环境后再安装。
转移根目录
在这之前请把额外需要做的工作全部做完。如果需要使用额外的内核模块(如btrfs
),请提前加载:
1 | modprobe btrfs |
为转移后的旧系统建立挂载点:
1 | mkdir /tmp/menhera/mnt/old |
开始转移根目录:
1 | mount --make-rprivate / |
将旧系统的系统目录改绑到当前系统下:
1 | mount --move /mnt/old/dev /dev |
初步解除旧系统的占用:
1 | swapoff -a # 卸载旧系统的SWAP分区 |
请按顺序执行。
执行完后,系统会进入到内存环境的登陆界面,输入root
和旧系统root
用户的密码即可登陆到内存环境中。
解除系统分区的占用
这部分的细节我其实也并不是很理解,希望有大佬来补充。
之前的清理其实并不彻底,此时 umount 系统分区会提示 busy 。
适合我的服务器的步骤如下,依次执行以下指令:
1 | fuser -kvm /mnt/old -15 # 再次清理旧系统的进程,此时会提示无法清除的进程 |
到这里应该就可以顺利操作系统分区了。
如果内存不足,进入内存环境后建议立刻创建 SWAP 分区并挂载。
安装 NixOS 的一点提示
如果要安装 NixOS 系统,必须在内存环境中安装 Nix 。
第一个问题就是空间不足。 Nix 会在根目录创建/nix
目录存放store path
,由于根目录挂载在内存中,也会占用内存空间,但并不会使用挂载的 SWAP 空间。所以建议提前建立分区挂载到/nix
。推荐使用btrfs
文件系统,建立一个临时的 subvolume ,使用完后直接删除 subvolume 即可。
第二个问题是使用官方脚本会出现不明原因的无法创建用户的错误。可以通过安装 Archlinux 系统源中的 Nix 让 pacman 创建用户实现曲线救国的效果。当然这里也可以直接使用 Archlinux 源中的 Nix ,但是有一点局限:
使用 Archlinux 官方源里的 Nix 安装的包,不能直接在系统的 Shell 里调用,需要用nix-shell -p <软件名>
的方式创建一个包含该软件的 Nix Shell 在里面使用,比较麻烦。
如果使用源中的 Nix ,安装系统时会出现文件不存在的问题,所以只能使用官方脚本安装。
如果使用官方脚本安装 Nix ,可以先安装源中的 Nix ,卸载后再运行官方脚本。
分步脚本使用方法
我把一些可以自动执行的指令整理成脚本的形式,放在了我的 Github 仓库中,可以自动化执行部分步骤。
首先clone
整个仓库,国内主机请注意网络环境:
1 | git clone https://github.com/alca7raz/menhera-archlinux |
里面包含如下分步脚本:
01-download-image.sh
第一步02-deploy-ram-sys-and-copy-config.sh
第二步03-config-ram-sys.sh
第三步和第四步的初始化密钥阶段
也可以直接执行00-init-ram-sys.sh
脚本,自动执行以上步骤。脚本执行完毕后需要自己给内存环境安装软件包,具体步骤参考上文。
安装完软件包后可以执行10-switch-root.sh
,进入内存系统。之后的步骤参考第六步。