研發(fā)背景
眾所周知,Docker容器跨主機(jī)互訪一直是一個(gè)問(wèn)題,Docker官方為了避免網(wǎng)絡(luò)上帶來(lái)的諸多麻煩,故將跨主機(jī)網(wǎng)絡(luò)開(kāi)了比較大的口子,而由用戶自己去實(shí)現(xiàn)。目前Docker跨主機(jī)的網(wǎng)絡(luò)實(shí)現(xiàn)方案也有很多種,主要包括端口映射、ovs、 fannel等。
但是這些方案都無(wú)法滿足我們的需求:端口映射服務(wù)內(nèi)的內(nèi)網(wǎng)IP會(huì)映射成外網(wǎng)的IP,這樣會(huì)給開(kāi)發(fā)帶來(lái)困惑,因?yàn)樗麄兺诳缇W(wǎng)絡(luò)交互時(shí)是不需要內(nèi)網(wǎng)IP的;而ovs與fannel則是在基礎(chǔ)網(wǎng)絡(luò)協(xié)議上又包裝了一層自定義協(xié)議,這樣當(dāng)網(wǎng)絡(luò)流量大時(shí),卻又無(wú)端的增加了網(wǎng)絡(luò)負(fù)載。最后我們采取了自主研發(fā)扁平化網(wǎng)絡(luò)插件,也就是說(shuō)讓所有的容器統(tǒng)統(tǒng)在大二層上互通。
Docker原生四種網(wǎng)絡(luò)模式
目前,基于Docker的網(wǎng)絡(luò)模式有很多種,接下來(lái)就簡(jiǎn)單的對(duì)Bridge、Host、Container、None模式進(jìn)行介紹。
A. Bridge模式該模式為Docker的默認(rèn)網(wǎng)絡(luò)模式,Docker daemon 會(huì)在宿主機(jī)上建立一個(gè)默認(rèn)的網(wǎng)橋docker0, 相信大家對(duì)docker0非常熟悉,但是在跨容器通信當(dāng)中它卻沒(méi)有派上用場(chǎng),因?yàn)槟J(rèn)的docker0的地址都是內(nèi)網(wǎng)地址,而且啟動(dòng)后容器雖然也橋接在docker0上,但是容器的默認(rèn)網(wǎng)關(guān)卻依然無(wú)法設(shè)置,這就是docker原生默認(rèn)網(wǎng)絡(luò)的一個(gè)弊端。當(dāng)然了,他卻實(shí)現(xiàn)了在當(dāng)先宿主機(jī)的網(wǎng)絡(luò)隔離,擁有自己的namespace, 網(wǎng)卡和IP,具體的橋接原理我將在后面繼續(xù)說(shuō)明。
B. Host模式
該模式其實(shí)就是和當(dāng)前宿主機(jī)共享網(wǎng)絡(luò)空間,而Docker本身并沒(méi)有進(jìn)行網(wǎng)絡(luò)隔離,說(shuō)的通俗點(diǎn),也就是說(shuō)容器其實(shí)都是和宿主機(jī)擁有相同的IP, 而如何具體區(qū)分各個(gè)容器的呢?那就是通過(guò)端口映射,在啟動(dòng)Docker容器的時(shí)候來(lái)指定-p參數(shù)來(lái)進(jìn)行設(shè)置端口映射。雖然這種方式在某種程度上也可以達(dá)到跨宿主機(jī)容器訪問(wèn)的目的,但是,卻喪失了Docker網(wǎng)絡(luò)隔離的意義,而且端口映射同樣給微服務(wù)遷移帶來(lái)一些麻煩,無(wú)法像非虛擬環(huán)境那樣的平滑遷移,而是要考慮到很多端口轉(zhuǎn)換的問(wèn)題。
C. Container模式
顧名思義,此模式會(huì)共享另一個(gè)容器的網(wǎng)絡(luò)命名空間,但是會(huì)限制在一臺(tái)宿主機(jī)上,依然無(wú)法實(shí)現(xiàn)容器間跨主機(jī)通信的功能。
D. None模式
該模式是容器擁有自己的網(wǎng)絡(luò)命名空間,自己的網(wǎng)路棧,自己的網(wǎng)卡,不和外界有任何瓜葛,容器網(wǎng)絡(luò)完全獨(dú)立,換句話說(shuō)就是容器不需要網(wǎng)絡(luò)功能,這種模式適用于容器包含寫(xiě)數(shù)據(jù)到磁盤(pán)卷的一些任務(wù)。這種模式依然無(wú)法實(shí)現(xiàn)我們的跨宿主機(jī)容器網(wǎng)絡(luò)通信的功能。
自研Docker Overlay網(wǎng)絡(luò)模式
目前Overlay網(wǎng)絡(luò)模式主要是由隧道和路由兩種方式實(shí)現(xiàn),一種是對(duì)基礎(chǔ)網(wǎng)絡(luò)協(xié)議進(jìn)行封包,另一種是配置更復(fù)雜的路由配置實(shí)現(xiàn)容器間跨主機(jī)的網(wǎng)絡(luò)通信。其實(shí),以上兩種或多或少的都會(huì)給我們網(wǎng)絡(luò)的實(shí)現(xiàn)帶來(lái)了復(fù)雜性以及性能上的損耗,因?yàn)楫?dāng)我們擁有龐大的業(yè)務(wù)集群以后,這些復(fù)雜度和性能損耗都是不能忽視的。
插件原理如下:
1.創(chuàng)建Docker自定義網(wǎng)絡(luò)
docker network create --opt=com.docker.network.bridge.enable_icc=true--opt=com.docker.network.bridge.enable_ip_masquerade=false--opt=com.docker.network.bridge.host_binding_ipv4=0.0.0.0--opt=com.docker.network.bridge.name=br0--opt=com.docker.network.driver.mtu=1500--ipam-driver=talkingdata--subnet=容器IP的子網(wǎng)范圍, 例:172.18.0.0/17
--gateway=br0網(wǎng)橋使用的IP,也就是宿主機(jī)的地址, 例:172.18.0.5
--aux-address=DefaultGatewayIPv4=容器使用的網(wǎng)關(guān)地址mynet
我們首先需要?jiǎng)?chuàng)建一個(gè)br0自定義網(wǎng)橋,這個(gè)網(wǎng)橋并不是通過(guò)系統(tǒng)命令手動(dòng)建立的原始Linux網(wǎng)橋,而是通過(guò)Docker的create network命令來(lái)建立的自定義網(wǎng)橋,這樣避免了一個(gè)很重要的問(wèn)題就是我們可以通過(guò)設(shè)置DefaultGatewayIPv4參數(shù)來(lái)設(shè)置容器的默認(rèn)路由,這個(gè)解決了原始Linux自建網(wǎng)橋不能解決的問(wèn)題. 用Docker創(chuàng)建網(wǎng)絡(luò)時(shí)我們可以通過(guò)設(shè)置subnet參數(shù)來(lái)設(shè)置子網(wǎng)IP范圍,默認(rèn)我們可以把整個(gè)網(wǎng)段給這個(gè)子網(wǎng),后面可以用ipam driver(地址管理插件)來(lái)進(jìn)行控制。還有一個(gè)參數(shù)gateway是用來(lái)設(shè)置br0自定義網(wǎng)橋地址的,其實(shí)也就是你這臺(tái)宿主機(jī)的地址啦。
2.IPAM
這個(gè)驅(qū)動(dòng)是專(zhuān)門(mén)管理Docker 容器IP的, Docker 每次啟停與刪除容器都會(huì)調(diào)用這個(gè)驅(qū)動(dòng)提供的IP管理接口,然后IP接口會(huì)對(duì)存儲(chǔ)IP地址的Etcd有一個(gè)增刪改查的操作。此插件運(yùn)行時(shí)會(huì)起一個(gè)Unix Socket, 然后會(huì)在docker/run/plugins 目錄下生成一個(gè).sock文件,Docker daemon之后會(huì)和這個(gè)sock 文件進(jìn)行溝通去調(diào)用我們之前實(shí)現(xiàn)好的幾個(gè)接口進(jìn)行IP管理,以此來(lái)達(dá)到IP管理的目的,防止IP沖突。
3.橋接
通過(guò)Docker命令去創(chuàng)建一個(gè)自定義的網(wǎng)絡(luò)起名為“mynet”,同時(shí)會(huì)產(chǎn)生一個(gè)網(wǎng)橋br0,之后通過(guò)更改網(wǎng)絡(luò)配置文件(在/etc/sysconfig/network-scripts/下ifcfg-br0、ifcfg-默認(rèn)網(wǎng)絡(luò)接口名)將默認(rèn)網(wǎng)絡(luò)接口橋接到br0上,重啟網(wǎng)絡(luò)后,橋接網(wǎng)絡(luò)就會(huì)生效。Docker默認(rèn)在每次啟動(dòng)容器時(shí)都會(huì)將容器內(nèi)的默認(rèn)網(wǎng)卡橋接到br0上,而且宿主機(jī)的物理網(wǎng)卡也同樣橋接到了br0上了。其實(shí)橋接的原理就好像是一臺(tái)交換機(jī),Docker 容器和宿主機(jī)物理網(wǎng)絡(luò)接口都是服務(wù)器,通過(guò)veth pair這個(gè)網(wǎng)絡(luò)設(shè)備像一根網(wǎng)線插到交換機(jī)上。至此,所有的容器網(wǎng)絡(luò)已經(jīng)在同一個(gè)網(wǎng)絡(luò)上可以通信了,每一個(gè)Docker容器就好比是一臺(tái)獨(dú)立的虛擬機(jī),擁有和宿主機(jī)同一網(wǎng)段的IP,可以實(shí)現(xiàn)跨主機(jī)訪問(wèn)了。
4.etcd
我們可以設(shè)置1,3,5,7個(gè)節(jié)點(diǎn)為Etcd集群去集中管理Docker集群的IP,而且我們也將宿主機(jī)的地址進(jìn)行了統(tǒng)一的管理,這樣做同樣也是為了避免IP的使用沖突導(dǎo)致線上資源不可用。我們會(huì)通過(guò)自己開(kāi)發(fā)的工具進(jìn)行IP初始化,也就是說(shuō)會(huì)傳進(jìn)來(lái)一個(gè)IP范圍,然后工具會(huì)將所有的IP存進(jìn)etcd中,每一個(gè)網(wǎng)絡(luò)ID就是一個(gè)etcd目錄,目錄下就會(huì)分成已分配與未分配的IP地址池。etcd本身會(huì)提供Go語(yǔ)言的API來(lái)訪問(wèn)etcd, 目前etcd還是相當(dāng)穩(wěn)定的,沒(méi)有出現(xiàn)過(guò)什么問(wèn)題。
結(jié)語(yǔ)
我們的Docker集群是采用Swarm進(jìn)行管理的,Swarm相對(duì)K8S來(lái)說(shuō)要簡(jiǎn)單的很多,但是在編排功能上也會(huì)有所欠缺,不過(guò)目前等新版本的dokcer 1.12發(fā)布以后,Swarm集成到Docker內(nèi)部,而且增加了許多的功能后,我想這個(gè)問(wèn)題就會(huì)迎刃而解。管理Swarm的是采用第三方的管理圖形界面軟件shipyard,這款軟件本身并不會(huì)兼容自定義網(wǎng)絡(luò),而且在設(shè)置每個(gè)容器使用的Cpu核數(shù)時(shí)又會(huì)有BUG,我們對(duì)此開(kāi)源軟件進(jìn)行了二次開(kāi)發(fā),解決了這些問(wèn)題。現(xiàn)如今我們已經(jīng)成功的在上面運(yùn)行了YARN集群,網(wǎng)絡(luò)性能也是沒(méi)有什么問(wèn)題的。
參考
https://docs.docker.com/engine/extend/plugins
https://github.com/docker/libnetwork/blob/master/docs/ipam.md
https://github.com/docker/go-plugins-helpers
該Docker插件的開(kāi)源地址:
https://github.com/TalkingData/Shrike
關(guān)于作者
馬超,TalkingData運(yùn)維部研發(fā)工程師,精通Golang和Python,五年技術(shù)工作經(jīng)歷,曾從事手機(jī)游戲服務(wù)端研發(fā), 技術(shù)運(yùn)營(yíng)研發(fā)工程師。關(guān)注 平臺(tái)穩(wěn)定性(監(jiān)控,問(wèn)題發(fā)現(xiàn)及響應(yīng))和資源充分利用(虛擬化,容器)。