熟悉個人人可能知道我這一年的精力基本都撲在 kube-ovn 這個項目上,天然而然的接觸了不少 ovs 社區的知識。而這篇論文其實很早就看到了,可是當時不少概念都理解不了。通過一年後再翻開,發現已經能明白一些了,因而作一下閱讀心得和本身的思考。算法
固然論文裏的一些東西其實仍是理解的比較模糊,有什麼不對的地方但願大佬們能夠指正。編程
**概述
**緩存
這篇論文發表在 NSDI‘15 上,是網絡系統學術領域中至關好的一個會議了,而論文的做者正是 ovs 的幾位主要做者,早期(包括如今)的代碼基本上都是由他們貢獻的,他們能夠說是介紹 ovs 設計理念和實現細節的地表最強戰隊了。不過這篇論文誕生在五年前,期間不少實現細節可能已經發生了變化,不過做者們思考的問題和解決方式仍是很值得一看。點擊文末的閱讀原文能夠獲取該論文的 pdf 文件。性能優化
論文主要分爲三個部分來介紹設計與實現的內容:網絡
**
OVS 面臨的挑戰**數據結構
相比於傳統硬件交換機相對固定的安裝和配置,軟件交換機在安裝和配置上都更加靈活。隨着虛擬化的流行咱們能夠經過軟件交換機從而實現對網絡的虛擬化,在固定的物理網絡拓撲上按需構建本身所指望的虛擬網絡拓撲。多線程
想象一下一個純物理的網絡環境,你須要本身購買物理的交換機,路由器和防火牆,將一根根網線插入到對應的網絡設備和主機接口之間,再登陸一臺臺設備進行配置。沒加入一個新的物理設備都須要手動的去聯線,若是要對網絡拓撲進行更改或者更換其中某個設備就又是一番體力活。因此咱們傾向於一個固定的物理網絡拓撲來減小維護量。架構
而在虛擬化和容器化流行的今天,接入網絡的計算節點會頻繁變化,每一個用戶都有特殊的網絡規劃和網絡策略,從用戶的視角看也更但願本身處在一個邏輯上獨立的網絡空間。所以網絡虛擬化憑藉其靈活性逐漸進入人們的視野。併發
爲了支持這種網絡的靈活性,ovs 對 OpenFlow 的語法進行了支持,使用戶具備了可對網絡流量進行編程的能力。想象一下傳統的物理交換機,其主要的功能就是根據從某個端口進來的數據包根據其目標 mac 地址選擇發送到哪一個端口,你能夠理解爲它的核心就是一個 mac 地址到交換機端口映射的哈希表,交換機裏面的硬件電路根據從各個端口收集上來的信息對這個哈希表進行查詢和更新。負載均衡
而經過 OpenFlow 你能夠設計針對某個特徵的數據包的處理流水線,這裏的特徵不侷限於 L2 的 mac 地址,能夠是 L3 的 IP 和 L4 的端口信息。最終結果能夠不止是一個轉發動做,能夠經過某些字段的替換,實現路由,防火牆,負載均衡等複雜功能。流水線也能夠不止一個階段,能夠組合多個階段實現不一樣的功能,甚至流水線中能夠經過條件分支選擇不一樣的功能。不過如今一些高級的硬件交換機也具有了L4的能力,可是基本上都是經過固定的硬件流水線來實現,而沒有軟件交換機這種能自定義流水線並快速迭代的能力。(其實支持 OpenFlow 的硬件交換機如今也出現了,軟硬件雙方都在不斷侵蝕對方的地盤)
可是 OpenFlow 的靈活性並非沒有代價的,性能問題成爲了工程中的難題。因爲流水線提供了靈活的編程能力,開發人員又多傾向於經過邏輯上的模塊化設計流水線,一個數據包的流水線可能會包含多個模塊,常常須要幾十個流表項的匹配計算才能得出最終的動做。而流表中又存在優先級的概念,在大規模集羣中極可能要計算了大量不匹配的流表項才找到匹配的流表項。本來硬件電路中一個哈希表查詢的操做,在這裏多是上百條正則匹配的操做。能夠想象若是是這種直接了當的每一個數據包走一遍流表的實現,CPU 很快就會跑滿,性能上是沒法接受的。
下面就將從 ovs 的架構模型和緩存實現的細節來介紹如何解決性能相關的問題。
OVS 設計架構
ovs 的核心架構以下圖所示:
其中和數據流轉發相關的主要是用戶態的 ovs-vswitchd 和內核態的 kernel datapath,其餘組件主要是提供控制平面的功能,未來有機會詳細介紹 ovs 技術細節時會再介紹。
其中 kernel datapath 的核心功能是一個和硬件交換機轉發哈希表相似的哈希表,區別在於硬件交換機大多隻須要根據數據包中目的mac地址便可進行哈希映射查找下一步操做,而 kernel datapath 須要根據數據包中L2到L4的全部信息進行哈希映射找到對應的動做。
而這個哈希映射表該如何構建呢,這就是用戶態 ovs-vswitchd 的功能,它會比對當前數據包的頭部信息和流表的規則,得出一個對應的動做,並把頭部信息和動做的對應關係做爲一個哈希條目插入到內核態 datapath 的哈希表中。所以內核態的哈希表能夠看作是當前流表的一個緩存。
具體來講,在一個數據鏈接的生命週期中,當第一個數據包到達 kernel datapath 中,當前的緩存中並無對應的規則,所以 kernel datapath 會向用戶態的 vswitchd 發送一個請求。vswitchd 計算出對應的規則後再將得出的規則發送給內核態的 datapath,datapath 更新本身的緩存,數據包按規則經過。因爲已經有了緩存的規則,該鏈接上以後的數據包無需再經過用戶態的 vswitchd 計算規則,能夠直接命中內核態的緩存執行對應操做。
這個架構在性能上並非一個最優的選擇。由於數據包的首包須要通過內核態和用戶態的來回切換,會形成額外的延遲,在大量短鏈接的狀況下會形成緩存大量 miss,延遲的效果會更明顯。將 vswitchd 和 datapath 合併在 kernel 來實現從性能角度顯然要更優一些。可是從工程和用戶接受角度來看當前的架構會更靈活一些。
首先,內核模塊的開發調試都有較高的門檻,若是全部數據平面都在內核態實現,那麼新開發者的加入和社區的建設都將面臨很大的挑戰。
其次,對於用戶來講軟件的安裝和更新都是之內核模塊形式進行,這須要有編譯內核模塊的經驗才能正確的使用 ovs,對用戶來講也有很高的門檻,並不利於軟件的推廣。
所以 ovs 目前採用了這種分層的結構,內核態只保留最爲簡單的緩存,能夠保證長時間的穩定,無需由於內核版本的變化而改變。而複雜的功能例如接受 controller 的 flow 信息,適配新的 flow 規則,flow 的計算等較爲多變的功能在用戶態實現,方便開發者和用戶進行快速的迭代。
既然出於工程的緣由選擇了當前架構,那麼如何優化緩存的命中率以及下降內核態和用戶態之間的性能損耗就成了重中之重。論文的剩餘部分就圍繞性能優化展開。
**
優化
**
Tuple Space Search
先來看下用戶態流表規則的計算,若是數據包按照流表的形式進行遍歷計算的話,即便是隻計算首包也須要大量的正則匹配,會消耗大量的 cpu。所以 ovs 並無採用決策樹這種較爲直觀的數據結構去計算規則,而是使用 Tuple Space Search Classifier 算法進行規則計算。
關於 Tuple Space Search (TSS)是一種特殊的數據包分類算法,有專門的論文進行了解釋。舉例來講,OpenFlow 的數據包匹配可能會涉及 L2~L4 的全部字段,然而並非全部的規則都用到了全部的字段,可能大部分只用到 L2 有的用到 L3 ,一小部分用到 L4,咱們能夠根據流表的內容將規則收斂到幾個只包含特定字段的哈希表,而後將數據包和每一個哈希表用到的字段進行匹配根據優先級獲得最終的結果。
相比較單一一個包含全部字段的哈希表,TSS 能夠極大下降內存的消耗。據估算包含全部字段的哈希表須要的條目數將會是2的275次方。而相比於決策樹,TSS 在規則的更新和增長上的複雜度會更低一些。
MicroFlow Caching
在 ovs 的早期設計中 kernel datapath 並無使用上面複雜的分類算法,而是使用了最簡單的將全部字段進行哈希的一個單一的哈希表。
這種模式下任意一個字段的變化都會致使緩存 miss,會觸發用戶態 vswitchd 去計算下一步動做,所以緩存從用戶態到內核態的下發時間變得十分重要。在這方面 ovs 作了大量的工程方面優化,例如緩存的批量下發,多核並行,減小系統調用次數等等。在哈希表的實現上也用到了不少新技術,例如 cuckoo hasing 和內核的 RCU 機制來保證併發的和 worst case 下的性能。
MegaFlow Caching
在一般的流量模式下,MicroFlow 能達到不錯的性能。然而在一些特殊的流量模式下,例如端口掃描,每一個鏈接數據包頭部都不同,cache 會大量 miss,ovs 須要來回在內核態和用戶態之間進行計算更新,CPU 資源會被耗盡。
爲了不這種性能極度惡化的狀況,ovs 引入了 MegaFlow。和 MicroFlow 的精確匹配不一樣,MegaFlow 能夠作到模糊匹配,一個條目能夠匹配一組數據包。它的實現和用戶態的 TSS 相似,可是在組織上有所不一樣。一是沒有優先級,這樣能夠快速返回無需遍歷全部的哈希表;二是 MegaFlow 中不像用戶態中大量 table 組成了 pipeline,只經過一個 table 來進行匹配。
MegaFlow Cache 性能最關鍵的就是看如何能實現更好的泛化能力,即每一個條目都能匹配儘量多的數據包,減小用戶態和內核態之間進行交互的次數。同時須要儘量下降哈希查詢的次數,在儘量少的表裏獲得預期的結果。
論文中介紹了多個優化的細節,例如用 Prefix Tracking 來對 IP 範圍和端口範圍進行聚類;使用分段查詢避免某個 L4 的規則更新致使全部的 Cache 失效。以及利用優先級對多個哈希表排序,下降平均查詢次數等等。
**
Cache Invalidation**
前面介紹了不少緩存實現的細節,能夠看到 ovs 的緩存體系已經比較複雜了,帶來的負面影響就是緩存的失效更新也變的十分複雜。MicroFlow 因爲是精確匹配能夠很容易的判斷哪一個條目失效了,而 MegaFlow 採用的是模糊匹配,那麼當一條 OpenFlow 規則更新時,判斷這個規則影響了哪些緩存就變的十分困難。若是控制平面規則變化頻繁,那麼緩存更新的計算一樣會消耗大量的資源。
在這裏做者兜了個大彎子,先是介紹了將流表變動進行分類。簡單的可追蹤的變動直接更新對應的 MegaFlow,對於影響範圍大很差判斷的變動須要從新 validate 全部的 MegaFlow。
然而隨着 ovs 的發展,流表的功能愈來愈複雜,幾乎全部的變動都變成了難以判斷影響範圍的變動,所以 ovs 最終放棄了對變動進行分類,轉而使用多線程來對全部 MegaFlow 進行更新。
**
一點小想法**
最近因爲在對 kube-ovn 進行了比較多的穩定性和性能方面的測試,也不斷的接觸到了不少性能相關的問題。ovs 最先是爲虛擬化進行設計的,其最爲核心的競爭力仍是在經過 OpenFlow 實現網絡拓撲的可編程和高度的靈活性,隨之而來在性能上會帶來一些損失。緩存對相對固定的流量模式有很好的加速效果,可是因爲 ovs 的靈活性,網絡拓撲和流量模式的變化也會更爲頻繁。在容器化的工做負載下,因爲迭代速度的加快,工做負載的增多,網絡的變化會更加的頻繁。
如今 dpdk 的用戶態加速方案和智能網卡的流表 offload 均可以很好的解決吞吐量和延遲的問題,可是因爲對硬件條件的依賴,在一些狀況下不能很好的實施。一套純軟件的解決方案依然頗有必要。
從性能角度來看其實仍是有一些優化的空間,例如:
固然這篇論文已是五年前的事情了,不少設計可能都發生了改變,歡迎大佬的指正。點擊閱讀原文能夠得到這篇論文的 pdf 文件。