写点什么

Container 及其内部进程监控剖析

  • 2020-02-13
  • 本文字数:2917 字

    阅读完需:约 10 分钟

Container及其内部进程监控剖析

目前市场上的虚拟化技术种类很多,例如 moby(docker)、LXC、RKT 等等。在带来方便应用部署和资源充分利用的好处的同时,如何监控相应 Container 及其内部应用进程成为运维人员不可避免遇到的新情况。UAV.Container 从虚拟化技术的基础原理和 Linux 操作系统的内核特性出发,得到 Container 容器和内部进程的各维度监控数据,使无论是虚拟机或物理机运维人员,还是业务运维人员角度,都能得到合适的监控维度。


虚拟化技术从基础原理上主要是 cgroups、namespace 和 file system 的应用,而操作系统作为 cgroup 和 namespace 根节点,无论在 container 里启动何种应用,从内核角度上来说,肯定在操作系统有其一定的特征和表现形式。我们需要做的就是对这些特征做加工处理,以得到相应的监控数据。


下面我们以 docker 技术举例,其他虚拟化技术类似。

1. Container ID

Container ID 是一个 Container 的唯一标识。从容器监控的角度我们需要能得到该进程在哪个 Container 里运行。在操作系统层面,进程的 cgroup 的挂载情况就能有所体现。如图所示,我们在一个 ID 为 3411554ff684 的 Container 内部跑一个 Tomcat 进程。



由于 Container 的 pid namespace 是操作系统的 pid namespace 的子 namespace,那么该进程在操作系统级也应该有相应的 pid,用 docker top 命令验证一下:



该容器内进程在宿主机上的进程号为 1848。接下来进入/proc/1848/cgroup 下看看该进程的 cgroup 挂载情况



从 cgroup 文件里清楚的显示了实现了该容器的虚拟化技术、Container ID 和此 container 的资源挂载路径,对比一下这里面显示的 Container ID,和创建 Container 时的 ID 完全相同。这也验证了通过扫描宿主机进程的 cgroup 信息可以获得 Container ID。这样就将 pid 和 Container ID 做了关联。

2. CPU

虽然 cgroup 管控了该 cgroup 下所有进程的 CPU 使用情况,但从操作系统的角度上,不论进程是否隶属于某个子 cgroup 下,仍然是共用宿主机的 CPU。所以监控宿主机上该进程的 CPU 就能得到进程的 CPU 监控指标。


Linux 上常用的 CPU 监控命令是 top。top 对 CPU 监控的原理是在 time1 时刻获取 CPU 从启动时的累计总时间 countAll1 和 busy 总时间 countBusy1,再到 time2 时刻获取 CPU 总时间 countAll2 和 busy 总时间 countBusy2,最后用 busy 的时间差值减去总时间的差值得到了在 time1 到 time2 这个时间段内机器 CPU 的占用情况。也就是:


CPU 占用率(%) = (countBusy2 - countBusy1)/(countAll2 - countAll1) * 100


进程同理,在两个时刻分别得到每个进程的 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 下,如图所示:



这个文件里面体现了很多进程的相关信息。其中第 14、15、16、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 占用率公式基础之上除以逻辑核数。

3. 内存

进程内存的监控有两个维度的数据:一是物理占用内存大小,二是进程内存占用百分比的大小。


进程内存占用率(%) = 进程物理内存占用大小 / 宿主机总内存大小 * 100


与 CPU 类似,/proc/$pid/status 文件记录了进程物理内存使用情况,其中 VmRSS 即为该进程目前所占实际物理内存的大小。



/proc/meminfo 文件下记录了机器内存占用情况,这个文件很长,截取其中的一部分展示一下,MemTotal 就是宿主机总内存大小:



这样,这个进程的物理内存占用和机器总内存就都得到了,相应的进程内存的占用率也就得到了。

4. 磁盘 IO

磁盘 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。

5. 端口号和连接数

由于 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://college.creditease.cn/detail/178


2020-02-13 21:471068

评论

发布
暂无评论
发现更多内容

聊聊Java中代码优化的30个小技巧

苏三说技术

ThreadLocal夺命11连问

苏三说技术

多线程

人人可参与开源活动正式上线,诚邀您来体验!

阿里巴巴云原生

阿里云 开源 云原生 龙蜥社区

Baklib:制作优秀的产品说明手册

Baklib

我们常用于猜数字游戏的二分查找算法怎么用python实现呢?

迷彩

算法 二分查找 7月月更 经典算法

伺服驱动器在机器人上的研究与应用

优必选科技

机器人

高效生成接口文档好方法

Xd

数据库 接口测试

当AI邂逅生命健康,华为云为他们搭建三座桥

脑极体

IPv4地址已经完全耗尽,互联网还能正常运转,NAT是最大功臣!

wljslmz

NAT ipv4 网络技术 7月月更

数据质量:数据治理的核心

奔向架构师

数据治理 7月月更

以寡治众各个击破,超大文件分片上传之构建基于Vue.js3.0+Ant-desgin+Tornado6纯异步IO高效写入服务

刘悦的技术博客

tornado 异步 数据分片 Vue 3 文件上传

怎样设计产品帮助中心?以下几点不可忽视

Baklib

【刷题记录】21. 合并两个有序链表

WangNing

7月月更

信息安全建设原则指导

穿过生命散发芬芳

7月月更 信息安全建设

一周活动速递|深入浅出第8期;Meetup成都站报名进行中

OceanBase 数据库

这次龙蜥展区玩的新花样,看看是谁的 DNA 动了?

OpenAnolis小助手

龙蜥社区 北京 开放原子全球开源峰会 7 月 27 日-29 日 分论坛

Bootstrap快捷开发【前端Bootstrap框架】

恒山其若陋兮

7月月更

聊聊sql优化的15个小技巧

苏三说技术

sql SQL优化

哪吒 D1-H 测试 microbench

贾献华

7月月更

git merge 不为人知的秘密

蛋先生DX

git merge 最长公共子序列 签约计划第三季 three-way 3-way

ORIGYN基金会正式启动$OGY Staking,引领新一轮生态利好

鳄鱼视界

ES6---4个非常好用的运算符(??、??=、?.、?:)

Java学术趴

7月日更

高并发下如何保证数据库和缓存双写一致性?

苏三说技术

MySQL redis

Docker 搭建 Redis Cluster集群

宁在春

redis Docker 集群 7月月更

动画曲线天天用,你能自己整一个吗?看完这篇你就会了!

岛上码农

flutter ios 前端 安卓开发 签约计划第三季

关爱一线防疫工作者,浩城嘉业携手高米店街道办事处共筑公益长城

联营汇聚

RTC 性能自动化工具在内存优化场景下的实践

字节跳动视频云技术团队

性能优化 RTC 内存优化

聊聊接口性能优化的11个小技巧

苏三说技术

性能优化

JS作用域与作用域链

程序员海军

7月月更

性能调试 -- Chrome Performance

空城机

chrome Performance 7月月更

【帮助中心】为您的客户提供自助服务的核心选项

Geek_da0866

Container及其内部进程监控剖析_行业深度_周新宇_InfoQ精选文章