目前裸機(物理機)、虛擬機、容器是云計算提供計算服務(wù)的主流形式
目前裸機(物理機)、虛擬機、容器是云計算提供計算服務(wù)的三種主流形式。那么如何判斷一個虛擬shell環(huán)境到底是物理機、虛擬機還是容器呢?
更進一步,如果是物理機,這個物理機廠商是什么,虛擬機到底是KVM還是XEN,容器是Docker還是rkt、lxc等?
更進一步,如果是虛擬機,是否可以判斷這個虛擬機是運行在AWS還是阿里或者OpenStack,是否能夠獲取虛擬機的UUID、instance-type、vpc-id、安全組等信息?
這有點像我們在開發(fā)中經(jīng)常使用的反射(reflection)機制,通過反射可以知道一個類實例(instance)的類(class)是什么,更進一步可以知道這個類的父類是什么、實現(xiàn)了哪些方法、包含哪些屬性等。
以下是我用到的一些方法,僅供參考。
01 判斷容器
目前還沒有什么方法能夠100%準(zhǔn)確判斷虛擬環(huán)境是否是容器,至少我沒有找到相關(guān)文獻。
如果環(huán)境有`systemd-detect-virt`命令,則可以直接通過`systemd-detect-virt -c`命令判斷,如果輸出為`none`則不是容器,否則會輸出容器類型,比如lxc。目前很少容器里面放`systemd`的,我見過的就只有LXD的`ubuntu`鏡像,因此這種方法適用性不廣。
除此之外,可通過其他tricks判斷,最簡便的方法判斷PID為1的進程,如果該進程就是應(yīng)用進程則判斷是容器,而如果是init進程或者systemd進程,則不一定是容器,當(dāng)然不能排除是容器的情況,比如LXD/lXC實例的進程就為`/sbin/init`。
容器和虛擬機不一樣的是,容器和宿主機是共享內(nèi)核的,因此理論上容器內(nèi)部是沒有內(nèi)核文件的,除非掛載了宿主機的`/boot`目錄:
另外,我們知道容器是通過cgroup實現(xiàn)資源限制,每個容器都會放到一個cgroup組中,如果是Docker,則cgroup的名稱為docker-xxxx,其中xxxx為Docker容器的UUID。
而控制容器的資源,本質(zhì)就是控制運行在容器內(nèi)部的進程資源,因此我們可以通過查看容器內(nèi)部進程為1的cgroup名稱獲取線索。
如下是我通過Docker跑busybox的cgroup信息:
我們不僅可以知道這是Docker容器,還獲取了Docker容器的UUID為9ba...11。
根據(jù)如上的結(jié)論,判斷一個虛擬環(huán)境是否Docker的腳本為:
當(dāng)然如果僅僅判斷是否Docker容器,還能通過判斷是否存在.dockerenv文件區(qū)分是否Docker容器:
rkt容器類似,輸出結(jié)果如下:
如上的\x2d為-號:
因此判斷一個虛擬環(huán)境是否rkt的腳本為:
好奇AWS lambda的運行環(huán)境是什么,于是寫了個函數(shù)輸出/proc/1/cgroup,結(jié)果為:
猜測是一種叫sandbox的運行環(huán)境,估計也是一種容器。
判斷虛擬環(huán)境是否為容器環(huán)境相對比較復(fù)雜,目前沒有完美的方案,總結(jié)過程如下:
判斷是否可運行systemd-detect-virt -c命令,如果輸出為none則不是容器,否則可確定容器類型。
判斷PID 1如果為應(yīng)用本身,則該虛擬環(huán)境是容器,否則不能確定是否是容器。
判斷是否存在加載的內(nèi)核文件,如果不存在,則可判斷為容器,否則不能確定是否為容器。
判斷是否存在/.dockerenv文件,如果存在則為Docker容器,否則不能確定是否為容器。
讀取/proc/1/cgroup文件,判斷是否包含docker、rkt等關(guān)鍵字,如果包含,則說明為容器,否則不能確定是否為容器。
另外,需要特別注意的是,容器必須最先判斷,因為容器本身并沒有任何的硬件虛擬化,容器看到的硬件特性信息和宿主機看到的完全一樣,因此下面介紹的通過`lscpu`以及DMI信息判斷是否是虛擬機或者物理機,對容器并不適用。換句話說,不能因為`lscpu`的`Hypervisor vendor`值為`KVM`就說明一定是KVM虛擬機,因為它也有可能是容器。下文均假設(shè)已經(jīng)排除為容器的情況。