关于弃用 Vagrant

Lobsters Hottest 工具

摘要

作者解释了弃用 Vagrant 并转而直接使用 KVM 和 libvirt 管理虚拟机的决定,认为对于其需求来说,额外的抽象层并无必要,原生 Linux 工具已足够。

<p><a href="https://lobste.rs/s/vuqsur/on_ditching_vagrant">评论</a></p>
查看原文
查看缓存全文

缓存时间: 2026/07/02 06:08

# 告别 Vagrant - benjamintoll.com 来源:https://benjamintoll.com/2026/06/29/on-ditching-vagrant/ 再见了,老朋友。从2010年起我们就一起同行,你忠实地满足了我所有的虚拟机(https://benjamintoll.com/2022/08/12/on-virtual-machines/)需求——起初用 VirtualBox,后来用 `libvirt`(https://libvirt.org/)和 KVM(https://en.wikipedia.org/wiki/Kernel-based_Virtual_Machine)。但常言道,天下无不散之筵席。发生了什么?什么变了? --- - [引言](#introduction) - [KVM 和 `libvirt` 和 `virsh`](#kvm-and-libvirt-and-virsh) - [喔嚯](#ruh-roh) - [预置配置](#preseeding) - [网络连接](#network-connectivity) - [挂载](#mounting) - [SSH 代理转发](#ssh-agent-forwarding) - [总结](#summary) - [参考文献](#references) --- ## 引言 那么,我们到底在做什么?为什么要分道扬镳?老实说,我发现 Vagrant(https://developer.hashicorp.com/vagrant)对于小小的我来说实在是过于臃肿了。随着我在这个短暂的生命中不断学习,接触到越来越多的事物,我开始质疑并重新评估一些早年因知识有限而做出的选择。我一直在回顾过去的项目和决策,这让我受益匪浅。这次,我意识到我把自己搞得太复杂了。 2010 年刚开始使用 Vagrant 时,我只满足于让它管理虚拟机的生命周期。Vagrant box(https://portal.cloud.hashicorp.com/vagrant/discover)很酷,节省了我的时间。后来我开始使用 Ansible(https://docs.ansible.com/projects/ansible/latest/index.html),于是开始用 Vagrant 内置的 Ansible 支持来配置机器。但随着对 Linux 的深入了解,我开始思考:为什么我不直接用 KVM(https://en.wikipedia.org/wiki/Kernel-based_Virtual_Machine),而非要用 Vagrant 呢?毕竟,KVM 从 Linux 内核 2.6.20 就合并进去了,我本来就拥有创建和管理虚拟机所需的工具(https://www.youtube.com/watch?v=ype12RuDJ4k)。为什么还要多加一层软件、多加一层抽象,来做 Linux 原生就能做的事情? 我开始觉得自己像个软蛋。我觉得自己像一块发臭的小屎。最糟糕的是,我意识到自己是在偷懒。于是,几年前我作为折中方案,从 VirtualBox 提供者(https://developer.hashicorp.com/vagrant/docs/providers/virtualbox)切换到了 `libvirt` 提供者(https://vagrant-libvirt.github.io/vagrant-libvirt/),然后继续生活。毕竟,我有更重要的事情要做,比如为当时的雇主和他们的客户拼命工作——因为他们告诉我这才是重要的、最重要的事情,重要到我要连续几天凌晨 3 点起床去修复那些因为敏捷和 Scrum master 以及“不提供客户价值”而在工作日没时间修的仓促基础设施。 总之,经过几年这种可耻的行为后,我终于抽出时间深入研究了 KVM 和 `libvirt`,想把它做好。没错,孩子们,我卸载了 Vagrant,我的妻子又开始爱我了。 那么,我做了什么?来,围坐在火边,让我们一起学习。 ## KVM 和 `libvirt` 和 `virsh` KVM(https://en.wikipedia.org/wiki/Kernel-based_Virtual_Machine)允许 Linux 内核通过内核虚拟化(https://en.wikipedia.org/wiki/Virtualization)模块充当 hypervisor(https://en.wikipedia.org/wiki/Hypervisor),创建并运行虚拟机(VM)。通过虚拟化,硬件在软件中被模拟,因此创建虚拟机就像在宿主机操作系统内拥有一个完整的操作系统。这难道不酷吗?当然酷! 内核模块可能已经启用了。检查方法: ``` $ lsmod | ag kvm kvm_intel 380928 0 kvm 1146880 1 kvm_intel irqbypass 16384 1 kvm ``` 或者: 而 `libvirt` 是一个库和网络守护进程(读作 demon,不是“daymon”),它管理 KVM 以及其他虚拟化平台,如 Xen(https://en.wikipedia.org/wiki/Xen)、LXC(https://en.wikipedia.org/wiki/LXC)和 QEMU(https://en.wikipedia.org/wiki/QEMU)。它允许你创建、启动、停止、暂停和删除虚拟机,以及管理存储和网络等其他操作。`libvirt` 的妙处在于,它为所有支持的平台提供了统一的通用库,因此无需使用其他工具,也无需为不同的 hypervisor 学习不同的命令和操作。换句话说,如果你更换了虚拟化后端,你仍然可以使用相同的命令来使用 `libvirt`。 安装方法: ``` $ sudo apt-get install libvirt-daemon-system ``` `virsh`(https://www.libvirt.org/manpages/virsh.html)是 `libvirt` 的命令行前端(`libvirt` 还支持其他前端,比如 `virt-manager`(https://en.wikipedia.org/wiki/Virt-manager),但那是 GUI,众所周知在有 CLI 工具的情况下没人用 GUI)。它提供了一个很好的抽象层来与 `libvirt` 守护进程交互,而守护进程再与 KVM 交互。 安装方法: ``` $ sudo apt-get install libvirt-clients virtinst ``` > 这也会安装 `virt-install`(https://man.archlinux.org/man/virt-install.1) 以下是一些有用的 `virsh`(https://www.libvirt.org/manpages/virsh.html)命令,用于获取虚拟机(即 domain)的信息: - `domblkinfo` - `domblkstat` - `domid` - `domiflist` - `domifstat` - `dominfo` - `dommemstat` - `domname` - `domstate` - `domuuid` 以及获取宿主机和节点的信息: - `capabilities` - `hostname` - `nodeinfo` 还有有用的管理命令: - `connect` - `destroy` - `dumpxml` - `edit` - `list` - `reboot` - `shutdown` - `start` - `undefine` 这里实在列不完。请参阅 `virsh`(https://www.libvirt.org/manpages/virsh.html)文档。 > 再次强调,概念模型是:用户 → `virsh` → `libvirt` → KVM。 关于这些主题还有很多需要了解的,但这足以让你入门。以下是一些与此相关的优秀文章: - [On Virtual Machines](https://benjamintoll.com/2022/08/12/on-virtual-machines/) - [On Unsharing Namespaces, Part One](https://benjamintoll.com/2022/08/08/on-unsharing-namespaces-part-one/) - [On Unsharing Namespaces, Part Two](https://benjamintoll.com/2022/12/14/on-unsharing-namespaces-part-two/) - [On Linux Container Networking](https://benjamintoll.com/2023/11/28/on-linux-container-networking/) ## 喔嚯 我正愉快地用 `libvirt` 和 KVM 创建虚拟机,无忧无虑。然后突然间,我的世界崩塌了。我从 `bookworm` 升级到了 `trixie`,现在启动虚拟机时收不到任何串口输出。我像往常一样在 `virt-install`(https://man.archlinux.org/man/virt-install.1)命令中传递了内核引导参数(稍后详述),这些参数用于通过串口启用宿主机与虚拟机之间的通信,但现在看来这些参数并没有实际写入虚拟机(即引导加载器配置中)。 > 要检查实际写入 grub(或任何你使用的引导加载器)的参数,请登录虚拟机并打开 grub 配置:`/etc/default/grub` 关键行如下: ``` GRUB_CMDLINE_LINUX_DEFAULT="quiet" GRUB_CMDLINE_LINUX="console=ttyS0,115200" ``` 如果做了任何更改,请运行以下命令: ``` $ sudo grub-mkconfig -o /boot/grub/grub.cfg $ sudo reboot ``` 我思考了一下,决定换一种方法。与其花(可能很长的)时间调试变化,我打算在安装命令中传入一个配置文件,创建一个所谓的预置(preseeded)虚拟机。这是一个更好的整体方案,因为它让构建具有确定性和版本可控性(或者放在构建机器可访问的地方),并且你知道文件中指定的任何值都会写入虚拟机。此外,你可以预安装所有机器都应该有的软件,稍后使用类似 `cloud-init`(https://packages.debian.org/en/stable/cloud-init)的工具,在虚拟机创建生命周期中挂钩,添加特定于每台机器的自定义软件。太棒了!我们来看看。 ## 预置配置 那么,什么是预置配置?正如你从我之前的描述中可能猜到的,预置是一种通过提供安装过程中所提问题的预定答案来自动创建虚拟机的方法,比如本地化、用户名和密码、安装包等等。你懂的,就是那些你做过上千次的东西。 > 我见“preseeding”这个词主要用于 Debian 构建的语境中,但其他操作系统也有类似的方法。 Debian `trixie` 友好地提供了一个示例预配置文件(https://www.debian.org/releases/trixie/example-preseed.txt),你可以将其作为自己配置的基础。更多精彩信息请参阅《使用预置自动安装》(https://www.debian.org/releases/stable/amd64/apbs02.en.html),这是一篇很好的文章,强烈推荐。 这一切听起来很美妙,你可能已经被震撼了,现在想知道如何开始。那么我们之前提到的小兄弟 `virt-install`(https://man.archlinux.org/man/virt-install.1)登场了: ``` $ virt-install \ --connect qemu:///system \ --name kilgore-trout \ --memory 8192 \ --extra-args="preseed/file=/preseed.cfg console=ttyS0,115200n8" \ --initrd-inject ./preseed.cfg \ --install debian13 \ --disk size=40 \ --filesystem type=mount,source=/home/btoll/libvirt/kilgore-trout/mnt,target=shared,accessmode=mapped,driver.type=path,driver.wrpolicy=immediate \ --network network=default \ --graphics none ``` 让我们看看这些参数及其值(大多数值直接引自 `virt-install`(https://man.archlinux.org/man/virt-install.1)手册页): | 参数 | 值 | |------|-----| | `--connect` | 连接到非默认的 hypervisor。如果未指定,libvirt 会尝试选择最合适的默认值。用于创建由系统 libvirtd 实例运行的 KVM 和 QEMU 客户机。这是 virt-manager 使用的默认模式,也是大多数 KVM 用户想要的。 | | `--name` | 新客户机虚拟机实例的名称。在连接中所有已知的客户机(包括非活动状态的)中必须唯一。 | | `--memory` | 分配给客户机的内存,单位 MiB。 | | `--extra-args` | 执行客户机安装时传递给安装程序的额外内核命令行参数。 | | `--initrd-inject` | 宿主机上预配置文件的路径,该文件将在 `--extra-args` 中被引用。 | | `--install` | `virt-install` 会从 libosinfo 获取 `--location` URL,并从那里填充默认值。 | | `--disk` | 创建一个新的 40G 磁盘镜像及关联的磁盘设备。`virt-install` 会生成路径名,并将其放在 hypervisor 的默认镜像位置。 | | `--filesystem` | 指定宿主机上要导出到客户机的目录。 | | `--network` | 将客户机连接到宿主机网络。该网络将与宿主机网络隔离,并通过虚拟网桥连接。 | | `--graphics` | 无头安装。客户机可能需要在第一个串行端口上配置文本控制台(可通过 `--extra-args` 选项完成)。 | > 注意,如果没有在 `--extra-args` 中包含 `console=ttyS0,115200n8`,虚拟机启动时似乎会挂起,但实际上并没有。问题在于终端和虚拟机之间没有连接,因此没有从串行端口输出日志到屏幕,因为根本没有连接。 如果你针对 `qemu:///session` 守护进程运行,上述命令会失败。网络必须在系统范围内可用(注意 URI 字符串): ``` $ virsh --connect qemu:///system net-list --all Name State Autostart Persistent ------------------------------------------------------- default active yes yes vagrant-libvirt active no yes ``` 顺便一提,这里列出了 `vagrant-libvirt` 网络,因为我之前用 Vagrant 搭配了 `libvirt` 提供者。 当 `virt-install` 命令成功执行后,如上所示,它会将你带到登录提示符: ``` $ virsh -c qemu:///system list --all Id Name State ----------------------------------- - kilgore-trout shut off $ virsh -c qemu:///system start kilgore-trout Domain 'kilgore-trout' started $ virsh -c qemu:///system console kilgore-trout Connected to domain 'kilgore-trout' Escape character is ^] (Ctrl + ]) The highlighted entry will be executed automatically in 0s. Booting `Debian GNU/Linux' Loading Linux 6.12.94+deb13-amd64 ... Loading initial ramdisk ... /dev/mapper/kilgore--trout--vg-root: clean, 51705/2428272 files, 650933/9700352 blocks [ 2.375045] systemd-ssh-generator[287]: Failed to query local AF_VSOCK CID: Cannot assign requested address [ 2.376760] (sd-exec-[279]: /usr/lib/systemd/system-generators/systemd-ssh-generator failed with exit status 1. Debian GNU/Linux 13 kilgore-trout ttyS0 kilgore-trout login: btoll Password: Linux kilgore-trout 6.12.94+deb13-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.12.94-1 (2026-06-20) x86_64 The programs included with the Debian GNU/Linux system are free software; the exact distribution terms for each program are described in the individual files in /usr/share/doc/*/copyright. Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent permitted by applicable law. ``` 登录后,我们来检查网络连接。 ## 网络连接 ``` btoll@kilgore-trout:~$ ip a 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host noprefixroute valid_lft forever preferred_lft forever 2: enp2s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000 link/ether 52:54:00:52:b4:2d brd ff:ff:ff:ff:ff:ff altname enx52540052b42d inet 192.168.122.112/24 brd 192.168.122.255 scope global dynamic noprefixroute enp2s0 valid_lft 3420sec preferred_lft 2970sec inet6 fe80::c7e7:221f:751d:773c/64 scope link valid_lft forever preferred_lft forever btoll@kilgore-trout:~$ ip route default via 192.168.122.1 dev enp2s0 proto dhcp src 192.168.122.112 metric 1002 192.168.122.0/24 dev enp2s0 proto dhcp scope link src 192.168.122.112 metric 1002 btoll@kilgore-trout:~$ ping benjamintoll.com PING benjamintoll.com (167.114.97.28) 56(84) bytes of data. 64 bytes from dinesh (167.114.97.28): icmp_seq=1 ttl=44 time=44.0 ms 64 bytes from dinesh (167.114.97.28): icmp_seq=2 ttl=44 time=43.5 ms 64 bytes from dinesh (167.114.97.28): icmp_seq=3 ttl=44 time=40.8 ms --- benjamintoll.com ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 2004ms rtt min/avg/max/mdev = 40.781/42.771/43.993/1.419 ms ``` 在宿主机上,使用 `ip`(https://www.man7.org/linux/man-pages/man8/ip.8.html): ``` $ ip link show type bridge 4: virbr0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000 link/ether 52:54:00:9d:81:89 brd ff:ff:ff:ff:ff:ff $ ip addr show dev virbr0 4: virbr0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue sta

相似文章

我不维护我的家庭实验室

Hacker News Top

作者描述了如何通过一台服务器、UniFi网络设备和Docker容器,搭配定时更新和备份,实现了家庭实验室的自动化,使其几乎无需手动维护。

从 GNU Stow 迁移到 Chezmoi

Hacker News Top

作者分享了从 GNU Stow 迁移到 Chezmoi 来管理多台机器上的点文件的经验,指出 Chezmoi 的真实文件方法和模板功能是主要的改进点。

从 Proxmox 迁移到 NixOS 和 Incus

Hacker News Top

作者描述了将其家庭实验室从 Proxmox 迁移到使用 Incus 的 NixOS 的过程,强调了声明式配置和可重现性相对于命令式系统的优势。

你很可能不需要 Yocto,这完全没问题

Lobsters Hottest

本文指出,对于嵌入式 Linux 项目,Yocto 常常过于复杂,建议开发者考虑更简单的替代方案,以避免维护负担,尤其是在 CRA 等法规下。

我的新家庭服务器软件选择

Lobsters Hottest

一篇个人博客文章,详细描述了作者为新家庭服务器选择操作系统和服务管理软件的经历,比较了Synology、TrueNAS、Debian以及使用Runit的Void Linux。