systemd-detect-virt —— 探测当前运行环境的虚拟化类型
一句话定义
systemd-detect-virt 告诉你当前这台机器跑在什么虚拟化技术上:是物理机、KVM/VMware/Hyper-V 虚拟机、还是 LXC/Docker 这种容器。
典型场景
Day0 装 K8s 之前 5 分钟必跑的探机器命令之一。这一步的核心目的是排除 LXC 容器——很多小 IDC 把 LXC 卖成"VPS",外层看像 KVM、内层是容器套娃,K8s 在里面装不上。
systemd-detect-virt # 一级探测:你跑在啥虚拟化里
systemd-detect-virt --container # 二级探测:是否再嵌在一个容器里
输出值速查
没有虚拟化(物理机)
$ systemd-detect-virt
none
$ echo $?
1 # 退出码 1 表示 "none"
注意:none 时退出码是 1,不是 0。在脚本里要小心,set -e 会让脚本因此退出。
虚拟机
| 输出 | 含义 |
|---|---|
kvm | KVM(Linux 内核虚拟化,最常见的国内云、自建 VM) |
qemu | 纯 QEMU 软件虚拟化(无 KVM 加速,很慢) |
vmware | VMware ESXi / Workstation |
microsoft | Hyper-V |
xen | Xen(AWS EC2 旧实例、阿里云某些规格) |
oracle | VirtualBox(个人桌面、Vagrant 环境) |
parallels | macOS 上的 Parallels |
bhyve | FreeBSD bhyve |
amazon | AWS Nitro 系实例(更新的 EC2) |
容器
| 输出 | 含义 |
|---|---|
docker | Docker 容器 |
lxc | LXC / LXD 容器(装 K8s 的最大坑) |
systemd-nspawn | systemd-nspawn 容器 |
podman | Podman 容器 |
containerd / cri-containerd | 普通 K8s pod |
wsl | WSL 1/2(Windows Subsystem for Linux) |
为什么 LXC 装 K8s 极难
K8s(更准确说 kubelet)要做几件 LXC 容器里做不了的事:
- 加载内核模块(
br_netfilter、overlay、ip_tables)—— LXC 没有自己的内核,共享宿主内核,但宿主可能没装这些模块;你也没权限装 - 写
/proc/sys/...—— K8s 要调一堆 sysctl 参数,LXC 通常只允许读不允许写 - 管自己的 cgroup namespace —— 容器内层 K8s pod 还要再开一层 cgroup,套娃 cgroup 现代 systemd 支持得很差
- 网络隔离 —— iptables / nftables 规则、IPVS 转发,LXC 通常被宿主网络策略包住
遇到 LXC,直接换机器,不要花一天试图修。
很多 IDC(特别是国内做"独立 IP VPS"那种)的坑爹之处:
$ systemd-detect-virt
kvm # 看起来是 KVM,挺正常
$ systemd-detect-virt --container
lxc # 实际是 KVM 里又开了个 LXC,套娃
外层 kvm 看不出来,必须跑 --container 子探测才暴露。买 VPS 之前可以让 IDC 给你跑这一条验证。
三个常用 flag
默认:综合判断
systemd-detect-virt
返回最外层的虚拟化类型。kvm / vmware / none 等。
--container:只判断"是不是在容器里"
systemd-detect-virt --container
# none / lxc / docker / podman / ...
如果不在容器里、即使在 VM 里也返回 none。
--vm:只判断"是不是在 VM 里"
systemd-detect-virt --vm
# none / kvm / vmware / ...
只看 VM 层。
--quiet / -q:静默模式
if systemd-detect-virt -q --container; then
echo "在容器里"
fi
-q 不输出,只用退出码判断。脚本里常用。
退出码语义:检测到任何虚拟化技术 → 0;检测到
none→ 1。和直觉相反——找到东西反而是 0。
装机检测脚本(Day0 §1)
ssh root@$ip '
echo "=== 虚拟化探测 ==="
echo "外层: $(systemd-detect-virt)"
echo "容器: $(systemd-detect-virt --container)"
if systemd-detect-virt -q --container; then
case $(systemd-detect-virt --container) in
lxc|lxc-libvirt)
echo "❌ LXC 容器,装不了 K8s,换机器"
exit 1
;;
docker|podman)
echo "⚠️ 你在 Docker/Podman 里,是 DinD 场景?确认一下"
;;
*)
echo "ℹ️ 在容器里但不是 LXC: $(systemd-detect-virt --container)"
;;
esac
else
echo "✅ 不在容器里"
fi
'
放在 Day0 装机第一步的脚本里,一票否决"看起来是 VPS 实际是 LXC"的机器。
相关探测命令
| 命令 | 用途 |
|---|---|
systemd-detect-virt | 虚拟化类型 |
dmidecode -s system-manufacturer | 硬件厂商(KVM 显示 "QEMU"、VMware 显示 "VMware") |
cat /proc/cpuinfo | grep hypervisor | CPU flag 含 hypervisor 表示在 VM 里 |
cat /sys/class/dmi/id/sys_vendor | 系统厂商 |
lscpu | grep Hypervisor | 检测 hypervisor 类型(lscpu 是更便携的查法) |
systemd-detect-virt 是这几个里最准、最快、最好脚本化的。其它命令在不同发行版输出格式差异大。
常见踩坑
坑 1:在 macOS Docker Desktop 里跑 K8s
$ systemd-detect-virt --container
none
$ systemd-detect-virt
none
输出是 none,但你其实在 Docker Desktop 的 Linux VM 里。Docker Desktop 暴露的 Linux VM 看起来像物理机(它 hide 了一层)。这不算坑,因为这个 VM 装 K8s 没问题(minikube / kind 就是这么干的)。
坑 2:套娃虚拟化误判
某些极端情况:嵌套虚拟化(VM 里跑 KVM)—— systemd-detect-virt 只能告诉你最内层。要看完整链路:
dmesg | grep -iE 'hypervisor|kvm|virt' | head
坑 3:脚本里 if 判断逻辑反了
# ❌ 错的:退出码反直觉
if systemd-detect-virt -q; then
echo "不在虚拟化里" # 错!退出码 0 表示"在虚拟化里"
fi
# ✅ 对的
if systemd-detect-virt -q; then
echo "在虚拟化里"
else
echo "物理机"
fi
总结:找到 = 0、没找到 = 1,和 grep 一样的语义。
关联命令
lscpu—— CPU 信息,含 hypervisor 类型dmidecode—— BIOS / 硬件元信息cat /proc/cpuinfo—— hypervisor flag- lsblk —— 接下来该看磁盘了