【六六互联】长期出售【美国抗投诉服务器】【欧洲抗投诉服务器】【亚洲抗投诉服务器】

目前裸机(物理机)、虚拟机、容器是云计算提供计算服务的主流形式

目前裸机(物理机)、虚拟机、容器是云计算提供计算服务的主流形式

目前裸机(物理机)、虚拟机、容器是云计算提供计算服务的三种主流形式。那么如何判断一个虚拟shell环境到底是物理机、虚拟机还是容器呢?

更进一步,如果是物理机,这个物理机厂商是什么,虚拟机到底是KVM还是XEN,容器是Docker还是rkt、lxc等?

更进一步,如果是虚拟机,是否可以判断这个虚拟机是运行在AWS还是阿里或者OpenStack,是否能够获取虚拟机的UUID、instance-type、vpc-id、安全组等信息?

这有点像我们在开发中经常使用的反射(reflection)机制,通过反射可以知道一个类实例(instance)的类(class)是什么,更进一步可以知道这个类的父类是什么、实现了哪些方法、包含哪些属性等。

以下是我用到的一些方法,仅供参考。

01 判断容器

目前还没有什么方法能够100%准确判断虚拟环境是否是容器,至少我没有找到相关文献。

如果环境有`systemd-detect-virt`命令,则可以直接通过`systemd-detect-virt -c`命令判断,如果输出为`none`则不是容器,否则会输出容器类型,比如lxc。目前很少容器里面放`systemd`的,我见过的就只有LXD的`ubuntu`镜像,因此这种方法适用性不广。

除此之外,可通过其他tricks判断,最简便的方法判断PID为1的进程,如果该进程就是应用进程则判断是容器,而如果是init进程或者systemd进程,则不一定是容器,当然不能排除是容器的情况,比如LXD/lXC实例的进程就为`/sbin/init`。

容器和虚拟机不一样的是,容器和宿主机是共享内核的,因此理论上容器内部是没有内核文件的,除非挂载了宿主机的`/boot`目录:

另外,我们知道容器是通过cgroup实现资源限制,每个容器都会放到一个cgroup组中,如果是Docker,则cgroup的名称为docker-xxxx,其中xxxx为Docker容器的UUID。

而控制容器的资源,本质就是控制运行在容器内部的进程资源,因此我们可以通过查看容器内部进程为1的cgroup名称获取线索。

如下是我通过Docker跑busybox的cgroup信息:

我们不仅可以知道这是Docker容器,还获取了Docker容器的UUID为9ba...11。

根据如上的结论,判断一个虚拟环境是否Docker的脚本为:

当然如果仅仅判断是否Docker容器,还能通过判断是否存在.dockerenv文件区分是否Docker容器:

rkt容器类似,输出结果如下:

如上的\x2d为-号:

因此判断一个虚拟环境是否rkt的脚本为:

好奇AWS lambda的运行环境是什么,于是写了个函数输出/proc/1/cgroup,结果为:

猜测是一种叫sandbox的运行环境,估计也是一种容器。

判断虚拟环境是否为容器环境相对比较复杂,目前没有完美的方案,总结过程如下:

判断是否可运行systemd-detect-virt -c命令,如果输出为none则不是容器,否则可确定容器类型。

判断PID 1如果为应用本身,则该虚拟环境是容器,否则不能确定是否是容器。

判断是否存在加载的内核文件,如果不存在,则可判断为容器,否则不能确定是否为容器。

判断是否存在/.dockerenv文件,如果存在则为Docker容器,否则不能确定是否为容器。

目前裸机(物理机)、虚拟机、容器是云计算提供计算服务的主流形式

读取/proc/1/cgroup文件,判断是否包含docker、rkt等关键字,如果包含,则说明为容器,否则不能确定是否为容器。

另外,需要特别注意的是,容器必须最先判断,因为容器本身并没有任何的硬件虚拟化,容器看到的硬件特性信息和宿主机看到的完全一样,因此下面介绍的通过`lscpu`以及DMI信息判断是否是虚拟机或者物理机,对容器并不适用。换句话说,不能因为`lscpu`的`Hypervisor vendor`值为`KVM`就说明一定是KVM虚拟机,因为它也有可能是容器。下文均假设已经排除为容器的情况。