
纯命令行装虚拟机这事儿听起来像在折腾自己但等你真的碰上服务器只有SSH、没有图形界面还得开一台Ubuntu虚拟机的需求时就会庆幸当初把这条链路摸透了。我最近就在一台Ubuntu 24.04 LTS服务器上用纯命令行搞定了QEMU虚拟机的创建、安装和日常运维全程零图形界面只靠一条条命令把Ubuntu虚拟机跑起来。这篇就当是我的成功备忘也是给同路人的完整操作手册。不管你是想复用这套流程还是单纯想知道命令行装虚拟机到底行不行这篇都适合你。1. 从一台没有桌面的服务器说起为什么非得用命令行装虚拟机先说背景。我要在一台无显示器的Ubuntu服务器上跑一个独立的Ubuntu虚拟机用来隔离测试一些会污染宿主机的环境变量和系统依赖。这台服务器安安静静地躺在机房里别说显示器连个显卡都不一定有我能用的只有SSH终端。这种场景下VirtualBox、VMware Workstation这类依赖图形界面的方案直接出局能选的就剩纯命令行方案。QEMU/KVM就是最合理的选择。QEMU本身是硬件模拟器配合Linux内核的KVM模块后可以做到接近原生的虚拟化性能而且它天生就是命令行工具所有操作都可以通过终端完成。对比一下其他方案就明白了VirtualBox虽然也有VBoxManage命令行接口但安装依赖图形库在精简服务器上安装时经常缺东少西LXD和Docker更轻量但隔离的是进程级别不是完整的硬件虚拟化二者解决的问题完全不同。我这里要的是真实的Ubuntu系统所以KVM/QEMU组合是唯一能打满分的答案。还有一个隐性优势命令行操作可复制、可审计。你把一整条qemu-system-x86_64启动命令写进脚本随时能重放换成图形界面操作换个机器就得重新点一遍鼠标纯属浪费时间。对于做运维或者搞自动化的人来讲这种一切皆可脚本化的特性比任何花哨的界面都值钱。我这次的宿主环境是Ubuntu 24.04 LTSCPU支持硬件虚拟化内存32GB磁盘500GB属于比较标准的服务器配置。下面的所有命令除非特别说明都是在宿主机上执行的。如果你用的是Ubuntu 20.04或22.04命令基本通用个别包名可能需要微调。2. 开工前必须做好的准备虚拟化支持、依赖包与磁盘空间很多人在命令行装虚拟机时卡在第一步敲了安装命令却报错或者启动虚拟机时直接提示KVM不可用。这些都和准备工作不到位有关所以我把准备环节单独拆出来讲。2.1 确认CPU虚拟化已开启QEMU可以纯软件模拟CPU但速度慢得让人怀疑人生要流畅运行必须依赖KVM硬件加速。先在宿主机上确认CPU虚拟化是否开启grep -E (vmx|svm) /proc/cpuinfoIntel CPU显示vmxAMD CPU显示svm能看到输出就说明CPU和主板BIOS里都开启了虚拟化。如果没输出大概率是BIOS里没开需要重启进BIOS找Intel VT-x或AMD-V选项打开。另外还要确认KVM设备节点存在ls -l /dev/kvm如果显示/dev/kvm存在万事俱备如果不存在执行sudo modprobe kvm_intel或sudo modprobe kvm_amd加载模块再检查一遍。这里有个很容易忽略的坑即使/dev/kvm存在当前用户如果不属于kvm组启动虚拟机时依然会报Permission denied后面我会专门说这个问题。2.2 安装QEMU相关软件包Ubuntu的软件源里有现成的QEMU包直接装就行sudo apt update sudo apt install -y qemu-system-x86 qemu-utils qemu-kvm解释一下这几个包的作用qemu-system-x86QEMU本体提供qemu-system-x86_64命令qemu-utils提供qemu-img等磁盘管理工具qemu-kvmKVM支持相关的辅助包如果是较老版本的Ubuntu包名可能略有不同但20.04及以后都是这样。安装完成后可以用qemu-system-x86_64 --version验证一下是否成功。另外原版系统可能还需要ovmf来支持UEFI启动但默认的BIOS启动方式不需要先不装。2.3 磁盘空间评估虚拟磁盘虽然可以按需增长但初始规划还是要留足余量。Ubuntu Server纯净安装大约占2~3GB加上基础软件、日志和缓存实际运行后轻松涨到10GB以上所以我建议给你的虚拟磁盘分配50GB的容量上限。在宿主机上这个虚拟磁盘文件一开始只占几百MB随着虚拟机使用而逐渐增长50GB只是上限不是立刻占满。确保宿主机有足够空间df -h /至少预留60GB以上空间再开始别等虚拟机磁盘膨胀时才发现宿主机分区满了。2.4 下载Ubuntu ISO并校验完整性命令行下载ISO用wget或curl都行。我这里以Ubuntu 24.04 Live Server为例wget https://releases.ubuntu.com/24.04/ubuntu-24.04.2-live-server-amd64.iso下载完一定要校验SHA256防止镜像文件损坏或下载不完整。去Ubuntu官网找到对应的checksum然后执行echo 官网给出的sha256值 ubuntu-24.04.2-live-server-amd64.iso | sha256sum -c -输出OK才算可靠。这一步千万别省我见过有人下载了半截文件就开始装安装程序在分区阶段各种报错浪费了一整个下午。如果你选的是Desktop版也没关系安装流程里带图形界面但既然都命令行装虚拟机了我强烈建议选Server版少装一堆用不上的桌面组件虚拟机更轻快。3. 创建虚拟磁盘qcow2格式的选择与实操虚拟磁盘是整个虚拟机的硬盘所有系统文件和数据都存在这里。QEMU支持多种磁盘格式最常见的是raw和qcow2我直接用qcow2。3.1 为什么优先选qcow2qcow2有3个对命令行运维特别友好的特性按需分配创建50GB的qcow2磁盘时实际文件只有很小的体积虚拟机里写多少数据宿主机上就增长多少不预占空间原生快照可以用qemu-img snapshot -c创建多个内部快照随时回滚这对测试环境来说是救命功能压缩和加密后续需要时可以在不破坏数据的前提下开启压缩raw格式性能上通常略好那么一点点性能敏感的生产环境可以考虑但对绝大多数自用虚拟机来说qcow2的便利性远大于那点性能差距。3.2 创建虚拟磁盘创建命令非常简单qemu-img create -f qcow2 ubuntu.qcow2 50G-f qcow2指定格式ubuntu.qcow2是磁盘文件名50G指定上限。执行完可以用qemu-img info ubuntu.qcow2查看磁盘信息会看到virtual size和disk size前者是上限50G后者是当前实际占用初始很小。以后任何时候想确认磁盘文件实际大小都用这个命令。3.3 文件放哪、怎么命名磁盘文件建议放在一个独立的目录里比如~/vm/ubuntu/同时把ISO镜像也放在这个目录下方便统一管理mkdir -p ~/vm/ubuntu mv ubuntu.qcow2 ~/vm/ubuntu/ mv ubuntu-24.04.2-live-server-amd64.iso ~/vm/ubuntu/ cd ~/vm/ubuntu磁盘文件名里建议带上项目或用途标识比如ubuntu-test.qcow2避免以后多台虚拟机时分不清谁是谁。我见过有人把所有虚拟机磁盘都叫ubuntu.qcow2过了半年想找某台特定的虚拟机全靠文件修改时间猜非常痛苦。4. 第一次启动通过VNC完成Ubuntu的图形化安装磁盘准备好了ISO也到手了现在进入最核心的部分启动虚拟机完成Ubuntu安装。因为宿主机没有显示器我们需要让QEMU把画面通过VNC协议暴露出来然后在另一台电脑上用VNC客户端连接上去操作。4.1 完整的启动命令qemu-system-x86_64 \ -name ubuntu-vm \ -enable-kvm \ -cpu host \ -smp 4 \ -m 8192 \ -drive fileubuntu.qcow2,ifvirtio,formatqcow2 \ -cdrom ubuntu-24.04.2-live-server-amd64.iso \ -boot d \ -vga virtio \ -display vnc127.0.0.1:1 \ -netdev user,idnet0,hostfwdtcp::2222-:22 \ -device virtio-net-pci,netdevnet0我拆开讲一下每个参数的意义这些理解透了后面排错会轻松很多。-enable-kvm和-cpu host-enable-kvm是启用KVM硬件加速相当于告诉QEMU别再用软件模拟CPU了直接用宿主机的CPU虚拟化能力。-cpu host则是让虚拟机直接使用宿主机的CPU型号和特性性能最好而且能支持更多的CPU指令集。这两个搭配起来虚拟机的CPU性能接近宿主机和出厂的默认qemu64软件模拟CPU完全不是一个量级。-smp和-m-smp 4分配4个虚拟CPU核心-m 8192分配8GB内存。这是根据我宿主机的资源选的分配原则是够用且不挤占宿主机。你可以按自己的需求调整但注意给宿主机留一部分内存和CPU别卡死系统。-drive和-cdrom-drive fileubuntu.qcow2,ifvirtio,formatqcow2指定了虚拟磁盘并启用virtio驱动-cdrom指定安装镜像路径-boot d表示从光驱启动也就是从ISO启动这样安装程序才会跑起来。-vga virtio显卡型号选virtio安装完成后图形性能好一些虽然我们主要用SSH但VNC安装画面也需要一个能正常显示的虚拟显卡。-display vnc127.0.0.1:1这个参数开启VNC服务监听在127.0.0.1的5901端口。1表示显示编号1对应端口590015901。这里有两个细节要注意监听地址设为127.0.0.1而不是0.0.0.0是为了安全只有宿主机本机能连外部无法直接访问VNC端口。从你的电脑连接时需要先建立SSH隧道后面我会说显示的端口号如果冲突可以换成:2对应的端口就变成5902-netdev和-device这部分配置了虚拟机的网络。-netdev user,idnet0,hostfwdtcp::2222-:22用的是QEMU的用户模式网络栈虚拟机内部能上外网同时把宿主机的2222端口映射到虚拟机的22端口这样SSH就能通过宿主机IP:2222连到虚拟机。-device virtio-net-pci,netdevnet0给虚拟机挂载virtio网卡网络性能远超默认的e1000模拟网卡。4.2 通过SSH隧道建立VNC连接启动命令执行后QEMU会一直占用终端这时候在宿主机上用ss -tlnp | grep 5901可以确认VNC已经在监听。由于我们监听的是127.0.0.1需要在你的本地电脑上开一条SSH隧道ssh -L 5901:127.0.0.1:5901 user宿主机IP然后打开任意VNC客户端连localhost:5901。Windows下我用TigerVNC或者RealVNCmacOS上可以用自带的屏幕共享输入vnc://localhost:5901。连上之后你就能看到Ubuntu安装界面了虽然是在终端里敲出的虚拟机但安装体验和本地装Ubuntu几乎没区别。4.3 安装过程中的几个关键选择Ubuntu Live Server安装器是交互式的有几个步骤值得注意语言和键盘布局按需选择默认英文即可装完系统再改也可以网络配置默认走DHCP自动获取IP。因为是NAT网络这一步基本不用动磁盘分区选Use an entire disk就行安装器会自动分区。如果你想折腾LVM或手动分区这里是唯一的机会但我建议第一遍先自动化别给自己增加变量Profile设置设置登录用户名和密码一定要记住。这步可以顺手勾选Install OpenSSH server这样装完就能直接用SSH连不用再手动装openssh-server安装进度等它跑完最后会提示重启安装完成后虚拟机会尝试从光驱再次启动但我们还没摘掉ISO所以它会重新进入安装界面。这时候直接在VNC界面里点Restart然后快速回到宿主机终端按CtrlC停掉QEMU进程即可。5. 安装完成后的启动方式从安装介质切换到virtio硬盘ISO安装OK接下来要把它变成一台可以独立启动、日常能用的虚拟机。这一步的核心是把启动命令里的安装介质去掉改成从虚拟磁盘启动。5.1 修改后的启动命令qemu-system-x86_64 \ -name ubuntu-vm \ -enable-kvm \ -cpu host \ -smp 4 \ -m 8192 \ -drive fileubuntu.qcow2,ifvirtio,formatqcow2 \ -boot c \ -vga virtio \ -display vnc127.0.0.1:1 \ -netdev user,idnet0,hostfwdtcp::2222-:22 \ -device virtio-net-pci,netdevnet0 \ -daemonize变化有三处删掉了-cdrom参数虚拟机不再是光盘引导-boot d改成了-boot c也就是从第一块硬盘虚拟磁盘启动加了-daemonize让QEMU在后台运行不占用当前终端这是日常启动的关键-daemonize之后QEMU进程会转入后台终端能立刻释放。查看虚拟机是否在跑用ps aux | grep qemu。5.2 SSH直接连进虚拟机启动后等个几秒让系统完成引导然后在你本地电脑上执行ssh -p 2222 user宿主机IP等等有个细节我之前提到过hostfwd把宿主机的2222端口映射到了虚拟机的22端口。但这里有个小坑——如果你在本地电脑上直接ssh -p 2222 user宿主机IP命令执行时宿主机IP是你的跳板2222端口是宿主机上的端口映射关系没问题可以直达虚拟机。如果你人就在宿主机上直接ssh -p 2222 user127.0.0.1也一样。这个映射很关键因为QEMU用户模式网络下虚拟机处于一个内部子网外部没法直接访问它的IP只能靠宿主机的端口转发进来。5.3 把启动命令固化成脚本命令行里的参数又多又长每次都手动敲不现实我把启动命令写成一个脚本放在虚拟机目录下#!/bin/bash cd ~/vm/ubuntu exec qemu-system-x86_64 \ -name ubuntu-vm \ -enable-kvm \ -cpu host \ -smp 4 \ -m 8192 \ -drive fileubuntu.qcow2,ifvirtio,formatqcow2 \ -boot c \ -vga virtio \ -display vnc127.0.0.1:1 \ -netdev user,idnet0,hostfwdtcp::2222-:22 \ -device virtio-net-pci,netdevnet0 \ -daemonize记得chmod x start-vm.sh之后启动虚拟机就是一条./start-vm.sh的事。如果你想调整内存、CPU或端口直接改脚本里的参数就行以后维护成本为零。6. 日常运维中最好用的管理操作快照、扩容与开机自启虚拟机正常运行只是开始真正让它省心的是这几个管理操作。我在这里把最常用的三件事说透。6.1 用qcow2快照实现秒级回滚qcow2格式天生支持快照这正是我当初选它而不是raw的重要原因。比如你马上要在虚拟机里装个可能搞坏系统的软件先打个快照qemu-img snapshot -c before-update ~/vm/ubuntu/ubuntu.qcow2-c是create后面跟快照名。查看快照列表qemu-img snapshot -l ~/vm/ubuntu/ubuntu.qcow2如果系统搞坏了恢复快照qemu-img snapshot -a before-update ~/vm/ubuntu/ubuntu.qcow2注意执行回滚操作时虚拟机必须处于关机状态否则数据会冲突。删除快照用qemu-img snapshot -d加快照名。这个功能我测试环境用得极多装坏一个环境一条命令回到解放前太舒服了。6.2 在线扩容虚拟磁盘的大小虚拟机的磁盘空间不够用了直接改qcow2文件大小qemu-img resize ~/vm/ubuntu/ubuntu.qcow2 50G但这一步只改了虚拟磁盘的上限虚拟机里的分区和文件系统不会自动扩展还需要进虚拟机里操作。以Ubuntu默认的分区方案为例假设磁盘从50G扩到100G进虚拟机执行sudo growpart /dev/sda 1 sudo resize2fs /dev/sda1growpart扩展分区resize2fs扩展文件系统。执行完用df -h /看看空间已经变大了。这里有个小细节新版Ubuntu的磁盘设备名可能是/dev/sda或/dev/vda取决于你启动时用的virtio磁盘还是默认IDE磁盘用lsblk看一下就能确认。这个操作全程无需关虚拟机对生产环境非常友好。6.3 用systemd实现虚拟机开机自启如果你希望虚拟机跟随宿主机开机自动启动而不是每次手动跑脚本写一个systemd service就解决了。在/etc/systemd/system/ubuntu-vm.service里写入[Unit] DescriptionUbuntu VM Afternetwork.target [Service] Typeforking ExecStart/home/你的用户名/vm/ubuntu/start-vm.sh ExecStop/usr/bin/pkill -f qemu-system-x86_64.*ubuntu.qcow2 Restartno User你的用户名 Group你的用户名 [Install] WantedBymulti-user.target然后执行sudo systemctl daemon-reload sudo systemctl enable ubuntu-vm sudo systemctl start ubuntu-vmExecStop里的pkill命令匹配了虚拟机磁盘文件名能精确停掉这一台虚拟机而不是误杀宿主机上其他QEMU进程。这里有个经验ExecStart如果直接写qemu-system-x86_64命令要注意脚本里的路径必须是绝对路径或先cd到对应目录否则systemd会因为找不到工作目录而报错。6.4 性能方面还可以再抠的点如果你的虚拟机对性能有更高要求有几个性价比很高的调整方向CPU核心数和内存一开始就按需分配别给太少也别给太多Ubuntu默认的-smp 4 -m 8192对大多数Web服务、构建任务够用virtio系列驱动磁盘和网卡都用virtio这个我已经在前面做了性能损耗极小IO线程加一个-object iothread,idio1 -device drivervirtio-blk-pci,drivedisk0,iothreadio1可以提升高并发磁盘IO场景的表现但配置复杂度提高普通使用不折腾也行避免宿主机swap如果宿主机内存不够虚拟机内存被换到swap里性能会急剧下降最好确保宿主机内存充足7. 几个让我卡壳的报错与排查备忘最后这部分是真正的备忘价值所在。我在整个过程中踩过几个坑每个都卡了一阵子列出来给大家避雷。7.1 /dev/kvm权限拒绝第一次启动直接报错Could not access KVM kernel module: Permission denied。当时我明明已经确认过/dev/kvm存在怎么会没权限原因是当前用户不属于kvm组。检查一下groups 你的用户名如果不包含kvm执行sudo usermod -aG kvm 你的用户名重新登录SSH会话让组权限生效再启动就没问题了。这个坑很典型因为Ubuntu默认安装的QEMU文档里可能不会专门提醒你加组。7.2 VNC显示编号和端口冲突有次启动报错Failed to bind socket: Address already in use。原因是之前有个QEMU没停干净5901端口还被占着。用ss -tlnp | grep 5901看到占用进程后或者彻底停掉旧虚拟机或者把启动命令里的:1改成:2换个端口问题解决。7.3 从外部连不上VNCVNC监听在127.0.0.1所以在宿主机之外是连不上的。这是有意为之的安全设计。正确姿势是在本地电脑先建SSH隧道再连localhost:5901。如果你确实图省事想直接暴露VNC端口可以改成-display vnc0.0.0.0:1但这时候VNC协议本身没有加密强烈建议别这么干尤其是服务器在公网上时。7.4 虚拟机安装完重启卡死/无法引导安装完成后我重启过一次结果卡在引导界面进不去。排查了一圈发现是我忘了摘掉-cdrom系统又从ISO启动了反复进入安装界面。解决办法就是前面说的二进制重启之前停掉QEMU进程重新用没有-cdrom、-boot c的命令启动。如果之后还有引导问题检查-boot参数是不是还是d。7.5 虚拟机内部时钟漂移QEMU虚拟机如果用默认的时钟源长时间运行后系统时间可能和宿主机偏差很大影响日志和定时任务。最简单的方案是在Guest里安装chrony或systemd-timesyncd配置NTP时间同步或者启动QEMU时加上-rtc baselocaltime让虚拟机的RTC跟随宿主机本地时间。我用的Ubuntu Server默认就带systemd-timesyncd联网后会自动校准时间基本不用额外操心。7.6 磁盘文件增长速度超出预期qcow2按需增长不假但如果虚拟机里大量写入数据宿主机磁盘会被快速吃满。我的缓解方法是随时用qemu-img info看实际占用定期清理虚拟机内的日志和缓存必要时用qemu-img convert做一次压缩qemu-img convert -O qcow2 ubuntu.qcow2 ubuntu-compact.qcow2新生成的ubuntu-compact.qcow2会回收已删除文件占用的空间大小往往能降不少。注意这个操作要在虚拟机关机状态下执行。说到底纯命令行安装QEMU虚拟机这件事难度不在于命令多复杂而在于你要理解每一个参数背后在干什么以及知道出问题后去哪查。我的经验是先确认KVM可用再分配好磁盘和资源然后用VNC走一遍安装流程之后用SSH接手日常管理一套流程顺下来虚拟机和宿主机之间的边界感会非常清晰。往后遇到任何需要隔离环境的需求我都不会再焦虑服务器没有图形界面这件事了。