☰
纯命令行QEMU虚拟机实战:Ubuntu安装、cloud-init自动化与网络配置
2026/10/1 23:27:23 网站建设 项目流程

1. 写在前面:为什么我要用纯命令行装虚拟机

说实话,我本来没打算折腾这事的,但手头那台跑服务的机器是纯命令行环境,没桌面、没鼠标,只有一个SSH终端能进去。要在上面再跑一个Ubuntu虚拟机,最省事的思路自然是找VirtualBox或者VMware那一类带图形界面的方案,但在没X11的服务器上这些工具根本装不干净,运行起来也全靠间接转发,性能损耗还大可不必。

所以这次我直接锁定了qemu,全程纯命令行操作,把一台Ubuntu虚拟机从无到有装了出来。这里说的“纯命令行”,不只是启动虚拟机时用qemu-system-x86_64敲命令,而是包括下载镜像、创建磁盘、配置网络、安装系统、日常开关机在内的一整条链路,全部在终端里完成,没有打开过任何图形管理界面。整个过程走通之后,我特意把命令、参数和踩过的坑整理出来,当作记事备忘录,也希望能给在命令行环境下装虚拟机的朋友省一点试错时间。

如果你也是下面这几类人,这篇内容应该对你有用:一是手头只有服务器终端、没有桌面环境的运维或后端开发;二是想把虚拟机创建过程脚本化、自动化,不想每次手动点下一步下一步的折腾党;三是想搞明白qemu各种启动参数到底是什么意思,而不是照抄别人命令的进阶玩家。这篇备忘会按照我从零到一的实际操作顺序来写,尽量还原当时现场。

2. 开始前的准备:环境检查与依赖安装

2.1 先确认硬件虚拟化是否可用

不管多急,安装qemu之前我建议先花一分钟确认CPU虚拟化支持。虽然qemu可以在纯软件模拟(TCG模式)下运行,但那个性能跑桌面系统还能忍,跑编译任务或者装软件会慢到让你怀疑人生。所以如果你之后想用KVM加速,这一步必须提前确认。

在终端里先看CPU虚拟化标志位:

grep -E "(vmx|svm)" /proc/cpuinfo

如果输出里有vmx(Intel)或者svm(AMD),说明CPU支持硬件虚拟化,之后可以放心加-enable-kvm参数。如果没有任何输出,大概率是CPU太老,或者虚拟机里没开启嵌套虚拟化,这一项后面排查部分会再提到。

紧接着检查一下KVM设备节点是否存在:

ls -l /dev/kvm

正常情况输出类似crw-rw---- 1 root kvm ... /dev/kvm,看到这个文件存在,且你的用户已经在kvm组里,才说明当前用户有权限使用KVM加速。如果没有这个设备文件,可以试试加载内核模块:

sudo modprobe kvm sudo modprobe kvm_intel # IOMMU或Intel CPU sudo modprobe kvm_amd # AMD CPU

如果modprobe之后/dev/kvm还是没有生成,那就得怀疑BIOS里有没有开启VT-x/AMD-V了,这个只能重启进BIOS设置,命令行帮不了你。

2.2 安装qemu及相关组件

环境确认没问题后,直接通过apt安装。Ubuntu的软件源里自带的qemu版本足够稳定,不需要额外加PPA。

sudo apt update sudo apt install -y qemu-system-x86 qemu-utils cloud-image-utils

这里我装了三个东西:

  • qemu-system-x86:x86架构的系统模拟器主包,提供qemu-system-x86_64命令。
  • qemu-utils:提供qemu-img等磁盘工具,用于创建转换虚拟磁盘。
  • cloud-image-utils:提供cloud-localds命令,之后做无人值守安装时会用到。

装完后顺手验证版本:

qemu-system-x86_64 --version qemu-img --version

确认命令存在后,准备工作就结束了。整个安装阶段就这么几行命令,没什么花头。

3. 镜像获取与磁盘创建:绕过图形界面的第一步

3.1 用命令行下载Ubuntu镜像

这一步原本没什么难度,但对于我这种“服务器在国内机房、带宽有限”的实际情况,怎么快速拉到Ubuntu安装镜像还是有点讲究的。我选择的是官方镜像站点,但在命令行里用wget大文件时,有个容易被忽略的技巧:先用--continue配合断点续传下载,避免中途网络抖动导致前功尽弃。

以Ubuntu 24.04 Server的ISO为例,下载命令大概是这种样式:

wget --continue https://releases.ubuntu.com/24.04/ubuntu-24.04-live-server-amd64.iso

这里我特意选择了Server版而不是Desktop版。原因很简单:既然是纯命令行环境,装Desktop版图形桌面既浪费磁盘空间,后期还要处理图形驱动和显示问题,得不偿失。Server版安装出来就是纯命令行系统,跟宿主机气质一致,占用资源还少。

如果你只是想要一个快速跑服务的虚拟机,甚至可以不下载ISO,直接用cloud镜像,这部分等第五节专门说。先讲讲大多数情况下大家都会走的ISO安装路径。

另外下载完成后建议校验一下SHA256,官网上会给出checksum文件:

echo "一串校验值 ubuntu-24.04-live-server-amd64.iso" | sha256sum --check

这一步虽然啰嗦,但为了确保镜像文件没损坏,还是建议做一次,不然装到一半报错会非常郁闷。

3.2 用qemu-img创建虚拟磁盘

镜像拿到手,接下来就是给虚拟机准备磁盘。qemu默认使用qcow2格式,这是一种“按需分配”的磁盘格式,也就是说,你创建一块名义上30G的磁盘,并不是立刻在宿主机上占用30G空间,而是随着虚拟机里数据量增长慢慢变大。这一点在宿主机磁盘剩余空间不太充裕的时候特别实用,也方便之后做快照。

创建磁盘的命令如下:

qemu-img create -f qcow2 ubuntu-vm.qcow2 30G

输出会显示Formatting 'ubuntu-vm.qcow2', fmt=qcow2 size=32212254720 ...,看到size数字对得上就不用担心。30G是我给的初始大小,如果你只是跑个轻量服务,20G也够;如果计划在里面跑数据库或者做编译缓存,建议至少50G起步,反正初始不占实际空间。

创建好磁盘后,我还习惯顺手看一眼磁盘info:

qemu-img info ubuntu-vm.qcow2

这个命令确认格式、虚拟大小和实际占用,快照多了以后也能靠它排查问题。

4. 核心环节:从ISO安装Ubuntu系统

4.1 启动安装虚拟机的命令

创建完磁盘,接下来就是把ISO挂进去、启动安装程序。这一步是整条链路里最关键、也是参数最多的部分。

我当时的启动命令如下:

qemu-system-x86_64 \ -name ubuntu-vm \ -m 4096 \ -smp 4 \ -enable-kvm \ -drive file=ubuntu-vm.qcow2,format=qcow2,if=virtio \ -cdrom ubuntu-24.04-live-server-amd64.iso \ -boot d \ -netdev user,id=net0 \ -device virtio-net-pci,netdev=net0 \ -vnc :1 \ -display none

逐个解释一下每个参数的含义,理解之后你就能自由组合:

  • -name ubuntu-vm:给虚拟机起名字,方便之后区分,也影响窗口标题之类,对命令行用户主要是个标记。
  • -m 4096:分配4G内存。这个数要看宿主机总内存,建议至少2G起步,否则安装和运行都会很卡。
  • -smp 4:给虚拟机4个虚拟CPU核心。如果宿主机是4核,可以给2到4;如果是8核以上的CPU,给4或者6都可以,后面需要可以随时改。
  • -enable-kvm:启用KVM硬件加速。这是性能的分水岭,不加这个参数qemu会退化为纯软件模拟,速度慢很多。
  • -drive file=ubuntu-vm.qcow2,format=qcow2,if=virtio:指定虚拟磁盘文件,使用virtio驱动。virtio是半虚拟化I/O方案,磁盘性能明显优于模拟的IDE设备,推荐优先使用。
  • -cdrom ...:挂载安装光盘镜像。
  • -boot d:优先从光盘启动,这样才能进入安装引导。
  • -netdev user,id=net0和-device virtio-net-pci,netdev=net0:配置一个用户模式的网络设备,让虚拟机可以通过NAT方式访问外网。
  • -vnc :1:在5901端口开VNC服务,方便从外部连接图形界面进行安装操作。
  • -display none:宿主机不弹图形窗口,因为我这边是无桌面环境,开着窗口也没用。

4.2 安装过程中的避坑点

因为我这台宿主机没有图形界面,所以安装过程是通过VNC远程完成的。在本地电脑用任意VNC客户端连接宿主机IP的5901端口,就能看到Ubuntu安装界面了。如果你跟我一样,宿主机SSH端口改了或者防火墙开着,记得先确认5901端口可以访问。

安装过程本身和平时在物理机上装Ubuntu Server没有区别,无非是选择语言、键盘布局、配置网络、设置用户、安装OpenSSH这些步骤。但是有几个地方纯命令行操作时需要特别注意:

第一,磁盘识别名问题。因为磁盘驱动用的是virtio,安装界面里看到的磁盘设备名可能不是sda,而是vda。分区的时候不要奇怪,直接选择整块磁盘让安装器自动分区就行,不需要手动改。

第二,网络配置可以先跳过。如果你是靠DHCP自动获取IP,那装完系统虚拟机的IP可能和宿主机不在同一个网段,因为用户模式网络是qemu内部虚构的一个NAT网络,默认网段是10.0.2.0/24。安装时网络界面自动配置好就行,能联网就能拿到地址。

第三,安装软件包的时候不要装桌面。既然是命令行虚拟机,安装时直接选择Ubuntu Server的标准工具集就行,不需要勾选桌面版,省得装一堆没用的包。

等安装进度条走完,系统会提示移除安装介质并重启。这时候回到宿主机,先把原来的qemu进程结束掉:

# 在qemu的终端里按 Ctrl+C,或另开终端用pkill pkill -f qemu-system-x86_64

然后用没有光盘的方式重新启动:

qemu-system-x86_64 \ -name ubuntu-vm \ -m 4096 \ -smp 4 \ -enable-kvm \ -drive file=ubuntu-vm.qcow2,format=qcow2,if=virtio \ -netdev user,id=net0 \ -device virtio-net-pci,netdev=net0 \ -vnc :1 \ -display none

注意这里去掉了-cdrom和-boot d,默认就从磁盘启动了。重启后虚拟机里的系统会用DHCP从qemu自带的DHCP服务获取一个10.0.2.x的地址。这个地址宿主机可以直接访问,虚拟机也能上网,但是外部机器访问不到它。这部分网络细节后面会展开。

4.3 安装完成后,用命令行直接登录

按理说到这一步,如果带着VNC图形界面操作,直接在VNC里就能登录系统。但既然追求纯命令行,我更推荐用SSH进入虚拟机。

在宿主机里用ping 10.0.2.15看看能不能通,能通的话说明网络没问题。但注意,默认的user网络模式下,只有宿主机能访问虚拟机的10.0.2.x地址,从局域网其他机器访问不到。如果你想从外部SSH进虚拟机,最简单的办法是用端口转发,在-netdev user参数里加上hostfwd:

-netdev user,id=net0,hostfwd=tcp::2222-:22

这里的意思是把宿主机的2222端口映射到虚拟机的22端口,然后就可以这样SSH登录:

ssh -p 2222 你的用户名@127.0.0.1

端口转发是命令行管理虚拟机时非常实用的功能,不用关心虚拟机内部IP是什么,只要宿主机端口通,就能穿透进去。

5. 无人值守方案:用cloud image一条命令装好系统

如果你觉得每次都经历一遍VNC安装交互太麻烦,或者希望把整个虚拟机安装做成完全无人工干预的自动化流程,那么cloud image方案才是真正的“纯命令行”终极形态。

cloud image是Ubuntu官方提供的针对云环境优化的镜像,它不能直接启动,必须通过cloud-init注入初始配置,包括用户名、SSH密钥、密码、网络配置等。但正因为有这个机制,我们可以完全绕开安装向导,真正做到“下载一个镜像,生成一个配置,启动即可用”。

5.1 下载cloud镜像

cloud镜像同样是走命令行下载,以Ubuntu 24.04为例:

wget --continue https://cloud-images.ubuntu.com/releases/24.04/release/ubuntu-24.04-server-cloudimg-amd64.img

这个镜像文件比ISO小很多,一般只有几百MB,下载速度快很多。下载后先改名:

mv ubuntu-24.04-server-cloudimg-amd64.img ubuntu-cloud.qcow2

官方cloud镜像本身就是qcow2格式,所以不需要额外创建磁盘了。

5.2 准备cloud-init配置

cloud-init是启动时读取配置并自动执行的机制,配置写入两个文件:user-data和meta-data。

先创建user-data:

cat > user-data << 'EOF' #cloud-config hostname: ubuntu-cloud users: - name: ubuntu sudo: ALL=(ALL) NOPASSWD:ALL shell: /bin/bash lock_passwd: false plain_text_passwd: '你的密码' ssh_authorized_keys: - ssh-ed25519 AAAAC3NzaC1l... 你本机的公钥 ssh_pwauth: true package_update: true packages: - qemu-guest-agent EOF

这里我配置了一个ubuntu用户,设置密码并且禁用了密码锁定,同时把宿主机公钥塞进ssh_authorized_keys,方便之后免密登录。注意ssh-ed25519那一行换成你自己本机~/.ssh/id_ed25519.pub的内容,没有的话先生成一下。

meta-data文件一般很简单,甚至可以留空:

cat > meta-data << 'EOF' instance-id: ubuntu-cloud-001 local-hostname: ubuntu-cloud EOF

然后用cloud-localds生成一个seed镜像,这个镜像会挂载给虚拟机当作配置注入源:

cloud-localds seed.img user-data meta-data

5.3 一条命令启动并启用SSH

配置好之后,启动虚拟机的命令只需要下面这一条:

qemu-system-x86_64 \ -name ubuntu-cloud \ -m 2048 \ -smp 2 \ -enable-kvm \ -drive file=ubuntu-cloud.qcow2,format=qcow2,if=virtio \ -drive file=seed.img,format=raw,if=virtio \ -netdev user,id=net0,hostfwd=tcp::2222-:22 \ -device virtio-net-pci,netdev=net0 \ -display none

注意这里多了个-drive file=seed.img,就是把cloud-init的配置盘挂上去了。启动之后等一两分钟,让cloud-init完成初始化,然后就可以在宿主机上尝试SSH连接:

ssh -p 2222 ubuntu@127.0.0.1

只要能看到登录成功的提示,这台Ubuntu虚拟机就装好了。整个过程没有出现任何一个图形界面交互,全程命令行,真正的无人值守。Cloud镜像方案特别适合批量创建虚拟机,比如你要一次性起三台测试机,只需要分别准备三个seed.img,启动三次qemu就好,配置不变,效率很高。

6. 虚拟机网络配置:让虚拟机联网、能被宿主机访问

qemu的网络配置是我当时踩坑最多的地方,所以专门拿出来单独说。

6.1 默认的用户模式网络(user networking)

我前面所有命令里用的-netdev user就是qemu最常见的网络方案,也叫用户模式网络。它的工作方式类似家用路由器:qemu进程内部实现了一个小型的NAT,虚拟机从内置DHCP获得IP(默认10.0.2.15),通过这个NAT访问外网。优点是零配置、开箱即用、不需要root权限,缺点是外部设备无法主动连接虚拟机内部服务,除非你在启动命令里指定hostfwd做端口转发。

上面已经提到过SSH转发,这里再说一下怎么转发多个端口。比如虚拟机里跑了Web服务在80端口,我想通过宿主机的8080访问,那就把-netdev参数改成:

-netdev user,id=net0,hostfwd=tcp::2222-:22,hostfwd=tcp::8080-:80

多个端口之间用逗号分隔,规则就是hostfwd=tcp:宿主机端口-虚拟机IP:虚拟机端口,虚拟机IP默认可以不写,代表10.0.2.15。这个方案对大多数单机测试场景已经够用了,不折腾桥接也完全没有问题。

6.2 配置桥接网络,让虚拟机拥有独立IP

如果你想给虚拟机配一个局域网IP,让其他机器直接访问它,就得用桥接模式。

桥接的原理是创建一个虚拟网桥,把宿主机的物理网卡和虚拟机的虚拟网卡都挂在同一个二层网络中,这样虚拟机就像一台真实的物理机一样存在于局域网中,能够直接获得局域网IP。

创建桥接端口需要root权限,宿主机需要安装bridge-utils:

sudo apt install -y bridge-utils

然后创建一个网桥br0,并让物理网卡加入:

sudo ip link add name br0 type bridge sudo ip link set dev eth0 master br0 sudo ip address delete 192.168.x.x/24 dev eth0 sudo ip address add 192.168.x.x/24 dev br0 sudo ip link set br0 up

这里eth0要替换成你宿主机实际的物理网卡名,IP段也要换成你局域网实际的地址。这组命令只是临时生效,重启网络服务后可能需要重新配置。如果你想永久配置,需要改netplan或者NetworkManager的配置文件,那是另外一个话题了。

桥接模式启动qemu时,参数要换成这样:

-netdev bridge,id=net0,br=br0 \ -device virtio-net-pci,netdev=net0

启动后虚拟机会通过DHCP从局域网路由器获取一个IP,宿主机、同网段其他机器都能直接访问它,不需要端口转发。但注意,桥接模式有个硬性要求:宿主机连的必须是无线或有线网络,并且网络环境允许新设备接入,有些公共Wi-Fi的客户端隔离策略会导致虚拟机无法通信,这种情况建议换回user模式。

如果只是想让宿主机和虚拟机互通,其实还有一种轻量方案,就是给qemu加一个虚拟网络设备,再配一个虚拟网桥,也就是libvirt默认的virbr0方案。但这需要额外装libvirt-bin等组件,对纯命令行场景来说反而增加复杂度,不如直接在user模式下做端口转发来得干净。

7. 命令行管理虚拟机的日常操作

虚拟机装好后,日常的开关机、暂停恢复、快照备份这些操作也全部能在命令行里完成。这里把我惯用的命令整理出来,都是平时高频使用的。

7.1 暂停、恢复与关机

我在实际操作中发现,很多人习惯直接kill qemu来停止虚拟机,这样既粗暴又容易导致虚拟机内文件系统损坏。正确的做法是优雅关机,方式有几种:

如果虚拟机里已经配置好SSH,那最简单的方式是直接SSH进去执行:

ssh -p 2222 ubuntu@127.0.0.1 "sudo shutdown -h now"

如果虚拟机没装SSH,或者网络环境比较复杂,可以通过qemu的monitor控制台发送ACPI关机信号。启动qemu时加上monitor参数:

-monitor unix:/tmp/ubuntu-vm.sock,server,nowait

这样会在宿主机上创建一个控制socket。在宿主机上用socat连接进去:

sudo apt install -y socat socat - UNIX-CONNECT:/tmp/ubuntu-vm.sock

进入monitor之后,输入:

system_powerdown

虚拟机会像收到物理机电源按钮一样执行ACPI关机流程,系统内部优雅关闭。

如果只是暂时不用虚拟机,想保留当前运行状态,可以用stop和cont:

stop cont

这两条命令分别暂停和恢复虚拟机的CPU执行,暂停时虚拟机内存状态被保留,有点像合上笔记本盖子进入休眠状态,恢复速度比冷启动快得多。

7.2 快照与备份

qcow2格式最大的优势之一就是支持快照。创建快照之前,最好先让虚拟机进入一致状态,最简单的办法是暂停虚拟机,然后创建快照。

在monitor里执行:

savevm snapshot-name

或者直接在宿主机命令行用qemu-img:

qemu-img snapshot -c before-upgrade ubuntu-vm.qcow2

查看当前快照列表:

qemu-img snapshot -l ubuntu-vm.qcow2

回到指定快照:

qemu-img snapshot -a before-upgrade ubuntu-vm.qcow2

注意,恢复快照必须在虚拟机处于关闭状态时操作,否则会因为磁盘被占用而失败,而且恢复之后虚拟机磁盘会回到快照创建时的状态,之后的所有新增数据都会丢失。所以做快照前一定确认好。

另外,如果你想把qcow2转成其他格式传到别的平台,可以用convert命令:

qemu-img convert -f qcow2 -O raw ubuntu-vm.qcow2 ubuntu-vm.raw

这个命令也常用于把qcow2“压缩”成一个更紧凑的新镜像,因为qcow2删除文件后实际占用空间不会自动减少,转一次格式就能清理掉这些空洞,效果立竿见影。

8. 常见问题与排查备忘

这一节把我在整个过程中遇到过的典型问题集中整理成一个速查表,方便以后遗忘的时候快速排查。

现象可能原因排查与解决
启动时报Could not access KVM kernel module: Permission denied当前用户不在kvm组执行sudo usermod -aG kvm 你的用户名,重新登录终端生效
启动时报KVM is not supported或/dev/kvm not foundCPU虚拟化未开启,或运行在无嵌套虚拟化的虚拟机中检查BIOS里VT-x/AMD-V开关;如果宿主机是虚拟机,开启CPU嵌套虚拟化,并给qemu加-cpu host
虚拟机启动极慢,CPU占用100%没加-enable-kvm或KVM无法使用,qemu退回纯软件模拟确认ls -l /dev/kvm存在;确保启动命令里有-enable-kvm
虚拟机网络能ping通网关但上不了外网DNS配置问题或NAT网络异常检查虚拟机内/etc/resolv.conf,改成nameserver 223.5.5.5试一下;宿主机检查qemu进程的netdev配置
SSH连接宿主机2222端口超时端口转发没生效或防火墙拦截确认netdev参数里有hostfwd=tcp::2222-:22;检查宿主机ss -lntp看2222端口是否在监听
VNC连接不上qemu没开VNC端口或-display none后忘了-vnc :1确认启动命令里有-vnc :1;检查防火墙是否放行5901
虚拟机磁盘越用越大,删了文件空间也不释放qcow2格式空洞未回收在虚拟机内执行fstrim -av或sudo dd if=/dev/zero of=/tmp/zero bs=1M填满再删除,宿主机执行qemu-img convert做一次压缩转换
cloud-init启动后无法SSH登录密钥或密码配置错误检查user-data文件格式,注意用plain_text_passwd时lock_passwd必须为false;cloud-init开机自检日志在/var/log/cloud-init-output.log
快照恢复后数据丢失快照本质就是时间点备份恢复前明确这是回到某时刻的状态,之后新增数据都会清掉;重要数据先另做备份

除了表格里的问题,还有一个连宿主机性能都相关的坑:如果你让qemu线程使用默认CPU调度策略,当宿主机上还有别的服务时,虚拟机内的CPU时间会不稳定,导致编译或者跑任务时忽快忽慢。想给qemu进程调高优先级可以这么操作:

# 启动qemu后,用top找到qemu进程PID sudo chrt -f -p 50 进程PID

这个命令把qemu进程设为实时调度,避免被其他进程抢占CPU,但只建议在特定性能敏感场景使用,普通测试环境没必要。

另外再补充一个实用的命令行技巧:如果你在启动qemu时忘了加某个参数,不需要关掉虚拟机重启,通过monitor就能动态调整部分设备。比如想临时添加一块虚拟磁盘:

drive_add 0 file=/path/to/newdisk.qcow2,format=qcow2,if=virtio

这种动态修改的能力让qemu运维起来灵活很多,但要注意不是所有设备都支持热插拔,网络设备通常需要额外配置,还是以重启稳妥为主。

前面这些内容基本覆盖了我在Ubuntu宿主机上用纯命令行安装和管理qemu虚拟机的完整流程,从环境准备、镜像下载、磁盘创建、ISO安装、cloud-init无人值守、网络配置、日常操作到问题排查,每一条都是我当时实际敲过跑通的。最后再分享一个我后来养成的习惯:把这些启动参数写成一个shell脚本,每次开虚拟机前只需要改几个变量,脚本自动拼接启动命令,再也不用手敲那一长串参数。cloud image加cloud-init加脚本这三件套组合起来,基本可以实现“输入一条命令,几分钟后得到一台随手能用的Ubuntu虚拟机”,这才是纯命令行操作真正让人上瘾的地方。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询