上一節(jié)交代了Neutron基本的組網(wǎng)原理,本節(jié)我們來看一看Neutron在軟件層面的實現(xiàn)。
在架構(gòu)設(shè)計上, Neutron沿用了OpenStack完全分布式的思想,各組件之間通過消息機制進行通信,使得Neutron中各個組件甚至各個進程都可以運行在任意的節(jié)點上,如下圖所示。這種微內(nèi)核的架構(gòu)使得開發(fā)者可以集中精力在網(wǎng)絡(luò)業(yè)務(wù)的實現(xiàn)上。目前Neutron提供了眾多的插件與驅(qū)動,基本上可以滿足各種部署的需要,如果這些還難以支撐實際所需的環(huán)境,則可以方便地在Neutron的框架下擴展插件或驅(qū)動。
上圖中,除了消息機制以外還涉及5類Neutron組件,neutron-server,neutron agent,neutron plugin,neutron database,neutron provider,下面先對這幾個組件是什么進行簡單的介紹,再按照根據(jù)這幾個組件間的交互來介紹Neutron主體代碼的實現(xiàn)。
在開始分析代碼之前,還需要先對neutron-plugin進行一點更為詳細的介紹,neutron-plugin分為core-plugin和service-plugin兩類。
Core-plugin,Neutron中即為ML2(Modular Layer 2),負責管理L2的網(wǎng)絡(luò)連接。ML2中主要包括network、subnet、port三類核心資源,對三類資源進行操作的REST API被neutron-server看作Core API,由Neutron原生支持。其中:Network | 代表一個隔離的二層網(wǎng)段,是為創(chuàng)建它的租戶而保留的一個廣播域。subnet和port始終被分配給某個特定的network。Network的類型包括Flat,VLAN,VxLAN,GRE等等。 |
Subnet | 代表一個IPv4/v6的CIDR地址池,以及與其相關(guān)的配置,如網(wǎng)關(guān)、DNS等等,該subnet中的 VM 實例隨后會自動繼承該配置。Sunbet必須關(guān)聯(lián)一個network。 |
Port | 代表虛擬交換機上的一個虛機交換端口。VM的網(wǎng)卡VIF連接 port 后,就會擁有 MAC 地址和 IP 地址。Port 的 IP 地址是從 subnet 地址池中分配的。 |
上一節(jié)曾經(jīng)提到,“Neutron對Quantum的插件機制進行了優(yōu)化,將各個廠商L2插件中獨立的數(shù)據(jù)庫實現(xiàn)提取出來,作為公共的ML2插件存儲租戶的業(yè)務(wù)需求,使得廠商可以專注于L2設(shè)備驅(qū)動的實現(xiàn),而ML2作為總控可以協(xié)調(diào)多廠商L2設(shè)備共同運行”。在Quantum中,廠家都是開發(fā)各自的Service-plugin,不能兼容而且開發(fā)重復(fù)度很高,于是在Neutron中就為設(shè)計了ML2機制,使得各廠家的L2插件完全變成了可插拔的,方便了L2中network資源擴展與使用。
(注意,以前廠商開發(fā)的L2 plugin跟ML2都存在于neutron/plugins目錄下,而可插拔的ML2設(shè)備驅(qū)動則存在于neutron/plugins/ml2/drivers目錄下)
ML2作為L2的總控,其實現(xiàn)包括Type和Mechanism兩部分,每部分又分為Manager和Driver。Type指的是L2網(wǎng)絡(luò)的類型(如Flat、VLAN、VxLAN等),與廠家實現(xiàn)無關(guān)。Mechanism則是各個廠家自己設(shè)備機制的實現(xiàn),如下圖所示。當然有ML2,對應(yīng)的就可以有ML3,不過在Neutron中L3的實現(xiàn)只負責路由的功能,傳統(tǒng)路由器中的其他功能(如Firewalls、LB、VPN)都被獨立出來實現(xiàn)了,因此暫時還沒有看到對ML3的實際需求。
=============================== Codes Start ==================================
一般而言,neutron-server和各neutron-plugin部署在控制節(jié)點或者網(wǎng)絡(luò)節(jié)點上,而neutron agent則部署在網(wǎng)絡(luò)節(jié)點上和計算節(jié)點上。我們先來分析控制端neutron-server和neutron-plugin的工作,然后再分析設(shè)備端neutron-agent的工作。
=============================== 控制端的實現(xiàn) ==================================
從neutron-server的啟動開始說起。neutron-server的啟動入口在neutron.server.__init__中,主函數(shù)中主要就干了兩件事,第一是下圖l 48處啟動wsgi服務(wù)器監(jiān)聽Neutron REST API,第二是在l 52啟動rpc服務(wù),用于core plugin與agent間的通信,兩類服務(wù)作為綠色線程并發(fā)運行。從SDN的角度來看,wsgi負責Neutron的北向接口,而Neutron的南向通信機制主要依賴于rpc來實現(xiàn)(當然,不同廠家的plugin可能有其它的南向通信機制)。
(一)WSGI
從48行代碼回溯WSGI的機理。向前追蹤到neutron.service中的_run_wsgi方法,該方法中加載了各類Neutron資源(l 173),實例化wsgi server(l 177),然后啟動wsgi socket(l 178),這樣neutron-sever就開始監(jiān)聽Neutron REST API了。177和178行沒什么好說的,173行后面的學問比較多,下面來具體分析一下。
Load_paste_app這個方法屬于wsgi的paste deployment,用于發(fā)現(xiàn)、配置、連接WSGI Application和Server。對于Neutron來說就是在neutron –server和各類Neutron資源建立關(guān)系,使得neutron-server能夠?qū) plugin中資源進行操作的REST API分發(fā)給A plugin,將對B plugin中資源進行操作的REST API分發(fā)給B plugin。Neutron的paste deployment配置文件目錄路徑為/etc/neutron/api-paste.ini,該文件中設(shè)計Neutron REST API的主要涉及這么幾行,如下圖所示。
Sevice-plugins的處理發(fā)生在neutron.api.extensions文件的plugin_aware_extension_middleware_factory方法中。Filter_factory返回neutron.api.extensions中的ExtensionMiddleware(l 388),在該過程中實例化了/etc/neutron/neutron.conf中配置的core-plugin和service-plugin(l 387),ExtensionMiddleware則負責了擴展資源URL的生成和資源的Application化。這部分的代碼是在太過于底層,比較讓人頭疼,就沒有細致的捉摸,不過其大致的處理思路和下面要說的core_plugin是類似的。
Core-plugin的處理發(fā)生在neutron.api.v2.router.APIRouter。APIRouter類在實例化的過程中,首先獲得core_plugin(l 76)然后生成core_plugin資源的URL(l 103),最后將資源Application化為Controller的實例(l 104,l 109),Controller實例即支持大家喜聞樂見的“REST資源的增刪改查”。這樣當wsgi server收到REST API請求后,就能夠根據(jù)請求中的URL找到資源的Controller,然后Controller會自動拼接字符串,得到并調(diào)用相應(yīng)的core_plugin方法,比如所請求操作的資源是network,Action是“Create”,則應(yīng)該調(diào)用的core_plugin方法就是“create_network”。
默認配置中Neutron的core_plugin為ML2,ML2在執(zhí)行create_network方法時(neutron.plugins.ml2.plugin,l 365),首先通過Type Manager分配可用的network id(l 384),并向neutron database進行注冊(l 385),然后通過Mechanism Manager將該network的參數(shù)傳給底層各個Mechanism Driver(neutron.plugins.ml2.managers,l 193),Mechanism Driver再具體進行執(zhí)行(l 168)。
講到這里Neutron北向接口wsgi的部分就分析完了,而REST API中業(yè)務(wù)請求在網(wǎng)絡(luò)設(shè)備中的落實則主要通過rpc機制來完成。
(二)RPC
控制端neutron-plugin與設(shè)備端相應(yīng)neutron-agent間的通信一般由rpc機制實現(xiàn)。在開始進入Neutron rpc服務(wù)的代碼分析前,我們需要先簡單地了解一下rpc。
Openstack 組件內(nèi)部rpc機制的實現(xiàn)基于 AMQP作為通訊模型。AMQP 是用于異步消息通訊的消息中間件協(xié)議,有四個重要的概念:
Exchange:根據(jù)Routing key轉(zhuǎn)發(fā)消息到不同的Message Queue中。Routing key:用于Exchange判斷哪些消息需要發(fā)送對應(yīng)的Message Queue。Publisher:消息發(fā)送者,將消息發(fā)送給Exchange并指明Routing Key。Consumer:消息接受者,從Message Queue獲取消息,并在本地進行執(zhí)行。總體來說:消息發(fā)布者Publisher將Message發(fā)送給Exchange并且說明Routing Key。Exchange 負責根據(jù)Message的Routing Key進行路由,將Message正確地轉(zhuǎn)發(fā)給相應(yīng)的Message Queue。Consumer將會從Message Queue中讀取消息。
Publisher可以分為4類:Direct Publisher發(fā)送點對點的消息;Topic Publisher采用“發(fā)布——訂閱”模式發(fā)送消息;Fanout Publisher發(fā)送廣播消息的發(fā)送;Notify Publisher同Topic Publisher,發(fā)送 Notification 相關(guān)的消息。類似地,Exchange可以分為3類:Direct Exchange根據(jù)Routing Key進行精確匹配,只有對應(yīng)的 Message Queue 會接受到消息;
Topic Exchange根據(jù)Routing Key進行模式匹配,只要符合模式匹配的Message Queue都會收到消息;Fanout Exchange將消息轉(zhuǎn)發(fā)給所有綁定的Message Queue。
了解了基礎(chǔ)知識以后,我們接著從neutron_server啟動文件主函數(shù)(neutron.server.__init__)中來分析各個neutron-plugin中rpc服務(wù)的啟動。從L 52中調(diào)用的serve_rpc方法回溯,發(fā)現(xiàn)該方法(neutron.service l 141)只啟動了core_plugin的rpc監(jiān)聽(l 142,l 157,l 160),而沒有顯示地啟動service_plugins的rpc,實際上service_plugins的rpc在各個plugins實例化(在serve_wsgi方法中完成)的過程中已經(jīng)啟動了。
Neutron中控制端neutron-plugin和設(shè)備端相應(yīng)的neutron-agent間rpc通信是單向異步的, plugin和agent上都要開啟Publisher和Consumer。由于同一類的agent往往不止一個,因此Neutron rpc采用“發(fā)布——訂閱”模式在plugin和agent間傳遞消息,在Publisher和Consumer實例化時需要指定全局唯一的Topic(規(guī)定在neutron.common.topics)。
Neutron中Publisher類的命名規(guī)范為**AgentNotifierApi,它們的實例可以向特定的Consumer發(fā)送消息,Consumer接收到消息后,通過dispatcher解封裝,在調(diào)用**RpcCallBacks在本地執(zhí)行消息體。Core_plugin在構(gòu)造函數(shù)執(zhí)行過程中實例化了AgentNotifierApi,其RpcCallBacks的實例化由neutron-server主函數(shù)中的l 52完成,如下第一張圖所示;而service_plugins在構(gòu)造函數(shù)執(zhí)行過程中一起實例化了AgentNotifierApi和RpcCallBacks,如下第二張圖所示。思路就是這個樣子了,具體的代碼就不再一行一行分析了。
===================== 設(shè)備端的實現(xiàn) ========================
控制端neutron-server通過wsgi接收北向REST API請求,neutron-plugin通過rpc與設(shè)備端進行南向通信。設(shè)備端agent則向上通過rpc與控制端進行通信,向下則直接在本地對網(wǎng)絡(luò)設(shè)備進行配置。Neutron-agent的實現(xiàn)很多,彼此之間也沒什么共性的地方,下面選取比較具有代表性的ovs-neutron-agent的實現(xiàn)進行簡單的介紹。
Ovs-neutron-agent的啟動入口為/neutron/plugins/openvswitch/agent/ovs-neutron-agent.py中的main方法,其中負責干活的兩行代碼在l 1471和l 1476。L 1471實例化了OVSNeutronAgent類,負責在本地配置OVS,而l 1476則啟動了與控制端的rpc通信。
OVSNeutronAgent的實例化過程中主要干了如下6個工作,其中各個網(wǎng)橋的作用請參考上一小節(jié)的介紹,流表的具體邏輯這里也不細談了,下一節(jié)內(nèi)容會專門講。
rpc_loop做的工作主要就是輪詢一些狀態(tài),根據(jù)這些狀態(tài),進行相應(yīng)的操作。比如一旦探測到本地的OVS重啟了(l 1295,l 1309),就重新創(chuàng)建本地的網(wǎng)橋(l 1294-1300),并重新添加port(l 1336);再比如一旦rpc監(jiān)聽到update_port事件(l 1309),則在本地使能相應(yīng)的port(l 1336)。
L 1336執(zhí)行的process_network_ports方法(l 1129)有必要說一下,對port的增加和更新的處理調(diào)用treat_devices_added_or_updated方法(l 1152),對port的刪除調(diào)用treat_devices_removed(l 1176)方法。Treat_devices_added_or_updated方法(l 1008)調(diào)用treat_vif_port方法(l 1038),treat_vif_port方法(l 936)中調(diào)用port_bound方法(l 948),port_bound方法(l 597)調(diào)用了provision_local_vlan方法(l 611)。Provision_local_vlan方法(l 436)向各個網(wǎng)橋下發(fā)相應(yīng)的流表,完成port與本地vlan_id的綁定工作。
ovs-neutron-agent的啟動也就是這些工作了,啟動完畢后,便開始了與相應(yīng)plugin(OVS Plugin或者OVS Mechanism Driver)的rpc通信。
=============================== Codes End ===================================
Neutron的軟件實現(xiàn)就簡單地介紹到這里了,下一節(jié)我們來具體看看Neutron中各個OVS上的流表邏輯是怎樣的。
作者簡介:
張晨,2014/09-至今,北京郵電大學信息與通信工程學院未來網(wǎng)絡(luò)理論與應(yīng)用實驗室(FNL實驗室)攻讀碩士研究生。
主要研究方向:SDN、虛擬化、數(shù)據(jù)中心
個人博客:sdnv.xyz
個人郵箱:[email protected]