浅谈k8s+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并不是为k8s专门设计的,不会特地在获取网络IO时去遍历查找容器对应的pause容器。所以当前的cAdvisor没有办法获取容器的网络IO。
所以如果在使用k8s集群,想要加入网络IO监控功能的时候,可以从k8s自带的cAdvisor入手。
cAdvisor
上面讲到cAdvisor,那么cAdvisor是如何获取网络的IO的呢?
首先,在k8s(1.0.6版本)的/pkg/kubelet/cadvisor/cadvisor_linux.go中51行,New(port int)方法,这是kubelet调用cAdvisor的入口。实例化一个cAdvisorClient,并执行他的Start()方法,我们可以进入cAdvisor项目中找到该方法(github.com/google/cadvisor/manager/manager.go 195行)。
我们看到有一句
err := docker.Register(self, self.fsInfo)
先mark之。继续看:
glog.Infof("Starting recovery of all containers") err = self.detectSubcontainers("/")
这里程序程序检查所有在运行的容器,同内存中记录的容器作比较,有新的容器就新建一个相关的处理机制:一个Container的结构(有减少的,就删除),然后执行cont.Start() (github.com/google/cadvisor/manager/manager.go 766行)
持续追踪我们就可以知道每秒它收集一次监控信息,收集方法即
stats, statsErr := c.handler.GetStats()
(/cadvisor/manager/container.go 401行)
handler是一个接口。我们得知道当时是如何给他赋值的。这里不赘述了,我们直接找关键的方法:
func (self *dockerContainerHandler) GetStats() (*info.ContainerStats, error)
(cadvisor/container/docker/handler.go 第305行)
它又调用了:
func GetStats(cgroupManager cgroups.Manager, networkInterfaces []string) (*info.ContainerStats, error) (cadvisor/container/libcontainer/helpers.go 第77行)
这里就是真正获取监控信息的地方,它引入了libcontainer包中的同名方法,
最终是导入了"github.com/docker/libcontainer/cgroups"这个第三方包中的方法。是不是很熟悉?对就是上文中提到的docker stats获取网络IO用到的包。我们到这个包里找到函数:
(/libcontainer/cgroups/fs/apply_raw.go 第148行)
func (m *Manager) GetStats() (*cgroups.Stats, error) { m.mu.Lock() defer m.mu.Unlock() stats := cgroups.NewStats() for name, path := range m.Paths { //m.Paths中包括了cpu,memory,network等字段,根据这些字段找到相应的目录 sys, ok := subsystems[name] if !ok || !cgroups.PathExists(path) { continue } //读目录里的文件获取监控信息 if err := sys.GetStats(path, stats); err != nil { return nil, err } } return stats, nil }
我们可以看到libcontainer/cgroups/fs目录下有cpu.go,memory.go等文件,每一个文件中都有一个集成了GetStats()的结构,我们可以获取到任何数据。
我们再回到cadvisor项目中cadvisor/container/libcontainer/helpers.go 第77行,
往下看:
// TODO(rjnagal): Use networking stats directly from libcontainer. stats.Network.Interfaces = make([]info.InterfaceStats, len(networkInterfaces)) for i := range networkInterfaces { interfaceStats, err := sysinfo.GetNetworkStats(networkInterfaces[i]) if err != nil { return stats, err } stats.Network.Interfaces[i] = interfaceStats }
这里官方还用了别的手段再去找network的数据,虽然不知道是处于什么理由,也不管这里是去找哪个系统文件看状态,但是这样依然是拿不到数据的。因为根本没有找到容器对应的网卡。这里找网卡的方法在cadvisor/container/docker/handler.go 第311行:
var networkInterfaces []string if len(config.Networks) > 0 { // ContainerStats only reports stat for one network device. // TODO(vmarmol): Handle multiple physical network devices. for _, n := range config.Networks { // Take the first non-loopback. if n.Type != "loopback" { networkInterfaces = []string{n.HostInterfaceName} break } } } stats, err := containerLibcontainer.GetStats(self.cgroupManager, networkInterfaces)
很显然这里是要改进的(在k8s的环境下)。
注:随着版本更新,新版本的cAdvisor采用github.com/opencontainers
包。详见:github.com/opencontainers/runc/libcontainer/container_linux.go line 151
:
另外,cadvisor获取网络监控数据的正确途径应该是:
监控pause容器,在其Pid对应的/proc/$pid/net/dev文件中即可得到容器内部网络监控数据
应用
说了这么多,其实给大家在实现k8s容器网络IO监控这个需求时提供一点想法:
1.从cadvisor入手,找到对应pause容器的containerID,那么就可以用libcontainer包去获取到数据(也即用docker stats的方式)。
2.从docker入手,创建容器时记录网卡信息,能否提供一个接口,根据容器名找到对应网卡名,那么cadvisor只要通过这个docker API得到网卡名,就可以自己读文件获取网络IO。
本文转自中文社区-浅谈k8s+docker 资源监控
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
从集群外访问k8s的pod 的几种方式--hostNetwork
前言 有5种方法可以让集群外访问运行在Kubernetes集群上的应用程序(pod)。接下来我们 详细讨论Kubernetes的hostNetwork,hostPort,NodePort,LoadBalancer和Ingress功能。本章内容主要解读一下hostNetwork。 hostNetwork demo hostNetwork设置适用于Kubernetes pod。当pod配置为hostNetwork:true时,在此类pod中运行的应用程序可以直接查看启动pod的主机的网络接口。配置为侦听所有网络接口的应用程序,又可以在主机的所有网络接口上访问。以下是使用主机网络的pod的示例定义: apiVersion: v1 kind: Pod metadata: name: influxdb spec: hostNetwork: true containers: - name: influxdb image: influxdb 您可以使用以下命令启动pod: $ kubectl create -f influxdb-hostnetwork.yml 您可以检查InfluxDB应用程序是否...
- 下一篇
k8s与数据分析--利用redash做自助数据分析
前言 在之前文章中,一直讲prometheus的metrics以及apm的指标的重要性,多侧重于收据的收集和存储。如果不对这些数据进行数据分析,那么就没有收集的意义了。通过数据分析和挖掘,让数据产生价值。一直以来我认为devops必须是一 个闭环,即apm,日志,监控着三大系统的数据,必须经过分析对dev和ops有价值。 数据可视化是大数据的『最后一公里』,做好可视化是对于数据分析是重要的。 今天,主要介绍redash这款数据分析的利器。 redash简介 redash是一款开源的BI工具,提供了基于web的数据库查询和数据可视化功能。 支持 SQL, NoSQL, Big Data and API data等20几种常见的数据源: 基本上满足了大多数的场景。相比 superset,除了上手简单,支持influxdb等时序数据库。这点对于监控数据分析很有优势。 sql友好的SQL editor,更加高效的编写复杂的sql 随时写,随时查,实时看到查询的效果 支持丰富的可视化展示形式 Boxplot Chart - Line, Bar, Area, Pie, Scatter Cohort...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS7安装Docker,走上虚拟化容器引擎之路
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- Docker安装Oracle12C,快速搭建Oracle学习环境
- CentOS7设置SWAP分区,小内存服务器的救世主
- SpringBoot2全家桶,快速入门学习开发网站教程
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- CentOS8安装Docker,最新的服务器搭配容器使用
- CentOS关闭SELinux安全模块