Docker资源监控

f453 8年前

 

写在前面

最近在研究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资源监控

将这些数据统一起来,就是docker stats 这条命令干的事。

kubernetes如何调用上述的监控功能

kubernetes的监控采用了cAdvisor组件。因为kubernetes中记录了容器的信息(但是没有记录容器-网卡的映射关系),所以运行在节点上的cAdvisor不需要通过docker stats去获取容器的cpu和内存使用数据。而网络IO数据呢?

我们知道k8s部署运行一个容器是会先生成一个pause容器。是的,网络IO都记录在pause容器中。这里大家可以在自己的k8s环境中验证。

所以只要我们获取某容器对应的pause容器的containerID,我们就可以用如上的方式去抓到网络IO。

但是cAdvisor里并没有这么做。为什么?因为cAdvisor并不只是给k8s用的啊魂淡。。。。

所以如果在使用k8s集群,想要加入网络IO监控功能的时候,可以参考本文,从中获取一些灵感哦~