最近在研究docker集群(kubernetes)的监控,为了彻底弄清楚,也是看了一点源码。这里分享一下我学到的东西。
首先是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的监控采用了cAdvisor组件。因为kubernetes中记录了容器的信息(但是没有记录容器-网卡的映射关系),所以运行在节点上的cAdvisor不需要通过docker stats去获取容器的cpu和内存使用数据。而网络IO数据呢?
我们知道k8s部署运行一个容器是会先生成一个pause容器。是的,网络IO都记录在pause容器中。这里大家可以在自己的k8s环境中验证。
所以只要我们获取某容器对应的pause容器的containerID,我们就可以用如上的方式去抓到网络IO。
但是cAdvisor里并没有这么做。为什么?因为cAdvisor并不只是给k8s用的啊魂淡。。。。
所以如果在使用k8s集群,想要加入网络IO监控功能的时候,可以参考本文,从中获取一些灵感哦~
下周再更新cadvisor是如何抓取监控数据的