SSD Nodes Learn Hosting plans →
指南 Matt Connor作者: Matt Connor

VPS如何开启嵌套虚拟化运行Proxmox?

VPS嵌套虚拟化需服务商开启CPU虚拟化扩展。通过kvm-ok命令快速检测vmx或svm标志。若缺失该标志,则无法运行KVM或Proxmox虚拟机。本文解析嵌套虚拟化原理,并提供检查步骤与常见配置错误排查方法。

简短回答

嵌套虚拟化是指在虚拟机内部运行 hypervisor:您的 VPS 已经是一个 guest,您希望它继续托管自己的 guest。只有当服务商的 hypervisor 明确向您的实例暴露 CPU 的虚拟化扩展时,这种方式才可用。请检查 /proc/cpuinfo 中是否存在 vmx 标志(Intel)或 svm 标志(AMD)。如果两者都不存在,那么在 VPS 内进行任何配置都无法解决问题。

首先明确一点:Docker 不需要这些条件。 容器共享 VPS 的内核,从不接触 /dev/kvm。如果您的实际目标是“在服务器上运行多个容器化服务”,那么现有环境已经满足需求。只有在您需要使用第二个内核、搭建 Proxmox 实验环境、运行 Windows guest、运行 Firecracker microVM、使用 Android 模拟器、构建由真实 VM 组成的 Kubernetes 测试环境,或运行会启动 VM 镜像的 CI runner 时,才需要嵌套虚拟化。

实际嵌套的是什么

共有三层:

  • L0,运行在物理机上的服务商 hypervisor。您无法访问它。
  • L1,您的 VPS。对 L0 来说,它只是一个来宾。
  • L2,您要在 VPS 内运行的 VM。

硬件虚拟化在 Intel 上使用 VT-x(vmx 标志)和 EPT,在 AMD 上使用 AMD-V / SVM(svm)和 RVI/NPT。hypervisor 使用这些指令进入来宾模式,并让 CPU 同时遍历两级页表。

这些机制都不是为可重入设计的,因此嵌套需要模拟:当 L1 执行 VMLAUNCH 或 VMRESUME 等 VMX 指令时,指令会陷入 L0。L0 代表 L1 维护 L2 的影子结构。在支持 VMCS shadowing 的 CPU 上,L1 的 VMREAD 和 VMWRITE 可以在无需退出的情况下处理,从而减少大部分这类开销,但无法消除其他开销。KVM 对此支持良好,但每次退出仍由 L0 执行额外处理,因此服务商必须显式启用该功能。

当 L0 使用 KVM 时,加速 L2 必须同时满足以下两个条件:

  1. L0 的 KVM 模块加载时使用 nested=1。
  2. L0 为您的 VPS 提供包含该标志的 CPU 模型:在 libvirt 中为 <cpu mode='host-passthrough'/>,在 Proxmox 中为 cpu: host,在原始 QEMU 中为 -cpu host。通用模拟模型(qemu64、kvm64)会隐藏 vmx,即使全局已启用嵌套。

其他 hypervisor 使用各自的开关。例如,Hyper-V 会为其 VM 内的嵌套 hypervisor 模拟虚拟化扩展,但只有逐个启用嵌套的 VM 才支持此功能。

一分钟检查您的 VPS

# 1. Are you in a VM, and under what?
systemd-detect-virt          # kvm, vmware, xen, microsoft, or "none" on metal

# 2. Does the CPU expose the extensions to you?
grep -o -E 'vmx|svm' /proc/cpuinfo | sort -u
lscpu | grep -i -E 'virtual|hypervisor'

# 3. The definitive check
sudo apt update && sudo apt install -y cpu-checker
kvm-ok

# 4. The device node the whole stack depends on
ls -l /dev/kvm

可用的实例会输出 vmx 或 svm,kvm-ok 的值为 KVM acceleration can be used,并且 /dev/kvm 以 root:kvm 模式 660 存在。如果标志存在但设备节点不存在,请手动加载模块,然后查看内核日志:

sudo modprobe kvm_intel     # or kvm_amd
sudo dmesg | tail -n 20

有一个文件经常被引用,但也经常被误解:

cat /sys/module/kvm_intel/parameters/nested   # Y or N

在您的 VPS 中,该文件表示您的 KVM 模块的设置,并决定 L2 客户机是否可以再嵌套第 3 层。它不能说明 L0 是否为您启用了嵌套;/proc/cpuinfo 和 kvm-ok 才能回答这个问题。nested 参数是在您完全拥有的机器上设置的开关:

echo 'options kvm_intel nested=1' | sudo tee /etc/modprobe.d/kvm-nested.conf
sudo modprobe -r kvm_intel && sudo modprobe kvm_intel

虚拟机运行时会拒绝移除模块,因此请先关闭所有客户机。如果该机器是 Proxmox 主机,请参阅在 Proxmox VE 9 中启用嵌套虚拟化;其中包含相同的 modprobe.d 修改,以及使该标志在客户机内可见的每台虚拟机 cpu: host 设置。

为什么大多数 VPS 主机默认关闭嵌套虚拟化

  • 实时迁移。 向您提供 vmx,意味着暴露带有该标志的 CPU 模型;依赖这些 CPU 特性的虚拟机无法安全迁移到 CPU 不具备这些特性的机器。通过迁移客户来疏散节点的主机,一旦启用嵌套虚拟化,就会失去这种能力。
  • 攻击面。 嵌套 VMX/SVM 路径属于内核虚拟化层中最复杂的代码,其 CVE 历史也反映了这一点。
  • L0 可能不是 KVM。 如果 systemd-detect-virt 输出 vmware、xen 或 microsoft,那么嵌套规则由该虚拟化栈决定,而不是由 KVM 决定。

您的实例没有该标志?请联系支持团队(有些主机可按虚拟机单独启用),选择明确记录支持嵌套虚拟化的方案,或迁移到独立服务器。下文假设您在显示该标志的机器上拥有 root 权限。

使用 libvirt 运行 L2 虚拟机

sudo apt install -y qemu-system-x86 libvirt-daemon-system virtinst ovmf
sudo systemctl enable --now libvirtd
sudo usermod -aG libvirt,kvm "$USER"   # log out and back in

virt-install \
  --name guest1 \
  --memory 2048 \
  --vcpus 2 \
  --cpu host-passthrough \
  --disk path=/var/lib/libvirt/images/guest1.qcow2,size=20,format=qcow2,bus=virtio \
  --network network=default,model=virtio \
  --os-variant debian13 \
  --location https://deb.debian.org/debian/dists/trixie/main/installer-amd64/ \
  --graphics none \
  --console pty,target_type=serial \
  --extra-args 'console=ttyS0,115200n8'

无需图形会话。串行安装需要运行一段时间,因此请在持久化 shell 中启动它:相同的 可在 VPS 上保持 Claude Code 会话运行的 tmux 工作流 会让 virt-install 控制台在 SSH 连接断开后仍保持连接。如果 --os-variant debian13 被拒绝,说明您的 osinfo-db 早于该版本发布,请运行 osinfo-query os,并选择一个实际存在的名称。--cpu host-passthrough 会将 vmx 向下转发到 L2;仅当 L2 还必须继续运行虚拟化时才需要此设置。使用 virsh autostart guest1 确保虚拟机能够安全启动。

磁盘和 NIC 使用的 virtio 总线并非装饰:模拟的 IDE 和 e1000 设备进入 hypervisor 的频率远高于 virtio 队列。在嵌套虚拟化中,L0 无法自行完成的退出会向上反映到 L1;L1 处理该退出时,还可能进一步触发自身的退出。

网络:教程通常略过的部分

您的 VPS 只有一个公网 IP,并且位于会过滤未知 MAC 地址的网络设备之后。由此会产生两个后果。

将 L2 客户机桥接到公网通常无法工作。 将 br0 放到公网网卡上,为客户机分配独立的 MAC 地址后,您会看到 ARP 请求发出,但没有任何响应。因为提供商的交换机丢弃了来自未向您租用的 MAC 地址的帧。如果这是您的现象,请停止排查网桥;问题机制就是如此。

改用 NAT 网络。 libvirt 提供 default:virbr0、192.168.122.0/24,并通过 dnsmasq 分配租约,出站连接可立即使用。对于入站连接,请在 L1 上终止 TLS,然后将请求代理到内部服务。下面的证书路径来自 使用 Certbot 在 Nginx 上申请 Let's Encrypt 证书:

server {
    listen 443 ssl;
    server_name lab.example.com;

    ssl_certificate     /etc/letsencrypt/live/lab.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/lab.example.com/privkey.pem;

    location / {
        proxy_pass http://192.168.122.50:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

先为客户机分配静态租约(virsh net-edit default),确保 proxy_pass 中的地址保持不变。

管理界面不要暴露到互联网:5900 上的 VNC 和 8006 上的 Proxmox Web UI 应绑定到 loopback,通过 SSH 隧道(ssh -N -L 8006:127.0.0.1:8006 you@your-vps)访问,或通过 连接到 VPS 的自托管 WireGuard VPN 访问。这样,整个 192.168.122.0/24 客户机地址段都只需经过一个私有网络跃点即可到达。防火墙规则保持最小化,只允许 sudo ufw allow 22,80,443/tcp,其他一律禁止。如果启用 ufw 后客户机立即失去出站连接,通常是 /etc/default/ufw 中的 DEFAULT_FORWARD_POLICY="DROP" 配置导致的。将其设置为 ACCEPT,然后重新加载 ufw。

VPS 上的 Proxmox

Proxmox VE 9 的底层是 Debian 13,因此可以通过添加 pve-no-subscription 仓库和 proxmox-ve 软件包,将其安装到 Debian VPS 上。仓库和 keyring 配置行应以 Proxmox 当前官方文档为准;从旧博客文章复制的 URL 可能导致安装失败。在继续处理下面的网络配置之前,最好先确定 Proxmox 是否适合部署在租用的硬件上。家庭 Proxmox 主机与租用 VPS 的成本和能力对比已经完成了电力和硬件方面的计算,可以直接回答这个问题。

软件包不是难点。Proxmox 要求 vmbr0 桥接到物理 NIC,这会直接遇到上文所述的 MAC 过滤限制。VPS 上可行的结构是使用不连接物理端口的 NAT 或路由 vmbr0,让虚拟机和容器处于私有网段,并在主机上为需要公网访问的服务配置 DNAT 规则或反向代理。如果面向公网的服务是容器而不是虚拟机,使用 Traefik 从一个 Docker Compose 文件为多个应用提供入口可以完成相同的路由工作,并自动申请证书。先创建快照 /etc/network/interfaces:错误的网桥配置可能会让您无法访问一台未必提供控制台的主机。

如实说明性能

嵌套虚拟化比单层虚拟化更慢,原因很具体,并非各处都存在开销:开销主要出现在退出和地址转换上,而不是普通指令上。 启用 EPT/NPT 后,L0 会将 L1 为 L2 建立的页表与自身页表合并到一个影子页表中,因此 TLB 命中时可以达到硬件执行速度。TLB 未命中时开销更高:硬件会同时遍历客户机页表和影子页表;而 L1 每次修改自己的页表,都会触发一次退出,使 L0 能够重新同步影子页表。真正昂贵的是所有离开客户机模式的操作,包括 I/O、定时器中断、MMIO 和处理器间中断,因为 L2 的退出由 L0 处理,并且可能再经由 L1 反射回去。使用小型工作集执行 CPU 密集型任务时,性能通常接近原生;大型内存工作集会承担 TLB 未命中的开销,而系统调用、数据包和磁盘 I/O 占主导的工作负载则会明显受到多层虚拟化的影响。

因此,应尽可能使用 virtio 设备。你的 qcow2 文件还位于服务商已经虚拟化的磁盘上,形成两层精简配置;此时,客户机磁盘上的 cache=none 会阻止相同的数据块同时位于两个页缓存中。这里不提供基准测试数据:请在自己的实例上测量自己的工作负载。

故障模式及其对应的提示信息

INFO: /dev/kvm does not exist / KVM acceleration can NOT be used 来自 kvm-ok。模块可能未加载,或者未公开该标志。先检查 /proc/cpuinfo。

kvm: disabled by bios 出现在 dmesg 中。在裸机上,在固件中启用 VT-x/SVM 开关。在 VPS 中,这表示 L0 未向你提供这些扩展,在客户机中输入任何命令都无法改变这一点。

modprobe: ERROR: could not insert 'kvm_intel': Operation not supported。 内核检测到的 CPU 不包含 vmx。这同样由 L0 决定。

Could not access KVM kernel module: Permission denied。 这是权限问题,不是硬件问题。ls -l /dev/kvm 应显示组 kvm 和模式 660;将自己加入该组,然后启动新的登录 shell,因为组成员身份不会应用到已经运行的会话。

QEMU 启动时出现 kvm: Device or resource busy。另一个 hypervisor 模块占用了 CPU:运行 lsmod,查找 vboxdrv 或 VMware 模块,以及 kvm_intel,然后卸载不需要的模块。

从 virsh 获取 /var/run/libvirt/libvirt-sock: No such file or directory。daemon 已停止:sudo systemctl enable --now libvirtd。

Proxmox:KVM virtualisation configured, but not available. 主机无法提供 KVM 加速,但客户机启用了 KVM 加速。修复嵌套虚拟化配置,或者取消该选项并接受仿真。

Android emulator:x86_64 emulation currently requires hardware acceleration! 再次检查 /dev/kvm,通常是组权限问题。

完全没有错误,但运行极慢。 未指定 accelerator flag 的 QEMU 会回退到 TCG,即软件 emulator。它能够正常工作,但速度很慢;原本按秒计算的启动时间会变成按分钟计算。显式传入 -accel kvm,这样 QEMU 会在无法加速时直接报错,而不是静默使用仿真。

客户机在运行过程中消失。 在 dmesg 中查找 Out of memory: Killed process ... qemu-system-x86_64。L2 客户机是 L1 上的一个进程,OOM killer 会像处理其他进程一样处理它。L2 的 RAM 来自 L1 的固定分配,不能从主机借用。

运维:备份、升级和限制

备份。复制运行中的 guest 的 qcow2 镜像可能导致镜像不一致,因为 guest 在读取期间仍会持续写入。可以运行 virsh shutdown guest1,等待 virsh domstate guest1 报告 shut off 后再复制(关机命令返回时,guest 可能仍在停止);也可以创建外部快照(virsh snapshot-create-as guest1 snap1 --disk-only --atomic),让写入转到覆盖层,同时复制此时保持静态的基础镜像,然后使用 virsh blockcommit guest1 vda --active --pivot 将覆盖层合并回去。将副本转移到 VPS 之外;保存在同一磁盘上的快照无法防范磁盘故障。

升级。apt full-upgrade 会安装新的 kvm_intel/kvm_amd 模块,但运行中的内核会继续使用旧模块,直到重启。保留上一个内核,并在每次内核变更后重新运行 kvm-ok:如果主机启动后缺少 vmx,仍可通过另一个启动项恢复工作。

扩展的限制。一个公网 IP 意味着每个 L2 服务都必须通过 L1 上的代理或 DNAT 规则访问公网。无法进行实时迁移。CPU 发生争用时,嵌套退出路径会最先受到影响。运行多个 guest 的 hypervisor 也是一台内存已经分配完的机器;嵌套 VM 无法通过超额分配摆脱固定的资源分配。当实验环境超出这一规模时,解决方案不是继续增加嵌套层级,而是使用一台专用机器,在其中由您担任 L0,以上限制均不适用。

FAQ

运行 Docker 的 VPS 需要嵌套虚拟化吗?

不需要。容器共享 VPS 的内核,并且不会打开 /dev/kvm,因此没有 vmx 或 svm 标志的普通实例也能正常运行 Docker 和 Docker Compose。只有在需要第二个内核时,才需要嵌套虚拟化,例如运行 Proxmox 实验环境、Windows 客户机、Firecracker microVM、Android 模拟器,或启动 VM 镜像的 CI runner。

如何检查 VPS 是否支持嵌套虚拟化?

运行 grep -o -E 'vmx|svm' /proc/cpuinfo | sort -u,然后从 cpu-checker 软件包中运行 kvm-ok。可用的实例会输出 vmx(Intel)或 svm(AMD);kvm-ok 会报告 KVM acceleration can be used;/dev/kvm 存在,且组为 kvm、模式为 660。对于这个问题,请忽略 /sys/module/kvm_intel/parameters/nested。该文件描述的是你自己的 KVM 模块,而不是提供商的 hypervisor 向你公开的功能。

为什么大多数 VPS 提供商会禁用嵌套虚拟化?

公开 vmx 意味着向客户机提供带有该标志的 CPU 模型。依赖这些 CPU 功能的客户机无法实时迁移到缺少这些功能的机器上。提供商通过迁移客户来疏散节点,因此通常会放弃这一能力。嵌套 VMX/SVM 代码路径也有较长的 CVE 历史。一些主机仍可按 VM 请求启用该功能,另一些主机则将嵌套虚拟化列为套餐功能。

我的嵌套 VM 在公网桥接网络上没有网络。哪里出错了?

提供商的交换机会丢弃来自未向你租用的 MAC 地址的帧。因此,桥接到公网 NIC 的 L2 客户机会发送 ARP,但收不到任何响应。不要继续排查 br0。请使用 libvirt 的 NAT default 网络(virbr0、192.168.122.0/24),为客户机分配静态租约,并在 VPS 本身上通过反向代理或 DNAT 规则发布公网服务。

嵌套 VM 会慢多少?

开销主要来自 VM exit 和地址转换。启用 EPT/NPT 后,L2 内部的指令执行和 TLB 命中可达到硬件速度。但发生 TLB miss 时,需要遍历额外的页表;I/O、定时器中断、MMIO 和 IPI 由 L0 处理,并且可能需要经由 L1 再转发。工作集较小的 CPU 密集型任务接近原生性能;内存工作集较大,以及系统调用、网络包和磁盘操作密集型负载,则会明显受到每一层的影响。在所有位置使用 virtio 设备,并在客户机磁盘上使用 cache=none,然后根据自己的工作负载进行测量。