DPVS - 小米高效能負載均衡器
隨著網際網路的快速發展,負載均衡也承擔著越來越重要的角色,對於小米這種快速發展中的年輕公司來說,負載均衡的穩定及高效能更是重中之重。
本文將主要介紹小米基於 DPDK 的高效能負載均衡軟體 DPVS(Data Plane Virtual Server)。
負載均衡簡介
負載均衡是指透過一臺負載均衡器將客戶端請求分散到不同的伺服器上形成叢集模式。早期的負載均衡主要是針對某個域名利用 DNS 輪詢解析出不同的 IP 地址,以此來實現最簡單的輪詢式負載均衡。而目前,負載均衡主要包含四層和七層的負載均衡,四層負載均衡多用於轉發,七層負載均衡主要用於代理。
小米負載均衡現狀及面臨的問題
小米在初期選擇了開源四層負載均衡軟體 LVS,前期也對 LVS 的部署模式進行了一些探索,以下是幾種模式的優缺點對比
在幾種模式的權衡中,我們選擇了 FULLNAT + OSPF-ECMP(一致性 hash)模式部署,這種模式適合大規模的叢集化部署,也能夠實現單機 LVS 故障時的平滑遷移。而隨著公司業務的進一步發展,LVS 叢集需要頻繁的進行擴容操作,伺服器數量從幾十臺增長到了幾百臺,日常運維的痛點也隨之而來:
硬體成本居高不下
叢集管理難度越來越大
LVS 本身不再維護,故障排查困難
在這種背景下,我們自研了高效能的 DPVS 來替代現有的 LVS。
高效能 DPVS
什麼是dpdk
DPDK 全稱是資料平面開發套件 (Data Plane Development Kit),由 6WIND,Intel 等多家公司開發,主要基於 Linux 系統執行,用於快速資料包處理的函式庫與驅動集合,可以極大提高資料處理效能和吞吐量,提高資料平面應用程式的工作效率。
2
dpvs特性
支援 SYNPROXY
執行在 FULLNAT 模式下且支援 TCP/UDP
支援 OSPF、ICMP 協議
支援 PERSISTENT
DPVS 如何實現高效能的四層 LB
避免中斷
linux 系統使用中斷方式來通知 CPU 來處理資料包,在大流量的場景下,中斷會佔據大量的時間,影響資料包的轉發效率,在 DPVS 中使用了 DPDK 提供的 PMD 驅動透過輪詢方式收發包,避免了在大流量場景下的中斷開銷。同時由於 PMD 驅動的存在,使得資料包不再經過核心協議棧,這對於提高包轉發效率也至關重要。
2
Memory Pool
Memory pool 即為記憶體池,也被稱為動態記憶體分配,記憶體池能夠有效的避免記憶體碎片,並且 mempool 提供了一些其他可選服務,例如核本地快取和記憶體對齊輔助器,記憶體對齊輔助器可確保物件被均勻地填充到所有 DRAM 或 DDR3 通道上(物件被均勻的放在通道上,會提高存取速度)。
3
無鎖化
無鎖化佇列
DPDK 實現了 ring library 來提供一個無鎖的、有限大小的環形 FIFO 佇列,它支援多消費者或單消費者出隊和多生產者或單生產者入隊,同時也支援批次、爆發 (burst) 出、入隊。相比於連結串列,每次的入隊或出隊只需比較 void * 大小的資料並且只需執行一次 CAS(Compare-And-Swap)操作且該操作是原子的。
session 表無鎖化
對於多佇列網路卡,RSS 能夠根據資料包的元資訊將資料包分散到不同的網路卡佇列上,每個網路卡對應的 CPU 會從網路卡佇列中讀取資料包進行處理。但對於 FULLNAT 模式而言,其入向流量和出向流量在經過 RSS hash 後可能會分佈在不同 CPU 繫結的網路卡佇列上,例如:入向資料包攜帶的元資訊為(cip1,cport0,vip1,vport1),此時 RSS(cip1,cport0,vip1,vport1)= hash_value1 → cpu0;出向資料包攜帶的元資訊為(rip1,rport1,lip1,lport1),此時 RSS(rip1,rport1,lip1,lport1)= hash_value2 → cpu1。在不同的 CPU 上處理意味著 session 表需要加鎖(可能引發併發操作)。為了無鎖化,需要使入向和會向資料包經過同一網路卡佇列即同一 CPU(同一 CPU 將序列處理兩個方向的資料包,session 表無需加鎖)。DPVS 的實現是利用 INTEL 網路卡的對稱 RSS,選擇合適的 RSS_KEY 保證 RSS(rip1,rport1,lip1,lport1)→ cpu0,此時入向資料包和出向資料包都經過 cpu0 處理,而無需加鎖。
cpu親和
在 NUMA 系統下,CPU 訪問它自己的本地儲存器比非本地儲存器更快。在分配記憶體時,使用 DPDK 提供的 API 可以分配某個 CPU 所在 NUMA 的記憶體,在初始化網路卡收發佇列時,同樣可以控制網路卡和 CPU 和親和性。
巨頁
作業系統會對實體記憶體分成固定大小的頁,按照頁來進行分配和釋放,程式設計時只使用虛擬記憶體進行程式設計,不用關係實體記憶體的對映,而處理器在暫存器收到虛擬地址之後,需要根據頁表把虛擬地址轉換成真正的實體地址。TLB(translation lookaside buffer)是一種小、專用、快速的硬體緩衝,它只包括頁表中的一小部分條目,利用虛擬地址查詢實體地址時,先根據頁號在 TLB 查詢,如果命中則得到頁內地址,訪問記憶體;否則則在記憶體中的頁表中得到頁內地址,將其存入 TLB,訪問記憶體。從虛擬地址到實體地址的轉換邏輯我們知道:如果一個程式使用了 512 個內容頁也就是 2MB(512*4KB=2048KB=2MB)大小,那麼需要 512 個頁表表項才能保證不會出現 TLB 不命中的情況。
透過上面的介紹,TLB 是有限的,隨著程式的變大或者程式使用記憶體的增加,勢必會增加 TLB 的使用項,最後導致 TLB 出現不命中的情況。因此,大頁的優勢就顯現出來了。如果採用 2MB 作為分頁的基本單位,那麼只需要一個表項(2MB/2MB=1)就可以保證不出現 TLB 不命中的情況;對於消耗記憶體以 GB 為單位的大型程式,可以採用 1GB 為單位作為分頁的基本單位,減少 TLB 不命中的情況。
叢集化部署
圖1 多運營商單一部署
圖2 多運營商混合部署
對於 DPVS 部署方案我們的選型是 OSPF+ECMP(一致性 hash)。ECMP 能夠將不同流的資料包轉發到叢集的不同節點上,同時透過 OSPF 協議的心跳包保證了在某臺伺服器異常時能夠動態的將其剔除出叢集(如圖 1)。但是這種模式還存在一定的最佳化空間:單臺伺服器只能承擔單個運營商的流量,而不同運營商的流量可能是不均的,也就是說有一部分伺服器資源被浪費了,為了提高伺服器的使用率,我們利用 linux vlan 以及 PBR 在單臺伺服器上起多個 OSPF 程式,每個 OSPF 程式對應一個運營商線路(如圖 2),這樣我們能夠保證每臺伺服器上的流量都均分了每個運營商的流量。
效能測試
1
測試環境
CPU:Intel(R) Xeon(R) CPU E5-2620 v2 @ 2.10GHz,24 個邏輯核
記憶體:128G
網路卡:Intel Corporation 82599ES 10-Gigabit SFI/SFP+ Network Connection
作業系統:Ubuntu 16.04.1
核心版本:4.4.0-92-generic
dpvs 版本:v0.4
拓撲
三臺測試機,dpdk03 模擬 lvs 伺服器,執行 dpvs;dpdk01 模擬客戶端,執行 dpdk-pktgen,往 lvs 伺服器傳送資料包;dpdk02 模擬 real server,執行 dpdk-pktgen,從 lvs 伺服器接收資料包。所有的網路卡都接在同一臺交換機上。
dpvs 上配置 1 個 vip,10 個 real server IP,10 個 local address,LVS 連線數為 1000。
效能指標
丟包率:
負載為 10000Mbit/s 的情況下,60s 內沒有轉發的包占收到的包的百分比。
吞吐量:
不丟包的情況下,每秒轉發包的數量(單位 Mpps)。
測試方法
測試包大小為 64、128、256、512、1024 位元組
測試包為 UDP 包
DPVS配置
使用 16 個邏輯核作為 worker,對應 16 個網路卡佇列,2 個邏輯核作為傳送 IO 核,3 個邏輯核作為接收 IO 核,根據 vip、local address、real server 數量及連線數,dpvs 配置如下:
記憶體池大小:
- mbuf 2560000
- svc 1024
- rs 1024
- laddr 1024
- conn 2048
雜湊表大小:
- conn 2^11
- svc 2^8
- rs 2^4
- worker CPU 數量:16
- tx CPU 數量:2
- rx CPU 數量:3
- ring_sizes:1024, 1024, 1024, 1024
- burst_sizes:(144,144),(144,144),(144,144)
測試流程
1)在 dpdk03 上啟動 dpvs,按照上述配置要求,準備測試環境
2)在 dpdk02 上啟動 dpdk-pktgen,監控網路卡,準備收包
3)在 client 上啟動 dpdk-pktgen,按要求設定,準備發包
4)開始發包,收集測試結果
測試結果
丟包率:發包速率為 10000Mbit/s 時,60s 內傳送包數量、資料量與接受包數量、資料量:
發包速率為 10000Mbit/s 時,丟包率與包大小的關係:
吞吐量:萬兆網路卡上,不同大小的資料包吞吐量的理論值與實際值
總結
本文介紹了負載均衡的相關知識,以及小米對於高效能 4 層負載均衡的實踐 ---DPVS。DPVS 開發利用了 DPDK,充分發揮和利用 DPDK 的優勢,並且結合一些內部生產環境需求進行了一些最佳化和調整。並且介紹了一種 DPVS 的部署方式,希望能夠對大家有所幫助。
小米運維
和我們一起,成為更酷的運維工程師
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31559359/viewspace-2374693/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- HAProxy高效能軟負載均衡器負載
- iQiYi 高效能開源負載均衡器及應用負載
- 微服務負載均衡器 Ribbon微服務負載
- 微服務負載均衡器 LoadBalancer微服務負載
- Kuberntes部署MetalLB負載均衡器負載
- nginx部署基於http負載均衡器NginxHTTP負載
- HAProxy負載均衡器後續篇薦負載
- 負載均衡器部署方式和工作原理負載
- nginx負載均衡原理分析到手動編寫簡易負載均衡器Nginx負載
- Lambda@edge 實現負載均衡器功能負載
- Go實現了一個負載均衡器Go負載
- 用 Rust 編寫 eBPF/XDP 負載均衡器RusteBPF負載
- 幾個負載均衡器的小結薦負載
- HAProxy負載均衡器的安裝及配置負載
- 「知行學院」一節課搞懂負載均衡器負載
- Golang負載均衡器Balancer的原始碼解讀Golang負載原始碼
- Eureka詳解系列(一)--先談談負載均衡器負載
- F5負載均衡器的重要引數理解負載
- 在K8S中,負載均衡器有何作用?K8S負載
- F5BIG-IPLTM負載均衡器功能介紹上篇負載
- 用Rust和Pingora輕鬆構建高效負載均衡器RustGo負載
- PHP高效能框架適合負載併發PHP框架負載
- B站邊緣網路四層負載均衡器的探索與應用負載
- 高效能網站實用技巧之負載均衡篇網站負載
- 來!自己動手實現一個loghub(或kafka)分片消費負載均衡器Kafka負載
- ddosify:用Golang編寫的高效能負載測試工具Golang負載
- 負載均衡負載
- linux負載均衡總結性說明(四層負載/七層負載)Linux負載
- gRPC負載均衡(客戶端負載均衡)RPC負載客戶端
- gRPC負載均衡(自定義負載均衡策略)RPC負載
- 不懂高效能的負載均衡設計?沒關係,架構師帶你飛負載架構
- php列印負載函式、Linux awk列印負載PHP負載函式Linux
- NGINX 負載均衡Nginx負載
- WebSocket負載均衡Web負載
- IP負載均衡負載
- 【Nginx】負載均衡Nginx負載
- nginx負載均衡Nginx負載
- Nginx負載配置Nginx負載