一文理解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中轉站。
附,本段參考鏈接:
//niusmallnan.com/_build/html/_templates/openstack/metadata_server.html
//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規則,來控制的。這個有點太細節了,沒有細研究。可以去看看相應的文章:
//www.cnblogs.com/sammyliu/p/4713562.html
//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網路的原則,本文還是夠用的。