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