KVM、Xen 与 LXC 有什么区别?VPS 虚拟化对比
KVM、Xen 和 LXC 决定 VPS 是否拥有独立内核、真实 swap、可用的嵌套虚拟化,以及 steal time 是否可信。购买前先确认您实际使用的虚拟化方案。
VPS 方案真正提供的是什么
KVM、Xen 和 LXC 是构成 VPS 方案的三类虚拟化技术。选择哪一种,并不是提供商机架配置中的一个细节,而是决定您是否拥有自己的内核。买家关心的一切都取决于这一点:能否加载模块、能否控制 swap、能否运行嵌套虚拟化、/proc 描述的是您的服务器还是其他人的服务器,以及是否根本可以测量 steal time。
全虚拟化(KVM 和 Xen HVM)为每个租户提供一个内核和一台虚拟机。Xen 半虚拟化同样提供内核,但该内核知道自己是客户机,并请求 hypervisor 执行特权操作。容器方案(LXC,或 OpenVZ 和 Virtuozzo 系列)则在提供商的内核上为您提供一个文件系统和一组 namespace。三种方案都以相同的三个字母对外销售。
KVM、Xen 与 LXC:每个实例使用独立内核,还是共享一个内核
在 KVM 和 Xen 上,uname -r 表示您的内核。您可以安装其他内核、向其中加载模块,然后重启并使用该内核启动。您在此执行的任何操作都不会影响其他租户。在容器方案中,uname -r 表示提供商的内核。该内核运行在宿主机上,并由这台机器上的所有其他容器共享。您无法更换它,而 apt install linux-image-generic 只会解包永远不会启动的文件。
这一项差异比任何规格表都更重要。请将本指南的其余内容理解为这一差异带来的结果。
完全虚拟化:KVM 和 Xen HVM
KVM(基于内核的虚拟机)是 Linux 内核中的一个模块,可利用 CPU 内置的 Intel VT-x 或 AMD-V 指令,将普通 Linux 主机变成 hypervisor。QEMU 提供外围虚拟硬件,包括磁盘、网卡和串行控制台。Xen 采用不同的设计。Xen 自身就是 hypervisor,并且在 Linux 启动前启动。名为 dom0 的特权控制域运行管理组件,每个租户都是一个 domU。Xen HVM(硬件虚拟机)使用与 KVM 相同的 CPU 扩展,通常为磁盘和网络使用半虚拟化驱动,因为硬件模拟速度较慢。这种组合称为 PVHVM。
对于租户而言,两者的行为几乎完全相同。您会获得内核、bootloader、真实块设备、可用的 modprobe、真正的 /proc、由您管理的 swap,以及能够实际启动系统的重启功能。如果服务提供商允许挂载 ISO,您还可以安装其未提供的发行版。
代价是资源密度降低。分配给您的 4 GB 会固定用于您的虚拟机;即使您处于空闲状态,也不能借给其他租户使用。此外,每个客户机都需要一个 QEMU 进程、自己的页表和自己的页缓存。这些开销使得 KVM 方案的价格高于标示相同配置的容器方案。
半虚拟化 Xen,以及如何识别它
Xen PV 诞生时,CPU 还没有虚拟化指令。它不捕获特权指令,而是修改客户机内核,让内核直接调用 hypervisor。它无需 VT-x 即可运行,这正是它在 2005 年的设计目标。pygrub 或 pvgrub 从您自己的磁盘映像中加载内核,因此使用的是您的内核,但该内核必须构建时启用 PV 客户机支持。
以下迹象表明您运行在 Xen PV 上:lscpu 将虚拟化类型报告为 para,而不是 full;系统存在 /sys/hypervisor/type,并且其中标识了 Xen;磁盘显示为 xvda,而不是 vda 或 sda。读取 SMBIOS 或 DMI 表的工具不会找到任何内容,因为 PV 客户机没有用于发布这些表的固件。
代价是永久无法使用嵌套虚拟化。PV 客户机永远看不到 CPU 虚拟化扩展,因此其中无法运行 hypervisor。Xen 本身并未消亡。逐渐退出的是 Xen PV,项目自身已转向 PVH 和 HVM。如果方案只写着“Xen”,请确认具体是哪一种。HVM 是现代 VPS 的常规方案。PV 方案的价格应更低。
容器 VPS:LXC 和 OpenVZ 系列
容器 VPS 是一组 Linux namespace(分别隔离进程 ID、挂载点、网络接口、主机名和用户的视图)以及 cgroups(control groups,即内核资源限制),运行在服务商的内核上。您的 init 是主机上的一个进程。您的 ls 直接运行在主机内核上,中间没有仿真层,也没有第二个调度器。因此,容器启动快且资源密度高。
订购页面上的名称包括 LXC、Proxmox VE 容器(基于 LXC)、OpenVZ 和 Virtuozzo。OpenVZ 7 和 Virtuozzo 是同一理念的商业后继产品。
以下 4 点会影响您的使用:
- 模块。
modprobe不会插入任何模块。如果服务商的内核中没有预先提供 WireGuard、ZFS 或特定的 netfilter 模块,您就无法使用它们。 sysctl。/proc/sys大多是只读的。网络使用独立的 namespace,因此net.ipv4.ip_forward及其相关配置通常可写。vm.swappiness或fs.file-max等影响整台机器的内核参数属于主机。- 嵌套容器。在 LXC 容器中运行 Docker,只有在服务商启用 nesting 且存储驱动兼容时才可行。购买前应先测试,不要想当然地认为它一定可用。
- 内核版本。您必须采用服务商的升级计划,包括重启安排。
如何判断您购买的是哪一种
在该服务器上运行以下命令,并结合输出结果判断。没有任何单个命令可以单独确定结论。
systemd-detect-virt
systemd-detect-virt -c
lscpu | grep -iE 'hypervisor|virtualization'
uname -r
ls /lib/modules/$(uname -r) 2>/dev/null | head -n 3
cat /sys/hypervisor/type 2>/dev/nullsystemd-detect-virt会从固定词汇表中输出一个简短标识。物理机一侧包括 kvm、qemu、xen、amazon 和 vmware。容器一侧包括 lxc、lxc-libvirt、openvz、docker 和 systemd-nspawn。未检测到任何环境时,它会输出 none 并以非零状态退出。-c形式仅针对容器技术提供结果,因此只要该命令输出的结果不是 none,即可确定答案,不受销售页面描述的影响。
lscpu会显示 hypervisor 厂商,并说明虚拟化类型是 full 还是 para。借此可以区分 Xen HVM 和 Xen PV。/sys/hypervisor/type仅存在于 Xen 环境中。
/lib/modules检查是最容易被忽略的,但它提供的证据最直接。如果正在运行的内核版本对应的目录不存在或为空,而系统显然正在运行该内核,则该内核不是来自您的文件系统。它来自宿主机,并且其模块树从未安装到您的镜像中。这就是容器环境。
如需独立的第二个判断结果,可以使用 sudo apt install -y virt-what && sudo virt-what运行检测测试。该工具需要 root 权限,在裸机上完全不输出内容。
为什么 /proc 在容器中描述的是错误的机器
在 KVM 或 Xen 虚拟机中,/proc/meminfo 是内核根据虚拟机管理程序分配给你的内存所做的统计。它反映的是你的机器,与宿主机无关。这正是虚拟机的作用。
在容器中,没有第二个内核负责进行这项统计,因此 /proc 显示的是宿主机的 /proc。LXCFS 是一个小型文件系统,会重写其中一些文件,使其与 cgroup 限制匹配,覆盖 /proc/cpuinfo、/proc/meminfo、/proc/stat、/proc/uptime、/proc/swaps、/proc/diskstats 和 /sys/devices/system/cpu/online。Proxmox 默认会挂载它。许多较小的服务商不会挂载,因此 free -m 会报告宿主机的总内存,nproc 可能会报告整台机器上的所有核心,而 uptime 会报告宿主机已经运行了多长时间。
这不是表面问题,因为软件会根据这些文件中的数据设置自身参数。nginx 配合 worker_processes auto 会统计它能看到的核心数。make -j$(nproc) 在一台拥有 64 个核心但配额为 2 个核心的宿主机上运行时,会启动 64 个编译器。JVM 或数据库根据 MemTotal 选择缓存大小时,可能会选择一个 cgroup 无法允许的数值;进程达到限制后,内核会将其终止。该终止事件会记录在宿主机的内核日志中,而你无法读取该日志。
权威数据位于 cgroup 中,而不在 /proc 中:
cat /sys/fs/cgroup/memory.max
cat /sys/fs/cgroup/memory.current
cat /sys/fs/cgroup/cpu.max这些是 cgroup v2 路径,当前发行版使用的就是这种路径。读取 memory.max 时,如果 max 表示未设置限制。cpu.max 会以微秒为单位输出配额和周期,因此 200000 100000 表示每个周期可使用 2 个核心的 CPU 时间。在较旧的 cgroup v1 宿主机上,相同的数据位于 /sys/fs/cgroup/memory/memory.limit_in_bytes 和 /sys/fs/cgroup/cpu/cpu.cfs_quota_us 下。
Swap,以及实际由谁负责管理
在 KVM 和 Xen 上,swap 由您管理。它是磁盘上的文件或分区,分页由您的内核执行。
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
swapon --showswapon --show 现在应列出该文件及其大小和优先级。如果 swapon 拒绝使用该文件,请改用 dd if=/dev/zero of=/swapfile bs=1M count=2048 创建,因为某些文件系统会拒绝带有未写入 extent 的预分配文件。将 /swapfile none swap sw 0 0 添加到 /etc/fstab,否则下次重启后 swap 将消失。
在容器中,这些资源都不由您管理。swapon 需要一个非特权容器不具备的 capability,因此创建自有 swap 文件会因权限不足而失败,甚至不会访问磁盘。该方案所称的 swap 实际上是主机上的 cgroup 设置;在 cgroup v2 中为 memory.swap.max,由主机自己的 swap 设备提供支持。较旧的 OpenVZ 方案会提供名为“vswap”的额度,其行为更接近突发资源额度,而不是磁盘空间。您可以读取其上限,但无法控制该上限对应的设备。
嵌套虚拟化,以及会误导你的 CPU 标志
嵌套虚拟化是指在 VPS 内运行 hypervisor:QEMU guest、Vagrant box,或带有自有 VM 的嵌套虚拟化实验环境。两个条件必须同时满足。提供商必须在宿主机上启用嵌套虚拟化,并且必须向 guest 提供 CPU 的虚拟化扩展。
lscpu | grep -i virtualization
ls -l /dev/kvm
sudo apt install -y cpu-checker && sudo kvm-ok在已启用嵌套虚拟化的 KVM guest 上,/dev/kvm 存在,kvm-ok 会明确报告是否可以使用加速。在 Xen HVM 上,技术上可行,但很少提供。在 Xen PV 上则无法实现。
在容器中执行检查时,结果会以一种很有说明性的方式失败。/proc/cpuinfo 是宿主机的文件,因此其中存在 vmx 或 svm 标志,而且该标志确实为真:容器所在物理 CPU 确实支持这些指令。但这些资源并不属于你。你的命名空间中不存在 /dev/kvm,你无法加载 kvm_intel 模块,而你刚刚读取的标志描述的是你作为 guest 所在的机器,而不是你能控制的服务器。这是一般规则最清晰的例子。在容器中,/proc 描述的是命名空间及其周围的硬件,而不是你拥有的服务器。
AES-NI 与套餐提供的 CPU 特性
AES-NI(高级加密标准新指令)是一组 CPU 指令,可让 AES 加密速度达到纯软件实现的数倍。TLS 终止、磁盘加密、SSH 和备份流水线都依赖这些指令。
在 KVM 上,来宾系统能看到哪些特性,取决于服务商为 QEMU 配置的 CPU 模型。使用 host passthrough 时,您可以看到真实的 CPU 标志。使用 qemu64 等通用模型,或使用为支持不匹配主机之间的来宾迁移而特意选择的旧基线时,aes 标志可能不存在,OpenSSL 会静默回退到软件实现路径。
lscpu | grep -ow aes | head -n 1
openssl speed -evp aes-128-gcm
env OPENSSL_ia32cap='~0x200000000000000:~0x20000000000:~0x0:~0x0:~0x0' openssl speed -evp aes-128-gcm第三条命令是 OpenSSL 手册中用于在库内禁用这些指令的示例:它会清除 AES-NI 位和 VAES 位,同时保留其他设置不变。比较两次吞吐量。如果两者接近,说明原本就没有使用快速路径;在确定套餐前,花 5 分钟正确检查 VPS 中的 AES-NI是值得的。
容器没有独立的 CPU 模型,因此 /proc/cpuinfo 中的标志就是宿主机的真实标志,并且会应用于您。这是容器套餐的一个实际优势,也是本指南中共享内核对您有利的唯一场景。
Steal time 的来源,以及容器为什么没有该指标
Steal time 是虚拟 CPU 已准备好运行,却因为 hypervisor 正在运行其他任务而未获得运行时间。它在 top 和 vmstat 中显示为 st,也是 /proc/stat 中 cpu 行的第 8 个字段。
guest 自身无法测量这段时间,因为时间被占用时 guest 并未执行。必须由 hypervisor 告知 guest。KVM 会将累计值写入 guest 通过其半虚拟化时钟接口注册的页面;Xen 则为相同用途维护每个 vCPU 的运行状态区域。您读取的数值是 hypervisor 自己提供的记录,因此它能够存在,也值得信任。
Steal time 较高表示主机超额分配,且此时其他租户的负载较高。它直观反映了已售 vCPU 数量与物理核心数之间的比例;读取 steal time 以发现嘈杂邻居是判断方案实际资源是否如页面所声明那样充足的唯一测量方式。
vmstat 1 5
cat /sys/fs/cgroup/cpu.stat在容器中,该字段不会变化,因为您与调度器之间没有 hypervisor。您的进程会作为普通任务,与其他租户的进程一起排队,使用主机自身的 CPU 调度器。资源竞争只会表现为任务耗时变长,没有计数器说明原因。最接近的等效指标是配额限流:当提供商设置 cpu.max 时,/sys/fs/cgroup/cpu.stat 会统计 nr_throttled 个周期,以及等待下一个配额窗口期间消耗的 throttled_usec 微秒。这只反映您自己的配额,不表示邻居之间的竞争。需要注意的是:如果提供商的容器主机本身是虚拟机,/proc/stat 中可能会出现 steal time;该数值属于该主机,而不属于您。
超售,以及容器方案为何更便宜
直接回答很简单。容器方案成本更低,是因为提供商在同一台物理机上为更多用户共享更多资源。
内存方面的差距最大。KVM 虚拟机的 RAM 会分配给该虚拟机,因此一台拥有 256 GB 内存的主机,除去开销后,大约只能销售 256 GB 的虚拟机内存。容器的内存限制是上限,而不是预留值。容器未使用的内存可以立即提供给其他容器使用。因此,提供商可以销售总量达到物理内存数倍的内存上限,而且绝大多数时间都不会出问题。这并不是虚报。只要同时繁忙的租户数量没有超过承载能力,就能正常运行;一旦同时繁忙的租户足够多,所有人的服务都会受到影响。
所有方案类型都会对 CPU 进行超售,包括 KVM,方式是销售数量多于物理核心数的 vCPU。几乎所有地方都会使用精简配置磁盘。容器还会在此基础上进一步提高密度:所有容器共享一个内核和一个页缓存,也不需要为每个客户机运行独立的 QEMU 进程,因此一台主机可以容纳多出数倍的租户。
你放弃的是隔离性,这是真实的工程权衡,不是危言耸听。所有容器共享一个内核,因此内核漏洞会影响所有容器;而容器逃逸会直接进入主机。虚拟机逃逸则需要利用 hypervisor 漏洞,目标范围小得多,攻击难度也高得多。你还需要遵循提供商的内核升级和重启计划。如果这些因素对你很重要,请先阅读VPS 托管到底有多安全,不要只根据价格做选择。
选择哪一种
当您需要使用自己的内核时,应购买 KVM:例如需要 WireGuard 或 ZFS 模块、特定的内核版本、嵌套虚拟化、真正控制 swap,或需要向审计员说明隔离边界时。当您只需以较低成本运行普通服务、提供商使用的是最新内核,并且已确认所依赖的功能已编译进该内核时,应购买容器方案。对于大多数用途,Xen HVM 等同于 KVM。对于仍以 Xen PV 形式销售的方案,应在购买前先确认具体实现。
还有两种方案不属于上述分类。Firecracker microVM 为每个租户提供独立的真实内核,同时启动开销接近容器,这正是无服务器平台采用的模式。Incus system containers 让您在自己控制的硬件上运行容器模型,这与购买容器方案不同。如果您不确定这些术语的含义,请参阅VPS 实际上是什么和VPS、VM 与 VPC 的区别;本指南默认您已经了解这些术语。
FAQ
如何判断我的 VPS 是 KVM 还是容器?
运行 systemd-detect-virt -c。除 none 之外的任何结果,都表示你位于容器内,与套餐标注的产品名称无关。请再通过另外两种方式确认,因为检测结果可能被伪造。lscpu 会显示 hypervisor 厂商,并说明虚拟化类型是 full 还是 para。容器中通常不存在或为空 ls /lib/modules/$(uname -r),因为运行中的 kernel 来自主机,而对应的模块树从未安装到你的文件系统中。sudo virt-what 由专门用于此问题的工具提供独立判断。
为什么 free -m 显示的内存远多于套餐包含的内存?
你使用的是未挂载 LXCFS 的容器套餐,因此 /proc/meminfo 是主机上的文件,free 会如实报告主机内存。你的实际上限由 cgroup 决定。读取 /sys/fs/cgroup/memory.max 获取限制,读取 /sys/fs/cgroup/memory.current 获取当前使用量;在较旧的 cgroup v1 主机上,也可以读取 /sys/fs/cgroup/memory/memory.limit_in_bytes。任何根据该数值调整缓存或 worker pool 大小的服务,都应使用这个限制,而不是使用 free。
我可以在 LXC VPS 上运行 Docker 或 WireGuard 吗?
有时可以,但这绝不是由你安装的内容决定的。两者都依赖提供商的 kernel,因为你无法向其中加载模块。当主机上已经存在 WireGuard 模块并向你提供访问时,WireGuard 可以运行;否则可使用用户空间的 wireguard-go 实现。Docker 需要提供商允许嵌套运行,并且需要能在容器内正常工作的存储驱动。购买前先询问清楚,或者选择可以随时停止使用的测试期限。
为什么我的容器 VPS 从不报告 steal time?
只有 hypervisor 调度虚拟 CPU 时才会产生 steal time。hypervisor 会将该数值写入 kernel 可读取的页面,因此系统才能报告它。容器下方没有 hypervisor。你的进程只是主机调度器中的普通任务,因此资源争用只会表现为所有操作变慢,没有可用于定位问题的计数器。请改为读取 /sys/fs/cgroup/cpu.stat:nr_throttled 和 throttled_usec 统计 cgroup 等待下一个 CPU 配额时间窗口的时长,这是容器环境中最接近 steal time 的指标。