# 以 Linux XDP 為基礎的高效率網路負載平衡器 contributed by < `foxhoundsk` > ###### tags: `linux2020` ## Benchmarking HAProxy: ``` ./wrk -t8 -c250 -d60s -R20000 --latency http://10.10.0.2 Thread Stats Avg Stdev Max +/- Stdev Latency 1.08ms 480.11us 8.56ms 66.27% Req/Sec 2.64k 295.71 4.89k 71.14% Latency Distribution (HdrHistogram - Recorded Latency) 50.000% 1.06ms 75.000% 1.40ms 90.000% 1.71ms 99.000% 2.28ms 99.900% 2.95ms 99.990% 4.11ms 99.999% 7.35ms 100.000% 8.57ms #[Mean = 1.080, StdDeviation = 0.480] #[Max = 8.560, Total count = 996911] #[Buckets = 27, SubBuckets = 2048] ---------------------------------------------------------- 1198537 requests in 1.00m, 428.63MB read Requests/sec: 19975.33 Transfer/sec: 7.14MB ``` XDP: ``` ./wrk -t8 -c250 -d60s -R20000 --latency http://10.10.0.5 Thread Stats Avg Stdev Max +/- Stdev Latency 0.89ms 413.29us 8.80ms 64.24% Req/Sec 2.65k 280.36 4.20k 73.29% Latency Distribution (HdrHistogram - Recorded Latency) 50.000% 0.88ms 75.000% 1.19ms 90.000% 1.42ms 99.000% 1.86ms 99.900% 2.16ms 99.990% 2.62ms 99.999% 6.59ms 100.000% 8.81ms #[Mean = 0.885, StdDeviation = 0.413] #[Max = 8.800, Total count = 996909] #[Buckets = 27, SubBuckets = 2048] ---------------------------------------------------------- 1198574 requests in 1.00m, 456.02MB read Requests/sec: 19976.16 Transfer/sec: 7.60MB ``` 參數 `-R20000` 為目前實驗的瓶頸,應再調高,方可展現較低 latency 所帶來的效益(higher throughput)。 嘗試參數: ``` ./wrk -t6 -c250 -d60s -R150000 --latency http://10.10.0.5 ``` XDP: ``` Thread Stats Avg Stdev Max +/- Stdev Latency 4.09s 1.79s 11.73s 63.18% Req/Sec 22.22k 802.67 24.06k 66.36% Latency Distribution (HdrHistogram - Recorded Latency) 50.000% 3.91s 75.000% 5.37s 90.000% 6.59s 99.000% 8.34s 99.900% 9.58s 99.990% 11.65s 99.999% 11.72s 100.000% 11.74s ---------------------------------------------------------- 7948712 requests in 1.00m, 2.97GB read Requests/sec: 132479.84 Transfer/sec: 50.66MB ``` haproxy: ``` Thread Stats Avg Stdev Max +/- Stdev Latency 17.37s 7.55s 36.01s 59.98% Req/Sec 12.40k 328.25 13.15k 66.67% Latency Distribution (HdrHistogram - Recorded Latency) 50.000% 17.20s 75.000% 23.63s 90.000% 27.16s 99.000% 34.50s 99.900% 35.78s 99.990% 35.95s 99.999% 36.01s ---------------------------------------------------------- 4461288 requests in 1.00m, 1.57GB read Requests/sec: 74353.80 Transfer/sec: 26.73MB ``` ## 筆記 ### 實驗環境佈置 目前架構為,經過 [IP in IP](https://en.wikipedia.org/wiki/IP_in_IP) 包裝過的封包到達其他 LB 時,其他 LB 會將 IPIP header 截斷,使得封包在該 LB 上 (application, network stack 等等) 看起來就只是一個普通的 IP packet,最後,封包處理完後將直接以 [VIP](https://en.wikipedia.org/wiki/Virtual_IP_address) 的 IP 位置回傳給 client,而非實際 IP。 在此環境下,每台 LB 要設定其負責的 VIP,設定方式為將 VIP 新增到 loopback device 上,即: ``` $ ip addr add 10.10.0.5/8 dev lo ``` 其中 `10.10.0.5` 即為 VIP。 此外,我們還要對 ARP 做相關設定: ``` sysctl net.ipv4.conf.all.arp_ignore=1 sysctl net.ipv4.conf.eth0.arp_ignore=1 sysctl net.ipv4.conf.all.arp_announce=2 sysctl net.ipv4.conf.eth0.arp_announce=2 ``` 如此一來 VIP 才能正確做出 DSR (Direct Server Return),也就是以 VIP 作為 L3 中的 source address。 #### [ECMP](https://en.wikipedia.org/wiki/Equal-cost_multi-path_routing) Linux 設定 此機器用於將 ingress 的流量平均分發至個別 XDP LB,關於 ECMP 在 vanilla Linux 上的發展歷史可見此[連結](https://cumulusnetworks.com/blog/celebrating-ecmp-part-two/)。 首先,我們假設 `10.10.0.7` 為 [VIP](https://en.wikipedia.org/wiki/Virtual_IP_address)、做 ECMP 的機器的位置為 `10.10.0.3`、XDP LB \ application 機器的位置為 `10.10.0.4`, `10.10.0.5` 以及 `10.10.0.6`、做 benchmarking 的 client 的位置為 `10.10.0.2`。 - ECMP 的機器主要會有以下設定: ``` ip route add 10.10.0.7/32 nexthop via 10.10.0.4 weight 1 \ nexthop via 10.10.0.5 weight 1 \ nexthop via 10.10.0.6 weight 1 sysctl net.ipv4.ip_forward=1 ``` 其中需注意 nexthop 的位置必須是 reachable 的,倘若不是,可參考 `onlink` 這個 `iproute2 route` 的其中一個參數是否對你的環境設定有幫助。 `ip_forward` 用於將 MAC address 指向本機上的 interface 但 L3 的位置不屬於本機的封包轉送至其他機器。 設定完後執行命令 `$ ip r` 預期會見到以下輸出: ``` # ip r 10.10.0.0/24 dev eth0 proto kernel scope link src 10.10.0.3 10.10.0.7 nexthop via 10.10.0.4 dev eth0 weight 1 nexthop via 10.10.0.5 dev eth0 weight 1 nexthop via 10.10.0.6 dev eth0 weight 1 ``` - Benchmarking 機器設定: ``` ip route add 10.10.0.7 via 10.10.0.3 ``` 強制將指向 `10.10.0.7` 的封包發送給 `10.10.0.3` 這台機器(做 ECMP 的機器),讓它去做 routing。 - XDP LB / application 機器設定請參考[上一段落](#實驗環境佈置)的介紹。 ### LACP vs. ECMP 雖然兩者均用於負載平衡以及增加線路的冗餘性,但前者是作用於 layer 2,而後者則是作用於 layer 3。 ### netlink 建立 (socket(2)) [netlink](https://man7.org/linux/man-pages/man7/netlink.7.html) socket 時,SOCK_RAW 以及 SOCK_DGRAM 這兩種 socket 類型均可使用,因為 netlink 將其視為等價。 注意,載入 XDP program 時,相關 bpf 系統呼叫的成功僅是代表 XDP program 已載入到核心中(通過 in-kernel verifier 的檢測),此時 XDP program 尚未掛載(hook)到預期的 network interface 上,我們需要使用 netlink 中的 [NETLINK_ROUTE](https://man7.org/linux/man-pages/man7/rtnetlink.7.html) 相關功能方能將 XDP program 掛載到預期的 interface 上。 值得注意的是,[libbpf](https://github.com/libbpf/libbpf/blob/master/src/netlink.c) 已經實做了 XDP program 相關的 netlink 的 helper。 ### capabilities negotiation 因為每個 driver 所支援的 XDP capabilities 不一,所以當載入 XDP program 時,需檢查對應的 NIC device driver 是否支援此 XDP program 需要用到的功能。 ### ebpf helper function epbf program 若需使用 kernel API,則該 API 需經類似 `EXPORT_SYMBOL` 的巨集處理。 每種 ebpf program type 有個別可使用的 helper function 子集。 ### packet redirection ![](https://i.imgur.com/9g4YpAL.png) ![](https://i.imgur.com/2oGcHyH.png) ([Facebook netdevconf2.1](https://netdevconf.info/2.1/slides/apr6/zhou-netdev-xdp-2017.pdf)) ### HW offload 原生 NIC driver (eBPF driver mode) 對 eBPF 的應用有些限制,例如:僅能有 31 個 NIC ring buffer (單埠網卡),雙埠網卡的話 ring 的數量限制則僅有 15 個。 若將 NIC 的 driver 更換 (載入對應核心模組) 為對 eBPF 提供支援的 driver,則可以不受此限制的約束。 ### OSI src & dst IP 位於 L3,src & dst port 位於 L4。 ### IPv4 header 因 header 中的 `option` 成員的關係,header 的長度不定。長度由 4-bit 的 bitfield (在結構體中名為 `ihl`) 來定義,`ihl` 表示 header 長度為多少個 dword (32-bit)。 由於長度使用 4 個位元表示,並且除了 `option` 成員外 header 還有必要的長度。因此 IPv4 長度最小為 20 bytes,最大為 60 bytes (0b1111 * size_of_qword)。 ## 參考資料 - [History & Usage of Linux Native ECMP Functionality](http://codecave.cc/multipath-routing-in-linux-part-2.html) - [Prototype Kernel](https://prototype-kernel.readthedocs.io/en/latest/networking/XDP/index.html) - [XDP/AF_XDP and its potential ](https://pantheon.tech/what-is-af_xdp/) - [XDP patch for e1000e driver support](https://github.com/adjavon/e1000e_xdp) (the URL is given by [bcc](https://github.com/iovisor/bcc/blob/master/docs/kernel-versions.md#xdp)) - [eBPF HW offload](https://www.netronome.com/m/documents/UG_Getting_Started_with_eBPF_Offload.pdf) - [Nginx cookbook 2019 ed.](https://www.nginx.com/resources/library/nginx-cookbook-2019-edition/) - [consistent hash](https://medium.com/@chyeh/consistent-hashing-algorithm-%E6%87%89%E7%94%A8%E6%83%85%E5%A2%83-%E5%8E%9F%E7%90%86%E8%88%87%E5%AF%A6%E4%BD%9C%E7%AF%84%E4%BE%8B-41fd16ad334a) - [netlink](https://man7.org/linux/man-pages/man7/netlink.7.html) - [Create bridge with iproute2](https://wiki.archlinux.org/index.php/Network_bridge#With_iproute2) - 實驗環境佈置 - [adding VIP into lo device](https://askubuntu.com/questions/585468/how-do-i-add-an-additional-ip-address-to-an-interface-in-ubuntu-14) - [ARP related stuff](https://lyt0112.pixnet.net/blog/post/312242545-linux%E4%B8%8B-proc%E8%A3%A1%E9%9D%A2%E7%9A%84arp_announce%E5%92%8Carp_ignore) - [ARP setting](https://support.kemptechnologies.com/hc/en-us/articles/203861685-Configuring-DSR)