物理主机接收丢包排查:区分网卡、软中断与协议栈

物理主机接收丢包排查:区分网卡、软中断与协议栈

业务请求偶发超时,接口接收丢弃计数也在增长,并不意味着网卡已经损坏。物理主机的接收路径包括网卡队列、驱动、软中断和协议栈,同一个症状可能出现在不同层级。排查应把业务错误与同一时间窗口的计数增量对应起来,而不是看到 dropped 就扩大所有缓冲区。

以下以 Debian 12 为例,需要管理员权限及 iproute2、ethtool、sysstat、python3 工具。AmdMetal 物理主机付款后在控制台查看连接凭据,SSH 使用第一个 IPv4 地址;本文先执行只读采样,不修改地址和路由。

一、确定接收接口与故障窗口

先从 ip -br address 确认业务地址所在接口,再沿 VLAN、网桥或绑定设备找到实际承载流量的物理接口。不能直接选名称排序第一的网卡。下面通过交互输入指定接口,采样目录由命令自动建立:

read -r -p "Physical interface: " IFACE
ip link show dev "$IFACE" || exit 1
D=$(mktemp -d /var/tmp/rx-audit.XXXXXX)
printf '%s\n' "$IFACE" > "$D/interface"
uname -r > "$D/kernel"
ethtool -i "$IFACE" > "$D/driver"
ip -d link show dev "$IFACE" > "$D/link"
printf '%s\n' "$D"

记录业务错误发生时间、请求量、报文大小及是否存在批量导入。接口累计值可能跨越很长运行时间,不能拿它与刚出现的一次超时直接建立因果关系。驱动重载、重启和接口重建也可能让计数清零。

网卡统计、接口统计和协议栈统计不是可直接相加的独立账目。部分字段可能描述同一事件,必须先核对驱动定义,再解释增量。

若应用位于网络命名空间内,物理接口观察宿主接收路径,协议栈统计还需在应用所在命名空间采集;宿主没有报错不代表容器内部正常。

二、同时保存接口与驱动统计

在同一 Shell 中继续执行。示例观察窗口为 30 秒,这是采样参数,不是服务承诺。选择实际出现问题的业务阶段,正常窗口也按相同时长留一组对照。

snapshot() {
    label=$1
    date -Is > "$D/$label.time"
    ip -s -s link show dev "$IFACE" > "$D/$label.link"
    ethtool -S "$IFACE" > "$D/$label.nic" 2>&1
    cat /proc/net/softnet_stat > "$D/$label.softnet"
    nstat -az > "$D/$label.protocol"
}
snapshot before
mpstat -P ALL 1 30 > "$D/cpu"
snapshot after
diff -u "$D/before.nic" "$D/after.nic"

这些读取是顺序执行,并非原子快照;短突发场景应结合时间戳解释,不能要求不同文件逐包一致。ethtool -S 若返回不支持,应保留结果,不把“缺少统计”写成“没有丢包”。

先检查计数含义

名称含 CRC、frame 的错误增长,应检查链路层证据;名称含 missed、no_buffer 或 discard 的字段,需要查当前驱动的解释。它们可能涉及接收描述符、设备缓冲或过滤行为,不能仅凭英文名称判定原因。

同时保存 ethtool -g "$IFACE" 与 ethtool -l "$IFACE",分别查看环形队列和通道信息。不支持这些查询并不等于故障。最大值只是设备能力边界,也不是生产推荐值。

三、按 CPU 计算 softnet 增量

/proc/net/softnet_stat 使用十六进制,前三列依次是 processed、dropped、time_squeeze。后续列可能随内核变化,本次不解析。下面按行比较,输出的是采样行号;遇到 CPU 热插拔,应重新采样,不能继续机械对齐。

python3 - "$D/before.softnet" "$D/after.softnet" <<'PY'
import sys
from pathlib import Path

def load(path):
    return [
        [int(v, 16) for v in line.split()[:3]]
        for line in Path(path).read_text().splitlines()
    ]

before, after = map(load, sys.argv[1:])
if len(before) != len(after):
    raise SystemExit("CPU layout changed; sample again")

print("row processed_delta dropped_delta time_squeeze_delta")
for row, (old, new) in enumerate(zip(before, after)):
    delta = [b - a for a, b in zip(old, new)]
    if any(v < 0 for v in delta):
        raise SystemExit("Counter reset or wrap; sample again")
    print(row, *delta)
PY

这里的 dropped 增长提示软件接收积压队列发生丢弃;time_squeeze 增长表示处理轮次触及预算边界,不等于已经丢包。softnet 统计按 CPU 汇总,也不是单网卡专属,必须排除同期其他接口流量。

将增量与 mpstat 的 %soft 对照。整机 CPU 空闲仍可能伴随单核接收压力,因此不能仅看平均利用率。必要时保存 /proc/interrupts,观察网卡队列中断是否明显集中;中断分布只是线索,不是必须均匀的验收指标。

四、依据证据选择调整层级

网卡 missed 类计数增长而 softnet dropped 不变,优先检查接收环及驱动队列;相反,softnet dropped 增长并伴随单核压力,应检查软件积压和流量分发。两者都稳定时,再比较 nstat 中协议错误的增量及应用日志,不要继续盲调网卡。

调整前保存当前配置:

ethtool -g "$IFACE" > "$D/rings" 2>&1
ethtool -l "$IFACE" > "$D/channels" 2>&1
sysctl net.core.netdev_max_backlog \
       net.core.netdev_budget \
       net.core.netdev_budget_usecs > "$D/budgets"

环容量调整仅在驱动支持且有对应证据时考虑,不能把“设为最大”当通用方案。扩大积压队列可能用排队延迟换取较少丢弃;提高处理预算可能挤占应用执行时间。多队列分布调整还需要结合 IRQ、RSS 与业务流数量,单个流也不会因此自然分散到全部核心。

远程环境不应直接尝试通道重配、驱动重载或链路重置。这些操作可能影响 SSH,需先准备受控操作方案、原值记录与恢复路径。每轮只变更一项,异常时先恢复原值。

五、用业务复测完成验收

复测保持请求类型、持续时间和并发方式可比,同时记录实际流量。仅比较两个窗口的丢弃绝对数量,可能把负载下降误认为优化成功;吞吐相同但小包更多,接收处理压力也可能不同。

验收至少包含三组结果:

如果底层计数稳定但业务错误持续,应把结论收敛为“本次窗口未发现对应接收丢弃证据”,继续检查协议栈与应用处理,不宣称网络已经完全正常。保留接口、驱动、内核、采样文件及变更原值,才能在下一次相同症状出现时复核因果链。

常见问题

ip -s link 的 dropped 增长能直接证明网卡故障吗?

不能。接口 dropped 是汇总线索,必须结合驱动统计、链路错误和同窗口业务现象判断。驱动计数器含义也可能不同,不能把所有名称含 drop 的值直接相加。

softnet 的 time_squeeze 增长是否等于丢包?

不等于。它表示接收处理轮次触及预算边界,需要结合 dropped 增量、逐核软中断负载与业务错误判断,不能仅凭这个计数增加就扩大预算。

AMD EPYC 7543P 独享物理主机

$458.7/月起

配置 AmdMetal 物理主机 查阅 AmdMetal 租用价格

物理主机配置逐项确认

AMD EPYC 7543P 整机独享,机型与选配在配置页统一核算。