一、容器網(wǎng)絡(luò)概述
容器這一兩年火的不行,可以說是獨(dú)領(lǐng)IT風(fēng)騷,一時(shí)風(fēng)光無二。相比于虛擬機(jī)來說,容器更輕,一臺(tái)服務(wù)器上可以運(yùn)行成百上千的容器,這意味著更為密集的計(jì)算資源,因此基于容器運(yùn)行工作負(fù)載的模式深受云服務(wù)提供商們的青睞。
然而對于云管理員來說,管理容器確是一件相當(dāng)頭疼的事情,容器的生命周期更短了,容器的數(shù)量更多了,容器間的關(guān)系更復(fù)雜了。為了簡化大規(guī)模容器集群的運(yùn)維,各路容器管理與編排平臺(tái)應(yīng)運(yùn)而生,Docker社區(qū)開發(fā)了Swarm+Machine+Compose的集群管理套件,Twitter主推Apache的Mesos,Google則開源了自己的Kubernetes。這些平臺(tái)為大規(guī)模的容器集群提供了資源調(diào)度、服務(wù)發(fā)現(xiàn)、擴(kuò)容縮容等功能,然而這些功能都是策略向的,真正要實(shí)現(xiàn)大規(guī)模的容器集群,網(wǎng)絡(luò)才是最基礎(chǔ)的一環(huán)。
相比于虛擬機(jī)網(wǎng)絡(luò),容器網(wǎng)絡(luò)主要具有以下特點(diǎn),以及相應(yīng)的技術(shù)挑戰(zhàn):
1、虛擬機(jī)擁有完善的隔離機(jī)制,虛擬網(wǎng)卡與硬件網(wǎng)卡在使用上沒有什么區(qū)別,而容器則使用network namespace提供網(wǎng)絡(luò)在內(nèi)核中的隔離,因此為了保障容器的安全性,容器網(wǎng)絡(luò)的設(shè)計(jì)需要更為慎重的考慮。
2、出于安全考慮,很多情況下容器會(huì)被部署在虛擬機(jī)內(nèi)部,這種嵌套部署(nested deployment)需要設(shè)計(jì)新的網(wǎng)絡(luò)模型。
3、容器的分布不同于虛擬機(jī),一個(gè)虛擬機(jī)的運(yùn)行的業(yè)務(wù)可能要被拆分到多個(gè)容器上運(yùn)行,根據(jù)業(yè)務(wù)不同的需要,這些容器有時(shí)候必須放在一臺(tái)服務(wù)器中,有時(shí)候可以分布在網(wǎng)絡(luò)的各個(gè)位置,兩種情況對應(yīng)的網(wǎng)絡(luò)模型也很可能不盡相同。
4、容器的遷移速度更快,網(wǎng)絡(luò)策略的更新要能夠跟得上速度。
5、容器數(shù)量更多了,多主機(jī)間的ARP Flooding會(huì)造成大量的資源浪費(fèi)。
6、容器生命周期短,重啟非常頻繁,網(wǎng)絡(luò)地址的有效管理(IPAM)將變得非常關(guān)鍵。
不過,由于容器自身的特征使得它與應(yīng)用的綁定更為緊密,從交付模式來看更傾向于PaaS而非IaaS,因此容器網(wǎng)絡(luò)并沒有成為業(yè)界關(guān)注的焦點(diǎn)。起步較晚,再加上上述諸多的技術(shù)挑戰(zhàn),使得容器網(wǎng)絡(luò)相比于OpenStack Neutron來說發(fā)展的情況要落后不少。Docker在開始的很長一段時(shí)間內(nèi)只支持使用linux bridge+iptables進(jìn)行single-host的部署,自動(dòng)化方面也只有pipework這類shell腳本。
幸運(yùn)的是,目前業(yè)界已經(jīng)意識(shí)到了可擴(kuò)展、自動(dòng)化的網(wǎng)絡(luò)對于大規(guī)模容器環(huán)境的重要性:docker收購了容器網(wǎng)絡(luò)的創(chuàng)業(yè)公司socketplane,隨即將網(wǎng)絡(luò)管理從docker daemon中獨(dú)立出來形成libnetwork,并在docker 1.9中提供了多種network driver,并支持了multi-host;一些專業(yè)的容器網(wǎng)絡(luò)(如flannel、weave、calico等)也開始與各個(gè)容器編排平臺(tái)進(jìn)行集成;OpenStack社區(qū)也成立了專門的子項(xiàng)目Kuryr提供Neutron network driver(如DragonFlow、OVN、Midolnet等)與容器的對接。
二、容器網(wǎng)絡(luò)模型
這一節(jié)我們來介紹容器網(wǎng)絡(luò)的基礎(chǔ),包括容器的接入,容器間的組網(wǎng),以及幾種容器網(wǎng)絡(luò)的通用模型。
(一)容器的接入
1.和host共享network namespace
這種接入模式下,不會(huì)為容器創(chuàng)建網(wǎng)絡(luò)協(xié)議棧,即容器沒有獨(dú)立于host的network namespace,但是容器的其他namespace(如IPC、PID、Mount等)還是和host的namespace獨(dú)立的。容器中的進(jìn)程處于host的網(wǎng)絡(luò)環(huán)境中,與host共用L2-L4的網(wǎng)絡(luò)資源。該方式的優(yōu)點(diǎn)是,容器能夠直接使用host的網(wǎng)絡(luò)資源與外界進(jìn)行通信,沒有額外的開銷(如NAT),缺點(diǎn)是網(wǎng)絡(luò)的隔離性差,容器和host所使用的端口號(hào)經(jīng)常會(huì)發(fā)生沖突。
2.和host共享物理網(wǎng)卡
2與1的區(qū)別在于,容器和host共享物理網(wǎng)卡,但容器擁有獨(dú)立于host的network namespace,容器有自己的MAC地址、IP地址、端口號(hào)。這種接入方式主要使用SR-IOV技術(shù),每個(gè)容器被分配一個(gè)VF,直接通過PCIe網(wǎng)卡與外界通信,優(yōu)點(diǎn)是旁路了host kernel不占任何計(jì)算資源,而且IO速度較快,缺點(diǎn)是VF數(shù)量有限且對容器遷移的支持不足。
3.和另外一個(gè)容器共享network namespace
與1類似,容器沒有獨(dú)立的network namespace,但是以該方式新創(chuàng)建的容器將與一個(gè)已經(jīng)存在的容器共享其network namespace(包括MAC、IP以及端口號(hào)等),網(wǎng)絡(luò)角度上兩者將作為一個(gè)整體對外提供服務(wù),不過兩個(gè)容器的其他namespace(如IPC、PID、Mount等)是彼此獨(dú)立的。這種方式的優(yōu)點(diǎn)是,network namespace相聯(lián)系的容器間的通信高效便利,缺點(diǎn)是由于其他的namespace仍然是彼此獨(dú)立的,因此容器間無法形成一個(gè)業(yè)務(wù)邏輯上的整體。
4.Behind the POD
這種方式是Google在Kubernetes中的設(shè)計(jì)中提出來的。Kubernetes中,POD是指一個(gè)可以被創(chuàng)建、銷毀、調(diào)度、管理的最小的部署單元,一個(gè)POD有一個(gè)基礎(chǔ)容器以及一個(gè)或一組應(yīng)用容器,基礎(chǔ)容器對應(yīng)一個(gè)獨(dú)立的network namespace并擁有一個(gè)其它POD可見的IP地址(以IP A.B.C.D指代),應(yīng)用容器間則共享基礎(chǔ)容器的network namespace(包括MAC、IP以及端口號(hào)等),還可以共享基礎(chǔ)容器的其它的namespace(如IPC、PID、Mount等)。POD作為一個(gè)整體連接在host的vbridge/vswitch上,使用IP地址A.B.C.D與其它POD進(jìn)行通信,不同host中的POD處于不同的subnet中,同一host中的不同POD處于同一subnet中。這種方式的優(yōu)點(diǎn)是一些業(yè)務(wù)上密切相關(guān)的容器可以共享POD的全部資源(它們一般不會(huì)產(chǎn)生資源上的沖突),而這些容器間的通信高效便利,缺點(diǎn)是同一POD下的容器必須位于同一host中,從而限制了位置的靈活性。
5.連接在的vbridge/vswitch上
這種方式是最為常見的,容器擁有獨(dú)立的network namespace,通過veth-pair連接到vswitch上。這種方式對于網(wǎng)絡(luò)來說是最為直接的,在vswitch看來,通過這種方式連接的容器與虛擬機(jī)并沒有任何區(qū)別。vbridge/vswitch的實(shí)現(xiàn)有很多,包括linux bridge,Open vSwitch,macvlan等。這種方式的優(yōu)點(diǎn)是vbridge/vswitch可實(shí)現(xiàn)的功能比較豐富,尤其是Open vSwitch可以支持VLAN、Tunnel、SDN Controller等,缺點(diǎn)是可能會(huì)造成很多額外的開銷。
6.嵌套部署在VM中
這種方式在生產(chǎn)環(huán)境也比較常見,由于一臺(tái)host中往往部署著多方的容器,存在安全隱患,因此許多用戶會(huì)選擇先啟動(dòng)自己的虛擬機(jī),然后在自己的虛擬機(jī)上運(yùn)行容器。這種方式其實(shí)是一種嵌套虛擬化,因此本質(zhì)上來說,這種方式下容器的接入對于host可以是完全透明的,容器在虛擬機(jī)內(nèi)部的接入可以采用上述1-5中任意的方法。不過這對于云平臺(tái)來說就意味著失去了對容器接入的管理能力,為了保留這一能力,往往需要在虛擬機(jī)內(nèi)部和host中分別部署vswitch并實(shí)現(xiàn)級聯(lián),由虛擬機(jī)內(nèi)部的vswitch用來接入容器并對其進(jìn)行特定的標(biāo)記(云平臺(tái)分配),以便host中的vswitch進(jìn)行識(shí)別。一種常見的方式是使用Open vSwitch對容器標(biāo)記vlan id。
(二)MultiHost組網(wǎng)
1.Flat
Flat主要可分為L2 Flat和L3 Flat。L2 Flat指各個(gè)host中所有的容器都在virtual+physical網(wǎng)絡(luò)形成的VLAN大二層中,容器可以在任意host間進(jìn)行遷移而不用改變其IP地址。L3 Flat指各個(gè)host中所有的容器都在virtual+physical網(wǎng)絡(luò)中可路由,且路由以/32位的形式存在,使得容器在host間遷移時(shí)不需要改變IP地址。L2/L3 Flat下,不同租戶的IP地址不可以O(shè)verlap,L3 Flat下容器的IP編址也不可與physical網(wǎng)絡(luò)Overlap。L3 Flat簡單示意如下。
2.L3 Hierarchy
L3 Hierarchy中各個(gè)host中所有的容器都在virtual+physical網(wǎng)絡(luò)中可路由,且路由在不同層次上(VM/Host/Leaf/Spine)以聚合路由的形式存在,即處于相同CIDR的容器需要在物理位置上被組織在一起,因此容器在host間遷移時(shí)需要改變IP地址。L3 Hierarchy下,不同租戶的IP地址不可以O(shè)verlap,容器的IP編址也不可與physical網(wǎng)絡(luò)Overlap。下圖是L3 Hierarchy中的IP地址規(guī)劃示例。
3.Overlay
Overlay主要可分為L2 over L3和L3 over L3,少部分實(shí)現(xiàn)L2/L3 over UDP。L2 over L3中,容器可以跨越L3 Underlay進(jìn)行L2通信,容器可以在任意host間進(jìn)行遷移而不用改變其IP地址。L3 over L3中,容器可以跨越L3 Underlay進(jìn)行L3通信,容器在host間進(jìn)行遷移時(shí)可能需要改變IP地址(取決于Overlay是L3 Flat還是L3 Hierarchy)。L2/L3 Overlay下,不同租戶的IP地址也可以O(shè)verlap,容器的IP編址也可以與Underlay網(wǎng)絡(luò)Overlap。L2 over L3(VxLAN實(shí)現(xiàn))如下圖所示。
(三)容器網(wǎng)絡(luò)的兩種通用設(shè)計(jì)
1.CNM
CNM(Container Network Model)是Cisco的一位工程師提出的一個(gè)容器網(wǎng)絡(luò)模型(https://github.com/docker/docker/issues/9983),docker 1.9在libnetwork中實(shí)現(xiàn)了CNM(https://github.com/docker/libnetwork/blob/master/docs/design.md#the-container-network-model)。CNM的示意如下,主要建立在三類組件上Sandbox、Endpoint和Network。
Sandbox:一個(gè)Sandbox對應(yīng)一個(gè)容器的網(wǎng)絡(luò)棧,能夠?qū)υ撊萜鞯膇nterface、route、dns等參數(shù)進(jìn)行管理。一個(gè)Sandbox中可以有多個(gè)Endpoint,這些Endpoint可以屬于不同的Network。Sandbox的實(shí)現(xiàn)可以為linux network namespace、FreeBSD Jail或其他類似的機(jī)制。
Endpoint: Sandbox通過Endpoint接入Network,一個(gè)Endpoint只能屬于一個(gè)Network,but may only belong to one Sandbox(這句翻譯不好)。Endpoint的實(shí)現(xiàn)可以是veth pair、Open vSwitch internal port或者其他類似的設(shè)備。
Network:一個(gè)Network由一組Endpoint組成,這些Endpoint彼此間可以直接進(jìn)行通信,不同的Network間Endpoint的通信彼此隔離。Network的實(shí)現(xiàn)可以是linux bridge、Open vSwitch等。
Libnetwork對于CNM的實(shí)現(xiàn)包括以下5類對象:
NetworkController: 每創(chuàng)建一個(gè)Network對象時(shí),就會(huì)相應(yīng)地生成一個(gè)NetworkController對象,NetworkController對象將Network對象的API暴露給用戶,以便用戶對libnetwork進(jìn)行調(diào)用,然后驅(qū)動(dòng)特定的Driver對象實(shí)現(xiàn)Network對象的功能。NetworkController允許用戶綁定Network對象所使用的Driver對象。NetworkController對象可以看做是Network對象的分布式SDN控制器。
Network: Network對象是CNM Network的一種實(shí)現(xiàn)。NetworkController對象通過提供API對Network對象進(jìn)行創(chuàng)建和管理。NetworkController對象需要操作Network對象的時(shí)候,Network對象所對應(yīng)的Driver對象會(huì)得到通知。一個(gè)Network對象能夠包含多個(gè)Endpoint對象,一個(gè)Network對象中包含的各個(gè)Endpoint對象間可以通過Driver完成通信,這種通信支持可以是同一主機(jī)的,也可以是跨主機(jī)的。不同Network對象中的Endpoint對象間彼此隔離。
**
Driver:** Driver對象真正實(shí)現(xiàn)Network功能(包括通信和管理),它并不直接暴露API給用戶。Libnetwork支持多種Driver,其中包括內(nèi)置的bridge,host,container和overlay,也對remote driver(即第三方,或用戶自定義的網(wǎng)絡(luò)驅(qū)動(dòng))進(jìn)行了支持。
Endpoint: Endpoint對象是CNM Endpoint的一種實(shí)現(xiàn)。容器通過Endpoint對象接入Network,并通過Endpoint對象與其它容器進(jìn)行通信。一個(gè)Endpoint對象只能屬于一個(gè)Network對象,Network對象的API提供了對于Endpoint對象的創(chuàng)建與管理。
**
Sandbox:** Sandbox對象是CNM Sandbox的一種實(shí)現(xiàn)。Sandbox對象代表了一個(gè)容器的網(wǎng)絡(luò)棧,擁有IP地址,MAC地址,routes,DNS等網(wǎng)絡(luò)資源。一個(gè)Sandbox對象中可以有多個(gè)Endpoint對象,這些Endpoint對象可以屬于不同的Network對象,Endpoint對象使用Sandbox對象中的網(wǎng)絡(luò)資源與外界進(jìn)行通信。Sandbox對象的創(chuàng)建發(fā)生在Endpoint對象的創(chuàng)建后,(Endpoint對象所屬的)Network對象所綁定的Driver對象為該Sandbox對象分配網(wǎng)絡(luò)資源并返回給libnetwork,然后libnetwork使用特定的機(jī)制(如linux netns)去配置Sandbox對象中對應(yīng)的網(wǎng)絡(luò)資源。
2.CNI
CNI(Container Networking Interface)是CoreOS為Rocket(docker之外的另一種容器引擎)提出的一種plugin-based的容器網(wǎng)絡(luò)接口規(guī)范(https://github.com/containernetworking/cni/blob/master/SPEC.md),CNI十分符合Kubernetes中的網(wǎng)絡(luò)規(guī)劃思想,Kubernetes采用了CNI作為默認(rèn)的網(wǎng)絡(luò)接口規(guī)范,目前CNI的實(shí)現(xiàn)有Weave、Calico、Romana、Contiv等。
CNI沒有像CNM一樣規(guī)定模型的術(shù)語,CNI的實(shí)現(xiàn)依賴于兩種plugin:CNI Plugin負(fù)責(zé)將容器connect/disconnect到host中的vbridge/vswitch,IPAM Plugin負(fù)責(zé)配置容器namespace中的網(wǎng)絡(luò)參數(shù)。
CNI要求CNI Plugin支持容器的Add/Delete操作,操作所需的參數(shù)規(guī)范如下:
1、Version:使用的CNI Spec的版本。
2、Container ID:容器在全局(管理域內(nèi))唯一的標(biāo)識(shí),容器被刪除后可以重用。Container ID是可選參數(shù),CNI建議使用。
3、Network namespace path:netns要被添加到的路徑,如/proc/[pid]/ns/net。
4、Network configuration:一個(gè)JSON文件,描述了容器要加入的網(wǎng)絡(luò)的參數(shù)。
5、Extra arguments:針對特定容器要做的細(xì)粒度的配置。
6、Name of the interface inside the container:容器interface在容器namespace內(nèi)部的名稱。
其中,Network configuration的schema如下:
1、cniVersion:使用的CNI Spec的版本。
2、name:網(wǎng)絡(luò)在全局(管理域內(nèi))唯一的標(biāo)識(shí)。
3、type:CNI Plugin的類型,如bridge/OVS/macvlan等。
4、ipMasq:boolean類型,host是否需要對外隱藏容器的IP地址。CNI Plugin可選地支持。
5、ipam:網(wǎng)絡(luò)參數(shù)信息
5.1、type:分為host-local和dhcp兩種
5.2、routes:一個(gè)route列表,每一個(gè)route entry包含dst和gw兩個(gè)參數(shù)。
6、dns:nameservers+domain+search domains+options
為了減輕CNI Plugin的負(fù)擔(dān),ipam由CNI Plugin調(diào)用IPAM Plugin來實(shí)現(xiàn),IPAM Plugin負(fù)責(zé)配置容器namespace中的網(wǎng)絡(luò)參數(shù)。IPAM的實(shí)施分為兩種,一種是host-local,在subnet CIDR中選擇一個(gè)可用的IP地址作為容器的IP,route entry(可選)在host本地配置完成。另一種是dhcp,容器發(fā)送dhcp消息請求網(wǎng)絡(luò)參數(shù)。
Add操作后,會(huì)返回以下兩個(gè)結(jié)果:
lIPs assigned to the interface:IPv4地址/IPv6地址/同時(shí)返回IPv4和IPv6地址
lDNS information:nameservers+domain+search domains+options
三、Docker網(wǎng)絡(luò)
Docker是當(dāng)下最為火熱的容器引擎,為實(shí)現(xiàn)大規(guī)模集群,docker推出了Swarm+Machine+Compose的集群管理套件。然而,docker的原生網(wǎng)絡(luò)在很長一段時(shí)間內(nèi)都是基于linux bridge+iptables實(shí)現(xiàn)的,這種方式下容器的可見性只存在于主機(jī)內(nèi)部,這嚴(yán)重地限制了容器集群的規(guī)模以及可用性。其實(shí),社區(qū)很早就意識(shí)到了這個(gè)問題,不過由于缺乏專業(yè)的網(wǎng)絡(luò)團(tuán)隊(duì)支持,因此docker的跨主機(jī)通信問題始終沒有得到很好的解決。另外,手動(dòng)配置docker網(wǎng)絡(luò)是一件很麻煩的事情,盡管有pipework這樣的shell腳本工具,但是以腳本的自動(dòng)化程度而言,用來運(yùn)維大規(guī)模的docker網(wǎng)絡(luò)還是too na?ve。
2015年3月,docker收購了一家 SDN初創(chuàng)公司socketplane,隨即于5月宣布將網(wǎng)絡(luò)管理功能從libcontainer和docker daemon中抽離出來作為一個(gè)單獨(dú)的項(xiàng)目libnetwork,由原socketplane團(tuán)隊(duì)成員接手,基于GO語言進(jìn)行開發(fā)。2015年11月發(fā)布的docker 1.9中l(wèi)ibnetwork架構(gòu)初步形成,支持多種nework driver并提供跨主機(jī)通信,并在后續(xù)的1.10、1.11兩個(gè)版本中修復(fù)了大量bug。目前,libnetwork處于0.6版本。
(1)Docker0
Docker 1.9之前,網(wǎng)絡(luò)的實(shí)現(xiàn)主要由docker daemon來完成,當(dāng)docker daemon啟動(dòng)時(shí)默認(rèn)情況下會(huì)創(chuàng)建docker0,為docker0分配IP地址,并設(shè)置一些iptables規(guī)則。然后通過docker run命令啟動(dòng)容器,該命令可以通過—net選項(xiàng)來選擇容器的接入方式(參見“容器的網(wǎng)絡(luò)模型”),docker 1.9之前的版本支持如下4種接入方式。
1、bridge:新建容器有獨(dú)立的network namespace,并通過以下步驟將容器接入docker0
1)創(chuàng)建veth pair
2)將veth pair的一端置于host的root network namespace中,并將其關(guān)聯(lián)docker0
3)將veth pair的另一端置于新建容器的network namespace中
4)從docker0所在的subnet中選一個(gè)可用的IP地址賦予veth pair在容器的一端
2、host:新建容器與host共享network namespace,該容器不會(huì)連接到docker0中,直接使用host的網(wǎng)絡(luò)資源進(jìn)行通信
3、container:新建容器與一個(gè)已有的容器共享network namespace,該容器不會(huì)連接到docker0中,直接使用host的網(wǎng)絡(luò)資源進(jìn)行通信
4、none:新建容器有獨(dú)立的network namespace,但是不會(huì)配置任何網(wǎng)絡(luò)參數(shù),也不會(huì)接入docker0中,用戶可對其進(jìn)行任意的手動(dòng)配置
后3種沒什么好說的,下面介紹一下bridge方式。Docker0由linux bridge實(shí)現(xiàn),容器通過veth設(shè)備接入docker0,本地容器都處于同一subnet中,彼此間通信通過docker0交換,與外界通信以docker0的IP地址作為網(wǎng)關(guān)。Docker0的IP地址可以看做是內(nèi)置連接在linux bridge上的設(shè)備(類似于ovs br上的同名internal port),位于host的root namespace中,容器與外界的通信要依賴于host中的Iptables MASQUERADE規(guī)則來做SNAT,容器對外提供服務(wù)要依賴于host中的Iptables dnat規(guī)則來暴露端口。因此這種方案下,容器間的跨主機(jī)通信其實(shí)用的都是host的socket,容器本身的IP地址和端口號(hào)對其它host上的容器來說都是不可見的。
這個(gè)方案非常原始,除了不能支持直接可見的跨主機(jī)通信以外,NAT還會(huì)導(dǎo)致很多其它不合意的結(jié)果,如端口沖突等。另外,對于一些復(fù)雜的需求,如IPAM、多租戶、SDN等均無法提供支持。
(2)Pipework
容器就是namespace,docker0就是linux bridge,再加上一些iptables規(guī)則,實(shí)際上容器組網(wǎng)就是調(diào)用一些已有的命令行而已。不過,當(dāng)容器數(shù)量很多,或者是頻繁地啟動(dòng)、關(guān)閉時(shí),一條條命令行去配就顯得不是很合意了。于是,Docker公司的工程師Jerome Patazzoni就寫了一個(gè)shell腳本來簡化容器網(wǎng)絡(luò)的配置,主要就是對docker/ip nets/ip link/brctl這些命令行的二次封裝。Jerome Patazzoni自己認(rèn)為pipework是SDN tools for container,雖然有點(diǎn)too na?ve了,但是從實(shí)用性的角度來看,確實(shí)倒也可以滿足一些自動(dòng)化運(yùn)維的需要。
當(dāng)然pipework相比于docker0,除了提供了命令行的封裝以外,還是具備一些其他的優(yōu)勢的,比如支持多樣的network driver如OVS和macvlan,支持在host上開dhcp-server為容器自動(dòng)分配IP地址,支持免費(fèi)ARP,等等。
具體的實(shí)現(xiàn)這里就不講了,因?yàn)檫@東西其實(shí)完全說不上高深,總共加起來也就400多行代碼。
(3)Libnetwork
Socketplane是一家做容器網(wǎng)絡(luò)的startup,2014年4季度創(chuàng)建,2015年3月份就被docker收購了,可以看到當(dāng)時(shí)docker對于原生的網(wǎng)絡(luò)管理組件的需求是有多么迫切,而且socketplane團(tuán)隊(duì)的這幫子人是SDN科班出身的,docker也總算有了搞網(wǎng)絡(luò)的正規(guī)軍。不過,socketplane和libnetwork的設(shè)計(jì)在架構(gòu)上還是有很大不同的,我們先來看看socketplane的設(shè)計(jì)。
架構(gòu)上,數(shù)據(jù)平面是OVS VxLAN,南向協(xié)議是OVSDB,控制平面是基于consul的分布式k/v store,北向是socketplane CLI。控制平面的部署細(xì)節(jié)上,consul是放在一個(gè)socketplane容器中的,該容器通過host模式與host共享network namespace,consul通過eth0去做服務(wù)發(fā)現(xiàn)和狀態(tài)同步,狀態(tài)主要就是指容器與host IP的映射關(guān)系了。數(shù)據(jù)平面的流表情況,就是match MAC+IP,actions就是送到本地的容器或者遠(yuǎn)端的tunnel peer上,有點(diǎn)奇怪的是socketplane沒有使用tunnel_id,而是用了vlan_id標(biāo)識(shí)vnet,這與RFC 7348是有沖突的。另外,根據(jù)為數(shù)不多的資料來看,socketplane在被收購前只完成了L2的east-west,還沒有考慮routing和south-north。
可以看到,socketplane的設(shè)計(jì)并不復(fù)雜。但是被收購進(jìn)docker后,麻煩事可就多了——首先,數(shù)據(jù)平面決計(jì)不能演化為ovs monopolic,linux bridge要有,第三方driver也得玩得轉(zhuǎn);其次,控制平面k/v store也要可插拔,起碼要支持zookeeper和etcd,最好還要把自家的集群工具swarm集成進(jìn)來;另外,要考慮老用戶的習(xí)慣,原有的網(wǎng)絡(luò)設(shè)計(jì)該保留還要保留;最后,還要遵循社區(qū)提出的容器網(wǎng)絡(luò)模型CNM(https://github.com/docker/docker/issues/9983)。
于是,docker網(wǎng)絡(luò)在1.9變成了下面這個(gè)樣子(圖中只畫了一個(gè)host),libkv提供swarm的服務(wù)發(fā)現(xiàn),以及overlay network的k/v store,每個(gè)host上開啟docker daemon并加入swarm cluster,libcontainer負(fù)責(zé)管理容器,libnetwork負(fù)責(zé)管理網(wǎng)絡(luò)。libnetwork支持5種網(wǎng)絡(luò)模式,none/host/bridge/overlay/remote,圖中從左到右依次顯示了后4種,其中overlay和一些remote可以支持multi-host。
Overlay是libnetwork默認(rèn)的multi-host網(wǎng)絡(luò)模式,通過VxLAN完成跨主機(jī)。Libnetwork會(huì)把overlay driver放在單獨(dú)的network namespace中,默認(rèn)的overlay driver為linux bridge。當(dāng)容器(Sandbox)接入overlay(Network)時(shí),會(huì)被分到兩個(gè)網(wǎng)卡(Endpoint),eth0連在vxlan_driver上,eth1連在docker_gwbridge上。Vxlan_driver主要負(fù)責(zé)L2的通信,包括本地流量和跨主機(jī)流量,docker_gwbridge的實(shí)現(xiàn)原理和docker0一樣,負(fù)責(zé)處理Service的通信,包括不同網(wǎng)絡(luò)容器間,以及容器與Internet間兩類流量。Eth0和eth1各有一個(gè)IP地址,分屬于不同網(wǎng)段,eth0默認(rèn)以10開頭,eth1默認(rèn)以172開頭,L2和L3的通信直接通過容器內(nèi)部的路由表分流,送到不同的設(shè)備上處理。
Remote是libnetwork為了支持其它的Driver而設(shè)計(jì)的一種pluggble框架,這些Driver不要求一定支持multi-host。除了一些第三方的Driver外(如weave、calico等),目前l(fā)ibnetwork還原生提供了對macvlan driver和ipvlan driver的支持。當(dāng)然,就像Neutron的ML2一樣,為了打造生態(tài),plugin driver的接口還是要libnetwork自己來規(guī)范的,具體請參考https://github.com/docker/libnetwork/blob/master/docs/remote.md。
既然說是引入SDN,那么API的規(guī)范對于libnetwork來說就十分重要了,不過目前l(fā)ibnetwork的接口封裝還處于相當(dāng)初級的階段,基本上就是對Network和Endpoint的創(chuàng)建、刪除以及連接(https://github.com/docker/libnetwork/blob/master/docs/design.md), 并沒有提供很友好的業(yè)務(wù)API。
對于libnetwork的介紹就是這些了。盡管libnetwork實(shí)現(xiàn)了千呼萬喚的multi-host,也為docker網(wǎng)絡(luò)帶來了一定的靈活性與自動(dòng)化,但就目前來說,它的API尚不夠友好,Driver的生態(tài)還不夠成熟,而且并不具備任何高級的網(wǎng)絡(luò)服務(wù)。因此,libnetwork相比于老大哥neutron來說,仍然存在著較大的差距。
其他網(wǎng)絡(luò)容器選手速覽
其實(shí),早在docker社區(qū)將libnetwork提上日程之前,就已經(jīng)有不少人在為容器的multi-host操心了。除了socketplane以外,如CoreOS為k8s的網(wǎng)絡(luò)模型設(shè)計(jì)的flannel,通過P2P的控制平面構(gòu)建overlay的weave net,通過BGP RR構(gòu)建Flat L3的Calico,等等。最近,又有兩個(gè)開源項(xiàng)目開始琢磨新的容器組網(wǎng)辦法,一個(gè)是通過優(yōu)化IPAM邏輯來構(gòu)建Hierarchy L3的Romana,另一個(gè)是Cisco ACI派系的Contiv。當(dāng)然,網(wǎng)絡(luò)規(guī)模不大時(shí),直接手配OVS也是個(gè)可行的方案。
這一節(jié)我們就來對上述容器網(wǎng)絡(luò)選手來一個(gè)閱兵,先來介紹它們的架構(gòu),再來對它們做一個(gè)簡單的對比。
1.Flannel
在k8s的網(wǎng)絡(luò)設(shè)計(jì)中,服務(wù)以POD為單位,每個(gè)POD的IP地址,容器通過Behind the POD方式接入網(wǎng)絡(luò)(見“容器的網(wǎng)絡(luò)模型”),一個(gè)POD中可包含多個(gè)容器,這些容器共享該P(yáng)OD的IP地址。另外,k8s要求容器的IP地址都是全網(wǎng)可路由的,那么顯然docker0+iptables的NAT方案是不可行的。
實(shí)現(xiàn)上述要求其實(shí)有很多種組網(wǎng)方法,F(xiàn)lat L3是一種(如Calico),Hierarchy L3(如Romana)是一種,另外L3 Overlay也是可以的,CoreOS就采用L3 Overlay的方式設(shè)計(jì)了flannel, 并規(guī)定每個(gè)host下各個(gè)POD屬于同一個(gè)subnet,不同的host/VM下的POD屬于不同subnet。我們來看flannel的架構(gòu),控制平面上host本地的flanneld負(fù)責(zé)從遠(yuǎn)端的ETCD集群同步本地和其它host上的subnet信息,并為POD分配IP地址。數(shù)據(jù)平面flannel通過UDP封裝來實(shí)現(xiàn)L3 Overlay,既可以選擇一般的TUN設(shè)備又可以選擇VxLAN設(shè)備(注意,由于圖來源不同,請忽略具體的IP地址)。
Flannel可說的不多,做得比較早,技術(shù)選型也十分成熟,已經(jīng)可以用于大規(guī)模部署。下面是控制信道上通信內(nèi)容的一個(gè)實(shí)例。
2.Weave
Weave是Weaveworks公司的容器網(wǎng)絡(luò)產(chǎn)品,大家都叫慣了weave,實(shí)際上目前該產(chǎn)品的名字叫做Weave Nets,因?yàn)閃eaveworks現(xiàn)在并不是一家只做網(wǎng)絡(luò)的公司,最近它又做了兩款其它的容器管理產(chǎn)品,GUI+集群。不過,為大家所熟悉的還是它網(wǎng)絡(luò)口的產(chǎn)品。
不同于其它的multi-host方案,Weave可以支持去中心化的控制平面,各個(gè)host上的wRouter間通過建立Full Mesh的TCP鏈接,并通過Gossip來同步控制信息。這種方式省去了集中式的K/V Store,能夠在一定程度上減低部署的復(fù)雜性,Weave將其稱為“data centric”,而非RAFT或者Paxos的“algorithm centric”。
不過,考慮到docker libnetwork是集中式的K/V Store作為控制平面,因此Weave為了集成docker,它也提供了對集中式控制平面的支持,能夠作為docker remote driver與libkv通信。
數(shù)據(jù)平面上,Weave通過UDP封裝實(shí)現(xiàn)L2 Overlay,封裝支持兩種模式,一種是運(yùn)行在user space的sleeve mode,另一種是運(yùn)行在kernal space的 fastpath mode。Sleeve mode通過pcap設(shè)備在Linux bridge上截獲數(shù)據(jù)包并由wRouter完成UDP封裝,支持對L2 traffic進(jìn)行加密,還支持Partial Connection,但是性能損失明顯。Fastpath mode即通過OVS的odp封裝VxLAN并完成轉(zhuǎn)發(fā),wRouter不直接參與轉(zhuǎn)發(fā),而是通過下發(fā)odp 流表的方式控制轉(zhuǎn)發(fā),這種方式可以明顯地提升吞吐量,但是不支持加密等高級功能。
這里要說一下Partial Connection的組網(wǎng)。在多DC的場景下一些DC Sites無法直連,比如Peer 1與Peer 5間的隧道通信,中間勢必要經(jīng)過Peer 3,那么Peer 3就必須要支持做隧道的中間轉(zhuǎn)發(fā)。目前sleeve mode的實(shí)現(xiàn)是通過多級封裝來完成的,目前fastpath上還沒有實(shí)現(xiàn)。
上面主要介紹的是weave對multi-host L2的實(shí)現(xiàn)。關(guān)于Service的發(fā)布,weave做的也比較完整。首先,wRouter集成了DNS功能,能夠動(dòng)態(tài)地進(jìn)行服務(wù)發(fā)現(xiàn)和負(fù)載均衡,另外,與libnetwork 的overlay driver類似,weave要求每個(gè)POD有兩個(gè)網(wǎng)卡,一個(gè)就連在lb/ovs上處理L2 流量,另一個(gè)則連在docker0上處理Service流量,docker0后面仍然是iptables作NAT。
3.Calico
Calico是一個(gè)專門做DC網(wǎng)絡(luò)的開源項(xiàng)目。當(dāng)業(yè)界都癡迷于Overlay的時(shí)候,Calico實(shí)現(xiàn)multi-host容器網(wǎng)絡(luò)的思路確可以說是返璞歸真——pure L3,pure L3是指容器間的組網(wǎng)都是通過IP來完成的。這是因?yàn)椋珻alico認(rèn)為L3更為健壯,且對于網(wǎng)絡(luò)人員更為熟悉,而L2網(wǎng)絡(luò)由于控制平面太弱會(huì)導(dǎo)致太多問題,排錯(cuò)起來也更加困難。那么,如果能夠利用好L3去設(shè)計(jì)DC的話就完全沒有必要用L2。
不過對于應(yīng)用來說,L2無疑是更好的網(wǎng)絡(luò),尤其是容器網(wǎng)絡(luò)對二層的需求則更是強(qiáng)烈。業(yè)界普遍給出的答案是L2 over L3,而Calico認(rèn)為Overlay技術(shù)帶來的開銷(CPU、吞吐量)太大,如果能用L3去模擬L2是最好的,這樣既能保證性能、又能讓應(yīng)用滿意、還能給網(wǎng)絡(luò)人員省事,看上去是件一舉多得的事。用L3去模擬L2的關(guān)鍵就在于打破傳統(tǒng)的Hierarchy L3概念,IP不再以前綴收斂,大家干脆都把32位的主機(jī)路由發(fā)布到網(wǎng)絡(luò)上,那么Flat L3的網(wǎng)絡(luò)對于應(yīng)用來說即和L2一模一樣。
這個(gè)思路不簡單,刨了L3存在必要性的老底兒,實(shí)際上如果不用考慮可擴(kuò)展性、也不考慮private和public,IP地址和MAC地址標(biāo)識(shí)endpoint的功能上確實(shí)是完全冗余的,即使考慮可擴(kuò)展性,一個(gè)用L3技術(shù)形成的大二層和一個(gè)用L2技術(shù)形成的大二層并沒有本質(zhì)上的差距。而且,L3有成熟的、完善的、被普遍認(rèn)可接受的控制平面,以及豐富的管理工具,運(yùn)維起來要容易的多。
于是,Calico給出了下面的設(shè)計(jì)。L3選擇的是BGP,控制平面是開源的Bird做BGP RR,etcd集群+Felix做業(yè)務(wù)數(shù)據(jù)同步,數(shù)據(jù)平面直接是Linux kernel做vRouter,F(xiàn)IB全是/32的v4或者/128的v6。具體來說,etcd接受業(yè)務(wù)數(shù)據(jù),F(xiàn)elix向etcd同步后向host本地的路由表注入32/128位的主機(jī)路由,以及iptables的安全規(guī)則,然后Bird BGP Client將host的本地路由發(fā)送給Bird BGP RR,然后再由RR發(fā)布到其它host。
這個(gè)架構(gòu)沒什么好說的,技術(shù)成熟、高性能、易維護(hù),看起來是生產(chǎn)級別的容器網(wǎng)絡(luò)環(huán)境最好的選擇。但是,也有不如意的地方:
1、沒有了外面的封裝,就談不上VRF,多租戶的話地址沒法Overlap
2、L2和L3的概念模糊了,那么network級別的安全就搞不了,port級別的安全難搞因?yàn)樾枰囊?guī)則都是1:1的,數(shù)量上實(shí)在是太多了
不過,這都不是什么嚴(yán)重的問題。但有一點(diǎn)嚴(yán)重的是,Calico控制平面的設(shè)計(jì)要求物理網(wǎng)絡(luò)得是L2 Fabric,這樣vRouter間都是直接可達(dá)的,路由不需要把物理設(shè)備當(dāng)做下一跳。如果是L3 Fabric,控制平面的問題馬上就來了:下一跳是物理設(shè)備,那么它的IP是多少?物理設(shè)備如果要存32位的路由,能存多少?
這絕對是個(gè)難題。因此,為了解決以上問題,Calico不得不采取了妥協(xié),為了支持L3 Fabric,Calico推出了IPinIP的選項(xiàng),但是這明顯屬于自己打自己的臉,這么玩的話還不如用VxLAN來的實(shí)在呢。不過,對于使用L2 Fabric、沒有多租戶需求的企業(yè)來說,用Calico上生產(chǎn)環(huán)境應(yīng)該是不錯(cuò)的選擇。
4.Romana
說完了Calico的Flat L3,再來看看Romana給出的Hierarchy L3的方案。Romana是Panic Networks在2016年新提出的開源項(xiàng)目,旨在解決Overlay方案給網(wǎng)絡(luò)帶來的開銷,雖然目標(biāo)和Calico基本一致,但是采取的思路卻截然不同,Romana希望用Hierarchy L3來組織DC的網(wǎng)絡(luò)——沒有大二層什么事兒了。
當(dāng)然,Romana想要的是SDN的Hierarchy L3,因此控制平面的路由比較好控制了,不用搞RR這種東西了,不過IPAM的問題就比較關(guān)鍵了。IP地址有32位,哪些用來規(guī)劃leaf-spine?哪些用來規(guī)劃host?哪些用來規(guī)劃VM?哪些用來規(guī)劃POD?如果要多租戶,哪些用來規(guī)劃Tenant/Segment?可以說,這些如果有規(guī)劃好的可能,而且都可以動(dòng)態(tài)調(diào)整,那么Romana會(huì)是個(gè)“很SDN”的方案。
不過,這可不是說笑的,想要規(guī)劃好談何容易啊?問題歸根結(jié)底我認(rèn)為有如下幾點(diǎn):
1、要表示好DC中那么復(fù)雜的網(wǎng)絡(luò)資源,32的地址空間捉襟見肘,無論你系統(tǒng)設(shè)計(jì)的多么精妙,巧婦難為無米之炊
2、如果不用Overlay,想要IPAM能夠SDN化,邊緣的host沒問題,物理設(shè)備怎么辦?一旦規(guī)模擴(kuò)大了,或者組網(wǎng)有了新的需求,造成原有的地址規(guī)劃不合適了,host說改也就改了,物理網(wǎng)絡(luò)誰來搞動(dòng)態(tài)調(diào)整?
3、另外,關(guān)鍵的關(guān)鍵是,大二層不要了,遷移怎么弄?
可以看一看Romana的slides里面給的IPAM實(shí)例,255 hosts、255 tenants、255 endpoints,對于對于IDC、云服務(wù)提供商、容器用戶,是不是都顯得局促了一些呢?
因此從網(wǎng)絡(luò)設(shè)計(jì)的角度來說,個(gè)人目前還沒有想到Romana能夠支持大規(guī)模容器網(wǎng)絡(luò)的理由,至于項(xiàng)目具體會(huì)發(fā)展成什么樣子,仍需觀察。
下面來看一看它的架構(gòu)。倒也畫的比較簡單,managers是控制端,agent在設(shè)備端。控制端幾個(gè)組件的功能,看了名字也就知道了,這里不再解釋。設(shè)備端接受調(diào)度,給容器配IP,在host上配路由,也沒什么好說的了。
5.Contiv
Cisco在2015年搞的開源項(xiàng)目,準(zhǔn)備把ACI那一套EPG的東西用到容器網(wǎng)絡(luò)中來,號(hào)稱要做容器的micro-segment。具體的沒什么能講的,因?yàn)镚ithub上確實(shí)也還沒有什么東西,而且Cisco做開源向來是奔著讓人捉摸不透去的。Cisco的人說會(huì)集成到docker libnetwork中去,但項(xiàng)目的模樣能不能出來,還得看未來的進(jìn)展。項(xiàng)目鏈接在這:https://github.com/contiv/netplugin。
Neutron對容器網(wǎng)絡(luò)的集成
眼看著容器一步一步火起來,幾乎搶走了虛擬機(jī)的所有風(fēng)頭,OpenStack也按耐不住要做集成了。有Magnum做Swarm、K8S這些COE(Container Orchestration Engine)的前端,OpenStack就有了編排大規(guī)模容器集群的入口,而除了編排以外,網(wǎng)絡(luò)側(cè)的集成也是一個(gè)大頭。其實(shí)從network driver的角度來看,容器和虛擬機(jī)倒也沒什么特別大的差別,那么再搞一套Neutron for Container顯然是沒有必要(也不現(xiàn)實(shí))的了。于是,Kuryr項(xiàng)目應(yīng)運(yùn)而生,旨在將現(xiàn)有Neutron的network driver銜接到容器網(wǎng)絡(luò)中。
Kuryr是捷克語“信使”的意思,顧名思義,就是要把容器網(wǎng)絡(luò)的API轉(zhuǎn)化成Neutron API傳遞給Neutron,然后仍然由Neutron來調(diào)度后端的network driver來為容器組網(wǎng)。要做成這件事情,主要得解決三個(gè)問題:
1、建立容器網(wǎng)絡(luò)模型,如CNM和CNI,和Neutron網(wǎng)絡(luò)模型的映射關(guān)系
2、處理容器和Neutron network driver的端口綁定
3、容器不同于虛擬機(jī)的特征,可能會(huì)對現(xiàn)有Neutron network造成影響
第一個(gè)問題,通俗點(diǎn)說就是要做好翻譯工作。以docker libnetwork舉例,用戶調(diào)了libnetwork的API要新建一個(gè)(CNM模型中的)Network對象,那Kuryr就得翻譯成Neutron能聽得懂的API——幫我起一個(gè)(Neutron)Subnet。這要求Kuryr作為remote driver,于是Neutron和Neutron driver對于libnetwork就是完全透明的了。
上面舉了一個(gè)好理解的例子,不好辦的是當(dāng)兩側(cè)的模型不一致,尤其是左邊有新概念的時(shí)候。比如,現(xiàn)在要為部署在VM中的Nested Container搞一個(gè)Security Group,但是Neutron目前只能管到host上,是看不見這個(gè)藏起來的家伙的,那這時(shí)就要對Neutron做擴(kuò)展了,思路就是為Neutron Port新擴(kuò)展一個(gè)屬性來標(biāo)記VM中這個(gè)Nested Container,這樣做識(shí)別的時(shí)候帶上這個(gè)標(biāo)記就行了。
從實(shí)現(xiàn)上來講,Kuryr要負(fù)責(zé)管理兩側(cè)的資源實(shí)例的ID的映射關(guān)系,以保證操作的一致性,否則會(huì)直接帶來用戶間的網(wǎng)絡(luò)入侵。另外,IPAM現(xiàn)在在兩側(cè)都被獨(dú)立出來了,IPAM的API也要能銜接的上。
至于第二個(gè)問題,拍腦袋想似乎是不應(yīng)該存在的。但是,目前絕大多數(shù)Neutron network driver在綁定端口時(shí)的動(dòng)作只有更新數(shù)據(jù)庫,并不會(huì)為容器做plug。這個(gè)做法的原因在于,之前在處理虛擬機(jī)的時(shí)候,plug被看作是虛擬機(jī)啟動(dòng)時(shí)自帶的動(dòng)作,因此plug就放在了Nova的poweron函數(shù)里面。改network driver自然是不好的,于是Kuryr就得負(fù)責(zé)起處理這個(gè)歷史遺留問題的任務(wù)了。
第三個(gè)問題可就是學(xué)問了。講道理的話,業(yè)務(wù)的API沒問題了,容器也都接入網(wǎng)絡(luò)了,而轉(zhuǎn)發(fā)的邏輯都是network driver寫好的,跟接的是容器還是虛擬機(jī)也沒有一毛錢關(guān)系,那不就應(yīng)該萬事大吉了嗎?可是現(xiàn)實(shí)確很有可能不是這樣的,比如:由于容器作為工作負(fù)載,其特征與虛擬機(jī)完全不同,因此業(yè)務(wù)對二者需求也是大相徑庭。容器都是批量批量的起,而且它們的生命周期可能很短——需要來回來去的起,而Neutron的API都是走軟件的消息總線的,而過于密集的API操作很有可能會(huì)造成消息總線崩潰掉。一個(gè)新建容器的API等了1分鐘失敗了,那么可能業(yè)務(wù)的需求就過去了(比如搶票),這個(gè)損失自然是不可接受的。
類似的問題可能都在潛伏著,如果Kuryr要走上生產(chǎn)環(huán)境,可就需要Gal Sagie多動(dòng)腦筋了。
雖然Kuryr是OpenStack中比較新的項(xiàng)目,但目前Kuryr進(jìn)展的還不錯(cuò),對libnetwork和k8S的集成都有demo出來了。一旦Kuryr成熟后,這意味著Neutron下面的各路vendor們都可以不費(fèi)吹灰之力直接上容器了,這對于Weave、Calico這些靠容器起家的vendor不可算是個(gè)好消息。
看來,云網(wǎng)絡(luò)vendor間的競爭最后也都將演變?yōu)镺penStack、K8S、Mesos這些大部頭對于DCOS的爭奪了,畢竟胳膊擰不過大腿啊。
審核編輯:郭婷
評論
查看更多