用 QEMU 踩的一些坑

在做项目的过程中,需要在虚拟机内进行测试,由于需要可备份与灵活性,在与学长商讨之后最终决定使用 qemu,在此记录一些使用 qemu 过程中碰到的问题和解决方案

创建虚拟机

首先,需要创建一个虚拟机,并且进行基础的配置,例如静态 IP,ssh 登录等

新建磁盘

首先创建一个 qcow2 文件,这种格式是 QEMU 支持的一种磁盘镜像格式,具有动态扩容、压缩和快照功能

1
qemu-img create -f qcow2 ubuntu.qcow2 50G

安装 OS

在有了磁盘镜像之后,需要给这个虚拟机装上操作系统,我选择的是 ubuntu 22.04,首先从官网下载镜像:

1
wget https://releases.ubuntu.com/jammy/ubuntu-22.04.5-live-server-amd64.iso

之后,由于磁盘是空的,且服务器上是无图形界面的,所以需要通过 -kernel-initrd 参数来指定启动虚拟机的过程中使用的内核与初始内存磁盘,而不能直接从下载的 ISO 文件引导

所需要的文件可以从下载下来的 ISO 文件中提取:

1
2
3
4
5
6
7
8
9
10
11
mkdir -p /tmp/iso_mount

# 挂载 ISO
sudo mount -o loop ./ubuntu-22.04.5-live-server-amd64.iso /tmp/iso_mount

# 提取文件
mkdir -p ./iso/casper
cp /tmp/iso_mount/casper/vmlinuz ./iso/casper/
cp /tmp/iso_mount/casper/initrd ./iso/casper/

sudo umount /tmp/iso_mount

之后使用以下命令启动虚拟机并安装操作系统:

1
qemu-system-x86_64 -m 4096 -smp 4 -hda k8s-c.qcow2 -enable-kvm -cpu host -nographic -cdrom ./ubuntu-22.04.5-live-server-amd64.iso -boot d -kernel ./iso/casper/vmlinuz -initrd ./iso/casper/initrd -append 'console=ttyS0'

之后再安装过程中,会有一堆一堆的选项要我们选,基本可以全用默认选择,一路选 Done 即可,到最后会提示我们 reboot,注意这个时候如果直接 enter 去重启的话会报错:

1
2
Please remove the installation medium, then press ENTER:
[FAILED] Failed unmounting /cdrom.

这个意思是让我们把光盘拔出来,因为我们的启动命令中 -boot d 指定了从光盘启动,但是启动的光盘是通过 -cdrom 指定的虚拟光驱,所以没办法“拔”出来,解决方法是直接 ctrl+A, X 关闭 qemu,下一次手动重启即可

配网

为了保证虚拟机里面的服务能够在后台运行,并且能够在有需要的时候 ssh 上去,同时不用 tmux,我需要给虚拟机分配一个静态 IP,并且配置一个网桥,让其能够接入外网

第一步是配置网桥,过程是:

1
2
3
4
5
6
7
8
9
10
# 新建网桥
sudo brctl addbr ubuntubr
sudo ip addr add dev ubuntubr 192.168.10.1/24
sudo ip link set up dev ubuntubr

# 添加 iptables 规则
sudo iptables -t nat -A POSTROUTING -o <INTERFACE> -s 192.168.10.0/24 -j MASQUERADE

# 允许内核 IP 转发
sudo sysctl -w net.ipv4.ip_forward=1

其中 <INTERFACE> 可以是本机任何能够访问外网的网卡名

有了网桥之后,我们首先需要前台运行虚拟机,在里面配置好静态 IP,具体来说,根据如下命令启动:

1
sudo qemu-system-x86_64 -m 4096 -smp 4 -hda http-server.qcow2 -enable-kvm -cpu host -nographic -netdev bridge,br=httpbr,id=n2

等到 ubuntu 长长的启动 log 结束之后(注意,这个过程中会有一个 FAILED 信息,不过问题不大,这是因为虚拟机的网络还未配置),登录 os 安装时配置的用户,然后使用 netplan 来配一下网

/etc/netplan/ 目录下,会有一个 50-cloud-init.yaml 文件,这是 netplan 的默认配置文件(数字代表优先级,字典序越大越靠后加载),但注意,这个是由 cloud-init 自动生成的,并且在每次重启的时候都会被覆盖!!

想要持久化配置静态 IP,我们需要禁用 cloud-init 服务,并在 50-cloud-init.yaml 里面配置,具体来说

首先将 50-cloud-init.yaml 里面的内容改成:

1
2
3
4
5
6
7
8
9
10
network:
version: 2
ethernets:
ens3:
dhcp4: no
addresses:
- 192.168.10.2/24
routes:
- to: default
via: 192.168.10.1 # 宿主机网桥 IP

然后通过以下命令来禁用 cloud-init 服务:

1
echo "network: {config: disabled}" | sudo tee /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg

之后 sudo reboot 即可

在配好之后,可以尝试一下互 ping 与 ssh,如果都可以的话,就可以通过 sudo poweroff 关闭虚拟机了

后台启动

后台启动命令是:

1
sudo qemu-system-x86_64 -m 4096 -smp 4 -hda http-server.qcow2 -enable-kvm -cpu host -nographic -netdev bridge,br=httpbr,id=n2 -device virtio-net,netdev=n2,mac=52:54:00:12:34:58 -monitor none -serial none &

但是 -nographic -monitor none -serial none 似乎不一定能够保证 qemu 完全不监听 stdin,所以有的时候放在后台跑的时候会被挂起,这种时候可以重定向一下:

1
sudo qemu-system-x86_64 -m 4096 -smp 4 -hda http-server.qcow2 -enable-kvm -cpu host -nographic -netdev bridge,br=httpbr,id=n2 -device virtio-net,netdev=n2,mac=52:54:00:12:34:58 -monitor none -serial none < /dev/null &

当我们启动之后,可以使用 jobs 命令来查看命令状态,正常情况下应该是 Running,并且可以 ping 192.168.10.2 查看,如果发现进程状态是 Stopped,可以注意一下是不是还是有某些操作需要读取 stdin,例如可能是 sudo 需要用户输入密码


© 2024 本网站由 Ywang22 使用 Stellar主题 创建
总访问 次 | 本页访问
共发表 76 篇 Blog(s) · 总计 173.7k 字