流量统计这件事,很多人以为装个面板就完事了。真到自己排查问题的时候才发现:面板只给了一张月度总流量图,看不出某个小时为什么突增,也算不出下一台机器该买多少带宽。真正有用的统计,是能回答三个问题的:现在用多少、什么时候用得多、这波多出来的流量是谁产生的。
这篇讲的是用轻量工具把这三个问题回答清楚。不依赖任何商业监控,全在服务器上跑,一条命令级别的成本。适合个人站长、小团队运维,以及任何被带宽超了被限速坑过一次的人。
一、先分清四种流量
这是最容易混淆的地方。同一台机器上,至少有四个数字都会被叫做流量,它们的大小关系与用途完全不同。
第一个是网卡计数,也就是 /proc/net/dev 里的字节数。它是内核在驱动层累加的收发字节总和,最准确也最底层,vnstat 就是读它。它统计的是这一块网卡上流过的一切,包括你没意识到的流量:ARP、IPv6 的邻居发现、监控探针、甚至邻居机器的广播。
第二个是应用层日志,也就是 Nginx 的 access.log。它统计的是有请求、有响应的那部分,不含 TCP 重传、不含握手开销、不含被 Nginx 在 access 阶段之前就拒掉的连接。所以 Nginx 统计的字节数通常比网卡少百分之五到百分之十五,HTTPS 站点差距更大,因为 TLS 握手和记录层开销全在网卡里、不在日志里。
第三个是计费口径。云厂商的流量通常只统计出方向(公网出流量),入方向免费或不计,而且不同产品线口径不同:对象存储算请求数和回源流量,CDN 算边缘响应字节,负载均衡另算。看账单时发现服务器才用了 200G、账单却有 800G,八成是把 CDN 回源和服务器出流量重复算了。
第四个是业务流量,比如这个月用户下载了多少文件。它需要从日志里按 URL 前缀、按状态码、按 Referer 过滤出来,是四个数字里唯一能对应到具体业务的。
排查问题的正确顺序是:账单异常先看计费口径,网卡突增先看 vnstat 曲线,曲线正常但业务抱怨慢则看 access.log 的字节分布。
二、vnstat:最小成本的长期曲线
vnstat 是这类工具里性价比最高的一个。它从 /proc/net/dev 读取计数,按小时、天、月聚合,存进一个很小的数据库,几乎不占资源,也不需要常驻的高开销进程。
apt install vnstat
systemctl enable --now vnstat
vnstat -i eth0
vnstat -i eth0 -h
vnstat -i eth0 -m
vnstat -i eth0 -d
vnstat -i eth0 -t安装后第一件事是确认网卡名字。云主机上常见的是 eth0、ens3、ens5 都有,装错了就会出现数据库是空的这种迷惑现象。用 ip -br link 看一眼实际名字,再去 /etc/vnstat.conf 里把 Interface 改成正确的。
vnstat 的价值在于回看。面板监控通常只保留几天到两周的原始数据,而 vnstat 的月度数据能留很久。当你要判断这台机器该不该升级带宽时,看的就是过去几个月每天同时段的变化,而不是此刻的瞬时值。
它有个明显的限制:只统计网卡总量,不区分进程、不区分端口、不区分方向以外的维度。所以它回答什么时候多,不回答多在哪。后者要靠下面的工具。
三、按连接看:谁在占用带宽
vnstat 告诉你流量突增,接下来问是谁。第一层是看当前连接的实时占用,工具是 iftop 或 nethogs。
iftop 按连接对排序,显示每个远端 IP 与本地端口的实时速率。它适合回答现在是不是有人在拖大文件。安装后加 -n -P 参数跑起来,加上显示端口的开关,能直接看出是 443 还是 22 在跑流量。
nethogs 按进程排序,适合回答是不是某个进程在偷偷上传。比如备份任务、日志同步、或者被入侵后的挖矿进程。它需要 root 权限,因为要读进程的网络统计目录。
这两个工具都是现在时,看不了历史。要沉淀成历史数据,得靠定期采样:
while true; dodate +%s
ss -tnpi state established | awk '/:443/ {print $1, $2, $3}'
sleep 60
done >> /var/log/conn_samples.log
这类采样日志不要长期保留原始明细,一天一个文件、保留七天就够。需要长期趋势的话,只保留每小时的汇总值:连接数峰值、入出字节增量、唯一的远端 IP 数量。这三个数字比原始明细有用得多,而且体积小两个数量级。
四、按 URL 看:日志里的字节分布
真正的带宽去哪了,最终都要落到 URL 上。Nginx 日志里默认的响应体字节数就是最直接的口径,按 URL 聚合就能排出消耗榜单。
第一个容易踩的坑是响应体字节数不含响应头。对于一个 200KB 的图片,响应头几十字节可以忽略;但对于大量的小请求,响应头占比可能达到三成。如果日志格式里同时有请求长度字段,可以用两者之和作为更接近真实的口径。更精确的做法是把上游响应时间一起记下来,用来区分慢和大。
第二个坑是压缩。Nginx 开了 gzip 之后,记录的响应体字节是压缩后的字节数,也就是实际在线上传输的量,这一点是对的;但如果你的分析目标是源站产生的内容有多大,就得看上游响应长度字段。两个数字都要在日志格式里定义清楚,不要事后再猜。
按 URL 聚合的常用写法:
zcat -f /var/log/nginx/access.log* \
| awk '{print $7, $10}' \
| awk '{a[$1]+=$2} END {for(k in a) print a[k], k}' \
| sort -rn | head -30榜单出来后,重点看三类:单个文件的字节数极大(大视频、大压缩包)、大量请求集中在小文件(雪碧图、图标字体)、以及来自少数 IP 的重复下载(爬虫或盗链)。三类的处理方式完全不同,前者考虑分片或换对象存储,中者考虑合并或长缓存,后者靠防盗链和限速。
还有一类容易被忽略的隐形带宽:条件请求命中后的 304 响应。浏览器缓存命中时会发条件请求,服务端返回 304,body 只有几十字节——看着很小,但如果你的站点有几百个静态资源、用户每次访问都发一轮条件请求,连接建立与 TLS 握手的成本会在网卡上体现为可观的流量。这也是 Nginx 统计很小但网卡很大的常见原因之一。
五、把三个数字对应起来
三个工具各自跑一阵子之后,要做的最关键一步是对账:把同一天的网卡总量、Nginx 字节总量、业务侧统计放在一起看。数字对得上,说明统计口径一致,后续判断才有依据;对不上,要先找出差值来源,别急着下结论。
常见的差值形态有四种。差值是恒定的百分之几,通常是协议开销,正常。差值与 HTTPS 请求数成正比,是 TLS 开销,正常。某一天突然差了很大一块,多半是那天有备份、同步或大文件传输这类不走 Nginx 的流量。差值是负的(Nginx 比网卡多),说明日志里可能存在重复记录或者多台机器共用一个日志文件。
对账还有一个衍生用途:估算扩容。如果过去三个月的出流量一直在月度配额的四分之三上下波动,那么增长一倍的业务量就会超配额。提前把 CDN 或对象存储接上,比等到限速了再救火便宜得多。
六、告警:只在该响的时候响
统计的目的是提前知道,所以告警是必要的一环。带宽类告警的原则是少而准,宁可漏一次也不要天天响,否则第三次之后你就不看了。
建议设三条线,都用 vnstat 的天数据或月度累计值判断。第一条是日流量超过近 30 天日均的 2.5 倍,用来发现盗链、被抓取、或者某次异常下载。第二条是月度累计超过配额的大部分(比如八成),给你留出处理时间。第三条是连续两小时出流量速率超过带宽上限的七成,用来发现突发的持续占用。
判断逻辑写成一个小脚本,配合 cron 每天跑几次,超阈值时发邮件或 webhook。脚本里最容易出问题的是拿什么做基线:用固定阈值会在业务增长后频繁误报,用当天数据和历史比则要保证历史数据完整。折中的做法是同时保留固定阈值和相对阈值,任一触发都告警,但在告警内容里注明是哪一条触发的,方便下次调整。
告警内容要能直接指导动作,至少包含四项:当前值、阈值、对比基线、以及最耗流量的三个 URL 或 IP。前面几节的聚合命令正好可以复用,别让告警只说一句流量异常,那样收到之后还是得从头查。
七、按用途分档保留数据
统计系统跑起来之后,真正的成本在存储。原始日志一天可能几百 MB,全部保留一年就是几十 GB,而且真出事的时候没人去翻一年前的明细。
比较实用的分档是三层。原始明细保留七天,用于精确排查某次故障。按小时聚合的数据保留三个月,用于看趋势和设基线,体积大概是原始数据的千分之一。按天和月的汇总永久保留,只存几个数字,用于长期容量规划和年度对比。
分档的实现不需要额外系统:原始日志交给 logrotate 轮转并压缩,聚合数据用一个定时脚本每天写进一个 CSV 文件,两者都放在同一台机器上就够。等站点的机器数量超过五台,再考虑集中式方案,那时候数据量和查询需求才撑得起额外的一套组件。
另外提醒一点:日志里记录的 IP 属于个人信息范畴。如果你所在的地区有相关合规要求,统计脚本应当只保留聚合后的计数,不要在报表里留下可识别到个人的原始 IP 与完整 URL 组合。这一点在对外提供报表时尤其重要。
八、小结
流量统计真正的门槛不是工具,而是想清楚要回答什么问题。网卡数字回答用了多少,vnstat 回答什么时候用的,iftop 与日志聚合回答是谁用的,三条线对得上账,你对自己服务器的带宽情况就有了完整图景。剩下的事都是例行工作:每周看一眼曲线,每月对一次账,每条告警都写清楚下一步该做的动作。