编者按:sysAK(system analyse kit),是龙蜥社区系统运维 SIG 下面的一个开源项目,聚集阿里百万服务器的多年运维经验,针对不同的运维需求提供了一系列工具,形成统一的产品进行服务。本文总结了实际工作中 IO 打满、IO util 高问题的处理经验,将它梳理成一套理论分析方法并形成 iofsstat 工具,集成到了sysAK 工具集里。以下将由作者带大家一道领略 iofsstat 的独特魅力。
文/李光水:系统运维SIG核心成员、 毛文安:系统运维SIG负责人。
一、 需求背景经常碰到这样一类问题:磁盘被打满,然后 io utils 高,触发业务监控告警,磁盘使用的是 HDD,出现问题的时候 iops 已经被打到几百、bps 也已经到了上百 MB/s,然后持续个几秒钟结束,然后过个几十秒又出现,这就造成了业务监控频繁告警。业务方会苦恼,磁盘被打满了到底是真实的业务需求量上来了呢?还是有什么野进程在占用 IO。比如之前碰到一例线上问题,平常都是正常的,突然某一天发现 IO 高了很多,然后客户想知道是谁把 IO 整高了,后面通过系统的各个命令组合 +ftrace 脚本统计,找到了贡献最高的进程来自于一个与业务不相关的容器,他会定时启动,谜一般的做大量的文件拷贝动作。
二、现有工具在定位问题这类问题的过程中,我们会通过系统的现有工具,定位具体的进程、文件或者容器,然后采取下一步措施解决问题,如停掉进程、容器并查看问题现象是否消失。一般地,如下几类工具会使用比较频繁:
基于内核 diskstats 衍生的工具,如 iostat、sar-类命令的IO统计功能、vmstat-d
——可以宏观的从整个磁盘角度去统计 io 信息,如统计整盘的 iops、bps
基于内核 proc/$pid/io 衍生的工具,如 pidstat -d
——可以统计到进程贡献的总体IO
基于 Taskstats 衍生的工具,如 iotop
——可以统计到进程贡献的总体 IO 以及贡献的 iowait
尽管系统为我们提供了比较丰富的工具,但总有这样一种感觉:使用已有的命令,尽管知道磁盘的 IO 高了,但不知道是哪个进程贡献的;知道系统里面的某个进程贡献的 IO 高了,但又不确定这里有多少 IO 是被我关心的磁盘给消费的,也不知道这些 IO 都是在操作什么文件,总觉得哪哪都差一点儿。所以总结下来,得到如下几点诉求:
● 在磁盘 IO 被打满的情况下,希望观察是哪个进程贡献了比较多的 IO
● 系统上统计到某个进程贡献了大量的 IO,希望观察到这些 IO 最终是被哪个磁盘给消费,或者这些 IO 是在访问哪个文件,如果这个进程是来自某个容器,希望依然可以获取访问的文件以及此进程所在的容器
而本文所介绍的 iofsstat,实现了从进程角度来统计 IO 信息、文件读写信息,满足了之前提到的几个诉求,可以应用到 io utils 高、io 打满类问题的定位中。
三、iofsstat 功能介绍 3.1 统计指定磁盘的进程文件 IO主要统计某进程对某文件贡献了多少 IO,对用户呈现的指标为进程角度+磁盘角度,磁盘角度这个毋庸置疑是为了能够看到磁盘的全貌,进程角度,是更细粒度的,可以看到对应的在这个时刻,各进程的贡献情况(PS:这里并非进程贡献的总和要等于磁盘整体的统计,因为统计的原理不一致,这里可以更多的关注两个角度之间的关联性,如在某一时刻,磁盘统计到高了,这个时候可以看到对应的这个时刻的各个进程的贡献程度),各指标如下:
进程角度:
comm:进程名、pid:进程id、cnt_rd:读文件次数、bw_rd:读文件"带宽"、cnt_wr:写文件次数、bw_wr :写文件"带宽"、inode:文件inode编号、filepath:文件路径,当在一次采集周期内由于进程访问文件很快结束情况下,获取不到文件名则为"-"。
如进程来自某个容器,在文件名后缀会显示 [containterId:xxxxxx]
磁盘角度:
xxx-stat:r_iops:磁盘总的读 iops、xxx-stat:w_iops:磁盘总的写 iops、xxx-stat:r_bps:磁盘总的读 bps、xxx-stat:w_bps:磁盘总的写 bps、xxx-stat:wait:磁盘平均 io 延迟、xxx-stat:r_wait:磁盘平均读 io 延迟、xxx-stat:w_wait:磁盘平均写 io 延迟、xxx-stat:util%:磁盘 io utils
sysak iofsstat -d vdb1 --fs 1 #间隔1秒统计一次vdb磁盘上的进程读写文件情况2022/01/19 14:13:48vda-stat: r_iops w_iops r_bps w_bps wait r_wait w_wait util% 0.00 98.00 0.00 91.5MB/s 946.34 0.00 946.34 93.20comm pid cnt_rd bw_rd cnt_wr bw_wr inode filepathdd 55937 0 0 1096 137.0MB/s 9226 /home/data/tfatsf...
显示结果按照 bw_rd 与 bw_wr 的和做降序排列,如输出结果较多想只看某进程情况下,可以使用 -p PID 只查看指定进程。
此功能基于 surftrace 开发,surftrace 是在 ftrace 基础上封装的一系列工具集,用于 trace 内核信息,当前发行版主要包含 surftrace、surfGuide 两大工具,后期还将包含 pylcc(python libbpf compile collections)。更多详情参考可点击文末 surftrace 项目代码链接查看。
3.1.1 进程、磁盘、文件信息因为复杂的 IO 软件栈,从用户态到落盘,IO 经历了不同的数据结构上的变化,同时经过 fs 和 block 之后,IO 的生产者也可能发生变化,因此在现有的条件下,无法做到不实现内核代码就可以直接获取到这些信息,我们所关注的信息仍需要在内核中解析某些数据结构体外加推演来获取,像获取文件名、IO 大小、进程信息等,比如我们通过在 block 层解析每个 io request 就可以获取到相当饱满的信息:
通过这种方式,首先可以捕捉到所有类别的 IO,也肯定可以获取 io 对应的文件信息,但是在 buffer io 情况下,需要在内核大动干戈的去轮询所有的 task 的 files strcut 反推得到实际写这个文件的进程,而且一旦进程已经关闭了这个文件,将会推导不出进程,因此不可取,另外应尽可能的避开内核 ko,避免后续使用上受限于内核版本问题;因此考虑 fs 层是否存在可用且稳定不会变化的 tracepoint。
实际上满足功能的需求,只需要获取进程信息、IO 大小、dev 编号、文件 inode 编号就够了,而在文件系统层没有现成的符合这些需求的 tracepoint,因此考虑通过 kprobe_events 去 hook 特定函数;kprobe_events 又绕不开因为不同内核版本,数据结构会不一致,造成输入到 kprobe_events 的表达式不一致的问题,幸运的是因为 surftrace 已经帮我们解决了这个问题,我们只需要关注 kprobe 之后数据的解析,而无需关注表达式的变化。
3.1.2 如何获取文件路径通过 3.1.1 已经获取文件 inode 编号,通过 find -inum 指定磁盘的挂载目录,或者通过 debugfs 方式,获取到文件名,但这种方式不仅耗时间也可能会耗 io;因为已经获取到了进程信息,接下来可以在 /proc/$pid/fd 过滤出来属于指定磁盘下的文件,然后比对文件的 inode 即可获取文件名,简单高效,但缺点是当比对文件过程中文件访问结束被 close 掉了,就获取不到文件名了,但这种情况一般在有问题的情况下出现概率也很低(一般关注的 IO 打满问题基本持续时间也都是秒级别)。
3.2 统计指定磁盘的进程IO贡献主要统计某进程对某磁盘贡献了多少 IO,对用户呈现的指标为进程角度+磁盘角度,磁盘角度这个毋庸置疑是为了能够看到磁盘的全貌,进程角度,是更细粒度的,可以看到对应的在这个时刻,各进程的贡献情况(PS:这里并非进程贡献的总和要等于磁盘整体的统计,因为统计的原理不一致,这里可以更多的关注两个角度之间的关联性,如在某一时刻,磁盘统计到高了,这个时候可以看到对应的这个时刻的各个进程的贡献程度),各指标如下:
进程角度:
comm:进程名、pid:进程 id、iops_rd:进程贡献的读 iops、bps_rd :进程贡献的读bps、iops_wr:进程贡献的写iops、bps_wr :进程贡献的写bps。
磁盘角度:
同 3.1 节磁盘角度。
sysak iofsstat -d vdb 1 #间隔1秒统计一次vdb磁盘上的进程io贡献情况2022/01/19 12:04:38vda-stat: r_iops w_iops r_bps w_bps wait r_wait w_wait util% 0.00 118.00 0.00 104.2MB/s 976.82 0.00 976.82 95.60comm pid iops_rd bps_rd iops_wr bps_wr[dd] 98675 1 4.0KB/s 259 32.4MB/s[kworker/u12:0] 91022 1 4.0KB/s 198 167.5MB/s[jbd2/vdb1-8] 19510 0 0 1 4.0KB/s...
显示结果按照 iops_rd 与 iops_wr 的和做降序排列,如输出结果较多想只看某进程情况下,可以使用 -p PID 只查看指定进程,假设看到的是 kworker 类进程贡献的 io 最高,可以进一步通过 3.1 节功能从 fs 角度来查看是哪个进程、哪个文件的 IO 最多。
3.2.1 实现原理此功能基于 block 的 tracepoint,可获取到指定磁盘的来自各进程的 IO 详细信息,然后统计每个进程的 iops、bps 贡献。
四、性能开销iofsstat 本质上是基于 ftrace 的实现,不会引发宕机,可以放心使用,由于使用的 ftrace 基本内核版本稳定,因此对内核版本依赖性也低,性能开销方面,经过统计纯 IO 统计功能(3.2 节)开销单核 1% 以下,文件 IO 统计功能(3.1节)稍微高一点,0.x%~3.x%,还在优化。
五、代码开源iofsstat 代码将在 3 月中旬开源,敬请期待。
Gitee sysak 代码仓链接:https://gitee.com/anolis/sysak
surftrace 项目代码链接:https://github.com/aliyun/surftrace
系统运维 SIG 地址:https://openanolis.cn/sig/sysom
—— 完 ——
加入龙蜥社群
加入微信群:添加社区助理-龙蜥社区小龙(微信:openanolis_assis),备注【龙蜥】与你同在;加入钉钉群:扫描下方钉钉群二维码。欢迎开发者/用户加入龙蜥社区(OpenAnolis)交流,共同推进龙蜥社区的发展,一起打造一个活跃的、健康的开源操作系统生态!
关于龙蜥社区
龙蜥社区(OpenAnolis)是由企事业单位、高等院校、科研单位、非营利性组织、个人等在自愿、平等、开源、协作的基础上组成的非盈利性开源社区。龙蜥社区成立于 2020 年 9 月,旨在构建一个开源、中立、开放的Linux 上游发行版社区及创新平台。
龙蜥社区成立的短期目标是开发龙蜥操作系统(Anolis OS)作为 CentOS 停服后的应对方案,构建一个兼容国际 Linux 主流厂商的社区发行版。中长期目标是探索打造一个面向未来的操作系统,建立统一的开源操作系统生态,孵化创新开源项目,繁荣开源生态。
目前,龙蜥OS 8.4已发布,支持 X86_64 、Arm64、LoongArch 架构,完善适配飞腾、海光、兆芯、鲲鹏、龙芯等芯片,并提供全栈国密支持。
欢迎下载:
https://openanolis.cn/download
加入我们,一起打造面向未来的开源操作系统!
https://openanolis.cn