内容简介:目前裸机(物理机)、虚拟机、容器是云计算提供计算服务的三种主流形式。最近有人问,如何判断一个虚拟shell环境到底是物理机、虚拟机还是容器?更进一步,如果是物理机,这个物理机厂商是什么,虚拟机到底是KVM还是XEN,容器是Docker还是rkt、lxc等?更进一步,如果是虚拟机,是否可以判断这个虚拟机是运行在AWS还是阿里或者OpenStack,是否能够获取虚拟机的UUID、instance-type、vpc-id、安全组等信息?
目前裸机(物理机)、虚拟机、容器是云计算提供计算服务的三种主流形式。最近有人问,如何判断一个虚拟 shell 环境到底是物理机、虚拟机还是容器?
更进一步,如果是物理机,这个物理机厂商是什么,虚拟机到底是KVM还是XEN,容器是 Docker 还是rkt、lxc等?
更进一步,如果是虚拟机,是否可以判断这个虚拟机是运行在AWS还是阿里或者OpenStack,是否能够获取虚拟机的UUID、instance-type、vpc-id、安全组等信息?
这有点像我们在开发中经常使用的反射(reflection)机制,通过反射可以知道一个类实例(instance)的类(class)是什么,更进一步可以知道这个类的父类是什么、实现了哪些方法、包含哪些属性等。
以下是我用到的一些方法,仅供参考。
1 判断容器
目前应该还没有什么方法能够100%准确判断虚拟环境是否是容器,至少我没有找到相关文献。
如果环境有 systemd-detect-virt
命令,则可以直接通过 systemd-detect-virt -c
命令判断,如果输出为 none
则不是容器,否则会输出容器类型,比如lxc。目前很少容器里面放 systemd
的,我见过的就只有LXD的 ubuntu
镜像,因此这种方法适用性不广。
除此之外,可通过其他tricks判断,最简便的方法判断PID为1的进程,如果该进程就是应用进程则判断是容器,而如果是init进程或者systemd进程,则不一定是容器,当然不能排除是容器的情况,比如LXD实例的进程就为 /sbin/init
。
容器通过PID namespace实现与宿主机以及其他容器的进程PID隔离,但是所有容器的进程在宿主机是完全可见的,并且PID不一样。
容器和虚拟机不一样的是,容器和宿主机是共享内核的,因此理论上容器内部是没有内核文件的,除非挂载了宿主机的 /boot
目录:
KERNEL_PATH=$(cat /proc/cmdline | tr ' ' '\n' | awk -F '=' '/BOOT_IMAGE/{print $2}') test -e $KERNEL_PATH && echo "Not Sure" || echo "Container"
另外,我们知道容器是通过cgroup实现资源限制,每个容器都会放到一个cgroup组中,如果是Docker,则cgroup的名称为 docker-xxxx
,其中 xxxx
为Docker容器的UUID。而控制容器的资源,本质就是控制运行在容器内部的进程资源,因此我们可以通过查看容器内部进程为1的cgroup名称获取线索。
如下是我通过Docker跑busybox的cgroup信息:
# docker run -t -i --rm busybox cat /proc/1/cgroup 11:memory:/system.slice/docker-9da...11.scope 10:pids:/system.slice/docker-9da...11.scope 9:hugetlb:/system.slice/docker-9da...11.scope 8:blkio:/system.slice/docker-9da...11.scope 7:cpuset:/system.slice/docker-9da...11.scope 6:devices:/system.slice/docker-9da...11.scope 5:perf_event:/system.slice/docker-9da...11.scope 4:freezer:/system.slice/docker-9da...11.scope 3:cpuacct,cpu:/system.slice/docker-9da...11.scope 2:net_prio,net_cls:/system.slice/docker-9da...11.scope 1:name=systemd:/system.slice/docker-9da...11.scope
我们不仅可以知道这是Docker容器,还获取了Docker容器的UUID为 9ba...11
。
根据如上的结论,判断一个虚拟环境是否Docker的脚本为:
cat /proc/1/cgroup | grep -qi docker \ && echo "Docker" \ || echo "Not Docker"
当然如果仅仅判断是否Docker容器,还能通过判断是否存在 .dockerenv
文件区分是否Docker容器:
[[ -f /.dockerenv ]] && echo "Docker" || echo "Not Docker"
rkt容器类似,输出结果如下:
# rkt --insecure-options=image run docker://busybox --exec cat -- '/proc/1/cgroup' [ 1547.858418] busybox[6]: 11:perf_event:/ [ 1547.858758] busybox[6]: 10:hugetlb:/ [ 1547.859055] busybox[6]: 9:memory:/machine.slice/machine-rkt\x2dd6863650\x2da1fa\x2d4a12\x2db754\x2da4e6641023cd.scope [ 1547.859309] busybox[6]: 8:cpuset:/ [ 1547.859549] busybox[6]: 7:pids:/machine.slice/machine-rkt\x2dd6863650\x2da1fa\x2d4a12\x2db754\x2da4e6641023cd.scope [ 1547.859781] busybox[6]: 6:net_cls,net_prio:/ [ 1547.860038] busybox[6]: 5:blkio:/machine.slice/machine-rkt\x2dd6863650\x2da1fa\x2d4a12\x2db754\x2da4e6641023cd.scope [ 1547.860278] busybox[6]: 4:cpu,cpuacct:/machine.slice/machine-rkt\x2dd6863650\x2da1fa\x2d4a12\x2db754\x2da4e6641023cd.scope [ 1547.860509] busybox[6]: 3:freezer:/ [ 1547.860756] busybox[6]: 2:devices:/machine.slice/machine-rkt\x2dd6863650\x2da1fa\x2d4a12\x2db754\x2da4e6641023cd.scope [ 1547.861026] busybox[6]: 1:name=systemd:/machine.slice/machine-rkt\x2dd6863650\x2da1fa\x2d4a12\x2db754\x2da4e6641023cd.scope/init.scope
如上的 \x2d
为 -
号:
# python -c 'print("\x2d".decode())' - # echo "machine-rkt\x2dd6863650\x2da1fa\x2d4a12\x2db754\x2da4e6641023cd.scope" \ | sed 's/\\x2d/-/g' machine-rkt-d6863650-a1fa-4a12-b754-a4e6641023cd.scope
因此判断一个虚拟环境是否rkt的脚本为:
cat /proc/1/cgroup | \ grep -q 'machine-rkt' \ && echo 'rkt' \ || echo 'not rkt'
好奇AWS lambda的运行环境是什么,于是写了个函数输出 /proc/1/cgroup
,结果为:
9:perf_event:/ 8:memory:/sandbox-8a8cb2 7:hugetlb:/ 6:freezer:/sandbox-55f57b 5:devices:/ 4:cpuset:/ 3:cpuacct:/sandbox-cf5b48 2:cpu:/sandbox-root-HaLK4d/sandbox-305dc7 1:blkio:/
猜测是一种叫 sandbox
的运行环境,估计也是一种容器。
判断虚拟环境是否为容器环境相对比较复杂,目前没有完美的方案,总结过程如下:
-
判断是否可运行
systemd-detect-virt -c
命令,如果输出为none
则不是容器,否则可确定容器类型。 -
判断
PID 1
如果为应用本身,则该虚拟环境是容器,否则不能确定是否是容器。 - 判断是否存在加载的内核文件,如果不存在,则可判断为容器,否则不能确定是否为容器。
-
判断是否存在
/.dockerenv
文件,如果存在则为Docker容器,否则不能确定是否为容器。 -
读取
/proc/1/cgroup
文件,判断是否包含docker
、rkt
等关键字,如果包含,则说明为容器,否则不能确定是否为容器。
另外,需要特别注意的是,容器必须最先判断,因为容器本身并没有任何的硬件虚拟化,容器看到的硬件特性信息和宿主机看到的完全一样,因此下面介绍的通过 lscpu
以及DMI信息判断是否是虚拟机或者物理机,对容器并不适用。换句话说,不能因为 lscpu
的 Hypervisor vendor
值为 KVM
就说明一定是KVM虚拟机,因为它也有可能是容器。下文均假设已经排除为容器的情况。
2 判断物理机
如果使用了systemd,则可以直接通过 systemd-detect-virt
命令判断是否物理机:
systemd-detect-virt none
如果输出为 none
,则说明是物理机。
当然也可根据lscpu命令输出,看是否有 Hypervisor vendor
属性,如果没有该属性,则一般为物理机,如果存在该属性则一定是虚拟机:
lscpu | grep -Piq 'Hypervisor vendor' \ && echo "Virtual Machine" \ || echo "Physical Machine"
获取物理机的信息最直接的方式是查看DMI信息 /sys/firmware/dmi/tables/DMI
,使用 dmidecode
命令解码:
# dmidecode -t system System Information Manufacturer: HP Product Name: ProLiant DL380 Gen9 Version: Not Specified Serial Number: 6CU6468KKD UUID: 30393137-3136-4336-5536-3436384B4B44 Wake-up Type: Power Switch SKU Number: 719061-B21 Family: ProLiant
如上可以看出这是台物理机,厂商为HP,型号为ProLiant DL380 Gen9,序列号为6CU6468KKD。
通过 ipmitool
命令可以查看物理服务器的带外IP:
ipmitool lan print \ | grep -Pi 'IP Address\s+:' \ | cut -d ':' -f 2 \ | tr -d ' ' 192.168.0.35
当然如果是虚拟机,如上命令会执行失败。
另外也可以通过其他命令查看物理信息,如 lshw
命令。
3 判断虚拟机
其实前面已经提到了,如果使用了systemd,则可以直接通过 systemd-detect-virt
命令判断是否虚拟机:
systemd-detect-virt
如果是虚拟机,则会输出虚拟机类型,如kvm、oracle(virtualbox)、xen等。
当然也可根据lscpu命令输出,查看 Hypervisor vendor
属性值:
lscpu | grep -i 'Hypervisor vendor' | cut -d ':' -f 2 | tr -d ' '
通过如上命令,我的一台AWS虚拟机输出为 Xen
,阿里云虚拟机为 KVM
,VirtualBox虚拟机也输出为 KVM
,这是因为我使用了KVM硬件加速虚拟化。
我的搬瓦工虚拟机输出也为 KVM
,可见搬瓦工主机也是KVM虚拟机。
通过如上方法可以获取虚拟机的虚拟化类型,能否获取更多信息呢?参考物理机的获取方式,我们可以通过 dmidecode
命令获取更多的虚拟机信息。比如我在一台OpenStack虚拟机运行如下命令:
# dmidecode -t system # dmidecode 3.1 Getting SMBIOS data from sysfs. SMBIOS 2.8 present. Handle 0x0100, DMI type 1, 27 bytes System Information Manufacturer: OpenStack Foundation Product Name: OpenStack Nova Version: 15.0.1 Serial Number: 00310981-9899-e411-906e-00163566263e UUID: 1a48e29f-a023-48b8-b06b-afa63a9cff00 Wake-up Type: Power Switch SKU Number: Not Specified Family: Virtual Machine
如上Manufacturer为 OpenStack Foundation
,说明运行在OpenStack平台,Version为Nova版本,根据OpenStack的 releases
可知 15.0.1
对应为OpenStack Ocata版本,而UUID即虚拟机的UUID。
AWS上的一台虚拟机输出为:
$ sudo dmidecode -t system System Information Manufacturer: Xen Product Name: HVM domU Version: 4.2.amazon Serial Number: ec24ea61-ebbd-d428-e3d0-50ca37b49074 UUID: ec24ea61-ebbd-d428-e3d0-50ca37b49074 Wake-up Type: Power Switch SKU Number: Not Specified Family: Not Specified
在Version中标明了 amazon
字样。
阿里云虚拟机如下:
# dmidecode -t system System Information Manufacturer: Alibaba Cloud Product Name: Alibaba Cloud ECS Version: pc-i440fx-2.1 Serial Number: f1099eb2-f7b6-4b8f-a02e-0a004b66dc6a UUID: F1099EB2-F7B6-4B8F-A02E-0A004B66DC6A Wake-up Type: Power Switch SKU Number: Not Specified Family: Not Specified
可见虽然可以从system信息中获取云厂商的线索,但其实虚拟机的system信息并没有统一的标准,有的在 version
中体现,有的在 Product Name
中表现,完全取决于云厂商自己的配置。
如上整合如下脚本初略判断:
#!/bin/bash detect_cloud_provider() { if dmidecode -t system | grep -qi 'amazon'; then echo "AWS" return 0 fi if dmidecode -t system | grep -qi 'openstack'; then echo "OpenStack" return 0 fi if dmidecode -t system | grep -qi 'alibaba'; then echo "Aliyun" return 0 fi # ... echo "Unknown" return 1 } detect_cloud_provider $@
如上也可以判断公有云是否基于OpenStack实现,比如华为虚拟机输出为OpenStack,可大致猜测华为的公有云是基于OpenStack实现的。
AWS以及OpenStack系的虚拟机还可以通过metadata或者ConfigDrive获取更多信息,以metadata为例:
获取虚拟机的ID:
$ curl -L 169.254.169.254/latest/meta-data/instance-id && echo i-060a8dca9edf35512
获取内网IP:
$ curl -L 169.254.169.254/latest/meta-data/local-ipv4 && echo 192.168.0.111
获取instance type(规格):
$ curl -L 169.254.169.254/latest/meta-data/instance-type && echo t2.medium
获取虚拟机的公有IP(弹性IP),这个挺有用的,因为在虚拟机没法通过 ifconfig
查看弹性IP,经常登录虚拟机后,忘记自己的公有IP:
$ curl -L 169.254.169.254/latest/meta-data/public-ipv4 && echo 52.82.103.54
其他的比如vpc-id、ami id(镜像id)、安全组、公钥名等都可以通过该方式获取。
如果是OpenStack,还可以使用OpenStack的metadata获取更多信息:
# curl -sL 169.254.169.254/openstack/latest/meta_data.json \ | python -m json.tool { "availability_zone": "nova", "devices": [], "hostname": "int32bit-test-1", "launch_index": 0, "meta": { "cinder_img_volume_type": "07c40142-f06e-483c-8c27-2970579e94b1", "volume_type": "07c40142-f06e-483c-8c27-2970579e94b1" }, "name": "int32bit-test-1", "project_id": "ad19a76ab29c4e448d6efc9645369d0e", "random_seed": "...", "uuid": "1a48e29f-a023-48b8-b06b-afa63a9cff00" }
如上可获取虚拟机的租户ID、volume type等信息。当然邪恶点可以通过查看userdata获取虚拟机初始化root密码。AWS甚至可以查看 AccessKeyId
以及 SecretAccessKey
。
4 总结
如上总结了几种判断虚拟化环境类型的方法,不一定准确,仅供参考,当然也可能还有其他更好的方法。
如下是根据前面的结论写的一个探测虚拟化类型的脚本,不一定健壮完备,仅供参考:
#!/bin/sh detect_container() { if which systemd-detect-virt >/dev/null 2>&1; then TYPE=$(systemd-detect-virt -c) if [ "$TYPE" = "none" ]; then return 1 else echo "Container: $TYPE" return 0 fi fi if [ -n "$container" ]; then echo "Container: $container" return 0 fi if grep -qi docker /proc/1/cgroup; then echo "Container: Docker" return 0 fi if test -f /.dockerenv; then echo "Container: Docker" return 0 fi if grep -qi 'machine-rkt' /proc/1/cgroup; then echo "Container: rkt" return 0 fi # Other container type detect here return 1 } detect_physical() { if ! lscpu | grep -qi 'Hypervisor vendor'; then echo "Physical: $(cat /sys/class/dmi/id/product_name)" return 0 fi return 1 } detect_virtual_machine() { if lscpu | grep -qi 'Hypervisor vendor'; then HYPER_TYPE=$(lscpu | grep -i "Hypervisor vendor" \ | cut -d ':' -f 2 | sed 's/^ *//g') if dmidecode -t system | grep -qi 'amazon'; then echo "Virtual Machine: AWS/$HYPER_TYPE" elif dmidecode -t system | grep -qi 'openstack'; then echo "Virtual Machine: OpenStack/$HYPER_TYPE" elif dmidecode -t system | grep -qi 'alibaba'; then echo "Virtual Machine: Aliyun/$HYPER_TYPE" else Manufacturer=$(dmidecode -t system | grep 'Manufacturer' \ | cut -d ':' -f 2 | sed 's/^ *//g') ProductName=$(dmidecode -t system | grep 'Product Name' \ | cut -d ':' -f 2 | sed 's/^ *//g') Version=$(dmidecode -t system | grep 'Version' \ | cut -d ':' -f 2 | sed 's/^ *//g') echo "Virtual Machine: $Manufacturer $ProductName($Version)/$HYPER_TYPE" fi return 0 fi return 1 } detect_virtual_type() { detect_container || detect_physical \ || detect_virtual_machine || echo "Unknown" } detect_virtual_type "$@"
以上所述就是小编给大家介绍的《如何探测虚拟化环境是物理机、虚拟机还是容器?》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 虚拟化生态系统及实现从虚拟化走向云端
- KVM虚拟化技术(一)虚拟化简介以及按安装
- 如何探测虚拟化环境是物理机、虚拟机还是容器?
- 云计算技术分享之桌面虚拟化中的3D虚拟化解决方案经验总结
- Xen server虚拟化中虚拟机磁盘文件丢失的处理办法
- Xen server虚拟化中虚拟机磁盘文件丢失的处理办法
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
常用算法深入学习实录
张子言 / 电子工业出版社 / 2013-10 / 89.00元
对于任何一门编程语言来说,算法都是程序的“灵魂”。正是因为算法如此重要,所以笔者精心编写了本书,希望通过书中的内容引领广大读者一起探讨学习算法的奥秘,带领广大读者真正步入程序开发的高级世界。 本书共分15章,循序渐进、由浅入深地详细讲解算法的核心内容,并通过具体实例的实现过程演练各个知识点的具体用法。本书首先详细讲解算法的基础知识,剖析了将算法称为“程序灵魂”的原因。然后详细讲解算法技术的核......一起来看看 《常用算法深入学习实录》 这本书的介绍吧!