TalkingData的Docker扁平化網絡設計與實現
開發背景
眾所周知,Docker容器跨主機互訪一直是一個問題,Docker官方為了避免網絡上帶來的諸多麻煩,故將跨主機網絡開了比較大的口子,而由用戶自己去實現。
目前Docker跨主機的網絡實現方案也有很多種,主要包括端口映射、OVS、Fannel等。但是這些方案都無法滿足我們的需求,端口映射服務內的內網IP會映射成外網的IP,這樣會給開發帶來困惑,因為他們往往在跨網絡交互時是不需要內網IP的,而OVS與Fannel則是在基礎網絡協議上又包裝了一層自定義協議,這樣當網絡流量大時,卻又無端的增加了網絡負載,最后我們采取了自主研發扁平化網絡插件,也就是說讓所有的容器統統在大二層上互通。
網絡模式
目前, 基于Docker的網絡模式有很多種,接下來就簡單的對他們進行一下介紹。
Docker原生四種網絡模式:
1. Bridge模式
該模式為Docker的默認網絡模式,Docker daemon會在宿主機上建立一個默認的網橋docker0, 相信大家對docker0非常熟悉,但是在跨容器通信當中它卻沒有派上用場,因為默認的docker0的地址都是內網地址,而且啟動后容器雖然也橋接在docker0上,但是容器的默認網關卻依然無法設置,這就是docker原生默認網絡的一個弊端。當然了,他卻實現了在當先宿主機的網絡隔離,擁有自己的Namespace、網卡和IP,具體的橋接原理我將在后面繼續說明。
2. Host 模式
該模式其實就是和當前宿主機共享網絡空間,而Docker本身并沒有進行網絡隔離,說的通俗點,也就是說容器其實都是和宿主機擁有相同的IP, 而如何具體區分各個容器的呢?那就是通過端口映射,在啟動Docker容器的時候來指定-p參數來進行設置端口映射。雖然這種方式在某種程度上也可以達到跨宿主機容器訪問的目的,但是,卻喪失了Docker網絡隔離的意義,而且端口映射同樣給微服務遷移帶來一些麻煩,無法像非虛擬環境那樣的平滑遷移,而是要考慮到很多端口轉換的問題。
3. Container 模式
顧名思義,此模式會共享另一個容器的網絡命名空間,但是會限制在一臺宿主機上,依然無法實現容器間跨主機通信的功能。
4. None 模式
該模式是容器擁有自己的網絡命名空間,自己的網路棧,自己的網卡,不和外界有任何瓜葛,容器網絡完全獨立,換句話說就是容器不需要網絡功能,這種模式適用于容器包含寫數據到磁盤卷的一些任務。這種模式依然無法實現我們的跨宿主機容器網絡通信的功能。
Docker Overlay網絡模式:
目前Overlay網絡模式主要是由隧道和路由兩種方式實現,一種是對基礎網絡協議進行封包,另一種是配置更復雜的路由配置實現容器間跨主機的網絡通信。其實,以上兩種或多或少的都會給我們網絡的實現帶來了復雜性以及性能上的損耗,因為當我們擁有龐大的業務集群以后,這些復雜度和性能損耗都是不能忽視的。
插件原理
1. 創建Dokcer自定義網絡
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的范圍 --gateway=br0網橋使用的IP,也就是宿主機的地址 --aux-address=DefaultGatewayIPv4=容器使用的默認網關地址 mynet
我們首先需要創建一個br0自定義網橋,這個網橋并不是通過系統命令手動建立的原始Linux網橋,而是通過Docker的cerate network命令來建立的自定義網橋,這樣避免了一個很重要的問題就是我們可以通過設置DefaultGatewayIPv4參數來設置容器的默認路由,這個解決了原始Linux自建網橋不能解決的問題. 用Docker創建網絡時我們可以通過設置subnet參數來設置子網IP范圍,默認我們可以把整個網段給這個子網,后面可以用ipam driver(地址管理插件)來進行控制。還有一個參數Gateway是用來設置br0自定義網橋地址的,其實也就是你這臺宿主機的地址啦。
2. IPAM
這個驅動是專門管理Docker 容器IP的, Docker 每次啟停與刪除容器都會調用這個驅動提供的IP管理接口,然后IP接口會對存儲IP地址的Etcd有一個增刪改查的操作。此插件運行時會起一個Unix Socket,然后會在docker/run/plugins 目錄下生成一個.sock文件,Docker daemon之后會和這個sock 文件進行溝通去調用我們之前實現好的幾個接口進行IP管理,以此來達到IP管理的目的,防止IP沖突。
3. 橋接
通過Docker命令去創建一個自定義的網絡起名為“mynet”,同時會產生一個網橋br0,之后通過更改網絡配置文件(在/etc/sysconfig/network-scripts/下ifcfg-br0、ifcfg-默認網絡接口名)將默認網絡接口橋接到br0上,重啟網絡后,橋接網絡就會生效。Docker默認在每次啟動容器時都會將容器內的默認網卡橋接到br0上,而且宿主機的物理網卡也同樣橋接到了br0上了。其實橋接的原理就好像是一臺交換機,Docker 容器和宿主機物理網絡接口都是服務器,通過veth pair這個網絡設備像一根網線插到交換機上。至此,所有的容器網絡已經在同一個網絡上可以通信了,每一個Docker容器就好比是一臺獨立的虛擬機,擁有和宿主機同一網段的IP,可以實現跨主機訪問了。
4. ETCD
我們可以設置1、3、5、7個節點為Etcd集群去集中管理Dcoker集群的IP,而且我們也將宿主機的地址進行了統一的管理,這樣做同樣也是為了避免IP的使用沖突導致線上資源不可用。我們會通過自己開發的工具進行IP初始化,也就是說會傳進來一個IP范圍,然后工具會將所有的IP存進Etcd中,每一個網絡ID就是一個Etcd目錄,目錄下就會分成已分配與未分配的IP地址池。Etcd本身會提供Go語言的API來訪問Etcd,目前Etcd還是相當穩定的,沒有出現過什么問題。
最后
我們的Docker集群是采用Swarm進行管理的,Swarm相對Kubernetes來說要簡單的很多,但是在編排功能上也會有所欠缺,不過目前等新版本的Dokcer 1.12發布以后,Swarm集成到Docker內部,而且增加了許多的功能后,我想這個問題就會迎刃而解。管理Swarm的是采用第三方的管理圖形界面軟件Shipyard,這款軟件本身并不會兼容自定義網絡,而且在設置每個容器使用的CPU核數時又會有BUG,我們對此開源軟件進行了二次開發,解決了這些問題。現如今我們已經成功的在上面運行了Yarn集群,網絡性能也是沒有什么問題的。
參考
-
https://docs.docker.com/engine/extend/plugins
-
https://github.com/docker/libnetwork/blob/master/docs/ipam.md
-
https://github.com/docker/go-plugins-helpers
來自:https://mp.weixin.qq.com/s?__biz=MzA5OTAyNzQ2OA==&mid=2649691475&idx=1&sn=958dad3379f7b2aea46d3ffe43825f4c&chksm=88932a30bfe4a326107a65591c2e8d77f83691b12e24288b6adfff4f7418e8719453853f07fe&scene=1&srcid=0906Gl7nVAdmvlIR1GqukxL1&pass_ticket=C%2F3kKniu5R%2B79QjlpXe48GEhKJPpP3suKU2NN%2BjA7MM%3D#rd