Docker概念和默認(rèn)網(wǎng)絡(luò)
什么是Docker網(wǎng)絡(luò)呢?總的來(lái)說(shuō),網(wǎng)絡(luò)中的容器們可以相互通信,網(wǎng)絡(luò)外的又訪問(wèn)不了這些容器。具體來(lái)說(shuō),在一個(gè)網(wǎng)絡(luò)中,它是一個(gè)容器的集合,在這個(gè)概念里面的一個(gè)容器,它會(huì)通過(guò)容器的IP直接去通信,又能保證在這個(gè)集合外的一些容器不能夠通過(guò)這個(gè)容器IP去通信,能做到網(wǎng)絡(luò)隔離。網(wǎng)絡(luò)這個(gè)概念是由網(wǎng)絡(luò)的驅(qū)動(dòng)去創(chuàng)建、管理的。網(wǎng)絡(luò)的驅(qū)動(dòng)又分為全局的和本地的,全局的意思是這個(gè)網(wǎng)絡(luò)可以跨主機(jī),沒(méi)必要說(shuō)我的兩個(gè)容器非要在一個(gè)主機(jī)上才能通過(guò)這個(gè)網(wǎng)絡(luò)去通信,我可以在不同主機(jī)上還是通過(guò)容器的IP相互通信。
本地網(wǎng)絡(luò)
首先我們看一個(gè)本地網(wǎng)絡(luò)的圖,這個(gè)圖中有一臺(tái)主機(jī),上面跑了四個(gè)容器,分別分布在兩個(gè)網(wǎng)絡(luò)里面,一個(gè)是NET1,一個(gè)是NET2。NET1里面的兩個(gè)容器是可以互相通過(guò)自己的IP通信的,但是NET1里面的容器和NET2里面的容器又是隔離的,這樣就是Docker的一個(gè)網(wǎng)絡(luò)概念。
全局網(wǎng)絡(luò)
全局網(wǎng)絡(luò)意思是,一個(gè)網(wǎng)絡(luò)里面的容器是可以跨主機(jī)的。C1的容器可以和主機(jī)2上的C3容器,通過(guò)它們IP直接通信,而不用管所在的主機(jī)在哪個(gè)地方。這個(gè)圖里面我們還可以看到一個(gè)細(xì)節(jié),同一個(gè)容器可以加入到兩個(gè)網(wǎng)絡(luò)里面,比如C4它可以和網(wǎng)絡(luò)1里面的兩個(gè)容器通信,也可以和網(wǎng)絡(luò)2里面的C5進(jìn)行通信,這樣我們就可以實(shí)現(xiàn)更加復(fù)雜的一些組合。比如C5是一個(gè)數(shù)據(jù)庫(kù)的容器,我不想讓這個(gè)數(shù)據(jù)被前面的WEB應(yīng)用或者其他的應(yīng)用去訪問(wèn)到,只想讓我的中間鍵應(yīng)用去訪問(wèn)到。這樣就可以通過(guò)讓中間鍵應(yīng)用加入到一個(gè)后臺(tái)網(wǎng)絡(luò)、一個(gè)前臺(tái)網(wǎng)絡(luò)的組合來(lái)實(shí)現(xiàn)這種互聯(lián)和網(wǎng)絡(luò)的控制。
網(wǎng)絡(luò)服務(wù)發(fā)現(xiàn)
在1.10版本增加了網(wǎng)絡(luò)服務(wù)發(fā)現(xiàn),什么意思呢?比如,我的一個(gè)網(wǎng)絡(luò)有兩個(gè)容器,我們可以直接通過(guò)IP進(jìn)行通信,但是我的容器它可能某一段時(shí)間跪掉了,它可能在跪掉之后再去重啟,我這時(shí)就不知道它的IP了,或者它一關(guān)閉之后,我這邊感知不到,這樣就要觸發(fā)很多復(fù)雜的一些處理流程。在1.10時(shí)它去做這樣的一件事,它在每個(gè)容器里面內(nèi)置了一個(gè)DNS的服務(wù)器,在服務(wù)器RUN起來(lái)之后,會(huì)寫(xiě)一個(gè)容器名,或者是這個(gè)容器的一個(gè)別名和它IP的一個(gè)解析。外部容器去訪問(wèn)我的DB容器時(shí),它能夠通過(guò)DB這個(gè)名字來(lái)解析到DB容器的IP地址,這樣在DB容器重啟之后,這個(gè)解析會(huì)動(dòng)態(tài)去調(diào)整,就能實(shí)現(xiàn)一個(gè)服務(wù)的發(fā)現(xiàn)。
Docker默認(rèn)的網(wǎng)絡(luò)驅(qū)動(dòng)
Docker默認(rèn)的會(huì)有一些網(wǎng)絡(luò)驅(qū)動(dòng),它本地的網(wǎng)絡(luò)驅(qū)動(dòng)有這幾種。none網(wǎng)絡(luò)驅(qū)動(dòng)意思是,我創(chuàng)建一個(gè)容器,但是沒(méi)有它的一些網(wǎng)絡(luò)配置,單獨(dú)一個(gè)命名空間,它不能和其他容器通信,也不能訪問(wèn)外網(wǎng)。host的網(wǎng)絡(luò)驅(qū)動(dòng)和宿主機(jī)共享網(wǎng)絡(luò)棧、它和宿主機(jī)看到同樣的接口,包括它自己暴露的端口,都是和宿主機(jī)在同一個(gè)網(wǎng)絡(luò)空間里面。
bridge網(wǎng)絡(luò)驅(qū)動(dòng),它是一個(gè)本地的網(wǎng)絡(luò)驅(qū)動(dòng),上圖主機(jī)上有兩個(gè)bridge網(wǎng)絡(luò),它實(shí)際上是通過(guò)Linux的bridge去驅(qū)動(dòng)的,通過(guò)容器里面掛載一個(gè)網(wǎng)卡,對(duì)應(yīng)的是在宿主機(jī)上一個(gè)veth,容器的請(qǐng)求會(huì)直接轉(zhuǎn)發(fā)到這個(gè)veth上面,bridge上面會(huì)對(duì)這個(gè)請(qǐng)求進(jìn)行洪泛,它就能到達(dá)這個(gè)網(wǎng)絡(luò)里面的另外一個(gè)容器。同理,另外一個(gè)網(wǎng)絡(luò),它跟這個(gè)網(wǎng)絡(luò)里面的容器沒(méi)有在同一個(gè)網(wǎng)橋上面,所以他們之間是互相不能通信的。bridge驅(qū)動(dòng)還會(huì)實(shí)現(xiàn)另外一個(gè)功能,比如說(shuō)C1的容器,他要訪問(wèn)公網(wǎng),它通過(guò)bridge出去的時(shí)候,他最終到達(dá)宿主機(jī)的接口,他會(huì)做一個(gè)網(wǎng)絡(luò)地址的轉(zhuǎn)換,把容器的IP轉(zhuǎn)換成宿主機(jī)的IP,這樣就能訪問(wèn)公網(wǎng)。或者我的容器可能希望服務(wù)映射到外面去,讓別的機(jī)器或別的公開(kāi)服務(wù)可以訪問(wèn)到,他可以做一個(gè)DNAT一個(gè)規(guī)則,比如我的容器是Nginx,它里面是80端口,我想映射到主機(jī)的一個(gè)9080,可以直接通過(guò)主機(jī)的9080去訪問(wèn),他可以去做一個(gè)DNAT的配置,這是bridge的驅(qū)動(dòng)。
Docker默認(rèn)還有一個(gè)overlay的驅(qū)動(dòng),它主要是去實(shí)現(xiàn)跨主機(jī)的一個(gè)通信,它會(huì)在主機(jī)之間通過(guò)vxlan的方式打一個(gè)隧道,實(shí)現(xiàn)我的主機(jī)上的容器去訪問(wèn)主機(jī)上的容器,最終會(huì)轉(zhuǎn)換成一個(gè)vxlan里面的通信。
Docker跨主機(jī)網(wǎng)絡(luò)
沒(méi)有跨主機(jī)網(wǎng)絡(luò)時(shí),比如一個(gè)應(yīng)用有兩個(gè)服務(wù),兩個(gè)服務(wù)不可能部署到同一個(gè)機(jī)器上,這樣沒(méi)辦法做到高可用,所在的主機(jī)一掛,上面所有的服務(wù)都掛了。如果讓它部署到兩個(gè)主機(jī)上面,但是兩個(gè)主機(jī)上面這個(gè)容器的IP在主機(jī)外面是看不到的。所以只能把容器的IP轉(zhuǎn)換成了一個(gè)宿主機(jī)的一個(gè)IP,或者宿主機(jī)的一個(gè)訪問(wèn)端點(diǎn),才能讓外部的主機(jī)訪問(wèn)。所以,以前的方式是通過(guò)端口映射,把容器的一個(gè)端口映射到主機(jī)的一個(gè)端口,讓另外一個(gè)主機(jī)和這個(gè)主機(jī)映射到這個(gè)容器上去通信。這樣會(huì)帶來(lái)哪些問(wèn)題?首先我通過(guò)端口映射,比如說(shuō)我映射到8090端口,我另外再想起一個(gè)容器。他就得映射到另外一個(gè)端口,因?yàn)樗拗鳈C(jī)上只有這一個(gè)8090端口,它不可能說(shuō)共享這個(gè)端口,所以一個(gè)主機(jī)上去起容器的時(shí)候,需要管理端口的列表。映射出去之后是宿主機(jī)的一個(gè)端口,這樣這個(gè)端口就是對(duì)外暴露的,可能要去控制一下,這個(gè)端口只能說(shuō)主機(jī)要去訪問(wèn),這樣要去做一些安全的配置,所以這樣的整個(gè)架構(gòu)是非常復(fù)雜的。在Docker1.9之后,增加了跨主機(jī)的網(wǎng)絡(luò),它就可以直接通過(guò)容器的IP去通信,通過(guò)這個(gè)外部跟它是隔離的,這樣又能做到安全,我就算再起一個(gè)容器,它容器的IP端口是不會(huì)沖突的。
封包模式
封包模式,比如overlay驅(qū)動(dòng),它是用VXLAN的協(xié)議去把容器之間的請(qǐng)求封裝成VXLAN的請(qǐng)求,將鏈路層的以太網(wǎng)包封裝到UDP包中進(jìn)行傳輸。
這里有一個(gè)簡(jiǎn)單的圖來(lái)介紹封包模式,首先舉個(gè)例子,從C1去訪問(wèn)宿主機(jī)上C2的容器,先通過(guò)C1發(fā)出這個(gè)包之后,它首先進(jìn)行封包,要查找中心化存儲(chǔ)C2是在哪個(gè)主機(jī)上的,查到的是這個(gè)主機(jī)上,就把這個(gè)請(qǐng)求的包封裝成宿主機(jī)之間的包,把這個(gè)包送達(dá)到對(duì)應(yīng)宿主機(jī)上,再通過(guò)一定的約定去解包,最終轉(zhuǎn)發(fā)到另外一個(gè)主機(jī)上的一個(gè)容器。其缺點(diǎn)是對(duì)帶寬的損耗比較大,因?yàn)榭吹竭@里去封包,它肯定是把這個(gè)容器的包上面又加了一層包,這個(gè)包上面肯定會(huì)有一些自己的占用,一般像封包模式,MTU最終都會(huì)小于宿主機(jī)之間的MTU,它還會(huì)造成一些資源占用。比如說(shuō)在這個(gè)地方去封包的時(shí)候,它可能會(huì)占用CPU去做一下封包操作,還會(huì)占用CPU去做解包的操作,所以會(huì)增加主機(jī)上的負(fù)載,但是它的好處是對(duì)基礎(chǔ)設(shè)施要求比較低,它只需要兩個(gè)宿主機(jī)之間可以三層互通。
下面我們以Docker的overlay驅(qū)動(dòng)來(lái)介紹下封包的實(shí)現(xiàn),它的封包方式是基于VXLAN。上圖是它的針格式,VXLAN內(nèi)部把一個(gè)二層的包、鏈路層的一個(gè)請(qǐng)求封裝成一個(gè)VXLAN的一個(gè)包,這里面會(huì)有一個(gè)約定的信息,比如VXLAN ID,還有一個(gè)外部的UDP的頭,最終會(huì)把這個(gè)包通過(guò)UDP發(fā)往對(duì)應(yīng)的主機(jī)上面去,對(duì)應(yīng)的主機(jī)再做VXLINE的解包。Docker還會(huì)做IP的地址管理和網(wǎng)絡(luò)信息同步。
路由模式
如上圖,比如,主機(jī)1上的容器想要訪問(wèn)主機(jī)2的容器,首先這個(gè)機(jī)器上是沒(méi)有主機(jī)2的IP地址,它出了機(jī)器之后到達(dá)路由器,再通過(guò)路由器上的路由表,去把對(duì)應(yīng)的請(qǐng)求網(wǎng)端轉(zhuǎn)發(fā)到對(duì)應(yīng)的主機(jī)2上面,主機(jī)2上面是有這個(gè)容器的IP,所以它就能夠做到這個(gè)容器的跨主機(jī)通信。它的好處在于它沒(méi)有封包,所以它的性能很好,但是它對(duì)于基礎(chǔ)設(shè)施有一些要求,比如我的基礎(chǔ)設(shè)施要支持、能夠配置這個(gè)路由表,如果用Linux路由要支持二層的轉(zhuǎn)發(fā)。
VPC網(wǎng)絡(luò)驅(qū)動(dòng)是在阿里云的VPC基礎(chǔ)上,能夠?qū)崿F(xiàn)容器互通的一種方案。首先在Docker DM啟動(dòng)時(shí),或者是在創(chuàng)建網(wǎng)絡(luò)時(shí),會(huì)從每個(gè)機(jī)器的一個(gè)中心化存儲(chǔ)里面,拿到兩個(gè)自己的網(wǎng)端,比如這個(gè)機(jī)器上的網(wǎng)端,比如左邊主機(jī)上的網(wǎng)端就是172.19.1.1/24,它通過(guò)阿里云的openapi去配置這個(gè)轉(zhuǎn)發(fā)表,把這個(gè)網(wǎng)端的地址都轉(zhuǎn)發(fā)到這個(gè)主機(jī)上面。另外一個(gè)主機(jī)啟動(dòng)時(shí),把它拿到一個(gè)可用的網(wǎng)端,去配置阿里云的一個(gè)路由表,把這個(gè)網(wǎng)端的請(qǐng)求轉(zhuǎn)發(fā)到自己的主機(jī)上面,比如說(shuō)我這邊的一個(gè)容器想要去訪問(wèn)主機(jī)2上C2的容器時(shí),它這個(gè)包最終到達(dá),通過(guò)vSwitch到達(dá)阿里云VPC的vRouter時(shí),通過(guò)查詢路由表把實(shí)時(shí)的請(qǐng)求包轉(zhuǎn)發(fā)到主機(jī)2上面,最終實(shí)現(xiàn)C1和C2的通信。
跨主機(jī)網(wǎng)絡(luò)方案對(duì)比
封包模式的優(yōu)點(diǎn)是對(duì)基礎(chǔ)設(shè)施的要求比較低,但是它性能損耗比較大,路由模式是沒(méi)有封包的,所以它性能比較好,但是對(duì)基礎(chǔ)設(shè)施有一定要求,比如說(shuō)要支持路由表,或者要支持二層的轉(zhuǎn)發(fā)。
阿里云服務(wù)的網(wǎng)絡(luò)方案
容器服務(wù)上面現(xiàn)在有兩種集群,一種是阿里云的ECS集群,這種集群的節(jié)點(diǎn)都是在阿里云的同地域或者同一個(gè)VPC下面的ECS,或者是混合云的集群,阿里云的混合云集群節(jié)點(diǎn)是在用戶自己建立的機(jī)房,或者分布在不同的云供應(yīng)商上面。
在阿里云的ECS集群上面,網(wǎng)絡(luò)架構(gòu)是這樣的,容器之間去通信,還是通過(guò)overlay驅(qū)動(dòng),它的好處是對(duì)基礎(chǔ)設(shè)施要求比較低,所以它只需要主機(jī)之間互通就可以了,容器去訪問(wèn)外網(wǎng),或者容器想要暴露端口給外網(wǎng)就通過(guò)一個(gè)gateway_bridge的網(wǎng)橋、本地的一個(gè)驅(qū)動(dòng),這個(gè)是供容器訪問(wèn)外網(wǎng)和容器的端口映射。在ECS集群上面,VPC網(wǎng)絡(luò)是通過(guò)VPC驅(qū)動(dòng)的路由表把對(duì)應(yīng)的請(qǐng)求轉(zhuǎn)發(fā)到容器所在機(jī)器上面,它去訪問(wèn)外網(wǎng)和做端口映射也是和剛才所介紹的overlay是同樣的。
混合云集群是結(jié)合阿里云的VPC技術(shù)去實(shí)現(xiàn)容器之間的網(wǎng)絡(luò)互通。在混合云集群里面是通過(guò)專線的方式,把對(duì)應(yīng)的數(shù)據(jù)中心的請(qǐng)求轉(zhuǎn)發(fā)到我的數(shù)據(jù)中心里面,或者把數(shù)據(jù)中心的請(qǐng)求訪問(wèn)VPC的,通過(guò)專線的一個(gè)路由也可以轉(zhuǎn)發(fā)到VPC里面,再通過(guò)路由表把容器的請(qǐng)求轉(zhuǎn)發(fā)到對(duì)應(yīng)的主機(jī)上面,最終實(shí)現(xiàn)的方式是把我VPC里面的容器和數(shù)據(jù)中心里面的容器互通。
剛才我們介紹的都是容器之間通信,我們可以不通過(guò)端口映射的方式做到端口不沖突,怎么能夠做到我想要外部訪問(wèn)時(shí),也能做到就一個(gè)負(fù)載均衡,怎么能做到一個(gè)機(jī)器上面部署多個(gè)容器,然后端口不沖突呢?這時(shí)候阿里云就提供了這樣的一個(gè)負(fù)載均衡方案。比如說(shuō)我的網(wǎng)站有兩個(gè)域名,一個(gè)是購(gòu)物的域名,一個(gè)是付款的,但是我總共就三臺(tái)機(jī)器,每個(gè)服務(wù)要做到高可用,所以起了很多的容器,這樣在同一個(gè)主機(jī)上就要去處理,怎么讓它保障單個(gè)不沖突,容器服務(wù)是提供一個(gè)這樣的方案,我們?cè)诩豪锩鏁?huì)部署一套路由的一個(gè)服務(wù),首先請(qǐng)求,比如說(shuō)我的公司域名的泛解析會(huì)解析到我的路由服務(wù)上面去,路由服務(wù)再通過(guò)請(qǐng)求的域名Host,去把這個(gè)請(qǐng)求直接轉(zhuǎn)發(fā)到容器上面,最終實(shí)現(xiàn)我的一個(gè)請(qǐng)求可以轉(zhuǎn)發(fā)到容器上面,但是我的容器并沒(méi)有映射出端口,也不需要去解決端口沖突的問(wèn)題。