为什么要这样折腾?

众所周知,云服务器可选的发行版是有限的,并且某些发行版因为并不是原版安装的,会有一些问题。而且,云服务器提供的发行版可能会存在一些我们不希望看到的插件,所以便有了更换云服务器操作系统的需求。

准备工作:

需要的东西:服务器(废话),虚拟机(推荐KVM),你希望使用的发行版安装镜像(我这里是AlmaLinux8.10),带live环境的Linux发行版镜像(用于复制和覆盖硬盘数据,我这里用的Manjaro,KDE桌面),filebrowser(用于上传自定义的镜像)。
我这里提供一下快捷下载:
AlmaLinux-8.10-x86_64-boot.iso
filebrowser2.28.0-linux-x64.zip
系统因为大小限制切分压缩分卷了,要全部下载解压:
manjaro-kde-23.1.4-240406-linux66.part1.rar
manjaro-kde-23.1.4-240406-linux66.part2.rar

镜像制作:

虚拟机创建,磁盘分区以及系统安装

创建一个虚拟机,将虚拟机的硬盘设置得尽可能小,刚好把系统装下还剩一点空间能正常启动那是最好,这里我尝试的大小是8G。
Screenshot_20240618_224010.png
然后,按照你所想要的方式把系统安装进硬盘。大部分云服务器都是通过MBR引导启动的系统,但是以防万一还是搞个双引导的,即MBREFI,采用GPT分区,设置biosboot分区即可。
我这里演示因为AlmaLinux8的安装程序不能调整硬盘分区位置,而为了后续扩容,数据分区需要在最后面(我这里是根分区),就先用live镜像的KDE分区工具进行预先分区了,如下图所示:
Screenshot_20240618_225031.png
后续即可在安装程序里指定对应的分区:
Screenshot_20240618_225442.png

打包安装好的系统

在配置完成你所希望的功能后,我们需要对系统进行打包。
对于KVM虚拟机,我们可以使用virtiofs功能,将宿主机的一个目录映射进虚拟机,方便我们进行打包。
如图所示配置virtiofs文件系统,设置好名称和映射路径即可
Screenshot_20240618_225724.png
Screenshot_20240618_225714.png
然后,将虚拟机重启进入live镜像环境,创建一个用于挂载的文件夹,挂载virtiofs文件系统:

su
mkdir /data
mount -t virtiofs data /data

Screenshot_20240618_230223.png
然后,使用dd目录克隆虚拟机硬盘:

dd if=/dev/vda of=/data/Alma8.img bs=4M status=progress

Screenshot_20240618_230351.png
请注意,if=后面是要克隆的设备,of=后面是要输出的文件,设备可能会因环境而已,例如变成/dev/sda

我制作好的镜像

如果你实在不想折腾的话,也可以用我做好的镜像:AlmaLinux8.10-8G.zip

在云服务器上面部署filebrowser上传镜像

利用filebrowser,或者ssh自带的scp等工具,把live环境镜像和需要用于覆盖的预先制作好的镜像传到服务器。
对于scp,先cd到你目录或者直接在文件所在目录打开终端,然后:

scp ./Alma8_8G.img root@172.16.0.11:/root

对应的参数自己进行替换,上面只是个示例。这都不会还不愿意百度就别搞了
如果用fiebrowser,从我的直链下载,解压并且执行就行了:

wget https://fs.duifene.com/res/r2/u6620344/filebrowser2.28.0-linux-x64_e9fc85fa59bcbc39d5c7.zip
unzip ./filebrowser2.28.0-linux-x64_e9fc85fa59bcbc39d5c7.zip
./filebrowser -a 0.0.0.0

如果证书报错,替换为:

wget https://fs.duifene.com/res/r2/u6620344/filebrowser2.28.0-linux-x64_e9fc85fa59bcbc39d5c7.zip --no-check-certificate

什么?wget和uzip没找到命令?装啊!自己去必应搜索!
确认你云服务器防火墙放行了8080端口,因为默认fiebrowser就是走8080端口,然后不出意外的话,你访问你服务器的ip加端口8080(例如172.16.0.11:8080)就可以看到filebrowser的界面了,默认的用户名和密码都是admin
Screenshot_20240620_005604.png
登上去,把文件上传了就行,需要一点时间。个人建议是把自定义的系统镜像压缩后上传,因为有大量空的数据,压缩比例还是挺大的,上传完成云服务器解压就行。

配置GRUB从live环境iso启动live系统

以下操作都需要root权限运行!请执行su获取root权限

打开配置文件

使用文本编辑器编辑配置文件命令行文本编辑器不会用去学完再来

nano /etc/grub.d/40_custom

在文件后面添加如下内容,部分内容根据所选的live发行版的iso文件结构目录和镜像所在位置进行改变:
例如Ubuntu的配置方法:

menuentry "ubuntu-20.04.2.0-desktop-amd64.iso" {
  insmod ext2
  set isofile="/home/linux-terminal/Downloads/ubuntu-20.04.2.0-desktop-amd64.iso"
  loopback loop (hd0,5)$isofile
  linux (loop)/casper/vmlinuz boot=casper iso-scan/filename=$isofile quiet noeject noprompt splash
  initrd (loop)/casper/initrd
}

具体参数的含义:

  • menuentry:此条目将显示在 GRUB2 引导菜单上。您可以将其命名为任何您喜欢的名称。
  • insmod 命令插入一个模块。由于 ISO 文件存储在我的 ext4 主目录下,因此需要 ext2 模块。如果它存储在 NTFS 分区上,则需要insmod ntfs。请注意,GRUB 可能无法识别 XFS 和 Btrfs 文件系统,因此不建议将 ISO 文件存储在 XFS 或 Btrfs 分区上。
  • set isofile:指定 ISO 映像文件的路径。这里我使用的是保存在 Downloads 文件夹下的 Ubuntu 20.04 Desktop ISO 文件。
  • 环回:挂载ISO文件。 hd0 表示计算机中的第一个硬盘驱动器,5 表示 ISO 文件存储在第 5 个磁盘分区上。
  • linux 命令从指定路径加载 Linux 内核。 casper/vmlinuz.efi 是 Ubuntu ISO 映像中的 Linux 内核。
  • initrd 命令从指定路径加载初始 ramdisk。它只能在运行linux命令后使用。初始 ramdisk 是安装到 RAM 上的最小根文件系统。 casper/initrd.lz 是 Ubuntu ISO 映像中的 initrd 文件。

请注意,GRUB不区分 IDE 和 SCSI。在Linux内核中:

  • /dev/hda 指第一个 IDE 硬盘,
  • /dev/sda 指第一个 SCSI 或 SATA 硬盘。
  • /dev/nvme0n1 指第一个 NVMe SSD。 /dev/nvme1n1 指第二个 NVMe SSD。

但在 GRUB 中,第一个硬盘驱动器始终称为 hd0,无论接口类型是什么。另请注意,GRUB 中的分区号从 1 开始,而不是从 0 开始。

如果ISO文件存储在MBR磁盘的扩展分区上,则分区号从5开始,而不是1。例如,扩展分区内的第一个逻辑分区将编号为5;扩展分区内的第二个逻辑分区编号为 6。要检查分区编号,您可以在终端窗口中运行 lsblksudo parted -l 命令来检查分区编号。

如何查找 Linux 内核和 initrd 文件名:

对于不同的 Linux ISO 映像,Linux 内核和 initrd(初始 ramdisk) 文件可能有所不同。对于 Ubuntu,Linux 内核位于 /casper/vmlinuz,initrd 映像文件位于 /casper/initrd。命名都是比较类似的,照着找就行了。例如AlmaLinux8.10的iso:
Screenshot_20240620_012707.png

具体每个发行版的示例配置:

请不要照抄,要根据需要改动
Debian:

menuentry "debian-live-10.8.0-amd64-lxqt.iso" {
  insmod ext2
  set isofile="/home/linux-terminal/Downloads/debian-live-10.8.0-amd64-lxqt.iso"
  loopback loop (hd0,5)$isofile
  linux (loop)/live/vmlinuz-4.19.0-14-amd64 boot=live findiso=$isofile
  initrd (loop)/live/initrd.img-4.19.0-14-amd64
}

Arch:

menuentry "archlinux-2021.03.01-x86_64.iso" {
  insmod ext2
  set isofile="/home/linux-terminal/Downloads/archlinux-2021.03.01-x86_64.iso"
  loopback loop (hd0,5)$isofile
  linux (loop)/arch/boot/x86_64/vmlinuz-linux archisolabel=ARCH_202103 img_dev=/dev/sda5 img_loop=$isofile earlymodules=loop
  initrd (loop)/arch/boot/x86_64/initramfs-linux.img
}

Clonezilla:

menuentry "clonezilla-live-20210127-groovy-amd64.iso" {
  insmod ext2
  set isofile="/home/linux-terminal/Downloads/clonezilla-live-20210127-groovy-amd64.iso"
  loopback loop (hd0,5)$isofile
  linux (loop)/live/vmlinuz boot=live findiso=$isofile
  initrd (loop)/live/initrd.img
}

RHEL/AlmaLinux/RockyLinux:

menuentry "rhel-8.3-x86_64-boot.iso" {
  insmod ext2
  set isofile="/home/linux-terminal/Downloads/rhel-8.3-x86_64-boot.iso"
  loopback loop (hd0,5)$isofile
  linux (loop)/isolinux/vmlinuz noeject inst.stage2=hd:/dev/sda5:$isofile
  initrd (loop)/isolinux/initrd.img
}

Fedora:

menuentry "Fedora-Workstation-Live-x86_64-33-1.2.iso" {
  insmod ext2
  set isofile="/home/linux-terminal/Downloads/Fedora-Workstation-Live-x86_64-33-1.2.iso"
  loopback loop (hd0,5)$isofile
  linux (loop)/isolinux/vmlinuz root=live:CDLABEL=Fedora-WS-Live-33-1-2 rd.live.image verbose iso-scan/filename=$isofile
  initrd (loop)/isolinux/initrd.img
}

OpenSUSE Leap:

menuentry "openSUSE-Leap-15.2-KDE-Live-x86_64-Build31.383-Media.iso" {
  insmod ext2
  set isofile="/home/linux-terminal/Downloads/openSUSE-Leap-15.2-KDE-Live-x86_64-Build31.383-Media.iso"
  loopback loop (hd0,5)$isofile
  linux (loop)/boot/x86_64/loader/linux boot=isolinux root=live:CDLABEL=openSUSE_Leap_15.2_KDE_Live rd.live.image verbose iso-scan/filename=$isofile
  initrd (loop)/boot/x86_64/loader/initrd
}

Kali Linux:

menuentry "kali-linux-2021.1-live-amd64.iso" {
  insmod ext2
  set isofile="/home/linux-terminal/Downloads/kali-linux-2021.1-live-amd64.iso"
  loopback loop (hd0,5)$isofile
  linux (loop)/live/vmlinuz boot=live findiso=$isofile
  initrd (loop)/live/initrd.img
}

Linux Mint:

menuentry "linuxmint-20.1-cinnamon-64bit.iso" {
  insmod ext2
  set isofile="/home/linux-terminal/Downloads/linuxmint-20.1-cinnamon-64bit.iso"
  loopback loop (hd0,5)$isofile
  linux (loop)/casper/vmlinuz boot=casper iso-scan/filename=$isofile quiet noeject noprompt splash
  initrd (loop)/casper/initrd.lz
}

更新GRUB

使用以下命令更新 GRUB 引导菜单:

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

在 Fedora、CentOS、RHEL、OpenSUSE 上,运行的命令是:

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

在 Debian、Ubuntu、Linux Mint 上,您可以使用以下命令更新 GRUB 启动菜单。

sudo update-grub

实操示例:

在manjaro的live镜像找到那两个文件:
Screenshot_20240620_175324.png
根据镜像修改配置文件:

menuentry "manjaro.iso" {
  insmod ext2
  set isofile="/root/manjaro.iso"
  loopback loop (hd0,5)$isofile
  linux (loop)/boot/vmlinuz-x86_64 archisolabel=MANJARO img_dev=/dev/vda5 img_loop=$isofile earlymodules=loop
  initrd (loop)/boot/initramfs-x86_64.img
}

以下是我实战的终端输出,修改文件附加的就和上面的一样:

[root@NekoWorks ~]# df -hT
Filesystem     Type      Size  Used Avail Use% Mounted on
devtmpfs       devtmpfs  1.9G     0  1.9G   0% /dev
tmpfs          tmpfs     2.0G     0  2.0G   0% /dev/shm
tmpfs          tmpfs     2.0G  8.6M  2.0G   1% /run
tmpfs          tmpfs     2.0G     0  2.0G   0% /sys/fs/cgroup
/dev/vda5      ext4       37G  7.3G   30G  20% /
/dev/vda3      ext4      574M  156M  407M  28% /boot
/dev/vda2      vfat       50M  4.0K   50M   1% /boot/efi
tmpfs          tmpfs     393M     0  393M   0% /run/user/0
[root@NekoWorks ~]# nano /etc/grub.d/40_custom
[root@NekoWorks ~]# sudo grub2-mkconfig -o /boot/grub2/grub.cfg
Generating grub configuration file ...
done

重启之前的准备:

由于dd是从前往后覆盖,覆盖的范围是目标文件大小,为了防止原文件在dd读取之前被覆盖,建议是原地复制一遍镜像文件,然后指定复制后的文件进行覆盖。注意,这一步需要在原系统完成

切换到云服务器的vnc模式登陆服务器(阿里云改名叫救援模式了),这样才能看到虚拟机的启动过程,并且看到引导菜单。
重启后看到新增的启动项并且可以正常引导就说明成功了。
Screenshot_Alma8_2024-06-20_18:07:00.png
Screenshot_20240620_180315.png

把自定义的镜像覆盖原系统

复制好文件后,按如下命令进行覆盖(对应参数请自己替换):

dd if=/run/miso/img_dev/root/Alma8.10-8G.img of=/dev/vda bs=1M status=progress

Screenshot_Alma8_2024-06-20_18:32:02.png
如果不出意外的话,应该执行完成之后,重启就可以进入你之前封装的系统了。

如果你使用的是我的镜像,那用户名是vanilla,密码是GTX1080Ti,root密码相同

扩容分区

由于在其用的是小容量的虚拟机硬盘,所以覆盖过去后,分区大小还是一样的,需要扩容。
安装cloud-utils-growpart软件包:

yum install cloud-utils-growpart

确定需要扩容的分区(一般是/如果你用的是我的镜像,那应该是vda5):

[root@NekoWorks ~]# df -hT
Filesystem     Type      Size  Used Avail Use% Mounted on
devtmpfs       devtmpfs  1.9G     0  1.9G   0% /dev
tmpfs          tmpfs     2.0G     0  2.0G   0% /dev/shm
tmpfs          tmpfs     2.0G  8.6M  2.0G   1% /run
tmpfs          tmpfs     2.0G     0  2.0G   0% /sys/fs/cgroup
/dev/vda5      ext4      5.2G  2.8G  2.5G  53% /
/dev/vda3      ext4      574M  156M  407M  28% /boot
/dev/vda2      vfat       50M  4.0K   50M   1% /boot/efi
tmpfs          tmpfs     393M     0  393M   0% /run/user/0

然后,执行扩容:

[root@NekoWorks ~]# growpart /dev/vda 5
CHANGED: partition=5 start=5543936 old: size=11227136 end=16771071 new: size=78342111 end=83886046

注意,有一个5之前有一个空格
这还没完,需要登陆cockpit面板进一步操作,我的系统我已经预装了cockpit面板和mcsm面板,cockpit面板的用户名和密码和系统同步,mcsm的没有设置,打开即可进行初始化。cockpit面板端口9090,例如172.16.0.11:9090;mcsm的面板端口23333,例如172.16.0.11:23333
Screenshot_20240620_185246.png
登陆cockpit面板,找到硬盘分区,增长内容:
Screenshot_20240620_184842.png
Screenshot_20240620_184924.png

更改密码

如果你使用的是我的镜像,为了安全,完成工作后请立即更换密码:

passwd
passwd vanilla

同时,记得打开mcsm面板创建账户,以免被他人创建并利用!
到此,系统更换工作即可完成。