引自:http://www.10tiao.com/html/217/201708/2649694873/1.html

容器的網(wǎng)絡(luò)是在CaaS集群中無法避免的話題,作為當(dāng)下最主流的一種容器集群解決方案,Kubernetes對(duì)網(wǎng)絡(luò)進(jìn)行了合理的抽象,并采用了開放的CNI模型。面對(duì)各種容器網(wǎng)絡(luò)實(shí)現(xiàn),他們有什么不同,應(yīng)該如何選擇?本文將帶大家回顧Kubernetes各種主流網(wǎng)絡(luò)方案的發(fā)展歷程,并透過現(xiàn)象清本質(zhì),用形象的例子展示W(wǎng)eave、Flannel、Calico和Romana等網(wǎng)絡(luò)解決方案背后的原理。

這次講一講容器集群中的網(wǎng)絡(luò)。其實(shí)不同的容器集群解決方案,在網(wǎng)絡(luò)方面的核心原理都是相似的,只不過這次我們將以Kubernetes為線索,來窺斑見豹的一睹容器網(wǎng)絡(luò)的發(fā)展過程。

我是來自ThoughtWorks的林帆,我們從Docker的0.x版本開始就在對(duì)容器的應(yīng)用場(chǎng)景進(jìn)行探索,積累了一線的容器運(yùn)用經(jīng)驗(yàn)。這次分享會(huì)用簡(jiǎn)潔易懂的方式告訴大家我們對(duì)容器網(wǎng)絡(luò)方面的一些知識(shí)歸納。

初入容器集群的人往往會(huì)發(fā)現(xiàn),和單節(jié)點(diǎn)的容器運(yùn)用相比,容器的網(wǎng)絡(luò)和存儲(chǔ)是兩個(gè)讓人望而卻步的領(lǐng)域。在這些領(lǐng)域里,存在大量的技術(shù)名詞和術(shù)語,就像是一道道拒人于門外的高門檻。

為了便于理解,我們將這些名稱簡(jiǎn)單的分個(gè)類別,從簡(jiǎn)單到復(fù)雜,依次遞增。今天的話題會(huì)涉及的深度大致在這個(gè)大池子的中間,希望大家看完以后會(huì)對(duì)目標(biāo)線以上的內(nèi)容不再陌生,目標(biāo)線以下的內(nèi)容我們也會(huì)依據(jù)需要適當(dāng)?shù)奶峒啊?/p>

此外,這個(gè)話題會(huì)按照Kubernetes的網(wǎng)絡(luò)發(fā)現(xiàn)過程作為時(shí)間主線,其中重點(diǎn)介紹CNI標(biāo)準(zhǔn)和它的主流實(shí)現(xiàn)。

在早期的Kubernetes中,對(duì)網(wǎng)絡(luò)是沒有什么標(biāo)準(zhǔn)的。文檔中對(duì)網(wǎng)絡(luò)的描述只有很簡(jiǎn)單的幾句話,其實(shí)就是讓用戶在部署Kubernetes之前,預(yù)先準(zhǔn)備好容器互聯(lián)的網(wǎng)絡(luò)解決方案。Kubernetes只對(duì)網(wǎng)絡(luò)提出設(shè)計(jì)假設(shè),這三條假設(shè)總結(jié)起來就是:所有容器都可以和集群里任意其他容器或者主機(jī)通信,并且通信雙方看到的對(duì)方IP地址就是實(shí)際的地址(即不經(jīng)過網(wǎng)絡(luò)地址轉(zhuǎn)換)。
基于這樣的底層網(wǎng)絡(luò),Kubernetes設(shè)計(jì)了Pod - Deployment - Service的經(jīng)典三層服務(wù)訪問機(jī)制。
既然Kubernetes不提供底層網(wǎng)絡(luò)實(shí)現(xiàn),在業(yè)界就出現(xiàn)了很多企業(yè)級(jí)的以及開源的第三方解決方案,其中下面這頁圖中展示了這個(gè)時(shí)期的主流開源方案。

我們將這些方案依據(jù)配置的復(fù)雜度,分為“全自動(dòng)”和“半自動(dòng)”兩類,就像是汽車中的自動(dòng)擋和手動(dòng)擋的差別。
“全自動(dòng)”的解決方案使用起來簡(jiǎn)單,適用于標(biāo)準(zhǔn)網(wǎng)絡(luò)環(huán)境的容器跨節(jié)點(diǎn)通信。
“半自動(dòng)”的解決方案實(shí)際上是對(duì)底層協(xié)議和內(nèi)核模塊功能的封裝,提供了選擇十分豐富的網(wǎng)絡(luò)連接方法,但對(duì)使用者的網(wǎng)絡(luò)原理知識(shí)有一定要求。

在Kubernetes的1.1版本中,發(fā)生了一件很重要的變化,那就是Kubernetes全面采納CNI網(wǎng)絡(luò)標(biāo)準(zhǔn)。
CNI誕生于2015年4月,Kubernetes的1.1版本誕生于2015年9月,之間僅隔5個(gè)月。在這個(gè)時(shí)期,Docker也設(shè)計(jì)了一套網(wǎng)絡(luò)標(biāo)準(zhǔn),稱為CNM(即Libnetwork)。Kubernetes采用CNI而非CNM,這背后有很長的一段故事,核心的原因就是CNI對(duì)開發(fā)者的約束更少,更開放,不依賴于Docker工具,而CNM對(duì)Docker有非常強(qiáng)的依賴,無法作為通用的容器網(wǎng)絡(luò)標(biāo)準(zhǔn)。在Kubernetes的官方博客里有一篇博文詳細(xì)討論了個(gè)中細(xì)節(jié),InfoQ上有一篇該博客的翻譯,有興趣的讀者不妨一讀。

幾乎在Kubernetes宣布采納CNI以后的1個(gè)月,之前提到的“全自動(dòng)”網(wǎng)絡(luò)方案就悉數(shù)實(shí)現(xiàn)了CNI的插件,這也間接說明了CNI的簡(jiǎn)單。
那么CNI到底有多簡(jiǎn)單呢?舉幾個(gè)數(shù)字。
實(shí)現(xiàn)一個(gè)CNI插件需要的內(nèi)容包括一個(gè)Json配置文件和一個(gè)可執(zhí)行的文件(腳本或程序),配置文件描述插件的版本、名稱、描述等基本信息,可執(zhí)行文件就是CNI插件本身會(huì)在容器需要建立網(wǎng)絡(luò)和需要銷毀容器的時(shí)候被調(diào)用。
當(dāng)一個(gè)CNI插件被調(diào)用時(shí),它能夠通過6個(gè)環(huán)境變量以及1個(gè)命令行參數(shù)(Json格式文本)來獲得需要執(zhí)行的操作、目標(biāo)網(wǎng)絡(luò)Namespace、容器的網(wǎng)卡必要信息,每個(gè)CNI插件只需實(shí)現(xiàn)兩種基本操作:創(chuàng)建網(wǎng)絡(luò)的ADD操作,和刪除網(wǎng)絡(luò)的DEL操作(以及一個(gè)可選的VERSION查看版本操作)。
最新的CNI規(guī)范可以通過上圖中的鏈接訪問,只有一頁網(wǎng)頁的內(nèi)容而已。同時(shí)Kuberntes社區(qū)也提供了一個(gè)利用“docker network”命令實(shí)現(xiàn)的簡(jiǎn)單CNI插件例子,只用了幾十行Bash腳本。

那么面對(duì)這么多的社區(qū)CNI插件,我們?cè)鯓舆x擇呢?
直觀的說,既然是網(wǎng)絡(luò)插件,在功能差不多的情況下,我們當(dāng)然先關(guān)心哪個(gè)的速度快。
為此我此前專門做過一次對(duì)比測(cè)試,不過由于使用了公有云的網(wǎng)絡(luò)環(huán)境(云上環(huán)境的不同主機(jī)之間相對(duì)位置不固定),在匯總數(shù)據(jù)的時(shí)候才發(fā)現(xiàn)測(cè)試結(jié)果偏離理論情況過于明顯。
這個(gè)數(shù)據(jù)大家且當(dāng)娛樂,不過對(duì)于同一種插件的不同工作模式(比如Flannel的三種模型)之間,由于是使用的相同的虛擬機(jī)測(cè)試,還是具有一定可參考性。
先拋開測(cè)試結(jié)果,從理論上說,這些CNI工具的網(wǎng)絡(luò)速度應(yīng)該可以分為3個(gè)速度等級(jí)。
最快的是Romana、Gateway模式的Flannel、BGP模式的Calico。
次一級(jí)的是IPIP模式的Calico、Swarm的Overlay網(wǎng)絡(luò)、VxLan模式的Flannel、Fastpath模式的Weave。
最慢的是UDP模式的Flannel、Sleeve模式的Weave。

這里我也提供出測(cè)試容器網(wǎng)絡(luò)速度的具體方法,以便大家重復(fù)這個(gè)測(cè)試。

要解釋這些網(wǎng)絡(luò)插件和模式速度不同的原因,我們需要先回到這些工具最初要解決的問題上來。那就是跨節(jié)點(diǎn)的網(wǎng)絡(luò)不通。
如果仔細(xì)觀察,會(huì)發(fā)現(xiàn)在3種網(wǎng)絡(luò)速度模式中都有Flannel的身影。因此我們不妨先以Flannel為例來看這些網(wǎng)絡(luò)工具(和相應(yīng)的CNI插件)是如何解決網(wǎng)絡(luò)不通問題的。

跨節(jié)點(diǎn)網(wǎng)絡(luò)不同有幾個(gè)方面的原因,首先是容器的地址重復(fù)。
由于Docker等容器工具只是利用內(nèi)核的網(wǎng)絡(luò)Namespace實(shí)現(xiàn)了網(wǎng)絡(luò)隔離,各個(gè)節(jié)點(diǎn)上的容器是在所屬節(jié)點(diǎn)上自動(dòng)分配IP地址的,從全局來看,這種局部地址就像是不同小區(qū)里的門牌號(hào),一旦拿到一個(gè)更大的范圍上看,就可能是會(huì)重復(fù)的。

為了解決這個(gè)問題,F(xiàn)lannel設(shè)計(jì)了一種全局的網(wǎng)絡(luò)地址分配機(jī)制,即使用Etcd來存儲(chǔ)網(wǎng)段和節(jié)點(diǎn)之間的關(guān)系,然后Flannel配置各個(gè)節(jié)點(diǎn)上的Docker(或其他容器工具),只在分配到當(dāng)前節(jié)點(diǎn)的網(wǎng)段里選擇容器IP地址。
這樣就確保了IP地址分配的全局唯一性。

是不是地址不重復(fù)網(wǎng)絡(luò)就可以聯(lián)通了呢?
這里還有一個(gè)問題,是對(duì)于不同的主機(jī)、以及網(wǎng)絡(luò)上的路由設(shè)備之間,并不知道這些IP容器網(wǎng)段的地址是如何分配的,因此數(shù)據(jù)包即使被發(fā)送到了網(wǎng)絡(luò)中,也會(huì)因?yàn)闊o法進(jìn)行路由而被丟掉。
雖然地址唯一了,依然無法實(shí)現(xiàn)真正的網(wǎng)絡(luò)通信。

為此,F(xiàn)lannel采用了幾種處理方法(也就是Flannel的幾種網(wǎng)絡(luò)模式)。
早期時(shí)候用的比較多的一種方式是Overlay網(wǎng)絡(luò)。
在這種方式下,所有被發(fā)送到網(wǎng)絡(luò)中的數(shù)據(jù)包會(huì)被添加上額外的包頭封裝。這些包頭里通常包含了主機(jī)本身的IP地址,因?yàn)橹挥兄鳈C(jī)的IP地址是原本就可以在網(wǎng)絡(luò)里路由傳播的。
根據(jù)不同的封包方式,F(xiàn)lannel提供了UDP和Vxlan兩種傳輸方法。
UDP封包使用了Flannel自定義的一種包頭協(xié)議,數(shù)據(jù)是在Linux的用戶態(tài)進(jìn)行封包和解包的,因此當(dāng)數(shù)據(jù)進(jìn)入主機(jī)后,需要經(jīng)歷兩次內(nèi)核態(tài)到用戶態(tài)的轉(zhuǎn)換。
VxLAN封包采用的是內(nèi)置在Linux內(nèi)核里的標(biāo)準(zhǔn)協(xié)議,因此雖然它的封包結(jié)構(gòu)比UDP模式復(fù)雜,但由于所有數(shù)據(jù)裝、解包過程均在內(nèi)核中完成,實(shí)際的傳輸速度要比UDP模式快許多。
但比較不幸的是,在Flannel開始流行的時(shí)候,大概2014年,主流的Linux系統(tǒng)還是Ubuntu 14.04或者CentOS 6.x,這些發(fā)行版的內(nèi)核比較低,沒有包含VxLAN的內(nèi)核模塊。因此多數(shù)人在開始接觸Flannel時(shí)候,都只能使用它的UDP模式,因此Flanned一不小心落得了一個(gè)“速度慢”的名聲,特別是在之后的Calico出來以后(其實(shí)Flannel的Gateway模式與Calico速度相當(dāng),甚至理論上還要快一點(diǎn)點(diǎn),稍后解釋)。
這是第一種解決網(wǎng)絡(luò)無法路由的方法。

第二種思路是,既然在無法進(jìn)行路由是因?yàn)榫W(wǎng)絡(luò)中的節(jié)點(diǎn)之間沒有路由信息,但Flannel是知道這個(gè)信息的,能不能把這個(gè)信息告訴網(wǎng)絡(luò)上的節(jié)點(diǎn)呢?
這就是Flannel的Host-Gateway模式,在這種模式下,F(xiàn)lannel通過在各個(gè)節(jié)點(diǎn)上的Agent進(jìn)程,將容器網(wǎng)絡(luò)的路由信息刷到主機(jī)的路由表上,這樣一來所有的主機(jī)就都有整個(gè)容器網(wǎng)絡(luò)的路由數(shù)據(jù)了。
Host-Gateway的方式?jīng)]有引入像Overlay中的額外裝包解包操作,完全是普通的網(wǎng)絡(luò)路由機(jī)制,它的效率與虛擬機(jī)直接的通信相差無幾。
然而,由于Flannel只能夠修改各個(gè)主機(jī)的路由表,一旦主機(jī)直接隔了個(gè)其他路由設(shè)備,比如三層路由器,這個(gè)包就會(huì)在路由設(shè)備上被丟掉。
這樣一來,Host-Gateway的模式就只能用于二層直接可達(dá)的網(wǎng)絡(luò),由于廣播風(fēng)暴的問題,這種網(wǎng)絡(luò)通常是比較小規(guī)模的,但近年來也出現(xiàn)了一些專門的設(shè)備能夠構(gòu)建出大規(guī)模的二層網(wǎng)絡(luò)(就是我們經(jīng)常聽到的“大二層”網(wǎng)絡(luò))。
那么其他的CNI插件呢?
由于Flannel幾乎是最早的跨網(wǎng)絡(luò)通信解決方案,其他的方案都可以被看做是Fannel的某種改進(jìn)版。

比如Weave的工作模式與Flannel是很相似的,它最早只提供了UDP(稱為sleeve模式)的網(wǎng)絡(luò)方式,后來又加上了fastpass方式(基于VxLAN),不過Weave消除了Flannel中用來存儲(chǔ)網(wǎng)絡(luò)地址的額外組件,自己集成了高可用的數(shù)據(jù)存儲(chǔ)功能。

Calico的設(shè)計(jì)比較新穎,之前提到Flannel的Host-Gateway模式之所以不能跨二層網(wǎng)絡(luò),是因?yàn)樗荒苄薷闹鳈C(jī)的路由,Calico把改路由表的做法換成了標(biāo)準(zhǔn)的BGP路由協(xié)議。
相當(dāng)于在每個(gè)節(jié)點(diǎn)上模擬出一個(gè)額外的路由器,由于采用的是標(biāo)準(zhǔn)協(xié)議,Calico模擬路由器的路由表信息就可以被傳播到網(wǎng)絡(luò)的其他路由設(shè)備中,這樣就實(shí)現(xiàn)了在三層網(wǎng)絡(luò)上的高速跨節(jié)點(diǎn)網(wǎng)絡(luò)。
不過在現(xiàn)實(shí)中的網(wǎng)絡(luò)并不總是支持BGP路由的,因此Calico也設(shè)計(jì)了一種IPIP模式,使用Overlay的方式來傳輸數(shù)據(jù)。IPIP的包頭非常小,而且也是內(nèi)置在內(nèi)核中的,因此它的速度理論上比VxLAN快一點(diǎn)點(diǎn),但安全性更差。

Cannal將Calico和Flannel做了一下組合,同時(shí)支持兩者的特性。

Romana只支持與Flannel相同的Host-Gateway模式,但它在網(wǎng)絡(luò)策略方面做了比較多的增強(qiáng),通過額外引入的租戶概念簡(jiǎn)化了網(wǎng)絡(luò)策略所需的IPtables規(guī)則數(shù)量。

這是幾種主流CNI工具的橫向?qū)Ρ取?/p>

在Kubernetes的1.2版本以后開始引入了一個(gè)新的工具,叫做 kubernet,它實(shí)現(xiàn)了內(nèi)置的網(wǎng)絡(luò)地址分配功能。結(jié)合一些云平臺(tái)上的內(nèi)網(wǎng)路由表功能,就能夠直接執(zhí)行跨網(wǎng)絡(luò)通信,相當(dāng)于把跨網(wǎng)絡(luò)功能內(nèi)建到Kubernetes里面了。
這是一個(gè)從“只做假設(shè)”到“設(shè)定標(biāo)準(zhǔn)”到“內(nèi)置實(shí)現(xiàn)”的很大的改變。

在Kubernetes的1.3版本以后,開始加入網(wǎng)絡(luò)策略相關(guān)的支持。并且在1.7版本后結(jié)束了Beta階段,成為正式API的一部分。
值得一說的是,Kubernetes的網(wǎng)絡(luò)策略采用了比較嚴(yán)格的單向流控制,即假如允許服務(wù)A訪問服務(wù)B,反過來服務(wù)B并不一定能訪問服務(wù)A。這與Docker內(nèi)置的Network命令實(shí)現(xiàn)的隔離有很大不同。

縱向的對(duì)比一下主流的容器集群對(duì)今天提到的這些網(wǎng)絡(luò)特性的支持情況和時(shí)間點(diǎn)。

Q:Kubernetes的網(wǎng)絡(luò)策略采用了比較嚴(yán)格的單向流控制,即假如允許服務(wù)A訪問服務(wù)B,反過來服務(wù)B并不一定能訪問服務(wù)A。為什么要設(shè)計(jì)成嚴(yán)格單向流呢?A:主要是安全性的原因,這是一種更精細(xì)的權(quán)限控制策略,除了方向,Kuberetes還允許對(duì)可訪問的端口進(jìn)行控制。
Q:Open vSwitch有測(cè)過么?
A:沒有測(cè)試,Open vSwitch同樣可以配置成Overlay網(wǎng)絡(luò)或者M(jìn)acvlan網(wǎng)絡(luò)等不同的通信模式,速度也會(huì)落到相應(yīng)的檔位上。那個(gè)測(cè)試?yán)又皇菫榱苏f明網(wǎng)絡(luò)速度與采用的通信原理有關(guān),同時(shí)引出幾種主流的通信模式原理,測(cè)試數(shù)據(jù)是不準(zhǔn)確的,建議以在自己的實(shí)際環(huán)境中測(cè)試結(jié)果為準(zhǔn)。
Q:Calico怎么做網(wǎng)段間的隔離?
A:各種網(wǎng)絡(luò)工具的網(wǎng)絡(luò)策略基本上都是基于內(nèi)核的Iptables模塊實(shí)現(xiàn)的。比如Calico只是根據(jù)用戶配置,自動(dòng)管理各個(gè)節(jié)點(diǎn)上的Iptables規(guī)則。其他有些改進(jìn)的,比如Romana設(shè)計(jì)了一種基于“租戶”的規(guī)則管理機(jī)制,可以用更少的Iptables規(guī)則實(shí)現(xiàn)網(wǎng)絡(luò)隔離。
Q:如果在Kubernetes里面需要做到平行網(wǎng)絡(luò),讓每一個(gè)Pod獲取一個(gè)業(yè)務(wù)IP,除了Bridge+Vlan的方式,還有更好的建議么?
A:這次講的這些CNI插件都會(huì)讓每一個(gè)Pod獲得一個(gè)獨(dú)立業(yè)務(wù)IP??梢愿鶕?jù)實(shí)際網(wǎng)絡(luò)情況和對(duì)速度的需求選擇。
Q:Calico BGP IPIP NAT三種模式分別怎么配置?原理是怎樣的?其中IPIP還有兩種模式,區(qū)別在哪?
A:在Calico的配置中設(shè)置spec.ipip.enabled: ture就會(huì)開啟IPIP模式,否則默認(rèn)是純BGP模式。IPIP的兩種模式其實(shí)是指純IPIP(ipip always模式)或者混合IPIP和BGP(ipip cross-subnet),后者指的是“同子網(wǎng)內(nèi)路由采用BGP,跨子網(wǎng)路由采用IPIP”,主要用于即想在內(nèi)網(wǎng)獲得高速,又想在跨公網(wǎng)時(shí)能保持聯(lián)通的情況。這種模式需要每個(gè)節(jié)點(diǎn)啟動(dòng)時(shí)用--ip參數(shù)預(yù)先配置節(jié)點(diǎn)的子網(wǎng),并且所有節(jié)點(diǎn)版本都在v2.1以上。
Q:能麻煩具體介紹一下kube-proxy這種網(wǎng)絡(luò)模式的優(yōu)缺點(diǎn)嗎,在測(cè)試過程中發(fā)現(xiàn)很不穩(wěn)定,但是又沒發(fā)現(xiàn)足夠的證據(jù)。
A:kube-proxy是Kubernetes的一個(gè)組件,提供通過Service到Pod的路由,并不是一種網(wǎng)絡(luò)模式,不同的網(wǎng)絡(luò)插件都會(huì)使用到這個(gè)組件。