ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

纯命令行QEMU虚拟机实战:Ubuntu安装、cloud-init自动化与网络配置

纯命令行QEMU虚拟机实战:Ubuntu安装、cloud-init自动化与网络配置 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如果输出里有vmxIntel或者svmAMD说明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-x86x86架构的系统模拟器主包提供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, fmtqcow2 size32212254720 ...看到size数字对得上就不用担心。30G是我给的初始大小如果你只是跑个轻量服务20G也够如果计划在里面跑数据库或者做编译缓存建议至少50G起步反正初始不占实际空间。创建好磁盘后我还习惯顺手看一眼磁盘infoqemu-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 fileubuntu-vm.qcow2,formatqcow2,ifvirtio \ -cdrom ubuntu-24.04-live-server-amd64.iso \ -boot d \ -netdev user,idnet0 \ -device virtio-net-pci,netdevnet0 \ -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 fileubuntu-vm.qcow2,formatqcow2,ifvirtio指定虚拟磁盘文件使用virtio驱动。virtio是半虚拟化I/O方案磁盘性能明显优于模拟的IDE设备推荐优先使用。-cdrom ...挂载安装光盘镜像。-boot d优先从光盘启动这样才能进入安装引导。-netdev user,idnet0和-device virtio-net-pci,netdevnet0配置一个用户模式的网络设备让虚拟机可以通过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的终端里按 CtrlC或另开终端用pkill pkill -f qemu-system-x86_64然后用没有光盘的方式重新启动qemu-system-x86_64 \ -name ubuntu-vm \ -m 4096 \ -smp 4 \ -enable-kvm \ -drive fileubuntu-vm.qcow2,formatqcow2,ifvirtio \ -netdev user,idnet0 \ -device virtio-net-pci,netdevnet0 \ -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,idnet0,hostfwdtcp::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-datacat 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-data5.3 一条命令启动并启用SSH配置好之后启动虚拟机的命令只需要下面这一条qemu-system-x86_64 \ -name ubuntu-cloud \ -m 2048 \ -smp 2 \ -enable-kvm \ -drive fileubuntu-cloud.qcow2,formatqcow2,ifvirtio \ -drive fileseed.img,formatraw,ifvirtio \ -netdev user,idnet0,hostfwdtcp::2222-:22 \ -device virtio-net-pci,netdevnet0 \ -display none注意这里多了个-drive fileseed.img就是把cloud-init的配置盘挂上去了。启动之后等一两分钟让cloud-init完成初始化然后就可以在宿主机上尝试SSH连接ssh -p 2222 ubuntu127.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,idnet0,hostfwdtcp::2222-:22,hostfwdtcp::8080-:80多个端口之间用逗号分隔规则就是hostfwdtcp:宿主机端口-虚拟机IP:虚拟机端口虚拟机IP默认可以不写代表10.0.2.15。这个方案对大多数单机测试场景已经够用了不折腾桥接也完全没有问题。6.2 配置桥接网络让虚拟机拥有独立IP如果你想给虚拟机配一个局域网IP让其他机器直接访问它就得用桥接模式。桥接的原理是创建一个虚拟网桥把宿主机的物理网卡和虚拟机的虚拟网卡都挂在同一个二层网络中这样虚拟机就像一台真实的物理机一样存在于局域网中能够直接获得局域网IP。创建桥接端口需要root权限宿主机需要安装bridge-utilssudo 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,idnet0,brbr0 \ -device virtio-net-pci,netdevnet0启动后虚拟机会通过DHCP从局域网路由器获取一个IP宿主机、同网段其他机器都能直接访问它不需要端口转发。但注意桥接模式有个硬性要求宿主机连的必须是无线或有线网络并且网络环境允许新设备接入有些公共Wi-Fi的客户端隔离策略会导致虚拟机无法通信这种情况建议换回user模式。如果只是想让宿主机和虚拟机互通其实还有一种轻量方案就是给qemu加一个虚拟网络设备再配一个虚拟网桥也就是libvirt默认的virbr0方案。但这需要额外装libvirt-bin等组件对纯命令行场景来说反而增加复杂度不如直接在user模式下做端口转发来得干净。7. 命令行管理虚拟机的日常操作虚拟机装好后日常的开关机、暂停恢复、快照备份这些操作也全部能在命令行里完成。这里把我惯用的命令整理出来都是平时高频使用的。7.1 暂停、恢复与关机我在实际操作中发现很多人习惯直接kill qemu来停止虚拟机这样既粗暴又容易导致虚拟机内文件系统损坏。正确的做法是优雅关机方式有几种如果虚拟机里已经配置好SSH那最简单的方式是直接SSH进去执行ssh -p 2222 ubuntu127.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和contstop cont这两条命令分别暂停和恢复虚拟机的CPU执行暂停时虚拟机内存状态被保留有点像合上笔记本盖子进入休眠状态恢复速度比冷启动快得多。7.2 快照与备份qcow2格式最大的优势之一就是支持快照。创建快照之前最好先让虚拟机进入一致状态最简单的办法是暂停虚拟机然后创建快照。在monitor里执行savevm snapshot-name或者直接在宿主机命令行用qemu-imgqemu-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参数里有hostfwdtcp::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 bs1M填满再删除宿主机执行qemu-img convert做一次压缩转换cloud-init启动后无法SSH登录密钥或密码配置错误检查user-data文件格式注意用plain_text_passwd时lock_passwd必须为falsecloud-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,formatqcow2,ifvirtio这种动态修改的能力让qemu运维起来灵活很多但要注意不是所有设备都支持热插拔网络设备通常需要额外配置还是以重启稳妥为主。前面这些内容基本覆盖了我在Ubuntu宿主机上用纯命令行安装和管理qemu虚拟机的完整流程从环境准备、镜像下载、磁盘创建、ISO安装、cloud-init无人值守、网络配置、日常操作到问题排查每一条都是我当时实际敲过跑通的。最后再分享一个我后来养成的习惯把这些启动参数写成一个shell脚本每次开虚拟机前只需要改几个变量脚本自动拼接启动命令再也不用手敲那一长串参数。cloud image加cloud-init加脚本这三件套组合起来基本可以实现“输入一条命令几分钟后得到一台随手能用的Ubuntu虚拟机”这才是纯命令行操作真正让人上瘾的地方。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进