安装 img/qcow2 镜像中的系统

安装 img/qcow2 镜像中的系统
狂犬主子此处以 Armbian x86_64 UEFI 还原到 Legacy Boot 的机器为例,恢复环境选用 Alpine Linux
为什么选择Alpine Linux?
Alpine Linux 镜像小,apk 安装、换源速度快,适合轻量化使用(
最主要是没用过,第一次学习使用)当然可以使用其他 Live CD 环境,Arch Linux 的也行
模拟日常使用已经安装 Windows 系统的破电脑,我们要将 Armbian 安装到第三个分区(如图所示,分区相关事项前期已经处理好)
准备
Armbian 磁盘镜像文件:https://github.com/armbian/os/releases/latest
此处选择 Armbian_2x.xx.x_Uefi-x86_bookworm_current_6.x.xx_minimal.img.qcow2.xz
(可以提前下载好,此处打算进入 Alpine 后在线下载到第二个分区)
Alpine Linux 启动ISO镜像文件:https://mirrors.aliyun.com/alpine/latest-stable/releases/x86_64/
此处选择 alpine-standard-3.20.3-x86_64.iso
此处在虚拟机内演示,所以需要准备虚拟机,如果是实体机的话需要准备好 U 盘
启动环境
此处使用虚拟机演示,直接将 CD/DVD 指定到 ISO 镜像,如果是实机环境可以采取写盘或者 Ventoy
引导到 Alpine Linux ISO,如下图所示
此处输入用户名 root,无密码,登录
配置网络
输入下方命令,按照提示配置好即可上网
1 | # 使用内置脚本配置网卡 setup-interfaces # 重启网络服务 rc-service networking restart |
配置SSH
如果您想更方便地复制粘贴,可以配置 ssh
1 | # 设置密码 passwd # 使用内置脚本配置ssh服务,务必允许 root 登录 setup-sshd # 查看本机IP ip a |
然后在外部机器通过ssh连接
1 | ssh root@123.123.123.123 |
配置镜像
默认情况下 apk 只会从 cdrom 中获取软件包,我们需要配置在线镜像,否则无法安装在线软件包
1 | # 使用内置脚本配置在线镜像源 # 镜像列表:https://mirrors.alpinelinux.org/mirrors.txt setup-apkrepos # 根据提示自动选择/手动选择/输入镜像 # 注意需要启用 community 镜像 |
1 | localhost:~# setup-apkrepos (f) Find and use fastest mirror (s) Show mirrorlist (r) Use random mirror (e) Edit /etc/apk/repositories with text editor (c) Community repo enable (skip) Skip setting up apk repositories Enter mirror number or URL: [1] c Community repository enabled Community repository enabled (f) Find and use fastest mirror (s) Show mirrorlist (r) Use random mirror (e) Edit /etc/apk/repositories with text editor (c) Community repo disable (skip) Skip setting up apk repositories Enter mirror number or URL: [1] s Available mirrors: 1) dl-cdn.alpinelinux.org ...... 49) mirrors.aliyun.com (f) Find and use fastest mirror (s) Show mirrorlist (r) Use random mirror (e) Edit /etc/apk/repositories with text editor (c) Community repo disable (skip) Skip setting up apk repositories Enter mirror number or URL: [1] 49 Added mirror mirrors.aliyun.com Updating repository indexes... done. localhost:~# cat /etc/apk/repositories /media/dm-0/apks http://mirrors.aliyun.com/alpine/v3.20/main http://mirrors.aliyun.com/alpine/v3.20/community |
挂载下载分区
此处为下载到本机硬盘,如果您已经下载到 U盘,那么只需挂载 U盘即可
执行 fdisk -l
查看分区表
1 | localhost:~# fdisk -l Disk /dev/sda: 100 GB, 107374182400 bytes, 209715200 sectors 13054 cylinders, 255 heads, 63 sectors/track Units: sectors of 1 * 512 = 512 bytes Device Boot StartCHS EndCHS StartLBA EndLBA Sectors Size Id Type /dev/sda1 * 0,32,33 1023,254,63 2048 83890175 83888128 40.0G 7 HPFS/NTFS /dev/sda2 1023,254,63 1023,254,63 83890176 209715199 125825024 59.9G f Win95 Ext'd (LBA) /dev/sda5 0,32,33 1023,254,63 83892224 146808831 62916608 30.0G 7 HPFS/NTFS /dev/sda6 0,32,33 1023,254,63 146810880 209715199 62904320 29.9G 7 HPFS/NTFS |
根据我们的环境,需要先将镜像下载到第二个NTFS分区(/dev/sda5
),然后写入到第三个分区(/dev/sda6
)
由于是 NTFS,通过内核挂载可能会出现只读的情况,需要使用 ntfs-3g
(ntfs-3g - Alpine Linux packages)进行挂载
1 | apk add --no-cache ntfs-3g |
挂载/dev/sda5
到 /mnt/dpan
1 | mkdir -p /mnt/dpan mount -t ntfs-3g /dev/sda5 /mnt/dpan |
将.qcow2.gz格式的镜像用 wget 下载到该分区
1 | wget https://mirrors.tuna.tsinghua.edu.cn/armbian-releases/uefi-x86/archive/Armbian_24.11.1_Uefi-x86_bookworm_current_6.6.60_minimal.img.qcow2.xz -O /mnt/dpan/image.qcow2.xz |
不知道怎么回事,使用 wget 默认 UA 会报错HTTP/1.1 403 Forbidden
,可能是被拦截了,这里我们指定一个curl的UA
1 | localhost:~# wget -U curl https://mirrors.tuna.tsinghua.edu.cn/armbian-releases/uefi-x86/archive/Armbian_24.11.1_Uefi-x86_bookworm_current_6.6.60_minimal.img.qcow2.xz -O /mnt/dpan/image.qcow2.xz Connecting to mirrors.tuna.tsinghua.edu.cn ([2402:f000:1:400::2]:443) saving to '/mnt/dpan/image.qcow2.xz' image.qcow2.xz 100% |************************************************************************| 687M 0:00:00 ETA '/mnt/dpan/image.qcow2.xz' saved |
先解压 xz
1 | unxz /mnt/dpan/image.qcow2.xz |
解压完后看到只剩下 /mnt/dpan/image.qcow2
1 | localhost:~# ls -lh /mnt/dpan/ total 2G drwxrwxrwx 1 root root 0 Nov 30 07:17 $RECYCLE.BIN -rwxrwxrwx 1 root root 2.5G Nov 30 16:37 image.qcow2 |
挂载镜像
img 格式
使用 losetup
挂载 img 为 loop 设备
1 | losetup -f -P /mnt/dpan/image.img # 查看已经挂载的loop设备 losetup -a |
可以看到我们的qcow2镜像已经挂载到了 /dev/loop1
,我们可以将其视为一块硬盘
1 | localhost:~# losetup -a /dev/loop0: 0 /media/cdrom/boot/modloop-lts /dev/loop1: 0 /mnt/dpan/image.img |
此时我们还无法获取到分区,需要使用 partx
来使系统识别分区
1 | apk add --no-cache partx partx -l /dev/loop1 |
1 | localhost:~# partx -l /dev/loop1 # 1: 8192- 16383 ( 8192 sectors, 4 MB) # 2: 16384- 540671 ( 524288 sectors, 268 MB) # 3: 540672- 9584606 ( 9043935 sectors, 4630 MB) localhost:~# ls -l /dev/loop1* brw------- 1 root root 7, 1 Nov 30 22:32 /dev/loop1 brw-rw---- 1 root root 259, 0 Nov 30 23:11 /dev/loop1p1 brw-rw---- 1 root root 259, 1 Nov 30 23:11 /dev/loop1p2 brw-rw---- 1 root root 259, 2 Nov 30 23:11 /dev/loop1p3 |
qcow2 格式
我们可以将 qcow2 转换成 img,但此处不进行转换
经过测试无法直接使用 losetup
挂载,我们需要使用 qemu-nbd
(QEMU Disk Network Block Device)来挂载:
安装依赖 qemu-img
(qemu-img - Alpine Linux packages)
1 | # 首先配置镜像,注意需要启用 community,完了安装qemu-img apk add --no-cache qemu-img |
加载 nbd 内核模块
1 | modprobe nbd max_part=63 |
使用 qemu-nbd
挂载 qcow2 为 nbd 设备
1 | qemu-nbd -c /dev/nbd0 /mnt/dpan/image.qcow2 |
可以看到我们的 qcow2 镜像已经挂载到了 /dev/nbd0
1 | localhost:~# ls -l /dev/nbd0* brw-rw---- 1 root disk 43, 0 Nov 30 17:12 /dev/nbd0 brw-rw---- 1 root disk 43, 1 Nov 30 17:15 /dev/nbd0p1 brw-rw---- 1 root disk 43, 2 Nov 30 17:15 /dev/nbd0p2 brw-rw---- 1 root disk 43, 3 Nov 30 17:15 /dev/nbd0p3 localhost:~# fdisk -l /dev/nbd0 Found valid GPT with protective MBR; using GPT Disk /dev/nbd0: 7184384 sectors, 3508M Logical sector size: 512 Disk identifier (GUID): dd23ed05-9e0e-b640-81a8-d34a50763a10 Partition table holds up to 128 entries First usable sector is 2048, last usable sector is 7184350 Number Start (sector) End (sector) Size Name 1 8192 16383 4096K bios 2 16384 540671 256M efi 3 540672 7184350 3243M rootfs |
对拷分区
我们可以将任务简化为两块硬盘对拷分区,即:将 /dev/nbd0p3
对拷至 /dev/sda6
在对拷之前,我们最好格式化分区为目标格式,此处为 ext4,另外还需安装依赖 e2fsprogs
(e2fsprogs - Alpine Linux packages),不然没有 mkfs、e2fsck 命令
1 | apk add --no-cache e2fsprogs mkfs.ext4 /dev/sda6 |
使用 dd
命令
1 | dd if=/dev/nbd0p3 of=/dev/sda6 |
注意这里的 dd 是 busybox 的 dd,为精简版,不支持进度显示,如需完整dd需要安装
coreutils
(coreutils - Alpine Linux packages)
1apk add --no-cache coreutils
可以加上
oflag=direct
参数实现 Direct I/O,从而提高速度(超级快)
1dd if=/dev/nbd0p3 of=/dev/sda6 bs=4M oflag=direct status=progress
对拷完后,由于分区大小不一致,当前/dev/sda6
的文件系统为损坏状态,我们需要使用对应文件系统的工具(此处为 e2fsck
) 修复,否则后面无法挂载
1 | e2fsck /dev/sda6 |
最好卸载掉我们刚才挂载的 loop / nbd 设备,不然会被 os-prober 找到并修复引导
1 | losetup -d /dev/loop1 qemu-nbd -d /dev/nbd0 |
修复引导
此时我们不能直接重启进入系统,因为引导还未修复,当前的主引导还是 Windows,我们需要重新安装 GRUB,另外,由于硬盘的UUID已经发生改变,我们需要重新生成 GRUB 配置文件、fstab
由于我们是 Legacy Boot 启动,非 UEFI,所以我们需要执行适用于 Legacy Boot 的引导修复
挂载系统分区到 /mnt/sysroot
1 | mkdir -p /mnt/sysroot mount -t ext4 /dev/sda6 /mnt/sysroot |
除了系统分区外,我们还要挂载其他目录(使用 --bind)
1 | mount --bind /dev /mnt/sysroot/dev mount --bind /proc /mnt/sysroot/proc mount --bind /sys /mnt/sysroot/sys |
chroot
到硬盘上的 Linux 系统(原系统默认不知道什么shell,超级难用,这里指定使用bash)
1 | chroot /mnt/sysroot bash |
重新安装 GRUB
1 | grub-install /dev/sda |
启用 os-prober 支持自动添加其他系统启动项(如果是单系统可忽略)
1 | nano /etc/default/grub # 取消注释 GRUB_DISABLE_OS_PROBER=false |
重新生成引导文件
1 | update-grub # 或者 grub-mkconfig -o /boot/grub/grub.cfg |
如果你忘记了密码,可以在这里重设
1 | passwd |
退出 chroot 环境
1 | exit |
卸载已经挂载的设备与目录
1 | umount /mnt/sysroot/dev umount /mnt/sysroot/proc umount /mnt/sysroot/sys |
修复 fstab
此时如果重启,发现可以启动内核,但无法进入系统,因为 fstab (挂载信息)中的 UUID 未更新
自动修复
此处使用了来自 arch-install-scripts
(arch-install-scripts - Alpine Linux packages)的 genfstab
脚本,会引入其他依赖,且对于其他系统以及挂载比较复杂的情况不适用,不太推荐
1 | apk add --no-cache arch-install-scripts # 此包需要启用 community 镜像 genfstab -U /mnt/sysroot > /mnt/sysroot/etc/fstab |
手动修复
您也可以手动编辑修复
1 | # 查看每个分区的 UUID blkid # 手动编辑替换 vi /mnt/sysroot/etc/fstab |
1 | localhost:~# blkid /dev/loop0: TYPE="squashfs" /dev/loop/0: TYPE="squashfs" /dev/sr0: LABEL="alpine-std 3.20.3 x86_64" TYPE="iso9660" /dev/sda1: LABEL="Win10Pro X64" UUID="3E742B30742AEA7B" TYPE="ntfs" /dev/sda5: UUID="AB1BF8EF1A7CBDE8" TYPE="ntfs" /dev/sda6: LABEL="armbi_root" UUID="20853349-5763-4f08-86f5-ad6aafb84415" TYPE="ext4" localhost:~# cat /mnt/sysroot/etc/fstab UUID=20853349-5763-4f08-86f5-ad6aafb84415 / ext4 defaults,noatime,commit=120,errors=remount-ro 0 1 UUID=61C1-FFC3 /boot/efi vfat defaults 0 2 tmpfs /tmp tmpfs defaults,nosuid 0 0 |
由于我们这边是用 dd 克隆的分区,原UUID没有改变,但是引导类型由原镜像的 UEFI 改为了 Legacy Boot,所以不需要挂载 EFI 分区,将其删除即可
1 | UUID=20853349-5763-4f08-86f5-ad6aafb84415 / ext4 defaults,noatime,commit=120,errors=remount-ro 0 1 tmpfs /tmp tmpfs defaults,nosuid 0 0 |
顺便给它增加1x2048M的交换文件
1 | dd if=/dev/zero of=/mnt/sysroot/swapfile bs=1M count=2048 chmod 600 /mnt/sysroot/swapfile chown root:root /mnt/sysroot/swapfile mkswap /mnt/sysroot/swapfile echo '/swapfile none swap sw 0 0' >> /mnt/sysroot/etc/fstab |
卸载收尾
查看刚才手动挂载的分区
1 | localhost:~# mount sysfs on /sys type sysfs (rw,nosuid,nodev,noexec,relatime) devtmpfs on /dev type devtmpfs (rw,nosuid,noexec,relatime,size=10240k,nr_inodes=499135,mode=755,inode64) proc on /proc type proc (rw,nosuid,nodev,noexec,relatime) devpts on /dev/pts type devpts (rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000) shm on /dev/shm type tmpfs (rw,nosuid,nodev,noexec,relatime,inode64) /dev/sr0 on /media/cdrom type iso9660 (ro,relatime,nojoliet,check=s,map=n,blocksize=2048,iocharset=utf8) tmpfs on / type tmpfs (rw,relatime,mode=755,inode64) tmpfs on /run type tmpfs (rw,nosuid,nodev,size=803448k,nr_inodes=819200,mode=755,inode64) mqueue on /dev/mqueue type mqueue (rw,nosuid,nodev,noexec,relatime) /dev/loop0 on /.modloop type squashfs (ro,relatime,errors=continue) securityfs on /sys/kernel/security type securityfs (rw,nosuid,nodev,noexec,relatime) debugfs on /sys/kernel/debug type debugfs (rw,nosuid,nodev,noexec,relatime) pstore on /sys/fs/pstore type pstore (rw,nosuid,nodev,noexec,relatime) tracefs on /sys/kernel/debug/tracing type tracefs (rw,nosuid,nodev,noexec,relatime) /dev/sda5 on /mnt/dpan type fuseblk (rw,relatime,user_id=0,group_id=0,allow_other,blksize=4096) /dev/sda6 on /mnt/sysroot type ext4 (rw,relatime) |
卸载手动挂载的分区
1 | umount /mnt/dpan umount /mnt/sysroot |
重启并拔盘
1 | reboot |
大功告成