目前,裸机(物理机)、虚拟机、容器是云计算提供计算服务的三种主流形态。那么如何判断一个虚拟shell环境是物理机、虚拟机还是容器呢?再者,如果是物理机,物理机的厂商是什么,虚拟机是KVM还是XEN,容器是Docker、rkt、lxc等?再者,如果是虚拟机,是否可以判断虚拟机运行在AWS、Ali还是OpenStack上,是否可以获取虚拟机的UUID、instance-type、vpc-id、securitygroup等信息机器?这有点像我们在开发中经常用到的反射机制,通过反射可以知道一个类实例的类是什么,进而可以知道这个类的父类是什么,实现了哪些方法,它包含哪些属性等。以下是我使用的一些方法,仅供参考。1.判断容器目前还没有一种方法可以100%准确判断虚拟环境是不是容器,至少我没有找到相关文献。如果环境有systemd-detect-virt命令,可以直接通过systemd-detect-virt-c命令判断。如果输出为none,则不是容器,否则输出容器类型,如lxc。目前很少将systemd放在容器中。我看到的只有LXD的ubuntu镜像,所以这种方法适用范围不广。另外还可以用其他的tricks来判断,最简单的判断PID为1的进程,如果进程是application进程就判断是container,如果是init进程或者systemd进程,不一定是容器,当然不排除容器的情况,比如LXD/lXC实例的进程是/sbin/init。容器和虚拟机的区别在于容器和宿主机共享内核,所以理论上容器内部是没有内核文件的,除非挂载了宿主机的/boot目录:另外,我们知道容器通过cgroups实现资源,每个容器都会放在一个cgroup组中。如果是Docker,cgroup的名称是docker-xxxx,其中xxxx是Docker容器的UUID。控制容器的资源其实质就是控制容器内部运行的进程资源,所以我们可以通过查看容器内部进程号为1的cgroup名称来获得线索。下面是我通过Docker运行busybox的cgroup信息:我们不仅可以知道这是一个Docker容器,还可以得到Docker容器的UUID为9ba...11。根据以上结论,判断一个虚拟环境是否是Docker的脚本是:当然,如果只判断是否是Docker容器,也可以通过判断是否有.dockerenv来判断是否是Docker容器file:rkt容器类似,输出如下:\x2d上面是-No.:所以判断一个虚拟环境是否为rkt的脚本是:好奇AWSlambda的运行环境,所以写了一个函数输出/proc/1/cgroup,结果是:我猜是一种叫sandbox的运行环境。也是一个容器。判断虚拟环境是否为容器环境相对复杂。目前没有完美的解决方案。总结流程如下:判断是否可以运行systemd-detect-virt-c命令。如果输出为none,则它不是容器。否则,可以确定容器类型。判断PID1是否为应用本身,则虚拟环境为容器,否则无法判断是否为容器。判断是否有加载的内核文件,如果没有可以判断为容器,否则无法判断是否为容器。判断/.dockerenv文件是否存在,存在则为Docker容器,否则无法判断是否为容器。读取/proc/1/cgroup文件,判断是否包含docker、rkt等关键字。如果包含则说明是容器,否则无法判断是否为容器。另外需要特别注意的是,一定要先判断容器,因为容器本身没有任何硬件虚拟化,容器看到的硬件特征信息和宿主机看到的是完全一样的,所以下面介绍的lscpu和DMI信息判断是虚拟机还是物理机不适用于容器。也就是说,仅仅因为lscpu的Hypervisorvendor值为KVM并不代表它一定是KVM虚拟机,因为它也有可能是容器。以下假定容器已被排除。2.判断物理机如果使用systemd,可以直接使用systemd-detect-virt命令判断是否是物理机:如果输出为none,则表示是物理机。当然你也可以根据lscpu命令的输出查看是否有Hypervisorvendor属性。如果没有这个属性,一般是物理机。如果存在该属性,则一定是虚拟机:获取物理机信息最直接的方法是查看DMI信息/sys/firmware/dmi/tables/DMI,使用dmidecode命令解码:如可以上图,这是一台实体机,厂商是HP,型号是ProLiantDL380Gen9,序列号是6CU6468KKD。可以通过ipmitool命令查看物理服务器的带外IP:当然,如果是虚拟机,上述命令会失败。此外,您还可以通过其他命令查看物理信息,例如lshw命令。3、判断虚拟机前面说到,如果使用systemd,可以直接使用systemd-detect-virt命令判断是否是虚拟机:如果是虚拟机,会输出虚拟机类型,比如kvm、oracle(virtualbox)、xen等。当然你也可以根据lscpu命令的输出查看Hypervisorvendor属性值:通过上面的命令,我的一台AWS虚拟机输出为Xen,阿里云虚拟机是KVM,VirtualBox虚拟机也是输出KVM,因为我用的是KVM硬件加速虚拟化。我的瓦工虚拟机输出的也是KVM,所以可以看出瓦工主机也是KVM虚拟机。通过上述方法,可以得到虚拟机的虚拟化类型。能否获得更多信息?参考如何获取物理机,我们可以通过dmidecode命令获取更多的虚拟机信息。比如我在一个OpenStack虚拟机上运行如下命令:上面的Manufacturer是OpenStackFoundation,表示运行在OpenStack平台上,Version是Nova版本。根据OpenStack的发布,15.0.1对应的是OpenStackOcata版本,UUID是虚拟机的UUID。AWS上的一个虚拟机的输出是:Version中标注了amazon这个词。阿里云虚拟机如下(感谢L大神提供的输出):可见,虽然可以从系统信息中得到云厂商的蛛丝马迹,但是对于云厂商的系统信息,其实并没有统一的标准虚拟机。有的体现在版本上,有的体现在ProductName里。性能完全取决于云厂商自己的配置。如上集成如下脚本初步判断:同上也可以判断公有云是否基于OpenStack实现。比如华为的虚拟机输出就是OpenStack。大致可以猜到华为的公有云是基于OpenStack实现的。AWS和OpenStack虚拟机还可以通过元数据或ConfigDrive获取更多信息。以元数据为例:获取虚拟机的ID:获取实例类型(规格):获取虚拟机的公网IP(弹性IP),这个很有用,因为虚拟机无法通过查看弹性IPifconfig,登录虚拟机后经常忘记自己的公网IP:其他如vpc-id,amiid(镜像id),安全组,公钥名等都可以通过这个方法获取。如果是OpenStack,还可以使用OpenStack元数据获取更多信息:如上,可以获取虚拟机的租户ID、卷类型等信息。当然,邪点可以通过查看userdata获取虚拟机初始化root密码。AWS甚至可以查看AccessKeyId和SecretAccessKey。4.总结以上总结了几种判断虚拟化环境类型的方法,不一定准确,仅供参考。当然,可能还有其他更好的方法。下面是根据前面的结论写的检测虚拟化类型的脚本,可能不够健壮和完整,仅供参考:
