實體主機接收封包遺失排查:區分網卡、軟中斷與協定堆疊
業務請求偶爾逾時,介面的接收丟棄計數也持續增加,並不代表網卡已經損壞。實體主機的接收路徑涵蓋網卡佇列、驅動程式、軟中斷與協定堆疊,同一種症狀可能出現在不同層級。排查時,應將業務錯誤與同一時段的計數增量對照,而不是一看到 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,必須先準備受控操作方案、原始值紀錄與復原途徑。每輪只變更一項,若出現異常,先恢復原始值。
五、透過業務複測完成驗收
複測時,請維持請求類型、持續時間與並行方式的可比性,並同時記錄實際流量。若只比較兩個時段的丟棄絕對數量,可能把負載下降誤認為最佳化成功;即使吞吐量相同,小封包較多時,接收處理壓力也可能不同。
驗收至少應包含三組結果:
- 接收證據:目標計數的增量下降,且沒有新增鏈路錯誤;累計總值未下降不代表失敗。
- 處理代價:逐核心軟中斷負載與應用程式 CPU 資源未出現新的明顯排擠,排隊延遲也未惡化。
- 業務表現:逾時、失敗請求與尾端延遲同步改善,而不只是底層計數變好。
如果底層計數穩定,但業務錯誤仍持續,應將結論限定為「本次觀察時段未發現對應的接收丟棄證據」,接著檢查協定堆疊與應用程式處理,不要宣稱網路已經完全正常。保留介面、驅動程式、核心資訊、採樣檔案及變更前的原始值,才能在下次出現相同症狀時,重新核對因果鏈。
常見問題
ip -s link 的 dropped 增加就代表網卡故障嗎?
不代表。介面 dropped 只是彙總線索,必須搭配驅動程式統計、鏈路錯誤與同時段業務現象判斷。不同驅動程式的計數定義也可能不同,不能直接加總所有包含 drop 的欄位。
softnet 的 time_squeeze 增加是否等於封包遺失?
不等於。這表示接收處理回合觸及預算邊界,仍須搭配 dropped 增量、逐核心軟中斷負載與業務錯誤判讀,不能只憑此數值就擴大預算。
AMD EPYC 7543P 獨享實體主機
每月 $458.7 起