为云主机实现网络达量停机
涛叔无论是国内的阿里云、腾讯云,还是海外的 Azure/AWS/GCE/Oracle,大多数公有云的云主机都要按网络流量计费。如果被 DDoS 攻击,或者被坏人盯上,很容易出现巨额账单🧾而且各大公有云厂商都不提供达量停机功能,好像是商量好了似的。今天为大家分享一种基于 vnstat 的流量监控和自动停机方案,希望可以保住大家的房产证🏡
其实网上已经有很多 vnstat 相关的内容了。但这些内容要么是不完整,仅仅罗列了 vnstat 的命令参数;要么就是给出了一个神秘且巨长的脚本,不知道里面做了什么。做为一个负责任的系统管理员,我们不但要知其然,更要知其所以然。本文希望为大家提供一个简洁明了的解决方案。
首先需要安装 vnstat。我用的是 ubuntu 系统:
sudo apt-get install vnstat jq
如果用的不是 Debian 系发行版,请自行搜索安装指令。
💡Tip这里我还额外安装了 jq 工具,后面会用到。如果你不熟悉 jq,可以阅读我的另一篇文章。
vnstat 的本质是定时采集网卡流量状态,并根据规则将流量累加起来保存到一个 sqlite 文件。所以系统重启也不会影响流量统计。
vnstat 的配置文件在/etc/vnstat.conf
,需要注意的配置项如下:
SaveInterval 1
这个配置项表示流量数据落盘间隔,单位是分钟,最小也是一分钟存一次盘。换句话说流量统计的最小间隔是一分钟。如果在一分钟内产生巨额流量,vnstat 也没有办法。这种情况下可以考虑给网卡限流。
通常 vnstat 会自动选择网卡。如果你的主机网络比较复杂,也可以通过Interface
配置项指定网卡。一般此项留空就可以了。
配置好之后启动 vnstat
systemctl start vnstat
这样 vnstat 服务就开始采集流量数据了。
我们可以查看当月消耗的总流量,注意这里的-m
参数:
$ vnstat -m
enp0s6 / monthly
month rx | tx | total | avg. rate
------------------------+-------------+-------------+---------------
2024-11 97.15 MiB | 260.46 MiB | 357.61 MiB | 535.40 kbit/s
------------------------+-------------+-------------+---------------
estimated 28.71 GiB | 76.97 GiB | 105.68 GiB |
vnstat 也支持查看每天和每五分钟消耗的流量,对应的参数分别是-d
和-5
。输出结果我就不贴出来了,请大家自行试验🔬
功能验证没问题后需要设置开机自动启动。一般可以用systemctl enable
开启。但 vnstat 服务貌是历史遗留服务,还没迁移到 systemd 上,需要使用单独的命令来设置。systemctl
有提示🔔
$ systemctl enable vnstat
Synchronizing state of vnstat.service with SysV service script with /usr/lib/systemd/systemd-sysv-install.
Executing: /usr/lib/systemd/systemd-sysv-install enable vnstat
运行如下命令设置开机自启:
/usr/lib/systemd/systemd-sysv-install enable vnstat
现在我们来写自动关机脚本。
前面命令输出的内容是纯文本格式,不方便用脚本处理。vnstat 支持输出 json 和 xml 两种机读格式。因为我比较熟悉 json,所以就配置 jq 来处理了。
$ vnstat -m --json|jq -r ".interfaces.[0].traffic.month[0]"
{
"id": 1,
"date": {
"year": 2024,
"month": 11
},
"timestamp": 1730419200,
"rx": 102250665,
"tx": 274167696
}
上述代码中使用了--json
参数,所以 vnstat 会输出 json 格式结果。然后我们通过 jq 达式 .interfaces.[0].traffic.month[0]
来提取数据。
vnstat 会分网卡汇总数据,所以最外层的 map 中用interfaces
键,它的值是一个列表。列表中第一个对应我的公网网卡,所以用.[0]
提取。结果也是 map,其中的traffic
键对应每个汇总维度流量信息,我们继续用.month
提取按月统计的流量数据。最终,该对象的rx
和tx
表示当前的收发总流量。
接下来我们可以使用 shell 脚本提取rx
和tx
的值,把它们相加再跟流量阈值比较。如果超量就执行关机动作。这自 shell 脚本写起来比较繁琐,而且 shell 脚本的语法用过的人都说傻。我们可以直接使用 jq 提供的数学运算和分支比较功能。
这里我用的 jq 表达式为:
"if .rx + .tx > $1 then \"err\" else \"ok\" end"
上面的.rx
和.tx
分别是提取收发流量值,然后相加,如果大于变量$1
指定的数值就输出"err"
,否则输出"ok"
。我们在 bash 中可以通过检测 err 或者 ok 来确定是否停机。
最终完整 shell 代码如下:
#!/usr/bin/env bash
vnstat -m --json|jq -r ".interfaces.[0].traffic.month[0]|if .rx+.tx > $1 then \"err\" else \"ok\" end"|grep "ok"
if [ $? -ne 0 ]; then
shutdown
fi
这里我用grep "ok"
检查输出结果。如果 jq 输出 ok,那么对应的$?
变量值为0
。最后使用shutdown
关机📵
将代码保存到磁盘并设置可执行权限。我用的路径是/root/.bin
,大家可以按需放置。
该脚本运行时需要提供总流量参数,单位是字节。
一切准备就绪后配置 crontab 每分钟检测一次:
* * * * * /root/.bin/traffic.sh 1099511627776
这样就可以高枕无忧了💤