DPVS 是 iQiYi 採用 DPDK 技術開發的的高效能四層負載均衡器。與 Linux 核心的 LVS (Linux Virtual Server)相比,DPVS 具有如下特點:
- 更高的效能:DPVS 的包處理速度,1 個工作執行緒可以達到 2.3 Mpps,6 個工作執行緒可以達到萬兆網路卡小包的轉發線速(約 12Mpps)。這是主要因為 DPVS 繞過了核心複雜的協議棧,並採用輪詢的方式收發資料包,避免了鎖、核心中斷、上下文切換、核心態和使用者態資料拷貝產生的效能開銷。
- 更完善的功能:從轉發轉發模式看,DPVS 支援 Direct Routing(DR)、NAT、Tunnel、Full-NAT、SNAT 五種轉發模式,可以靈活適配各種網路應用場景;從協議支援上看,DPVS 支援 IPv4 和 IPv6 協議、且最新版本增加了 NAT64 的轉發功能,實現了使用者從 IPv6 網路訪問 IPv4 服務。
- 更好的維護性:DPVS 是一個使用者態程式,與核心功能相比,功能開發週期更短、除錯更方便、問題修復更及時。
DPVS 自 2017 年 10 月開源以來,受到了業內的廣泛關注。2018 年, DPVS 被列入 DPDK 生態系統。目前,DPVS 開源社群已經有關注度已突破一千,業內數十個開發者參與並貢獻了程式碼,包括 iQiYi 在內的很多公司已經將 DPVS 應用到生產環境。
DPVS 基本原理
DPVS 的總體架構如下圖所示,下面對相關的幾個點著重解釋說明一下。
Master/Worker 模型
DPVS採用經典的 Master/Worker 模型。Master 處理控制平面,比如引數配置、統計獲取等;Worker 實現核心負載均衡、排程、資料轉發功能。另外,DPVS 使用多執行緒模型,每個執行緒繫結到一個 CPU 物理核心上,並且禁止這些 CPU 被排程。這些 CPU 只執行 DPVS 的 Master 或者某個 Worker,以此避免上下文切換,別的程式不會被排程到這些 CPU,Worker 也不會遷移到其他 CPU 造成快取失效。
網路卡佇列 /CPU 繫結
現代的網路卡支援多個佇列,佇列可以和 CPU 繫結,讓不同的 CPU 處理不同的網路卡佇列的流量,分攤工作量,實現並行處理和線性擴充套件。DPVS是由各個 Worker 使用 DPDK 的 API 處理不同的網路卡佇列,每個 Worker 處理某網路卡的一個接收佇列,一個傳送佇列,實現了處理能力隨CPU核心、網路卡佇列數的增加而線性增長。
關鍵資料 per-cpu及無鎖化
核心效能問題的一大原因就是資源共享和鎖。所以,被頻繁訪問的關鍵資料需要儘可能的實現無鎖化,其中一個方法是將資料做到 per-cpu 化,每個 CPU 只處理自己本地的資料,不需要訪問其他 CPU 的資料,這樣就可以避免加鎖。就 DPVS 而言,連線表,鄰居表,路由表等,都是頻繁修改或者頻繁查詢的資料,都做到了 per-cpu 化。
在具體 per-cpu 的實現上,連線表和鄰居表、路由表並不相同。對於連線表,高併發的情況下,不光是查詢,還會被頻繁地新增、刪除。我們讓每個 CPU 維護的是不相同的連線表,不同的網路資料流(TCP/UDP/ICMP)按照 N 元組被定向到不同的 CPU,在此特定 CPU 上建立、查詢、轉發、銷燬。同一個資料流的包,只會出現在某個 CPU 上,不會落到其他的 CPU 上。這樣就可以做到不同的 CPU 只維護自己本地的表,無需加鎖。另一方面,對於鄰居和路由表,這種系統“全域性”的資料,每個 CPU 都是要用到它們的。如果不採用”全域性表+鎖保護“的方式,而要做成 per-cpu,也需要讓每個 CPU 有同樣的檢視,也就是每個 CPU 需要維護同樣的表。對於這兩個表,採用了跨 CPU 無鎖同步的方式,雖然在具體實現上有小的差別,本質上都是通過跨 CPU 通訊(路由是直接傳遞資訊,鄰居是克隆數資料並傳遞分組給別的 CPU),將表的變化同步到每個 CPU。不論用了什麼方法,關鍵資料做到了 per-cpu 之後沒有了鎖的需求,效能也就能提升了。
使用者態輕量級協議棧
四層負載均衡並不需要完整的協議棧,但還是需要基本的網路元件,以便完成和周圍裝置的互動(ARP/NS/NA)、確定分組走向 (Route)、迴應 Ping 請求、健全性檢查(分組完整性,Checksum校驗)、以及 IP 地址管理等基本工作。使用 DPDK 提高了收發包效能,但也繞過了核心協議棧,DPVS 依賴的協議棧需要自己實現。
跨 CPU 無鎖訊息
之前已經提到過這點了。首先,雖然採用了關鍵資料 per-cpu等優化,但跨 CPU 還是需要通訊的,比如:
- Master 獲取各個 Worker 的各種統計資訊
- Master 將路由、黑名單等配置同步到各個 Worker
- Master 將來自 KNI 的資料傳送到 Worker(只有 Worker 能操作 DPDK 介面傳送資料)
既然需要通訊,就不能存在互相影響、相互等待的情況,因為那會影響效能。為此,我們使用了 DPDK 提供的無鎖 rte_ring 庫,從底層保證通訊是無鎖的,並且我們在此之上封裝一層訊息機制來支援一對一,一對多,同步或非同步的訊息。
下圖給出了 DPVS 詳細的功能模組,主要包含如下五大部分:
網路裝置層
負責資料包收發和裝置管理,支援 vlan、bonding、tunnel 等裝置,支援 KNI 通訊、流量控制。
輕量級協議棧層
輕量級的 IPv4 和 IPv6 三層協議棧,包括鄰居、路由、地址管理等功能。
IPVS 轉發層
五種資料轉發模式的連線管理、業務管理、排程演算法、轉發處理等。特別地,Full-NAT 轉發模式下支援了 IPv6-to-IPv4(NAT64) 轉發、 SYN flood 攻擊防禦、 TCP/UDP 的源地址獲取(toa/uoa)等功能。
基礎模組
包含定時器、CPU 訊息、程式通訊介面、配置檔案等基礎功能模組。
控制面和工具
用於配置和管理 DPVS 服務的工具,包括 ipvsadm、keepalived、dpip,也支援使用進行 quagga 叢集化部署。
DPVS 的功能和應用
DPVS 能夠提供靈活多樣的四層資料轉發和負載均衡服務,下面我們通過例項介紹幾個 DPVS 的常用功能和應用場景。
1. 流量均衡
為高併發、大流量的業務提供流量均衡和訪問入口地址是負載均衡器的基本功能。下圖是 DPVS 採用 Full-NAT 轉發方式組成的 IPv6 負載均衡網路:多臺應用伺服器部署在內部網路,並通過 DPVS 組成一個服務叢集;在外部網路, DPVS 為該應用服務叢集提供訪問的外網入口 IP 地址 2001:db8::1(VIP)。使用者對內部應用服務的請求將會通過 DPVS 轉發到內部多臺應用伺服器中的其中一臺上,解決單臺應用伺服器併發、流量等服務能力限制問題。
下面我們給出上述 IPv6 負載均衡網路對應的 DPVS 的網路和業務轉發規則配置方法。
``` ./bin/dpip -6 addr add 2001:db8::1/64 dev eth1 # VIP
./bin/dpip -6 addr add 2001:db8:10::141/64 dev eth1 # local IP
./bin/ipvsadm -At [2001:db8::1]:80 -j enable # TCP
./bin/ipvsadm -Pt [2001:db8::1]:80 -z 2001:db8:10::141 -F eth0 ./bin/ipvsadm -at [2001:db8::1]:80 -r [2001:db8:11::51]:80 -b ./bin/ipvsadm -at [2001:db8::1]:80 -r [2001:db8:11::52]:80 -b ./bin/ipvsadm -at [2001:db8::1]:80 -r [2001:db8:11::53]:80 -b
./bin/ipvsadm -Au [2001:db8::1]:80 # UDP
./bin/ipvsadm -Pu [2001:db8::1]:80 -z 2001:db8:10::141 -F eth0 ./bin/ipvsadm -au [2001:db8::1]:80 -r [2001:db8:11::51]:6000 -b ./bin/ipvsadm -au [2001:db8::1]:80 -r [2001:db8:11::52]:6000 -b ./bin/ipvsadm -au [2001:db8::1]:80 -r [2001:db8:11::53]:6000 -b
DPVS 轉發規則配置結果如下。
Prot LocalAddress:Port Scheduler Flags -> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP [2001:db8::1]:80 wlc synproxy -> [2001:db8:11::51]:80 FullNat 1 0 0 -> [2001:db8:11::52]:80 FullNat 1 0 0 -> [2001:db8:11::53]:80 FullNat 1 0 0 UDP [2001:db8::1]:80 wlc -> [2001:db8:11::51]:6000 FullNat 1 0 0 -> [2001:db8:11::52]:6000 FullNat 1 0 0 -> [2001:db8:11::53]:6000 FullNat 1 0 0
2. NAT64
目前國內網際網路服務正處於 IPv4 到 IPv6 的升級過渡階段,運營商、客戶端已經逐步支援了 IPv6 網路,但對於各種各樣的內部複雜網路應用服務,從 IPv4 切到 IPv6 網路意味著繁重的內部基礎網路的重建和應用服務的開發更新。通過 DPVS 的 Full-NAT 轉發模式提供的 NAT64 轉發功能,可以在不需要改變內部 IPv4 基礎網路的情況下,為內部的應用服務提供 IPv6 的網路接入能力。
下圖給出了這種情況下的網路結構,可以看到,使用者(Client)網路是 IPv6 網路,應用服務(RealServer)的網路是 IPv4 網路,DPVS 使用 IPv6 的 VIP(Virtual IP)和 IPv4 的 Local IP,將 使用者的 IPv6 請求轉發到內部的 IPv4 網路,並將內部 IPv4 應用伺服器的響應資料返回給 IPv6 使用者。
DPVS 的網路和業務轉發規則配置方法如下。
./bin/dpip -6 addr add 2001:db8::1/64 dev eth1 # VIP
./bin/dpip addr add 192.168.88.141/24 dev eth0 # local IP
./bin/dpip addr add 192.168.88.142/24 dev eth0 # local IP
./bin/ipvsadm -At [2001:db8::1]:80 -j enable # TCP
./bin/ipvsadm -Pt [2001:db8::1]:80 -z 192.168.88.141 -F eth0 ./bin/ipvsadm -Pt [2001:db8::1]:80 -z 192.168.88.142 -F eth0 ./bin/ipvsadm -at [2001:db8::1]:80 -r 192.168.12.51:80 -b ./bin/ipvsadm -at [2001:db8::1]:80 -r 192.168.12.53:80 -b ./bin/ipvsadm -at [2001:db8::1]:80 -r 192.168.12.54:80 -b
./bin/ipvsadm -Au [2001:db8::1]:80 # UDP
./bin/ipvsadm -Pu [2001:db8::1]:80 -z 192.168.88.141 -F eth0 ./bin/ipvsadm -Pu [2001:db8::1]:80 -z 192.168.88.142 -F eth0 ./bin/ipvsadm -au [2001:db8::1]:80 -r 192.168.12.51:6000 -b ./bin/ipvsadm -au [2001:db8::1]:80 -r 192.168.12.53:6000 -b ./bin/ipvsadm -au [2001:db8::1]:80 -r 192.168.12.54:6000 -b
DPVS 轉發規則配置結果如下。
Prot LocalAddress:Port Scheduler Flags -> RemoteAddress:Port Forward Weight ActiveConn InActConn TCP [2001:db8::1]:80 wlc synproxy -> 192.168.12.51:80 FullNat 1 0 0 -> 192.168.12.53:80 FullNat 1 0 0 -> 192.168.12.54:80 FullNat 1 0 0 UDP [2001:db8::1]:80 wlc -> 192.168.12.51:6000 FullNat 1 0 0 -> 192.168.12.53:6000 FullNat 1 0 0 -> 192.168.12.54:6000 FullNat 1 0 0
3. SNAT 訪問外網
大部分企業內網的使用者,由於資源、安全等諸多因素的限制,無法直接訪問 Internet 外部網路。DPVS SNAT 轉發模式提供了一種高效能、安全可控的外網訪問方式。如下圖,內網使用者沒有外網網路卡和地址,但想要訪問 Internet 上地址為 59.37.97.124 的網路資源。我們可以通過路由、內網隧道等途徑,讓該使用者對外網的訪問請求經過 DPVS SNAT 伺服器;SNAT 伺服器上配置相應的轉發規則,將使用者的私有內網源地址 102.168.88.59 轉換為 DPVS 的外網地址 101.227.17.140,然後請求 Internet 上的資源。Internet 的資源響應後,響應資料首先發到 DPVS SNAT 伺服器,SNAT 伺服器把目標地址 101.227.17.140 更換回使用者的私有地址 192.168.88.59 後,把響應資料傳給使用者。
DPVS 的網路和 SNAT 資料轉發規則配置方法如下。
./bin/dpip addr add 192.168.88.1/24 dev dpdk0 # VIP
./bin/dpip addr add 101.227.17.140/25 dev bond1 # WLAN IP
./bin/dpip route add default via 101.227.17.254 dev bond1 # default Gateway
# TCP Rule
./bin/ipvsadm -A -H proto=tcp,src-range=192.168.88.1-192.168.88.253,oif=bond1 -s rr ./bin/ipvsadm -a -H proto=tcp,src-range=192.168.88.1-192.168.88.253,oif=bond1 -r 101.227.17.140:0 -J
# UDP Rule
./bin/ipvsadm -A -H proto=udp,src-range=192.168.88.1-192.168.88.253,oif=bond1 -s rr ./bin/ipvsadm -a -H proto=udp,src-range=192.168.88.1-192.168.88.253,oif=bond1 -r 101.227.17.140:0 -J
# ICMP Rule
./bin/ipvsadm -A -H proto=icmp,src-range=192.168.88.1-192.168.88.253,oif=bond1 -s rr ./bin/ipvsadm -a -H proto=icmp,src-range=192.168.88.1-192.168.88.253,oif=bond1 -r 101.227.17.140:0 -J
DPVS 轉發規則配置結果如下。
Prot LocalAddress:Port Scheduler Flags -> RemoteAddress:Port Forward Weight ActiveConn InActConn MATCH icmp,from=192.168.88.1-192.168.88.253:0-0,oif=bond1 rr -> 101.227.17.140:0 SNAT 1 0 0 MATCH tcp,from=192.168.88.1-192.168.88.253:0-0,oif=bond1 rr -> 101.227.17.140:0 SNAT 1 0 0 MATCH udp,from=192.168.88.1-192.168.88.253:0-0,oif=bond1 rr -> 101.227.17.140:0 SNAT 1 0 0
DVPS的效能和高可用性
自 2018年底,DPVS開始釋出 v1.7 版本。該版本的核心功能是支援了 IPv6-to-IPv6包的四層轉發(v1.7.0) 和IPv6-to-IPv4 包的四層轉發 (v1.7.2)。下圖給出了該版本三種協議轉發的效能資料對比(由於測試環境的限制,目前我們只測試了 3 個 Worker 以下的效能資料),可以看到純 IPv6轉發的效能與純 IPv4 轉發效能相當,IPv6-to- IPv4轉發時由於在三層協議轉換時有資料拷貝開銷,效能相對差一些。
DPVS作為四層負載均衡器,在 iQiYi生產環境中運營已有 2年多的時間。目前線上有上千條業務轉發規則,每天承接著來自公司內部、外部共近 5T的服務頻寬和 1Billion併發連線量。下圖是我們一種常用的 DPVS叢集化部署方式:多臺 DPVS伺服器通過等價多路徑路由構成一個服務叢集,每臺 DPVS伺服器配置多臺 nginx作為其後端伺服器,nginx 以反向代理的方式為最終的應用伺服器提供七層負載均衡和高可用性服務。這是一種高可用、高伸縮性的叢集化方式,任何一臺 APPServer或中間轉發節點的故障不會影響整體服務,四層負載均衡器 DPVS、七層反向代理伺服器 nginx和應用伺服器都能在不影響現有服務的條件下很方便地完成擴容。
開源合作
DPVS 作為一個開源專案,歡迎更多的合作和貢獻。目前,DPVS 專案除了 iQiYi 部署和服務化相關的指令碼,程式碼全部託管在 github 上。我們維護 2 個長線分支:
- master(主幹分支):功能穩定的分支,除了緊急 hot bugfix,不接受直接向該分支提交程式碼。
- devel(開發分支):最新的功能會進入該分支,驗證穩定後進入 master 分支。
另外,在每次不向前相容的更新前(比如更新 DPDK 版本),我們會新建一個 LTS 分支,並維護約一年時間。
DPVS 開發合作採用 Git workflow 的工作方式,具體可以參考Contributing文件,我們歡迎更多的開發者的參與和貢獻。