服务器带宽跑满怎么处理

结论: 服务器带宽跑满分七步处理:用 sar -n DEV 1 确认网卡饱和 → 用 iftop -i eth0 看流量去往哪些 IP、nethogs 看是哪个进程发出的 → 用 ss -antp 统计连接数与来源 → 从 Nginx 访问日志统计 TOP IP 与 TOP URL → 对照判定表区分正常流量、盗链、爬虫、被控外发与 CC 攻击 → 针对性限流、封禁或上 CDN → 仍不够再决定扩容。盲目扩容最贵。

带宽跑满的直接表现是网站打开变慢、图片加载一半卡住、SSH 操作有明显延迟。需要注意的是,云厂商通常只对出方向(下行)流量计费和限速,也就是服务器向外发送数据的方向:用户下载网页、图片、视频都会消耗出方向带宽,而用户上传表单数据几乎不占。因此排查的重点永远是"服务器在往外发给谁、发什么"。

第一步:确认带宽是否真的跑满

结论:不要凭"感觉慢"判断,用 sar -n DEV 1 看网卡实时吞吐,并与云控制台的带宽监控曲线交叉验证。 两者的单位不同,容易看错—sar 默认输出的是 KB/s,而云厂商售卖的是 Mbps。

bash
# 安装 sysstat(sar 命令来源)
sudo apt install -y sysstat        # Ubuntu / Debian
# sudo dnf install -y sysstat      # CentOS Stream 9

# 每秒刷新一次,看各网卡实时流量(rxkB/s 入站,txkB/s 出站)
sar -n DEV 1

# 只看指定网卡,采样 10 次
sar -n DEV 1 10 | grep eth0

# 确认网卡名(可能是 eth0 / ens33 / enp1s0)
ip -brief link show

# 另一组常用实时工具
sudo apt install -y iftop nethogs

换算关系必须记牢:1 Mbps ≈ 128 KB/s。买了 5 Mbps 带宽的服务器,txkB/s 持续超过 640 就说明已经跑满。若是按流量计费的实例,带宽上限通常更高(如 100 Mbps 峰值),此时"跑满"的表现是费用异常而非固定上限。

第二步:看清楚流量去了哪里

结论:iftop 回答"和谁在通信",nethogs 回答"哪个进程在通信",两者配合能在两分钟内锁定嫌疑对象。 这一步是全部排查过程中信息密度最高的环节。

bash
# 按连接对端实时排序(-n 不解析域名,-P 显示端口号)
sudo iftop -i eth0 -nP

# 只看某个端口的流量(例如只看 443)
sudo iftop -i eth0 -nP -f "port 443"

# 按进程维度看实时带宽占用(第二列 SENT 即为出方向)
sudo nethogs eth0

在 iftop 界面中的判读要点:

  • 对端 IP 集中在少数几个:可能是大客户下载、盗链站点或被 CC 攻击。
  • 对端 IP 极其分散且数量上千:大概率是爬虫群、扫描器或 DDoS 的前奏。
  • 出站流量远大于入站,且对端是当前网站的访客:属于正常业务流量,该考虑 CDN 与压缩。
  • 出站流量巨大但对端 IP 与网站访客无关:高度怀疑服务器被控制后对外发包(如参与 DDoS、挖矿外联、邮件外发)。
bash
# 统计各状态的 TCP 连接数(ESTAB 突然暴涨通常意味攻击)
ss -ant | awk 'NR>1 {print $1}' | sort | uniq -c | sort -rn

# 统计连接数最多的来源 IP
ss -antp | grep ESTAB | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -rn | head -20

# 查看某个 IP 与本机的所有连接
ss -antp | grep '203.0.113.55'

第三步:从 Nginx 访问日志统计 TOP IP 与 TOP URL

结论:Nginx 访问日志是判定流量性质最可靠的依据,因为它能同时给出"谁、请求了什么、传了多少字节"。 下面这组 awk 命令不需要额外工具,可直接复制执行。

bash
LOG=/var/log/nginx/access.log

# 请求数最多的前 20 个 IP
awk '{print $1}' $LOG | sort | uniq -c | sort -rn | head -20

# 出流量(字节)最多的前 20 个 IP(第 10 列为 $body_bytes_sent)
awk '{print $1, $10}' $LOG | sort | awk '{a[$1]+=$2} END {for (i in a) print a[i], i}' \
  | sort -rn | head -20 | awk '{printf "%.2f MB  %s\n", $1/1048576, $2}'

# 被请求最多的前 20 个 URL
awk '{print $7}' $LOG | sort | uniq -c | sort -rn | head -20

# 消耗流量最多的前 20 个 URL
awk '{print $7, $10}' $LOG | sort | awk '{a[$1]+=$2} END {for (i in a) print a[i], i}' \
  | sort -rn | head -20 | awk '{printf "%.2f MB  %s\n", $1/1048576, $2}'

# 每分钟请求数(判断是否突发尖峰)
awk '{print $4}' $LOG | cut -c2-18 | uniq -c | sort -rn | head -20

判读经验:如果 TOP URL 里排在最前的是 .mp4、.zip、.jpg 这类大文件,说明瓶颈是静态资源分发,上 CDN 效果立竿见影;如果 TOP URL 是 /wp-login.php、/search、/api/xxx 这类动态接口且集中在极少数 IP,则基本可判定为 CC 攻击或恶意扫描。

第四步:判定流量性质的对照表

结论:先定性再动手。下面这张表覆盖了带宽跑满的五种典型成因,判定依据都可从上一步的数据中直接取得。

流量类型典型特征判定依据处置方式
正常业务增长出流量平稳上升,TOP IP 分散,TOP URL 是页面与图片请求数与独立 IP 数同步增长,转化率等指标同步上涨开压缩、上 CDN、按需扩容
盗链(Hotlinking)单个或少量 IP 大量拉取图片/视频TOP URL 为静态资源,且请求 Referer 是陌生域名Nginx valid_referers 拦截,或资源加鉴权签名
搜索引擎/恶意爬虫UA 含 bot/spider,IP 集中在少数段,请求路径规律TOP IP 的 UA 可识别,请求频率高但字节数不大robots.txt 约束、UA 限速、异常 UA 直接 403
被控外发出站大但 Nginx 日志无对应记录,iftop 对端为非 Web 端口nethogs 显示异常进程占用带宽,如未知二进制、curl、python断网排查、杀进程、查计划任务与后门
CC 攻击 / 慢速攻击短时间内连接数暴涨,动态接口被高频请求,服务端 CPU 同时飙高ss 中 ESTAB 数异常,TOP IP 集中在数十个,QPS 远超日常峰值limit_req 限流、fail2ban 封禁、接入高防或 WAF

判断"被控外发"的关键交叉验证是:iftop 看到的出站流量,在 Nginx 日志里完全找不到对应记录。因为这类流量不走 Web 服务,是机器上某个恶意进程直接对外发包,性质比带宽占用严重得多,属于安全事件。

第五步:分场景处置

结论:不同成因的处理手段差异很大,误用反而会伤及真实用户。 下面按四种高频场景给出具体做法。

场景一:静态资源与大文件下载

  • 开启 gzip 或 brotli 压缩,HTML/CSS/JS 可压缩掉 60%~80%。
  • 图片转 WebP、视频开启分片传输(Range 请求)。
  • 大文件(安装包、视频)迁移到对象存储(OSS/COS),用 CDN 域名分发,源站带宽可下降 70% 以上。

场景二:盗链

nginx
# 防盗链:只允许本站与空 Referer 访问静态资源
location ~* \.(jpg|jpeg|png|gif|webp|mp4|zip|rar)$ {
    valid_referers none blocked server_names *.example.com example.com;
    if ($invalid_referer) {
        return 403;
    }
    expires 30d;
    access_log off;
}

场景三:爬虫与 CC 攻击

nginx
# 在 http 块中定义限流区:按 IP 限速 10 请求/秒,内存区 10MB
limit_req_zone $binary_remote_addr zone=perip:10m rate=10r/s;
limit_conn_zone $binary_remote_addr zone=perconn:10m;

server {
    # 普通页面:允许突发 20 个请求排队
    location / {
        limit_req zone=perip burst=20 nodelay;
        limit_conn perconn 20;
        limit_req_status 429;
        proxy_pass http://127.0.0.1:8080;
    }

    # 登录、搜索、短信等敏感接口单独收紧到 1 请求/秒
    location ~ ^/(wp-login\.php|search|api/sms) {
        limit_req zone=perip burst=5 nodelay;
        proxy_pass http://127.0.0.1:8080;
    }
}

burst 是突发缓冲队列长度,nodelay 表示队列内的请求立即处理而不延迟——不加 nodelay 时超出的请求会被排队延时处理,用户体验是"卡住几秒才打开"。配置完成后务必 sudo nginx -t && sudo systemctl reload nginx。

场景四:临时封禁与自动封禁

bash
# 临时封禁单个 IP(立即生效,重启后失效)
sudo iptables -I INPUT -s 203.0.113.55 -j DROP
# 封禁整个网段
sudo iptables -I INPUT -s 203.0.113.0/24 -j DROP
# 查看与删除规则
sudo iptables -L INPUT -n --line-numbers
sudo iptables -D INPUT 1

# 持久化(Ubuntu 24.04)
sudo apt install -y iptables-persistent
sudo netfilter-persistent save

# 用 fail2ban 自动封禁高频访问的 IP(需先安装)
sudo apt install -y fail2ban
sudo systemctl enable --now fail2ban
sudo fail2ban-client status nginx-limit-req

Nginx 侧也可以直接拒绝,适合临时应急:deny 203.0.113.55; 写在 server 或 location 块内,reload 后生效。

第六步:CDN 分流与扩容决策

结论:带宽不够时,正确的顺序是先优化(压缩、CDN、限流),再扩容。 未经优化的站点直接升带宽,往往只是把浪费的成本放大。

  • CDN 分流:把静态资源接入 CDN,用户从边缘节点取数据,源站只剩动态请求。这是投入产出比最高的一步,多数站点接入后源站带宽下降 60%~90%。
  • 压缩与缓存:开启 gzip/brotli、设置合理的 expires 与 Cache-Control,让重复访问不再回源。
  • 扩容判断标准:完成上述优化后,若连续 7 天峰值带宽仍超过上限的 80%,说明是真实业务增长,应扩容。临时大促可按小时购买按量带宽或临时升配,比长期高配更划算。

需要留意的是,CDN 只对可缓存的静态内容有效,视频直播、大文件下载、实时 API 这类场景仍需源站带宽,此时扩容是唯一选择。

常见误区 / 排错提示

  1. 一看慢就升带宽:未查明流量来源就扩容,若实际是盗链或 CC,扩容后攻击流量会同步放大,成本翻倍但问题依旧。
  2. 混淆 Mbps 与 MB/s:sar 输出的 KB/s 除以 128 才是 Mbps。把 640 KB/s 当成 640 Mbps 会得出完全错误的结论。
  3. iftop 里看不到流量就以为没跑满:可能是网卡名不对(eth0 实为 ens33 等),先用 ip -brief link show 确认。
  4. 限流参数设得过严:rate=1r/s 对正常用户过于苛刻,会导致页面图片加载不全。应从 10r/s 起步,观察 429 状态码数量后再收紧。
  5. 只封 IP 不做限流:CC 攻击常换 IP,纯手工封禁跟不上。应以 limit_req + fail2ban 自动处置为主,手工封禁仅用于明显恶意的固定来源。
  6. 忽略了被控外发的可能:若出站流量与 Web 日志对不上,不要只当性能问题处理,这是主机失陷的信号,需要按入侵排查流程处理。