一文理解OpenStack網絡

摘要:如果你能理解OpenStack的網絡,那麼對於其他雲平臺的網絡,應該也可以通過分析後理解掌握了。

本文分享自華爲雲社區《《跟唐老師學習雲網絡》 - OpenStack網絡實現》,作者: tsjsdbd 。

整體設計

首先,OpenStack是用來管理大量的VM的“上帝”。他的目的是要像掌控物理世界一樣,去管理大量的VM。即:可以給VM分組,同一個組裏面的VM,在同一個網絡內,可以互通通信。不同組的VM,則相當於在不同的網絡中,互相不能通信。

至於爲什麼要分組,

1、是跟物理服務器一樣,那麼多機器,按照不同機房的服務器,連到不同的網絡。

2、是我可以把不同組的VM,賣給不同的“用戶”,這樣,組1的VM屬於張三,組2的VM屬於李四,這樣他們倆互相隔離,互不感知。於是我就可以化身成爲雲廠商,對外提供雲平臺服務了。

  • 當然你作爲雲廠商,也肯定要允許一個用戶,可以擁有2個網絡嘛。萬一該客戶人傻錢多,就是買了一堆VM,分着玩呢,是吧。

邏輯視圖

現實中,2個機房的服務器,網絡要想連通,是要靠路由器來幫忙的。在虛擬世界中也是類似的。

所以,邏輯上,VM世界的網絡就是長這個樣。

張三的VM的網絡,要想和李四VM的網絡 互通,或者張三自己的2個獨立網絡互通。就得通過一個叫做 Router 的“虛擬路由器”來完成。

物理視圖

上面的邏輯視圖,在物理上,則是這個樣子的:

至於如何在一根網線上面,同時跑多個虛擬網絡的報文。這個就是在一根網線上的報文,有不同門派的意思。具體的可以回去看VLAN/VxLAN章節。

另外,這裏你可以看到,虛擬網絡裏面的一個“Router”,其實不是什麼具體的虛擬路由器設備,而僅僅是一個“網絡namespace+轉發規則”就達成了,下面會細講。

簡單模型

假設現在你來設計OpenStack的網絡實現。

那從我們之前學到的OVS章節,可以知道,爲了達成上面提到的OpenStack的網絡虛擬化目的。最簡單的實現是給每個物理服務器上增加一個OVS虛擬交換機;然後每個VM都連到OVS端口上,每個端口則按照分組,打上對應的VLAN標籤。就可以達成基本要求。

但是,這個初始1.0版本的實現,有個不牛批的地方,就是沒法給VM設置安全組。你作爲想成爲雲平臺偉大目標,平臺怎麼能沒有安全組這個能力呢(雖然,在VM裏面,可以設置firewell或者iptables規則,但是VM裏面,那是已經賣給用戶的了,你跑人家房間裏面,去設置規則並不合適,可能和用戶自己的業務規則衝突)。

所以第2個版本,改進之。我們要在VM的外面設置安全組:

於是,我們在每個VM大門口,增加一個Bridge網橋,任何VM的流量,都會經過這個Bridge。這樣,通過在Bridge上面,增加iptables規則,就可以達到給VM設置安全組的目的了。(注意,這個時候,VM的報文還沒有到OVS,所以報文還是沒有打VLAN標籤的原始報文,所以iptables規則也好實現)。

這就是咱們的OpenStack網絡2.0版本。

但是,在實踐中,你發現這個獨苗OVS,要設置端口轉發規則有2部分:

  • 上半部分。即:給VM設置Tag標籤。

每增加一臺VM時,就給這個端口打標籤,插拔虛擬網線等配置動作。這一部分邏輯比較固定,不怎麼變化。

  • 下半部分。即:通過物理網線,怎麼給報文打“門派”標記。

這一部分變化很大,有時候物理網絡,咱得走GRE,有時候要走VLAN,有時候又得VxLAN。還有時候,得走專用的網絡設備。平臺得根據部署的機房網線,定製不同的規則。

這樣這2類OVS的規則不好管(都放openflow的轉發表裏面),本着程序員的“分庫分表”(或者咱們寫代碼時的,“抽取函數”的邏輯)的思路,咱們把1個獨苗OVS,分成多個VOS。分別做不同的事情。

於是,咱們到了3.0版本,這個版本就比較通用了。基本可以和實際OpenStack的網絡比較接近了。不過在網絡節點部分,還得再增強一下。就是咱們的VM,除了互訪之外(流量還在幾臺物理服務器之間轉悠),還得訪問外網呀(流量跑機房外部去)。 所以,還得繼續增強一下VM訪問外部網絡的能力:

這麼一來,就到了差不多4.0版本了。後面咱們介紹的OpenStack網絡,就是照着這個版本來的。在OpenStack網絡裏面,對各種ovs,bridge,接口的命名,有一套自己的規範。不像上面這麼隨意的取名。

控制節點

有了上面這些給VM設置虛擬網絡用的模型,那麼要搞成自動化(即:每創建一個VM,給它設置好配套的虛擬網線連接)。 你得寫個主控程序,用來控制這些計算節點上面的行爲吧。如下:

所以,按照OpenStack官方的架構,它需要有3種節點:管節點,計算節點,網絡節點。

每個節點上面,部署了一堆agent,用來接收老大的控制命令。老大就是Master管理節點了。

注:之前章節也提過,分佈式系統,可靠的控制都需要有個“代理商”,比如RabbitMQ(OpenStack選了這個),ETCD(Kubernetes選了這個),ZooKeeper(Hadoop選了這個)這種。

(1)首先是最上面紅色的線,就是“主控邏輯”用來控制Agent幹活的,簡稱管理面網絡。

(2)然後是中間綠色的線,那就是VM們在這根網線上面,發送大量的“自己門派”的報文,即VM間互相通信,都要走的網絡,數據量很大。簡稱數據面。

爲了確保管理面和數據面隔離,互不影響(即:VM瘋狂發包,別把管理命令的報文給衝沒了)。每臺物理服務器上面,得有2塊網卡,一塊用來走管理網線,一塊用來走數據網線。

(3)接着就是左下角的墨綠色線,這個是VM們訪問機房外部網絡用的。只需要網絡節點,有一個額外的網卡就行了。

(4)最後是紫色的線。你的主控邏輯,要不要包裝成API接口,對外部暴露訪問通道? 要的話,可以加上。不要的話,那就每次都登陸到主控節點裏面,手動敲命令控制也行。

計算節點

這裏咱們打開一個OpenStack的計算節點,看看它的網絡構造,遙記當年(2013年,OpenStack版本Havana)我學OS網絡的時候,看到一個資料,對我幫助很大,這裏直接貼上來:

照着前文的“設計思路”,你應該可以看懂上圖的網絡邏輯了吧。命名上,一般內部的ovs叫 br-int。隧道的叫 br-tun。如果你有環境的話,在節點上面查詢,使用各種網絡命令(ip,ovs-vsctl,brctl等),你可以證實一下。

網絡節點

同樣,網絡節點,網絡組成如下:

其中,上部的紅色虛線,表示一個 網絡namespace。dnsmasq是一個DHCP服務器(自動分配IP的程序,用來給VM分配IP地址)。

這個節點也使用各種網絡命令來查詢查看確認。

ps,由於該網絡節點上有很多網絡namespace,所以記得使用 ip netns exec命令來進入到對應的ns查詢這個虛擬空間裏面的詳情。

floating IP(EIP)

VM除了有自己的虛擬網絡內的IP,還可以擁有一個floating IP(注:對應雲廠商,一般把這個叫做 EIP)。咱們來看下這個“浮動IP”是個什麼實現邏輯。

邏輯概念

首先,浮動IP,是物理網絡世界的,即OpenStack的外部網絡的。它是一個真實存在的IP地址(不跟VM一樣,那是你虛擬出來的IP)。

如上圖,對floating IP,我總結的一句話概況就是:VM對外的名號。

當你從外部網絡,訪問這個“浮動IP”,就等於訪問這一臺VM。至於爲什麼要叫“浮動”這個詞,是因爲這個名號,會漂移。

舉個例子:“護國大法師”這個名號很響亮,當你一報你要找“護國大法師”這個人時,大家都知道你要找具體的誰。但是這個“護國大法師”名號,是可以從一個人身上轉移到另一個人身上的。

直接對應雲廠商的EIP,是不是就好理解了。

具體實現

我們關注點,直接聚焦到網絡節點的一個namespace裏面。(本例的浮動IP是 192.168.101.3)。如下圖:

在網絡節點,查詢ns。

root@netnode:/# ip netns
qdhcp-a7e512cf-1ca0-4ec7-be75-46a8998cf9ca
qrouter-4cdb0354-7732-4d8f-a3d0-9fbc4b93a62d

找到對應的 router 那個ns(上圖五角星處),然後查詢這個裏面的網卡信息:

root@netnode:/# ip netns exec qrouter-4cdb0354-7732-4d8f-a3d0-9fbc4b93a62d ip address

11: qg-1423ba35-7c: <BROADCAST,MULTICAST,PROMISC,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN 
    inet 192.168.101.2/24 brd 192.168.101.255 scope global qg-1423ba35-7c
    inet 192.168.101.3/32 brd 192.168.101.3 scope global qg-1423ba35-7c

12: qr-9f1fa61e-1e: <BROADCAST,MULTICAST,PROMISC,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN 
    inet 172.17.17.1/24 brd 172.17.17.255 scope global qr-9f1fa61e-1e

可以看到,有個叫qg-xx的網卡,擁有了這個 floating IP地址。

然後我們查詢一下這個ns裏面的iptables規則:

root@netnode:/# ip netns exec qrouter-4cdb0354-7732-4d8f-a3d0-9fbc4b93a62d iptables -t nat -S

你會發現有這麼2條規則:

-A quantum-l3-agent-float-snat -s 172.17.17.2/32 -j SNAT --to-source 192.168.101.3
-A quantum-l3-agent-PREROUTING -d 192.168.101.3/32 -j DNAT --to-destination 172.17.17.2

第1條是SNAT規則,就是把源IP地址換掉的意思。 具體內容是:如果源IP是 172.17.17.2 的(VM的),那麼把源IP換成192.168.101.3(floatingIP的)。

第2條是DNAT規則,就是把目的IP地址換掉。具體內容是:如果目的IP的192.168.101.3(floatingIP的),就把目的IP換成172.17.17.2 的(VM的)。

這樣一來,報文不就統統轉給了這個VM嘛。

於是,一個VM一旦擁有了floatingIP(也叫EIP),它就可以被外網訪問,也可以直接訪問外網。

不過,真正的外部IP,可能是有限的,得省着點用,這就有了下面的SNAT和DNAT功能。

SNAT功能

如果一臺VM,想訪問外部網絡,但是又不給它分配floatingIP。這時候就可以使用SNAT。

還是上一節的這個ns,查詢這個ns裏面的網卡信息,可以看到,還有一個 101.2 的IP。

root@netnode:/# ip netns exec qrouter-4cdb0354-7732-4d8f-a3d0-9fbc4b93a62d ip address

11: qg-1423ba35-7c: <BROADCAST,MULTICAST,PROMISC,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN 
    inet 192.168.101.2/24 brd 192.168.101.255 scope global qg-1423ba35-7c
    inet 192.168.101.3/32 brd 192.168.101.3 scope global qg-1423ba35-7c

12: qr-9f1fa61e-1e: <BROADCAST,MULTICAST,PROMISC,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN 
    inet 172.17.17.1/24 brd 172.17.17.255 scope global qr-9f1fa61e-1e

還是查詢一下這個ns裏面的iptables規則:

root@netnode:/# ip netns exec qrouter-4cdb0354-7732-4d8f-a3d0-9fbc4b93a62d iptables -t nat -S

你會發現有這麼1條規則:

-A quantum-l3-agent-snat -s 172.17.17.0/24 -j SNAT --to-source 192.168.101.2

該規則是,所有源IP是 172.17.17.0/24 這個網段的報文(就是該網絡內的所有VM),都把源IP地址換掉的意思。

所以,一旦給一個虛擬網絡設置了SNAT功能,那麼這個網絡裏面的所有VM,都可以訪問外網了。只是大家共用一個外部出口IP地址(本質還是EIP),這個就是省着點用的意思。

缺點是:只能從內部(虛擬網絡)訪問外部(外部網絡),外部不能訪問內部(畢竟,這個IP是大家共用的,不是某一臺VM的)。

DNAT功能

在本着省着點用的原則下(即好多VM共享一個外部IP)。如果希望外部訪問內部VM,還可以使用DNAT功能。

原理上,你應該想到了,就是在ns裏面增加一條,根據不同的端口,轉發不同目的IP地址的DNAT規則。

這一種省錢的辦法,缺點是:只能指定對應的目的端口。比如,外部端口80,分配給VM1佔用了。那麼VM2就不能用80了,它只能委屈下,使用外部端口號81(或其他)了。

省錢總得要失去點什麼,要不然,給每臺VM都買個EIP不就完了。

Router

OpenStack裏面的Router,是用來將“一個網絡”,連接到“另一個網絡”的。可以是2個虛擬網絡,也可以是1個虛擬網絡+1個實際外部網絡。

一個Router本質是一個 網絡namespace,同上一個章節描述浮動ip一樣,這個ns是一個虛擬的“中轉站”。 所有的網絡連接,需要先到這個中轉站“休息打扮一下”,然後再前往目的網絡。

注意,在同一個用戶的2個網絡互聯,和2個不同用戶的網絡互聯,在底層實現的技術上是一樣的。不同點是不同用戶的話,需要控制好權限,不然張三不就可以隨便去連李四的網絡了。

Router概念,對應到雲廠商,一般叫 “VPC互聯”。產品各式各樣,比如以前的“vpc peering”,現在的“雲企業網絡”“企業路由”“雲連接”等。

Metadata服務

metadata服務,就是允許每個VM去問上帝(OpenStack平臺):“你創建我的檔案上面,都寫了些什麼?”。 這是一個非常有意思的特性。

功能介紹

你(VM)去問上帝,你總得知道上帝在哪裏把?所以在OpenStack上,將上帝的地址,寫死了一個特殊的IP:169.254.169.254, 挺好記的。

詢問上帝的方法:

$ curl http://169.254.169.254
1.0
2007-01-19
2007-03-01
2007-08-29
2007-10-10
2007-12-15
2008-02-01
2008-09-01
2009-04-04
latest

你可以去試一下,如果發現這個IP可以訪問,說明你可以證明自己的機子是一臺被虛擬出來的VM,而不是一臺物理機了。

舉個例子,VM問:我被生出來後的啓動腳本是什麼?

即問自己的 “userdata信息”

$ curl http://169.254.169.254/openstack/latest/user_data
#!/bin/bash
echo 'Extra user data here'

這個功能,還是比較有用的,特別是在做VM自動化的時候(ps,可以去查一下一個稱作 cloud-init 的東西)。

metadata特性應該是來自AWS。OpenStack爲了兼容AWS的這個“詢問上帝”的功能(當然,肯定也是認可這個功能還是有用的)。也支持了這個 metadata服務。

具體實現

我們知道創造&管理VM的組件,是叫Nova。也就是metadata特性,要從虛擬世界(VM裏面)去訪問物理世界(Nova的API),經過上面的介紹,這種情況下,肯定要經過一個“中轉站”的。

我們先看下VM內部,訪問169.254.169.254的時候,報文去哪裏了:

在VM裏面敲:

ip route
default via 172.17.17.1 dev eth0
172.17.17.0/24 dev eth0 src 172.17.17.5
169.254.169.254 via 172.17.17.1 dev eth0

可以看到 訪問“上帝”時,報文去了 VM網絡的網關IP(172.17.17.1)那了。

那麼網關IP在哪裏?在網絡節點的namespace裏面:

root@netnode:/# ip netns exec qrouter-4cdb0354-7732-4d8f-a3d0-9fbc4b93a62d ip address

裏面有個網卡叫做:

12: qr-9f1fa61e-1e: <BROADCAST,MULTICAST,PROMISC,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN 
    inet 172.17.17.1/24 brd 172.17.17.255 scope global qr-9f1fa61e-1e

我們再來看下,訪問169.254的報文,到這個“中轉站”後,被如何“拿捏”的。

root@netnode:/# ip netns exec qrouter-7a44de32-3ac0-4f3e-92cc-1a37d8211db8 iptables -S

可以看到,目的地址是169.254的報文,會轉給本地的 9697端口。

-A quantum-l3-agent-PREROUTING -d 169.254.169.254/32 -p tcp -m tcp --dport 80 -j REDIRECT --to-ports 9697
-A quantum-l3-agent-INPUT -d 127.0.0.1/32 -p tcp -m tcp --dport 9697 -j ACCEPT

那麼,誰在本地(這個namespace內)監聽 9697端口呢?答案是上帝的代理,一個agent在這裏偷聽呢。

root@netnode:/# ip netns exec qrouter-7a44de32-3ac0-4f3e-92cc-1a37d8211db8 netstat -anpt
tcp        0      0 0.0.0.0:9697            0.0.0.0:*               LISTEN      11937/python 

看下這個進程號,具體的命令:。

root@netnode:/# ps -ef | grep 11937
root     11937     1  0 08:43 ?        00:00:00 python 
/usr/bin/neutron-ns-metadata-proxy -metadata_proxy_socket=/var/lib/neutron/metadata_proxy

可以看到,有一個proxy進程監聽者9697端口,並將“訪問上帝的請求”,轉給了本地 unix domain socket 的監聽者(即agent)。

使用

root@netnode:/# netstat -lxp | grep metadata

或者

root@netnode:/# lsof /var/lib/neutron/metadata_proxy 

查詢到在監聽本地unix domain socket的進程ID

然後看下這個進程ID,是不是上帝的agent:

root@netnode:/# ps -ef | grep “具體進程ID”

邏輯上,整體過程如下:

具體可參考該圖:

所以,關鍵其實還是那個 namespace中轉站。

附,本段參考鏈接:

http://niusmallnan.com/_build/html/_templates/openstack/metadata_server.html

http://techbackground.blogspot.com/2013/06/metadata-via-quantum-router.html

DVR(Distributed Virtual Routing)

在上面的介紹中可以看到,所有的VM虛機,要訪問外網,都要經過網絡節點。這樣也有不好地方,1是網絡節點的網絡流量壓力非常大;2是一旦網絡節點異常,大量的VM都要受影響。所以,這裏能不能把網絡節點的“中轉站”功能,複製一份到各個計算節點上去。然後在計算節點上面,增加判斷邏輯:

if (本地有“中轉站”) && (符合使用條件)  {使用本地“中轉站”};

else  {繼續使用原來的網絡節點的“中轉站”}。

答案就是DVR了。爲了降低網絡節點的負載,同時提高可擴展性,OpenStack在Juno版本引入了DVR特性,DVR部署在計算節點上。計算節點上的VM使用floatingIP訪問Internet,不必經過網絡節點,直接從計算節點的DVR就可以訪問。

這樣網絡節點只需要處理佔到整體流量一部分的 SNAT (無 floating IP 的 vm 跟外面的通信)流量,大大降低了負載和整個系統對網絡節點的依賴。

具體計算節點的if條件判斷,就是通過openflow規則,來控制的。這個有點太細節了,沒有細研究。可以去看看相應的文章:

https://www.cnblogs.com/sammyliu/p/4713562.html

https://docs.openstack.org/ocata/networking-guide/deploy-ovs-ha-dvr.html

所以你可以看到,原來網絡節點的路由器,現在分散到各個計算節點上面了。原來一個人(網絡節點的Router)要乾的活,現在分散給很多人(各計算節點的Router)幹。確實分佈式路由器了。

總結

基本上,OpenStack的網絡實現,是集成了目前所有的“網絡虛擬化零件”,包括:ovs交換機,bridge網橋,veth網線,tap網線,patch網線,namespace空間,iptables規則等。也是唐老師我接觸過最複雜的網絡實現(所以本文一直拖到最後)。如果你能理解OpenStack的網絡,那麼對於其他雲平臺的網絡,應該也可以通過分析後理解掌握了。

最後,基礎的雲網絡相關的課程,唐老師就只能教到此了。畢竟咱可以是一個入門導師,也不是專門負責網絡開發的。所以在入門後,如果還要繼續深入研究雲網絡,甚至開始設計網絡虛擬化方案的,還得靠你自己繼續修行。騷年,加油~

注:由於作者現階段主要專注於雲原生相關的業務(Kubernetes集羣),所以OpenStack網絡信息不就是最新的了。不過這個關係應該不大,因爲其網絡設計原理是繼承的。並且,咱們的課程,主要目的就是能看懂。要設計的話,還得自己再深入學習。So,本着能看懂OpenStack網絡的原則,本文還是夠用的。

 

點擊關注,第一時間瞭解華爲雲新鮮技術~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章