昨天那七關裡有五關是軟體。kernel bypass 讓封包從網卡直接交給應用程式,不經過作業系統的網路堆疊。但五關裡真正被拿掉的只有兩關,搞清楚是哪兩關,才知道什麼情況值得用它。
拿 Day 12 那張表來對:
| 第幾關 | 發生什麼 | 走 kernel | 走 bypass |
|---|---|---|---|
| 1 | 網卡收完 frame,驗 FCS | 硬體 | 一樣,硬體 |
| 2 | DMA 進主機記憶體 | 進 kernel 的 ring buffer | 進應用程式自己讀得到的記憶體 |
| 3 | 通知有東西進來 | 中斷或 NAPI | 沒有了,應用程式自己去看 |
| 4 | driver 取出封包往上送 | kernel | 搬到使用者空間 |
| 5 | 拆 header、查 socket | kernel 網路堆疊 | 搬到使用者空間,實作薄得多 |
| 6 | 複製到 socket 緩衝區 | kernel | 少一次,或完全不用 |
| 7 | 排程器喚醒行程 | 要等 | 沒有了,行程本來就在跑 |
第 4 跟第 5 關沒有消失,只是被搬到使用者空間,換成一份只服務這支程式的實作。真正被拿掉的是第 3 關和第 7 關。
所以 bypass 省掉的主要是等待。 複製就算省掉,也從來不是最大的一塊;大的是等中斷、等排程器排到你這個行程。機器越忙,第 7 關排得越久,bypass 省下來的就越多。就算機器很閒,叫醒一個行程本身也要時間,Day 2 的 UDP echo 比 ping 多出來的那幾微秒,主要就差在這一段。
攔截 socket 呼叫:用 LD_PRELOAD 把程式對 socket API 的呼叫接走,導到使用者空間的實作。exasock 跟 Onload 都是這類,賣點是不改程式就能改善延遲。
專用 API:libexanic、ef_vi 這類直接給你操作網卡佇列的介面,要改程式,換來的是碰得到更底層的東西跟硬體時戳。這 30 天的量測就是走這條路。
「完全不用改程式」這句話站不站得住、實際差多少,後面會拿同一支程式跑三種模式來比。
一顆核心被燒掉。 沒有中斷通知,應用程式得自己一直問「有沒有新封包」,這叫 busy poll,那顆核心使用率 100%,大部分時間在問一個答案是「沒有」的問題。26 核的機器分掉一顆不算什麼,但它不能跟別人共用,共用就等於把第 7 關請回來。
kernel 工具全部失效。 封包不經過 kernel 網路堆疊,tcpdump 抓不到、netstat 跟 ss 列不出那條連線、iptables 對它沒作用。最後這項在實驗室無所謂,在別的地方是安全問題。
一組佇列只能給一個行程用。 那組硬體佇列被對映進那支程式的記憶體空間,kernel 跟同一台機器上的其他程式就碰不到了。
三個代價裡,kernel 工具失效對維運的影響最大。一條走 bypass 的連線出問題,排查第一步通常是先 tcpdump 看封包有沒有進來,但這時候 tcpdump 抓不到東西,也不代表封包沒進來。所以替代的觀測手段要先準備好:
| 原本用什麼 | bypass 之後改用什麼 |
|---|---|
| 主機上 tcpdump | 交換器的 port mirror,或網卡自己的擷取工具 |
| netstat / ss 看連線狀態 | bypass 函式庫自己的統計介面 |
| 主機的介面計數器 | 交換器那一側的 port 計數器 |
排查的入口從主機移到了網路設備上。 這要在啟用之前就講清楚,不然故障當下才發現第一步做不了。
還有一件事要寫進變更流程。bypass 是函式庫層的東西,行為跟程式怎麼啟動綁在一起。有人改了啟動腳本、換了容器映像、忘記帶環境變數,程式會安靜地退回走 kernel,功能完全正常只是慢了,而且不會有告警,因為沒有東西壞掉。要抓它只能主動確認:部署之後檢查那個行程有沒有真的走在 bypass 上,這得是巡檢項的一部分。
明天把前八天量到的東西擺在一起看,其中只有一個真的是網路。