Note: The Listed platform will permanently shut down December 31, 2026. Your data published on Listed will still be available in your personal Standard Notes account. Learn more

监控服务器网络状态

通过 rrdtoolcrontab

我很早就想在服务器上跑一个软件来监测服务器当前的状态,特别是流量/带宽信息。但是,我之前使用的一直是国外的VPS,它们的流量不是无限,就是 1T 以上,以中国的国际带宽,基本上不可能在一个月之内用完这么多流量。然而,在不久以前,由于电信的国际带宽陷入炸裂状态,我使用 阿里云 的青岛节点做了一个中转,选择的是按流量计费的方式。由于国内的流量费再也不是白菜价,所以我急需一个流量统计的方式,顺便还应该可以监控一下从 阿里云 到我的国外VPS的延迟和丢包。

寻找

crontab 是肯定要用到的。我在 阿里云 上使用的是 CentOS 7,官方源里面有一个 cronie,这就是我所使用的 crontab 实现了。

然而关于绘图工具,我一直在纠结。本来想自己写一个脚本来绘图,但是我实在是懒,而且数据的保存还是一个大问题。后来在微博上看见别人做的统计图很好,是使用 rrdtool 绘制的。而事实上, rrd 还是一种统计数据归档的手段。

rrdtool 呃,据说还是个标准来着……

不管怎么样,就是它了。

创建数据源

这里我需要统计的是服务端某个端口的流入和流出流量。我并不希望通过什么其他软件来获得流量统计,因为 iptables 本身已经带有这个功能。只需要在头部插入两条规则

iptables -I INPUT -d your-server-ip -p tcp --dport your-port
iptables -I OUTPUT -s your-server-ip -p tcp --sport your-port

如果你要监控的端口不是 tcp 协议的话,只要把上面的 tcp 都换成 udp 就是了。以下我全都默认是 tcp 协议。

这样,iptables 会自动统计通过这两条规则的数据包和它们的大小。这就是我们所需要统计在数据库里面的东西。

接下来需要创建数据库,你需要先安装好 rrdtool。我以一个 bash 脚本的形式展示

#!/bin/bash

rrdtool create $1 \
        DS:in:COUNTER:600:U:U \
        DS:out:COUNTER:600:U:U \
        RRA:AVERAGE:0.5:1:576   \
        RRA:AVERAGE:0.5:6:720   \
        RRA:AVERAGE:0.5:24:720  \
        RRA:AVERAGE:0.5:288:730

这个脚本需要一个参数,就是要创建的数据库的路径 your-db.rrd, 然后创建了两个数据源,一个是 in,即输入流量,一个是 out,即输出流量,这两个都是计数器类型,即一直增加不会减少的。RRA 所定义的是归档,和每次归档的间隔与类型。所有归档的类型都是平均值。

当每次数据更新的时候,只需要调用

rrdtool update -t in:out $DB N:$in:$out

就可以更新数据源。

每次更新数据源的时候,rrd 会自动计算两次的差值,并按照我们上面定义的 AVERAGE 方法来计算平均值。所以,我们所输入的字节大小实际上会变成实际的网速。

那么接下来的问题就是获取数据更新了。iptables -n -v -L 的输出格式似乎更加适合人类阅读而不是通过脚本来截取。不过,iptables-save -c 指令可以将目前统计到的数据包全部显示在输出结果中,就像这样

[packets:bytes] -A INPUT .....

这样就可以通过 sed 截取。每次 cron job 执行的时候,先通过 iptables-save -c 获得当前状态,然后用 grep 截取你所需要的那一条规则,比如说上面那个就要截取

iptables-save -c | grep -- "-A \(INPUT\|OUTPUT\)" \
    grep -- "\(-d\|-s\) " | grep -- '\(--dport\|--sport\) your-port'

由于 INPUT 链在 OUTPUT 链之前,所以这样的输出可以保证输入数据在输出数据之前

接下来通过 sed 规则

sed -r 's/\[([0-9]*):([0-9]*).*/:\2/' | xargs echo | sed 's/ //g' )

可以输出形如

:inputbytes:outputbytes

的数据。这样在加入数据库的时候,只需要

rrdtool update -t in:out $DB N$data

就可以了。

将做好的数据更新脚本丢进 crontab 里面,每5分钟执行一次。

图形生成

当数据积累到一定的程度以后,就可以生成图像了。

rrdtool graph $GRAPH -w 700 -h 300 --start -5h \
        DEF:RawIn=$DB:in:AVERAGE \
        DEF:RawOut=$DB:out:AVERAGE \
        VDEF:TotalIn=RawIn,TOTAL \
        VDEF:TotalOut=RawOut,TOTAL \
        CDEF:NegIn=RawIn,-1,* \
        CDEF:Zero=RawIn,0,* \
        VDEF:MaxOut=RawOut,MAXIMUM \
        VDEF:MaxIn=NegIn,MINIMUM \
        LINE2:NegIn#0000ff:"Input Rate" \
        LINE2:RawOut#0080ff:"Output Rate" \
        LINE1:MaxOut#aa00df:"Max Output Rate" \
        LINE1:MaxIn#227865:"Max Input Rate" \
        LINE1:Zero#555555:"Zero line" \
        COMMENT:'\n' \
        GPRINT:TotalIn:"Total Input\:%.2lf%s" \
        COMMENT:'\n' \
        GPRINT:TotalOut:"Total Output\:%.2lf%s"

其中, $GRAPH 指向输出的 png 图片,$DB 指向数据库路径。-5h 说明这是生成最近5小时的统计图。实际上这里面的定义都非常好理解。不过,里面我为了绘制 x轴,即 y=0,我使用了一个方法,就是这一行

CDEF:Zero=RawIn,0,*

因为 rrdtool 不支持绘制不在数据库里面的数据,所以我就通过计算的方式,随便把哪个数据乘以0,就可以得到 0 这个数值以便绘制了。

另外,在这张图里面,我把输入值都乘了 -1

CDEF:NegIn=RawIn,-1,*

这样的好处是x轴的上方和下方分别代表输入和输出,以便阅读。

我还通过 MAXIMUM 绘制了最大值和最小值的直线。通过 VDEFTOTAL 方法,我们可以得到当前时段内的总流量,然后可以用 GPRINT 绘制到底部。(事实上目的不就是这个么= =)

另外,因为是平均值,所以绘制出来的图像实际上表示的是过去每个时间段内的输入/输出的平均速度。

效果

peter-ss

这是我用上面的方法统计得到的阿里云中转的 shadowsocks 流量。

其他

使用类似的方法,我们还可以统计得到某服务器到另一服务器的 ping 延迟和丢包率图。不过,这种情况下,我们就不能像上面一样使用 COUNTER 数据类型加上 AVERAGE 这个统计方法,而是应该使用 GAUGE 数据类型加上 LAST 统计。在绘图的时候,你还需要通过 --right-axis 系列参数创建一个对应的第二个y轴,以显示丢包率的数据轴,具体效果:

ping

这是我统计得到的阿里云青岛节点到 ConoHa 日本节点的数据。

参考

RRDtool

Network Statistics with iptables and rrdtool


You'll only receive email when they publish something new.

More from Typeblog
All posts