在前兩篇文章中,我們討論了《如何生成每秒百萬級別的HTTP 請求?》 以及 如何減少往返時間 。我們在 Linux 上做試驗,因為它是一個效能非常好的通用作業系統。
不幸的是,對於一些更加專業的工作,Vanilla Linux(譯註:Linux 的核心版本,代號“香草”) 核心的網路速度是不夠的。舉個例子,在 CloudFlare,我們持續地處理洪水般的資料包。 Vanilla Linux 處理速度僅能達到約 1M pps (譯註:單位 packet per seconds),這在我們的工作環境下是不夠的,特別是網路卡有能力處理大量的資料包。現代 10Gbps 網路卡的處理能力通常至少達到 10M pps 。
核心不給力
我們做一個小實驗來說明修改 Linux 確實是有必要的。我們看看理想狀態下核心能處理多少資料包。把資料包傳遞到使用者空間的代價是高昂的,讓我們嘗試一下在網路驅動程式收到資料包後就立刻丟棄它們。據我所知,Linux 上不修改核心丟棄資料包最快的方法是在 PREROUTING iptables 上設定一些丟棄規則。
1 2 3 4 5 6 7 |
$ sudo iptables -t raw -I PREROUTING -p udp --dport 4321 --dst 192.168.254.1 -j DROP $ sudo ethtool -X eth2 weight 1 $ watch 'ethtool -S eth2|grep rx' rx_packets: 12.2m/s rx-0.rx_packets: 1.4m/s rx-1.rx_packets: 0/s ... |
如上所示, Ethtool(譯者注:Ethtool 是 Linux 下用於查詢及設定網路卡引數的命令)的統計顯示,網路卡能達到每秒接收 12M 資料包的速度。通過 ethtool -X 來操作網路卡上的間接表,可以將所有的資料包引向 0 號 RX 佇列。正如我們看到的,在一顆 CPU 上,核心處理佇列的速度可以達到 1.4M pps。
在單核上能達到 1.4M pps 是一個相當不錯的結果,但不幸的是協議棧卻不能擴充套件。當資料包被分配到多核上,這個成績會急劇下降。讓我們看看把資料包分到 4 個 RX 佇列的結果。
1 2 3 4 5 6 7 |
$ sudo ethtool -X eth2 weight 1 1 1 1 $ watch 'ethtool -S eth2|grep rx' rx_packets: 12.1m/s rx-0.rx_packets: 477.8k/s rx-1.rx_packets: 447.5k/s rx-2.rx_packets: 482.6k/s rx-3.rx_packets: 455.9k/s |
此時每個核的處理速度是 480k pps。這是個糟糕的訊息。即使樂觀地假設增加多個核心不會進一步地造成效能的下降,處理資料包的核心也要多達 20 個才能達到線速度。所以核心是不起作用的。
核心旁路前來救駕
CC BY 2.0 image by Matt Brown
關於 Linux 核心網路效能的侷限早已不是什麼新鮮事了。在過去的幾年中,人們多次嘗試解決這個問題。最常用的技術包括建立特別的 API,來幫助高速環境下的硬體去接收資料包。不幸的是,這些技術總是在變動,至今沒有出現一個被廣泛採用的技術。
這裡列出一些廣為人知的核心旁路技術。
PACKET_MMAP
Packet mmap 是 Linux 上的API,用來實現資料包快速嗅探。然而它不是嚴格意義上的核心旁路技術,它是技術列表中的一個特例 —— 可以在 Vanilla 核心上使用。
PF_RING
PF_RING 是另一個已知的技術,用來提升捕獲資料包的速度。不像 packet_mmap,PF_RING 不在核心主線中,需要一些特殊模組。通過 ZC 驅動和把模式設定成 transparent_mode = 2(譯者注:是 PF_RING 的一種模式),只把資料包傳遞給 PF_RING 客戶端,而不會經過核心網路協議棧。由於核心比較緩慢,這樣可以確保高速運轉。
Snabbswitch
Snabbswitch 是一個 Lua 網路框架,主要用來寫 L2 應用。它可以完全接管一個網路卡,並且在使用者空間實現硬體驅動。它在一個 PCI 裝置上實現了使用者空間 IO(UIO),把裝置暫存器對映到 sysfs 上(譯者注:sysfs 是 Linux 核心中設計較新的一種虛擬的基於記憶體的檔案系統) 。這樣就可以非常快地操作,但是這意味著資料包完全跳過了核心網路協議棧。
DPDK
DPDK 是一個用 C 語言實現的網路框架,專門為 Intel 晶片建立。它本質上和 snabbswitch 類似,因為它也是一個基於UIO 的完整框架。
Netmap
Netmap 也是一個豐富的網路框架,但是和 UIO 技術不同,它是由幾個核心模組來實現的。為了和網路硬體整合在一起,使用者需要給核心網路驅動打補丁。增加複雜性的最大好處是有一個詳細文件說明的、裝置廠商無關的和清晰的 API。
由於核心旁路技術的目的是不再讓核心處理資料包,所以我們排除了 packet_mmap。因為它不能接收資料包 —— 它只是一個嗅探資料包的快速介面。同樣,沒有 ZC 模組的 PF_RING 也沒有什麼吸引力,因為它的主要目標是加速 libpcap(譯者注:libpcap是unix/linux平臺下的網路資料包捕獲函式包,大多數網路監控軟體都以它為基礎)。
我們已經排除了兩種技術,但很不幸的是,在餘下的解決方案中,也沒有我們能夠使用的!
讓我告訴你原因。為了用 剩下的技術 實現核心旁路技術:Snabbswitch、DPDK 和 netmap 會接管整個網路卡,不允許網路卡的任何流量經過核心。我們在 CloudFlare,根本不可能讓一個分擔負載的應用程式獨佔整個網路卡。
話說回來,很多人使用上面的技術。在其他環境中佔用一個網路卡,來實現旁路也許是可以接受的。
Solarflare 上的 EF_VI
雖然上面列出的技術需要佔用整個網路卡,但還有其它的選擇。
Solarflare 網路卡支援 OpenOnload,一個神奇的網路卡加速器。它通過如下方式來實現核心旁路,在使用者空間實現網路協議棧,並使用 LD_PRELOAD 覆蓋目標程式的網路系統呼叫。在底層訪問網路卡時依靠 “EF_VI” 庫。這個庫可以直接使用並且有很好的說明文件。
EF_VI 作為一個專用庫,僅能用在 Solarflare 網路卡上,你可能想知道它實際是如何工作的。 EF_VI 是以一種非常聰明的方式重新使用網路卡的通用功能。
在底層,每個 EF_VI 程式可以訪問一條特定的 RX 佇列,這條 RX 佇列對核心不可見的。預設情況下,這個佇列不接收資料,直到你建立了一個 EF_VI “過濾器”。這個過濾器只是一個隱藏的流控制規則。你用 ethtool -n 也看不到,但實際上這個規則已經存在網路卡中了。對於 EF_VI 來說,除了分配 RX 佇列並且管理流控制規則,剩下的任務就是提供一個API 讓使用者空間可以訪問這個佇列。
分叉驅動
雖然 EF_VI 是 Solarflare 所特有的,其他網路卡還是可以複製這個技術。首先我們需要一個支援多佇列的網路卡,同時它還支援流控制和操作間接表。
有了這些功能,我們可以:
- 正常啟動網路卡,讓核心來管理一切。
- 修改間接表以確保沒有資料包流向任一 RX 佇列。比如說我們選擇 16 號 RX 佇列。
- 通過流控制規則將一個特定的網路流引到 16號 RX 佇列。
完成這些,剩下的步驟就是提供一個使用者空間的 API ,從 16 號 RX 佇列上接收資料包,並且不會影響其他任何佇列。
這個想法在 DPDK 社群被稱為“分叉驅動”。它們打算在 2014 年建立分叉驅動,不幸的是 這個補丁 還沒進入核心的主線。
虛擬化方法
針對 intel 82599 還有另外一種選擇。我們可以利用網路卡上的虛擬化功能來實現核心旁路,而不需要通過分叉驅動程式。
首先我簡單說下背景。有結果證明,在虛擬化世界中將資料包從主機傳遞到客戶機,虛擬機器通常是瓶頸。因此,這些年對虛擬化效能的需求與日俱增,通過軟體模擬網路硬體的模擬技術成為了影響效能的主要障礙。
網路卡廠商增加一些特性來加速虛擬客戶端。其中一項虛擬化技術,要求網路卡虛擬成多個 PCI 裝置。虛擬客戶端可以操作這些虛擬介面,無需與主機作業系統進行任何合作。我演示一下它是如何工作的。舉個例子,這是我本機上的 82599 網路卡。這個“真實的”裝置被稱為 PF(物理功能)介面:
1 2 |
$ lspci 04:00.1 Ethernet controller: Intel Corporation 82599EB 10-Gigabit SFI/SFP+ Network Connection (rev 01) |
我們要求這個裝置建立一個 VF(虛擬功能)裝置:
1 2 3 4 |
$ echo 1 > /sys/class/net/eth3/device/sriov_numvfs $ lspci 04:00.1 Ethernet controller: Intel Corporation 82599EB 10-Gigabit SFI/SFP+ Network Connection (rev 01) 04:10.1 Ethernet controller: Intel Corporation 82599 Ethernet Controller Virtual Function (rev 01) |
比如說一個 KVM 客戶端很容易使用這個假的 PCI 裝置。同時,我們還是能夠使用主機環境。要做到這些僅需要載入 “ixgbevf” 核心模組,之後會出現另一個 “ethX” 介面。
你或許想知道核心旁路技術幹了什麼。核心沒有利用“ixgbevf”裝置正常聯網,我們可以把它專門用在核心旁路上。這樣看起來可以在 “ixgbevf” 裝置執行 DPDK。
概括來說:這個想法可以讓 PF 裝置正常處理核心工作,而 VF 介面專門用在核心旁路技術上。由於 VF 是專用的,所以我們可以執行“接管整個網路卡”的技術。
這聽起來似乎不錯,實際上卻沒那麼簡單。首先,只有 DPDK 支援“ixgbevf”裝置,netmap,snabbswtich 和 PF_RING 是不支援的。預設情況下, VF 介面不能接收任何資料包。若通過 PF 傳送資料給 VF ,你需要給 ixgbe 打上這個補丁。有了它,你可以對 VF 進行定址,即在ethtool中對“活動”“佇列號的高位進行編碼。
1 |
$ ethtool -N eth3 flow-type tcp4 dst-ip 192.168.254.30 dst-port 80 action 4294967296 |
最後一個障礙出現了,在 82599 晶片上啟用 VF 功能,RSS 組的最大規模變小了(譯者注:Really Simple Syndication,簡易資訊聚合)。沒有虛擬化時,82599 可以在 16 個 CPU 核上進行 RSS 。但隨著 VF 的啟用,這個數量卻變成了 4。如果 PF 上的流量比較低,只使用 4 個核來發布可能還好。不幸的是,我們在 Cloudflare 需要處理大規模的 RSS 組。
結束語
完成核心旁路技術沒有那麼簡單。雖然存在很多開源的技術,但它們看起來都需要一塊專用的的網路卡。這裡我們展示了 3 個可以選擇的框架:
- 類似 EF_VI, 隱藏 RX 佇列
- DPDK 分叉驅動
- VF 技術
不幸的是,在我們的環境下,這麼多技術中能起作用的似乎只有 EF_VI。我們祈禱開源的核心旁路 API 趕緊出現,唯一的要求是不需要一塊專用的網路卡。
打賞支援我翻譯更多好文章,謝謝!
打賞譯者
打賞支援我翻譯更多好文章,謝謝!
任選一種支付方式