實體主機連線追蹤表耗盡:定位封包遺失並驗證容量調整
業務服務間歇性出現連線逾時,既有工作階段卻仍能持續傳輸,應用程式程序與監聽狀態也都正常,這時應檢查 Linux 的連線追蹤表。具狀態追蹤功能的防火牆、位址轉換及部分容器網路都依賴 conntrack;當無法建立表項時,新流量可能尚未抵達應用程式就被丟棄。本文以 Debian 12 為例,依序進行蒐證、原因分析、容量調整與還原,不變更防火牆規則,也不清空既有連線。
一、先確認故障發生在哪個網路命名空間
CloudDDR 實體主機提供 AMD EPYC 7543P、128GB 或 256GB ECC 記憶體,但記憶體容量並不直接決定 conntrack 的上限。應檢查的是系統核心、網路命名空間與業務流量。付款後可在控制台查看登入憑證,透過 SSH 連線;若有多個位址,請使用第一個位址。
請在實際處理流量的網路命名空間中執行命令。宿主機上的結果不能代表客體系統;若容器流量經過宿主機的位址轉換,也需要一併檢查宿主機。先記錄核心版本、目前的網路命名空間、表項數量與上限:
uname -r
readlink /proc/self/ns/net
sudo sysctl net.netfilter.nf_conntrack_count
sudo sysctl net.netfilter.nf_conntrack_max
sudo sysctl net.netfilter.nf_conntrack_buckets
若設定鍵不存在,表示目前的命名空間未提供對應參數,不能直接判定為表項耗盡。應先確認相關模組是否已載入,以及業務是否使用連線追蹤;不要為了「補齊輸出」,在故障期間臨時啟用新功能。
不要執行
conntrack -F。清空追蹤表會刪除追蹤狀態,可能破壞既有工作階段與位址轉換對應,也會抹除定位問題所需的現場資訊。
二、以計數增量建立證據鏈
安裝使用者空間工具不會變更既有規則,但安裝仍應遵循既有的變更流程。以下操作會讀取現場資訊,並以兩次間隔取樣判斷計數是否持續增加:
sudo apt-get install conntrack
umask 077
sudo conntrack -S > /tmp/conntrack-before.txt
sudo journalctl -k --since "-10 min" --no-pager \
| grep -Ei 'nf_conntrack|table full'
sleep 30
sudo conntrack -S > /tmp/conntrack-after.txt
diff -u /tmp/conntrack-before.txt /tmp/conntrack-after.txt
conntrack -S 通常會依 CPU 輸出累計計數。應比對同一 CPU 的資料列,關注 insert_failed、drop、early_drop 的增量,而不是把開機以來的累計值當成目前故障的證據。計數的意義與輸出欄位可能因核心版本而異,插入失敗也不全是容量不足所致。
同時記錄業務逾時次數、表項使用量與核心日誌。只有在使用量接近上限、失敗計數持續增加,而且業務錯誤同步出現時,才構成較完整的證據鏈。日誌沒有符合的結果也不能排除問題,因為日誌輸出可能受到速率限制。取樣間隔內的短暫尖峰可能被漏掉,必要時可縮短觀察間隔,但不要循環匯出整張表。
三、區分連線數量與連線建立速率
從樣本判斷表項累積的原因
表項數量不等於目前的請求數。一條 TCP 連線可以承載多個請求,UDP 也會占用追蹤表項;通訊結束後,表項可能仍會保留,直到對應的逾時期限。因此,應區分長期穩定的連線、短連線突增與異常來源。
可進行一次有範圍限制的取樣,避免在高負載期間反覆輸出完整的連線表:
sudo timeout 5s conntrack -L -o extended 2>/tmp/conntrack-sample.err \
| head -n 200 > /tmp/conntrack-sample.txt
此命令只擷取有限的輸出,可能提早結束並產生非零結束狀態,因此樣本並非完整統計。檔案包含位址資訊,應限制讀取權限,作為支援單附件時則視需要遮蔽敏感資料。輸出包含原始方向與回覆方向的元組,分析來源時不要將兩組位址混在一起計算。
檢查通訊協定、TCP 狀態、剩餘逾時時間與原始方向的來源。若有大量短連線,應搭配應用程式連線池與重試日誌檢查;若少數來源持續建立新流量,應結合存取紀錄判斷;若 UDP 表項長期累積,則需核對實際通訊週期。不能只憑一個狀態名稱就認定為攻擊,也不要把流量異常簡化成容量問題。
若連線建立速率沒有下降,擴大追蹤表只能延後再次觸及上限的時間。修正無限重試、恢復連線重用或處理異常來源,通常需要與容量調整分別驗收。
四、評估記憶體後,只調整一個變數
上限不是預先配置的記憶體,也不是吞吐量保證
提高 nf_conntrack_max 不會立即建立所有表項,但表項持續增加會消耗核心記憶體。使用 free -h、/proc/meminfo 與 sudo slabtop -o 觀察記憶體與 slab;其中快取物件的大小並未涵蓋擴充欄位、配置額外負擔等全部成本,不宜套用固定的「每條連線占用多少位元組」公式。
nf_conntrack_buckets 是雜湊桶數量,不是連線容量。只提高上限可能增加桶鏈長度與查找成本,因此也需要觀察 CPU 與業務延遲;桶數量能否在線上修改,受到核心與命名空間的限制,本次不連動調整。
以下僅示範將目標上限暫時設為 262144。執行前必須確認此值高於目前設定、記憶體預算允許,且另有一個可用的管理工作階段;這不是適用於所有機器的建議值。命令應在具備管理權限的 shell 中執行:
sudo -i
OLD=$(sysctl -n net.netfilter.nf_conntrack_max)
test "$OLD" -lt 262144 || exit 1
umask 077
printf '%s\n' "$OLD" > /run/conntrack-max.before
sysctl -w net.netfilter.nf_conntrack_max=262144
sysctl -n net.netfilter.nf_conntrack_max
保留原值後,重複相同的業務探測與計數取樣。不要同時縮短 TCP established 狀態的逾時時間:閒置的長連線可能仍在業務使用範圍內,全域縮短逾時時間會引入新的斷線原因。
五、驗收、還原與持久化分開進行
驗收至少應涵蓋原本發生故障的流量,以及閒置後恢復通訊的長連線。檢查新連線成功率、失敗計數增量、使用量成長趨勢、剩餘記憶體、CPU 與業務延遲。只看到上限提高,並不代表問題已解決。
若記憶體壓力、CPU 使用情況或延遲惡化,請使用已儲存的值還原:
sudo sysctl -w \
net.netfilter.nf_conntrack_max="$(sudo cat /run/conntrack-max.before)"
降低上限不會立即清除既有表項;若使用量仍高於還原後的上限,新表項的建立可能繼續受阻,需要等待表項自然移除,並處理流量來源。不要為了加速恢復而清空追蹤表。
暫時調整通過驗收後,再檢查 /etc/sysctl.conf 與 /etc/sysctl.d/ 中是否已有同名設定,避免多個檔案互相覆寫。將已通過驗收的值寫入納入版本管理的專用設定檔,例如:
net.netfilter.nf_conntrack_max = 262144
只載入該檔案並再次確認實際值,不要在故障期間批次重新載入無關參數。後續進行受控重新啟動時,還應驗證模組載入後的最終值,因為寫入檔案不代表設定在啟動時必然生效。
最終紀錄應包含流量來源、變更前後的計數、業務探測、記憶體觀察、原值與還原步驟。連線追蹤容量管理是否完成,取決於能否解釋故障證據、重現調整結果,而不是表項上限是否變成一個更大的數字。
常見問題
連線追蹤表接近上限,就能確定它造成封包遺失嗎?
不能。必須將使用量與同一觀測期間的 insert_failed、drop 或 early_drop 增量、核心日誌及業務失敗互相比對。使用量偏高只能證明容量受到壓力。
提高 nf_conntrack_max 時,也需要縮短 TCP 逾時嗎?
不需要一起修改。先保留協定逾時,只驗證記憶體預算允許的表項上限。全域縮短 TCP established 逾時可能破壞閒置的長連線。