iostat 分析
2024-04-29
iostat 分析
需求
公司的磁盘监控项,需要 3 个监控指标分别为:io使用率、磁盘平均队列长度、磁盘读写平均延迟。通常的方法是查找有没有开源的对应 exporter 与计算表达式,但是这里的做法是提供了一个思路,让人换一种思考方式。
我们知道 iostat 命令可以全方面查看到系统磁盘状态,这里就是分析一下 sysstat 项目下的 iostat。分析它怎么实现的:io使用率、磁盘平均队列长度、磁盘读写平均延迟。
iostat 命令
使用 iostat -x 5 显示扩展状态,这里显示了磁盘状态,也显示了cpu状态,当然cpu状态我们不用关心,主要关系磁盘的几个指标:
标示 | 说明 |
---|---|
Device | 监测设备名称 |
rrqm/s | 每秒需要读取需求的数量 |
wrqm/s | 每秒需要写入需求的数量 |
r/s | 每秒实际读取需求的数量 |
w/s | 每秒实际写入需求的数量 |
rkB/s | 每秒实际读取的大小,单位为KB |
wkB/s | 每秒实际写入的大小,单位为KB |
avgrq-sz | 需求的平均大小区段 |
avgqu-sz | 每需求的平均队列长度 |
await | 等待I/O平均的时间 |
r_await | 读等待I/O平均的时间 |
w_await | 写等待I/O平均的时间 |
svctm | I/O需求完成的平均时间 |
%util | 被I/O需求消耗的CPU百分比 |
在 iostat 中,它已经计算好了我们所需要的内容,比如 avgqu-sz,所以通常情况下我们只需要使用 iostat 命令获取指标就可以了,但是作为监控程序来讲最好的方式是不需要依赖其他命令执行,所以这里分析一下 avgqu-sz 实现原理。
[root@deploy network-scripts]# iostat -x 5
Linux 3.10.0-1160.el7.x86_64 (deploy) 04/29/2024 _x86_64_ (2 CPU)
avg-cpu: %user %nice %system %iowait %steal %idle
0.58 0.00 1.27 0.05 0.00 98.10
Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await r_await w_await svctm %util
scd0 0.00 0.00 0.07 0.00 4.11 0.00 114.22 0.00 1.33 1.33 0.00 0.94 0.01
sda 0.02 0.12 27.60 1.33 1538.30 40.32 109.14 0.01 0.25 0.21 1.08 0.15 0.44
dm-0 0.00 0.00 19.52 1.44 1421.49 32.13 138.74 0.01 0.34 0.27 1.17 0.19 0.40
avg-cpu: %user %nice %system %iowait %steal %idle
0.10 0.00 0.20 0.00 0.00 99.70
Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await r_await w_await svctm %util
scd0 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
sda 0.00 0.00 0.20 0.00 1.60 0.00 16.00 0.00 1.00 1.00 0.00 1.00 0.02
dm-0 0.00 0.00 0.20 0.00 1.60 0.00 16.00 0.00 1.00 1.00 0.00 1.00 0.02
avgqu-sz
sysstat项目 包含了非常多的系统方面的观测数据,这里 iostat 就是其中一个功能
# ./iostat.c:1235
# 在 iostat.c 中找到了它的计算表达式
# 具体解释为:一个三元运算符,用于比较 ioi 和 ioj 的值。如果 ioi 小于 ioj,则为0.0;否则,计算 S_VALUE(ioj->rq_ticks, ioi->rq_ticks, itv) / 1000.0 的值。
/* aqu-sz */
cprintf_f(NO_UNIT, FALSE, 1, 7, 2,
ioi->rq_ticks < ioj->rq_ticks ? 0.0 :
S_VALUE(ioj->rq_ticks, ioi->rq_ticks, itv) / 1000.0);
...
# 接下来该找一下 S_VALUE、rq_ticks、itv 分别是什么
# ./iostat.c:816
# 这里发现其实是读取的系统的某个文件,然后对每一行的数据进行分解并赋值
# 这里找到了 rq_ticks
# 那么读取的文件是根据上面注释提示的 /proc/diskstats
/*
***************************************************************************
* Read stats from the diskstats file. Only used when "-p ALL" has been
* entered on the command line.
*
* IN:
* @curr Index in array for current sample statistics.
* @diskstats Path to diskstats file (e.g. "/proc/diskstats").
***************************************************************************
*/
if ((fp = fopen(diskstats, "r")) == NULL)
return;
while (fgets(line, sizeof(line), fp) != NULL) {
memset(&sdev, 0, sizeof(struct io_stats));
/* major minor name rio rmerge rsect ruse wio wmerge wsect wuse running use aveq dcio dcmerge dcsect dcuse flio fltm */
i = sscanf(line, "%u %u %s %lu %lu %lu %lu %lu %lu %lu %u %u %u %u %lu %lu %lu %u %lu %u",
&major, &minor, dev_name,
&rd_ios, &rd_merges_or_rd_sec, &rd_sec_or_wr_ios, &rd_ticks_or_wr_sec,
&wr_ios, &wr_merges, &wr_sec, &wr_ticks, &ios_pgr, &tot_ticks, &rq_ticks,
&dc_ios, &dc_merges, &dc_sec, &dc_ticks,
&fl_ios, &fl_ticks);
if (i >= 14) {
sdev.rd_ios = rd_ios;
sdev.rd_merges = rd_merges_or_rd_sec;
sdev.rd_sectors = rd_sec_or_wr_ios;
sdev.rd_ticks = (unsigned int) rd_ticks_or_wr_sec;
sdev.wr_ios = wr_ios;
sdev.wr_merges = wr_merges;
sdev.wr_sectors = wr_sec;
sdev.wr_ticks = wr_ticks;
sdev.ios_pgr = ios_pgr;
sdev.tot_ticks = tot_ticks;
sdev.rq_ticks = rq_ticks;
...
# ./common.c:776
# 这里计算 itv 时间,通过翻译大致知道 itv 当前时间 - 之前时间,那么就应该是一个时间间隔
# 然后这里又说了值 1/100th of a second ,1秒的 100 分之 1,那么间隔默认应该是 10ms 为单位
/*
***************************************************************************
* Compute time interval.
*
* IN:
* @prev_uptime Previous uptime value (in jiffies or 1/100th of a second).
* @curr_uptime Current uptime value (in jiffies or 1/100th of a second).
*
* RETURNS:
* Interval of time in jiffies or 1/100th of a second.
***************************************************************************
*/
unsigned long long get_interval(unsigned long long prev_uptime,
unsigned long long curr_uptime)
{
unsigned long long itv;
/* prev_time=0 when displaying stats since system startup */
itv = curr_uptime - prev_uptime;
if (!itv) { /* Paranoia checking */
itv = 1;
}
return itv;
}
# ./common.h:163
# 这里找到了计算函数
/* With S_VALUE macro, the interval of time (@p) is given in 1/100th of a second */
#define S_VALUE(m,n,p) (((double) ((n) - (m))) / (p) * 100)
# 如果我选择 5 秒采集一次文件的方法,那么根据多种分析总结出来表达式:(现在文件内容值 - 之前文件内容值) / (采集间隔 5 * 内部时间 10 毫秒) * 100
总结
简单的分析知道,iostat 信息依赖于文件,只是如何取的问题,其他磁盘监控指标,也可以通过阅读底层代码转成自己的逻辑实现。