php rrdtool,RRDtool 简介

2022-04-26 00:00:00 数据 操作 时间 记录 给出

更新 RRD 文件

一)前言

写了N多东东,总算到了 update 部分了。这里有必要比较一下 RRDtool 和 MRTG 在 update 方面的差别。

A)MRTG 可以通过 SNMP 协议直接访问 SNMP 对象,你只需要在 cfg 文件中的 Target 指定 OID ,MRTG 就可以自动替你取回数据。

例如Target[as1_eth0]: 2:n7css@172.17.64.11:::::1 ,表示使用 SNMP v1 协议访问 172.17.64.11 主机上 index 为 2 的那个接口,

默认是取 ifInOctets 和 ifOutOctets 这两个对象的值。RRDtool 则没有这个功能,只能你自己写脚本取数据。

B)MRTG 只支持 COUNTER 和 GAUGE 类型的 Target ;RRDtool 则还可以使用 DERIVE、ABSOLUTE、COMPUTE

C)由于上面的原因,MRTG 无法识别小数,负数。例如你给 MRTG 一个 -1 的值,它会解释为 1 ;这点可以通过 LOG 看出来。

小数也不行。例如 .72 (bc 的输出)会被识别为 72 ,而不是 0.72。

D)MRTG 每次 update 每次运行只更新一次,或者说只插入一行记录。但 RRDtool 可以在一个 updat操作中插入多个记录。

E)MRTG 一次要求2个值,RRDtool 则没有该方面的限制。

F)大的一个区别是, MRTG 在收到一个值后会自动得出 timestamp ,并记录在 log 的个字段;而 RRDtool 是需要你给出一个 timestamp ,

表示该数据是什么时间采集的。

二)update 操作的语法格式

rrdtool {update | updatev} filename

[--template|-t ds-name[:ds-name]...]

N|timestamp:value[:value...]

at-timestamp@value[:value...]

[timestamp:value[:value...] ...]

N 表示 now 的意思,会被 RRDtool 替换为当前的 timestamp ,也就是 date +%s 的结果。Timestamp 部分比较灵活,可以是数字形式,也可以是

AT-风格的时间(参考 at 命令的 manual),有点类似于自然语言的风格。

三)手工方式 update 数据库

我们先学习一下如何手工 update 数据库。Update 命令分成两部分 :

A)时间戳 (timestamp):表示该数据是在那个时间点采集的。Timestamp 的格式可以非常灵活 :

数字形式 :例如1164419418 ,表示 “六 11月 25 09:50:18 CST 2006”。通常用于手工插入的方式。

快捷方式 :N 。字母N 表示当前时间(Now)。如果是通过 crontab 的方式来运行 update 操作,这是实用的方式。

AT-风格 :所谓的 AT 风格的时间,可以参考 at 命令的 manual。例如 now、yesterday、now-1hour、now+5min 都是 AT风格的时间。

要注意的是,如果使用 AT风格的时间,则时间和个value之间使用 @ 分隔,而不是 “:”

B)数值部分 :一个 RRD 文件可以有多个 DS ,所以一次 update 可以给出多个 value 。多个 value 之间用 “:” 分隔。不过是不是所有的 DS 都必须

给出值呢?不一定。有时候你只想给出一部分 DS 的值,甚至有时候某些类型的 DS 是不允许赋值的,例如 COMPUTE 型的 DS 就是这样一个例子。

四)实际操作

实例一 :一个错误的例子

[iyunv@dns1 bob]# rrdtool update eth0.rrd 1163862980:1:2

ERROR: illegal attempt to update using time 0 when last update time is 1163862985 (minimum one second step)

[iyunv@dns1 bob]#

咦?为什么出错了呢?是语法错误吗?不是的,RRDtool 提示近一次更新是在1163862985 这个时候。也就是说,update 给出的时间戳必须大于该值。

不能等于或者小于该时刻。因为数据一旦插入到 RRA 中,就不允许再次修改。所以 update 会检查给出的时间戳是否大于后一次更新的时间戳,如果不是

则报错,不予执行。那如何才能知道近一次更新的时间戳呢?还记得前一篇“如何获取RRD文件的信息”中介绍的 last 和 info 命令吗?对了!就是它们。

执行一下看看是什么结果 ?

[iyunv@dns1 bob]# rrdtool last eth0.rrd

1163862985

[iyunv@dns1 bob]#

last 操作显示的时间戳和上面的报错信息给出的值一样。这个时间是

[iyunv@dns1 bob]# date -d '1970-01-01 1163862985 sec utc'

六 11月 18 23:16:25 CST 2006

[iyunv@dns1 bob]#

总之如果要 update 数据库,则 update 操作给出的时间戳必须晚于后一次 update 的时间。

实例二 :还是一个错误的例子

我们挑 23:16的下一个5分钟23:20作为时间戳吧。

[iyunv@dns1 bob]# date -d '2006-11-18 23:20' +%s

1163863200

[iyunv@dns1 bob]#

所以 update 命令为 :

[iyunv@dns1 bob]# rrdtool update eth0.rrd 1163863200:1

ERROR: expected 2 data source readings (got 1) from 1163863200:1:...

[iyunv@dns1 bob]#

还是不行?!!

仔细看错误信息,原来是我们给少了一个值。还记得 info 命令吗?这会儿它派上用场了。

[iyunv@dns1 bob]# rrdtool info eth0.rrd

filename = "eth0.rrd"

…..(省略)

last_update = 1163862985

ds[eth0_in].type = "COUNTER"

ds[eth0_out].type = "COUNTER"

原来是2个 DS ,怪不得 RRDtool 会报错了

实例三 :这次应该成功了吧?

[iyunv@dns1 bob]# rrdtool update eth0.rrd 1163863200:1:2

[iyunv@dns1 bob]#

这会倒是没有错误信息,那究竟数据是否别插入到 RRA 中了呢?

对于该问题,有两个方法,一个是通过 fetch 操作,从 RRA 中提取数据;但这个我们下一篇再讲。

还有一种方法就是用 updatev 操作来代替 update 。updatev 的 v 表示 verbose 的意思,现在就来看 updatev 的作用 :

实例四 :updatev 的好处

我们执行多个 update 操作

[iyunv@dns1 bob]# rrdtool last eth0.rrd

1163864400

[iyunv@dns1 bob]#

[iyunv@dns1 bob]# rrdtool update eth0.rrd 1163864700:3000:4000

[iyunv@dns1 bob]# rrdtool updatev eth0.rrd 1163865000:3300:4600

return_value = 0

[1163865000]RRA[AVERAGE][1]DS[eth0_in] = 1.0000000000e+00

[1163865000]RRA[AVERAGE][1]DS[eth0_out] = 2.0000000000e+00

可以看到 return value 是 0,这个 return value 你可以理解为 shell 编程中的 exit status 。updatev用 0 表示成功,-1 表示失败。

不过我们插入的值明明是 3300 和 4600 ,为什么出来的结果是 1和2 呢?

这是因为 eth0_in 和 eth0_out 都是 COUNTER 型的 DS,所以 RRDtool 在收到 3300 和 4600 后,会作一个运算,就是

(3300-3000)/ step (300)= 1,(4600-4000)/step(300)=2 ,这就是 1和 2 这两个值的来源了。还记得前面提到的 PDP 吗?

这两个值 (1 和2)就是 PDP 了,而不是 3300 和 4600  。这点要搞清楚。

实例五 :另外一个 updatev 的例子

[iyunv@dns1 bob]#

[iyunv@dns1 bob]# rrdtool updatev eth0.rrd 1163865300:3300:4600

return_value = 0

[1163865300]RRA[AVERAGE][1]DS[eth0_in] = 0.0000000000e+00

[1163865300]RRA[AVERAGE][1]DS[eth0_out] = 0.0000000000e+00

[iyunv@dns1 bob]#

在1163865300 这个时刻我们给出的值和上次一样,所以 eth0_in 和 eth0_out 的 PDP 都为 0

搞清楚了 PDP 的概念,现在我们来看什么是 CDP ,以及 CDP 是如何计算的

实例六 :通过 updatev 掌握 CF 的概念

[iyunv@dns1 bob]# rrdtool updatev eth0.rrd 1163865600:4000:5000

return_value = 0

[1163865600]RRA[AVERAGE][1]DS[eth0_in] = 2.3333333333e+00

[1163865600]RRA[AVERAGE][1]DS[eth0_out] = 1.3333333333e+00

[1163865600]RRA[AVERAGE][4]DS[eth0_in] = 1.6666666667e+00

[1163865600]RRA[AVERAGE][4]DS[eth0_out] = 1.6666666667e+00

[1163865600]RRA[AVERAGE][24]DS[eth0_in] = NaN

[1163865600]RRA[AVERAGE][24]DS[eth0_out] = NaN

[iyunv@dns1 bob]#

这次的输出和上次又不一样了。这次 update 操作影响到几个 RRA ,看到 [] 中的 1,4,24 了吗?它们就是代表不同的 RRA 中每个 CDP 所包含的 PDP 数量。

1 就是 1个 CDP 包含1 个 PDP,4 就是 一个 CDP 包含4个 PDP(20分钟)、24 就是 一个 CDP 包含24个 PDP(2小时)。

不过为什么没有 288 呢?eth0.rrd 的第4个 RRA 不是规定每288个 PDP 合并为一个 CDP 吗?

因为这个时候还轮不到它出场。1163865600 / 7200 = 161648 , 也就是说刚好1163865600 是在 7200 的某个周期上(161648)。

但1163865600 / 86400 ≈ 13470.66 ,说明1163865600 还不到 86400 的周期。

必须等到13471* 86400=1163894400 才会出现 [288] 的 CDP,那这个时间戳代表的时间是什么时候呢?看下面的 date 命令就知道了 :

[iyunv@dns1 bob]# date -d '1970-01-01 1163894400 sec utc'

日 11月 19 08:00:00 CST 2006

[iyunv@dns1 bob]#

[iyunv@dns1 root]# date -d '1970-01-01 1163865600 sec utc'

日 11月 19 00:00:00 CST 2006

[iyunv@dns1 root]#

这样不就是刚好相差1天的时间了吗?你可能会觉得很奇怪,为什么不是00:00 而是 08:00呢?

还记得create 操作的语法吗?其中有一个 –-start 参数吗?不记得了?没关系,那就得用 first 操作来重新找出来,

[iyunv@dns1 bob]# rrdtool first eth0.rrd --rraindex 3

1100822400

[iyunv@dns1 bob]#

[iyunv@dns1 bob]# date -d '1970-01-01 1100822400 sec utc'

五 11月 19 08:00:00 CST 2004

[iyunv@dns1 bob]#

看到了吗?是 2004 年的 11 月19日早上8点正,距离 2006-11-19 刚好是2年,也就是 730 天,因为 eth0.rrd 的第4个RRA

只保存730个记录。每个记录时间上相差1天。也就是个记录是 2004/11/19 8:00 ,第二个记录是 2004/11/20 8:00 ,

第三个记录代表 2004/11/21 8:00 ,依次类推。所以离1163865600 近的下一个记录是发生在 2006/11/19 8:00 。

所以严格意义上来说,RRDtool 中的一天并不一定是从 0:00 开始的,但可以保证的就是两个记录之间肯定相差86400秒(1天)。

四)自动更新数据库

其实这些都只不过是手工 update 时需要注意的一些地方,如果是自动更新数据库,时间戳方面就交给 RRDtool 去处理吧,我们不用操心了。

前面我们已经写好了一个脚本,现在就用它来更新

[iyunv@dns1 bob]# cat get_eth0_traffic.sh

#!/bin/bash

# 首先取得 eth0 接口的 ifIndex

index=$(snmpwalk -IR localhost RFC1213-MIB::ifDescr |grep eth0|cut -d '=' -f 1|cut -d '.' -f 2)

# 再通过 snmp 协议取得 ififInOctets 和 ifOutOctets 的值

# 由于在 /etc/snmp.conf 中配置了 defVersion 和 defCommunity ,所以 snmpget 命令不用指定这两个参数

eth0_in=$(snmpget -IR -Os localhost ifInOctets.${index}|cut -d ':' -f 2|tr -d '[:blank:]')

eth0_out=$(snmpget -IR -Os localhost ifOutOctets.${index}|cut -d ':' -f 2 |tr -d '[:blank:]')

echo ${eth0_in}

echo ${eth0_out}

# 需要我要用这些数据来更新 eth0.rrd ,注意 update 时的 timestamp 我们用的是 N

/usr/local/rrdtool-1.2.14/bin/rrdtool updatev /home/bob/eth0.rrd N:${eth0_in}:${eth0_out}

[iyunv@dns1 bob]#

五)接下来是什么呢?

有了数据,下面该学什么了呢?是绘图吗?

不是!^_^!! (估计有人快疯了吧)

在绘图之前,你有没有想过 RRDtool 在绘图时如何取数据的呢?

例如我想画2小时内的数据,那么我们有4个 RRA ,其 resolution 分别是 300,1200,7200,86400

(还记得什么是 resolution 吗?就是每个 RRA 中两个 CDP 相隔的时间)。是从个 RRA 取出 7200/300=24 个记录,

还是从第二个 RRA 取出 7200/1200=6 个记录呢?或者是从第三个 RRA 中取出1个记录就可以呢?

这些问题我们就留待下一篇再学习吧。这里给大家留几个问题 :

A)        如果 eth0.rrd 在5分钟内收到不止1个更新,结果会怎样?提示:用 updatev 就可以看出来了

B)        如果过了 eth0.rrd 在5分钟内没有收到脚本返回的值,是否立即就用 UNKNOWN 作为 PDP 的值?

C)        参考上面的例子,搞清楚 heartbeat 的含义

D)        在搞清楚 heartbeat 后,再想一下 heartbeat 和 step 之间的关系。

相关文章