Docker資源監控
寫在前面
最近在研究docker集群(kubernetes)的監控,為了徹底弄清楚,也是看了一點源碼。這里分享一下我學到的東西。
docker api: stats
首先是docker的api,stats的具體使用場景如:
http://$dockerip:2375/containers/$containerid/stats
可以獲取docker機器上某一個容器的狀態,該請求的response會持續的寫響應報文回來(每秒一次)
http://$dockerip:2375/containers/$containerid/stats?stream=false
參數stream默認是true,設為false后,response只寫一次。
docker中該api對應的操作,就相當于docker stats $CID這條命令,它調用到了ContainerStats()方法(位于docker項目目錄的/daemon/stats.go第19行),函數中啟動了一個收集器:
daemon.SubscribeToContainerStats(name)
收集器定時寫數據到update變量中。然后啟動一個協程讀數據,獲取數據的途徑包括:
update := v.(*execdriver.ResourceStats)
和
nwStats, err := daemon.getNetworkStats(name)
可以看到網絡狀態是另外讀的,而cpu,內存狀態在哪讀呢?我們一步步跳轉,看到在這里:/daemon/execdriver/driver_linux.go 112行:
func Stats(containerDir string, containerMemoryLimit int64, machineMemory int64) (*ResourceStats, error)
在這個函數里我們看得到,其實所謂的獲取容器狀態,就是讀文件而已。我們舉一個已經在docker運行的容器來說:
cat /run/docker/execdriver/native/$containerID/state.json
這里面記錄了一個cgroup_paths字段,他的值是一個路徑,通過cstats, err := mgr.GetStats()程序才真正拿到了監控數據,如cpu的狀態信息,存儲在一個如下的路徑中:
cd /sys/fs/cgroup/cpu,cpuacct/system.slice/docker-9b479ceaf3bef1caee2c37dfdc982a968144700a2c42991b238b938788a8a91f.scope
又比如內存的大致信息存在:
cat /sys/fs/cgroup/memory/system.slice/docker-9b479ceaf3bef1caee2c37dfdc982a968144700a2c42991b238b938788a8a91f.scope/memory.stat
具體里面放了什么數據,大家就自己看咯。
還剩下一個數據,也是我們討論的重點:網絡IO。
我們回到/daemon/stats.go:
看看函數getNetworkStats(name string):
每個docker容器創建后都會又docker創建一個網卡,網卡名以veth開頭,后面是一串隨機生成的十六進制碼。
我們只要找到該網卡,然后,像上文的cpu,內存狀態獲取的方法一樣,去找文件就行了。
然而這個網卡名似乎是存在內存中,至少我沒有看到docker將這個網卡名存到別的地方。
代碼中:
nw, err := daemon.netController.NetworkByID(c.NetworkSettings.NetworkID)
可以看出獲取了網卡(network),爾后有一個Statistics()方法,我們繼續跟蹤會發現,docker調用了一個組件包:
github.com/docker/libcontainer
其中有statistics方法,并執行了一個cat的命令(該組件包的/sandbox/interface_linux.go 第174行):
data, err := exec.Command("cat", netStatsFile).Output()
是的,又是讀文件。
這次讀的文件在:/sys/class/net/目錄下,
我們進入該目錄可以看到有許多子目錄,其中就包括了docker啟動容器時生成的網卡。
個人猜測:docker在內存中保存了容器到網卡的映射關系。通過這個映射關系可以找到網卡的統計數據(數據由內核生成,記錄在機器的文件中)
我們可以看到如下的數據:

將這些數據統一起來,就是docker stats 這條命令干的事。
kubernetes如何調用上述的監控功能
kubernetes的監控采用了cAdvisor組件。因為kubernetes中記錄了容器的信息(但是沒有記錄容器-網卡的映射關系),所以運行在節點上的cAdvisor不需要通過docker stats去獲取容器的cpu和內存使用數據。而網絡IO數據呢?
我們知道k8s部署運行一個容器是會先生成一個pause容器。是的,網絡IO都記錄在pause容器中。這里大家可以在自己的k8s環境中驗證。
所以只要我們獲取某容器對應的pause容器的containerID,我們就可以用如上的方式去抓到網絡IO。
但是cAdvisor里并沒有這么做。為什么?因為cAdvisor并不只是給k8s用的啊魂淡。。。。
所以如果在使用k8s集群,想要加入網絡IO監控功能的時候,可以參考本文,從中獲取一些靈感哦~