目前市場上的虛擬化技術種類不少,例如moby(docker)、LXC、RKT等等。在帶來方便應用部署和資源充分利用的好處的同時,如何監控相應Container及其內部應用進程成爲運維人員不可避免遇到的新狀況。UAV.Container從虛擬化技術的基礎原理和Linux操做系統的內核特性出發,獲得Container容器和內部進程的各維度監控數據,使不管是虛擬機或物理機運維人員,仍是業務運維人員角度,都能獲得合適的監控維度。node
虛擬化技術從基礎原理上主要是cgroups、namespace和file system的應用,而操做系統做爲cgroup和namespace根節點,不管在container裏啓動何種應用,從內核角度上來講,確定在操做系統有其必定的特徵和表現形式。咱們須要作的就是對這些特徵作加工處理,以獲得相應的監控數據。linux
下面咱們以docker技術舉例,其餘虛擬化技術相似。docker
Container ID是一個Container的惟一標識。從容器監控的角度咱們須要能獲得該進程在哪一個Container裏運行。在操做系統層面,進程的cgroup的掛載狀況就能有所體現。如圖所示,咱們在一個ID爲3411554ff684的Container內部跑一個Tomcat進程。網絡
因爲Container的pid namespace是操做系統的pid namespace的子namespace,那麼該進程在操做系統級也應該有相應的pid,用docker top命令驗證一下:運維
該容器內進程在宿主機上的進程號爲1848。接下來進入/proc/1848/cgroup下看看該進程的cgroup掛載狀況electron
從cgroup文件裏清楚的顯示了實現了該容器的虛擬化技術、Container ID和此container的資源掛載路徑,對比一下這裏面顯示的Container ID,和建立Container時的ID徹底相同。這也驗證了經過掃描宿主機進程的cgroup信息能夠得到Container ID。這樣就將pid和Container ID作了關聯。socket
雖然cgroup管控了該cgroup下全部進程的CPU使用狀況,但從操做系統的角度上,不論進程是否隸屬於某個子cgroup下,仍然是共用宿主機的CPU。因此監控宿主機上該進程的CPU就能獲得進程的CPU監控指標。tcp
Linux上經常使用的CPU監控命令是top。top對CPU監控的原理是在time1時刻獲取CPU從啓動時的累計總時間countAll1和busy總時間countBusy1,再到time2時刻獲取CPU總時間countAll2和busy總時間countBusy2,最後用busy的時間差值減去總時間的差值獲得了在time1到time2這個時間段內機器CPU的佔用狀況。也就是:ide
CPU佔用率(%) = (countBusy2 - countBusy1)/(countAll2 - countAll1) * 100spa
進程同理,在兩個時刻分別獲得每一個進程的busy總時間countProcBusy1和countProcBusy2,則獲得進程CPU佔用率:
進程CPU佔用率(%) = (countProcBusy2 - countProcBusy1)/(countProcAll2 - countProcAll1)*100
宿主機從啓動開始的CPU總時間片能夠從/proc/stat下獲取:
第一行是總的CPU使用狀況,具體參數的意思:
因此,選擇當前爲time1,3秒後爲time2,countAll = user + nice + system + idle + iowait + irq + softirq + stealstolean + guest + guest_nice。countBusy爲countAll減去idle的值,這樣上面第一個公式的全部須要的值就齊了,能夠直接計算。
第二行、第三行是每一個邏輯CPU的使用狀況,這裏記下有兩個邏輯CPU,CPU的邏輯核數與CPU顯示模式irix和solaris有關。
接下來是countProcBusy的計算,進程的CPU時間片位於/proc/$pid/stat下,如圖所示:
這個文件裏面體現了不少進程的相關信息。其中第1四、1五、1六、17個參數與CPU有關。
因此,countProcBusy = utime + stime + cutime + cstime,該值包括其全部線程的cpu時間。而countProcAll2-countProcAll1=3s,經過兩個時刻的countProcBusy和countProcAll,進程CPU的佔用率就能獲得了。
其中須要注意的問題有兩點:
1).jiffies實際上指的是內核時鐘使用的節拍總數,因此這裏的jiffies須要換算成秒才能應用上面的除法公式。
2).剛剛咱們談到CPU顯示模式irix和solaris,簡單來講irix模式就是機器有N個邏輯CPU,CPU顯示上限就是N*100%,solaris模式就是無論機器有多少邏輯CPU,CPU顯示上限就是100%,而/proc/$pid/stat顯示的是計算了全部邏輯CPU時間的,因此兩種顯示方式意味着計算方法稍有差別,solaris模式的結果須要在上面進程CPU佔用率公式基礎之上除以邏輯核數。
進程內存的監控有兩個維度的數據:一是物理佔用內存大小,二是進程內存佔用百分比的大小。
進程內存佔用率(%) = 進程物理內存佔用大小 / 宿主機總內存大小 * 100
與CPU相似,/proc/$pid/status文件記錄了進程物理內存使用狀況,其中VmRSS即爲該進程目前所佔實際物理內存的大小。
/proc/meminfo文件下記錄了機器內存佔用狀況,這個文件很長,截取其中的一部分展現一下,MemTotal就是宿主機總內存大小:
這樣,這個進程的物理內存佔用和機器總內存就都獲得了,相應的進程內存的佔用率也就獲得了。
磁盤IO獲取也很簡單,/proc/$pid/io已經幫咱們把這個進程的io狀況記錄下來了,可是與CPU相似,io文件裏存的也是該進程從啓動到如今的io總量,那麼:
磁盤I/O(bytes/秒) = (time2時刻I/O – time1時刻I/O) / (time2 – time1)
其中的read_bytes和write_bytes分別爲該進程從啓動到目前爲止的讀取字節數和寫入字節數,分別取2個時刻的值,根據上面的公式,就獲得了該進程的磁盤IO。
因爲Network Namespace對網絡作了隔離,因此若是進程在Container內部運行,該進程的端口信息也應該是進程自己監聽的端口號,而不是真正實際對外的端口,而Container內外端口的映射機制是由應用的虛擬化技術自己控制的,這就避免不了與實現容器的虛擬化技術打交道了,那麼問題就轉化成獲取容器內進程自己監聽的端口了。
/proc/$pid/net/tcp(tcp6,udp,udp6)就對端口號和鏈接數作了相應的歷史記錄。這些文件格式都相似,以tcp6舉例
解釋幾個關鍵的key:
由於st = 0A表明listen,因此從其中挑選出st = 0A的數據,取出對應的inode號,這裏這個inode號是socket號,則問題轉換爲了這個進程是否還存在這個socket號表明的socket。在/proc/$pid/fd下有該進程全部的fd(file descriptor),截取一段舉個例子。
每一個文件描述符後面的軟鏈實際上就是打開的文件,以socket開頭的就是這個進程打開的socket,在中括號中間的部分就是socket號。拿着這個socket號和上面tcp6裏得到的inode號作一個匹配,若是對應上,那麼tcp6裏的st = 0A的端口就是這個進程監聽的。至於容器內外端口的映射,這就須要根據應用的虛擬化技術的映射方法來獲取了。鏈接數計算與端口掃描是同理的,區別只在於須要對st = 01(establish)進行掃描計數累加。
1. 上面的方法將容器內全部進程的CPU、內存、磁盤IO、端口號和鏈接數都拿到了。根據Container ID就能夠對不一樣的Container的對應數據作加和,就得到了Container級的監控數據。
2. 在容器內的進程是經過在操做系統級別反映出的pid和Container ID的對應關係來關聯的。這樣就能夠經過讀取/proc下的文件來獲取監控數據。
參考文檔:
http://elixir.free-electrons.com/linux/v3.10/ident
來源:宜信技術學院 做者:周新宇