自從Docker容器出現以來,容器網絡通信就一直是被關注的焦點,也是生產環境的迫切需求。容器網絡通信又分為兩大方面:單主機容器上的相互通信,和跨主機的容器相互通信。
一、Docker單主機容器通信
基于對net namespace的控制,docker可以為在容器創建隔離的網絡環境,在隔離的網絡環境下,容器具有完全獨立的網絡棧,與宿主機隔離,也可以使容器共享主機或者其他容器的網絡命名空間,基本可以滿足開發者在各種場景下的需要。
按docker官方的說法,docker容器的網絡有五種模式:
bridge模式,--net=bridge(默認)
這是dokcer網絡的默認設置,為容器創建獨立的網絡命名空間,容器具有獨立的網卡等所有單獨的網絡棧,是最常用的使用方式。在docker run啟動容器的時候,如果不加--net參數,就默認采用這種網絡模式。安裝完docker,系統會自動添加一個供docker使用的網橋docker0,我們創建一個新的容器時,容器通過DHCP獲取一個與docker0同網段的IP地址,并默認連接到docker0網橋,以此實現容器與宿主機的網絡互通。
host模式,--net=host
這個模式下創建出來的容器,直接使用容器宿主機的網絡命名空間。將不擁有自己獨立的Network Namespace,即沒有獨立的網絡環境。它使用宿主機的ip和端口。
none模式,--net=none
為容器創建獨立網絡命名空間,但不為它做任何網絡配置,容器中只有lo,用戶可以在此基礎上,對容器網絡做任意定制。這個模式下,dokcer不為容器進行任何網絡配置。需要我們自己為容器添加網卡,配置IP。因此,若想使用pipework配置docker容器的ip地址,必須要在none模式下才可以。
其他容器模式(即container模式,join模式),--net=container:NAME_or_ID
與host模式類似,只是容器將與指定的容器共享網絡命名空間。這個模式就是指定一個已有的容器,共享該容器的IP和端口。除了網絡方面兩個容器共享,其他的如文件系統,進程等還是隔離開的。
用戶自定義:docker 1.9版本以后新增的特性,允許容器使用第三方的網絡實現或者創建單獨的bridge網絡,提供網絡隔離能力。
這些網絡模式在相互網絡通信方面的對比如下所示:
南北向通信指容器與宿主機外界的訪問機制,東西向流量指同一宿主機上,與其他容器相互訪問的機制。
1)host模式
由于容器和宿主機共享同一個網絡命名空間,換言之,容器的IP地址即為宿主機的IP地址。所以容器可以和宿主機一樣,使用宿主機的任意網卡,實現和外界的通信。其網絡模型可以參照下圖:
采用host模式的容器,可以直接使用宿主機的IP地址與外界進行通信,若宿主機具有公有IP,那么容器也擁有這個公有IP。同時容器內服務的端口也可以使用宿主機的端口,無需額外進行NAT轉換,而且由于容器通信時,不再需要通過linuxbridge等方式轉發或數據包的拆封,性能上有很大優勢。當然,這種模式有優勢,也就有劣勢,主要包括以下幾個方面:
1)最明顯的就是容器不再擁有隔離、獨立的網絡棧。容器會與宿主機競爭網絡棧的使用,并且容器的崩潰就可能導致宿主機崩潰,在生產環境中,這種問題可能是不被允許的。
2)容器內部將不再擁有所有的端口資源,因為一些端口已經被宿主機服務、bridge模式的容器端口綁定等其他服務占用掉了。
2)bridge模式
bridge模式是docker默認的,也是開發者最常使用的網絡模式。在這種模式下,docker為容器創建獨立的網絡棧,保證容器內的進程使用獨立的網絡環境,實現容器之間、容器與宿主機之間的網絡棧隔離。同時,通過宿主機上的docker0網橋,容器可以與宿主機乃至外界進行網絡通信。其網絡模型可以參考下圖:
從上面的網絡模型可以看出,容器從原理上是可以與宿主機乃至外界的其他機器通信的。同一宿主機上,容器之間都是連接掉docker0這個網橋上的,它可以作為虛擬交換機使容器可以相互通信。然而,由于宿主機的IP地址與容器veth pair的 IP地址均不在同一個網段,故僅僅依靠veth pair和namespace的技術,還不足以使宿主機以外的網絡主動發現容器的存在。為了使外界可以方位容器中的進程,docker采用了端口綁定的方式,也就是通過iptables的NAT,將宿主機上的端口端口流量轉發到容器內的端口上。舉一個簡單的例子,使用下面的命令創建容器,并將宿主機的3306端口綁定到容器的3306端口:?
1 |
|
在宿主機上,可以通過iptables -t nat -L -n,查到一條DNAT規則:
1 |
|
上面的172.17.0.5即為bridge模式下,創建的容器IP。
很明顯,bridge模式的容器與外界通信時,必定會占用宿主機上的端口,從而與宿主機競爭端口資源,對宿主機端口的管理會是一個比較大的問題。同時,由于容器與外界通信是基于三層上iptables NAT,性能和效率上的損耗是可以預見的。
3)none模式
在這種模式下,容器有獨立的網絡棧,但不包含任何網絡配置,只具有lo這個loopback網卡用于進程通信。也就是說,none模式為容器做了最少的網絡設置,但是俗話說得好“少即是多”,在沒有網絡配置的情況下,通過第三方工具或者手工的方式,開發這任意定制容器的網絡,提供了最高的靈活性。
4)其他容器(container)模式
其他網絡模式是docker中一種較為特別的網絡的模式。在這個模式下的容器,會使用其他容器的網絡命名空間,其網絡隔離性會處于bridge橋接模式與host模式之間。當容器共享其他容器的網絡命名空間,則在這兩個容器之間不存在網絡隔離,而她們又與宿主機以及除此之外其他的容器存在網絡隔離。其網絡模型可以參考下圖:
在這種模式下的容器可以通過localhost來同一網絡命名空間下的其他容器,傳輸效率較高。而且這種模式還節約了一定數量的網絡資源,但它并沒有改變容器與外界通信的方式。在一些特殊的場景中非常有用,例如,kubernetes的pod,kubernetes為pod創建一個基礎設施容器,同一pod下的其他容器都以其他容器模式共享這個基礎設施容器的網絡命名空間,相互之間以localhost訪問,構成一個統一的整體。
5)用戶定義網絡模式
在用戶定義網絡模式下,開發者可以使用任何docker支持的第三方網絡driver來定制容器的網絡。并且,docker 1.9以上的版本默認自帶了bridge和overlay兩種類型的自定義網絡driver。可以用于集成calico、weave、openvswitch等第三方廠商的網絡實現。 除了docker自帶的bridge driver,其他的幾種driver都可以實現容器的跨主機通信。而基于bdrige driver的網絡,docker會自動為其創建iptables規則,保證與其他網絡之間、與docker0之間的網絡隔離。 例如,使用下面的命令創建一個基于bridge driver的自定義網絡:
1 |
|
則docker會自動生成如下的iptables規則,保證不同網絡上的容器無法互相通信。
1 2 |
|
除此之外,bridge driver的所有行為都和默認的bridge模式完全一致。而overlay及其他driver,則可以實現容器的跨主機通信。
二、Docker跨主機容器通信
早期大家的跨主機通信方案主要有以下幾種:
1)容器使用host模式:容器直接使用宿主機的網絡,這樣天生就可以支持跨主機通信。雖然可以解決跨主機通信問題,但這種方式應用場景很有限,容易出現端口沖突,也無法做到隔離網絡環境,一個容器崩潰很可能引起整個宿主機的崩潰。
2)端口綁定:通過綁定容器端口到宿主機端口,跨主機通信時,使用主機IP+端口的方式訪問容器中的服務。顯而易見,這種方式僅能支持網絡棧的四層及以上的應用,并且容器與宿主機緊耦合,很難靈活的處理,可擴展性不佳。
3)docker外定制容器網絡:在容器通過docker創建完成后,然后再通過修改容器的網絡命名空間來定義容器網絡。典型的就是很久以前的pipework,容器以none模式創建,pipework通過進入容器的網絡命名空間為容器重新配置網絡,這樣容器網絡可以是靜態IP、vxlan網絡等各種方式,非常靈活,容器啟動的一段時間內會沒有IP,明顯無法在大規模場景下使用,只能在實驗室中測試使用。
4)第三方SDN定義容器網絡:使用Open vSwitch或Flannel等第三方SDN工具,為容器構建可以跨主機通信的網絡環境。這些方案一般要求各個主機上的docker0網橋的cidr不同,以避免出現IP沖突的問題,限制了容器在宿主機上的可獲取IP范圍。并且在容器需要對集群外提供服務時,需要比較復雜的配置,對部署實施人員的網絡技能要求比較高。
上面這些方案有各種各樣的缺陷,同時也因為跨主機通信的迫切需求,docker 1.9版本時,官方提出了基于vxlan的overlay網絡實現,原生支持容器的跨主機通信。同時,還支持通過libnetwork的plugin機制擴展各種第三方實現,從而以不同的方式實現跨主機通信。就目前社區比較流行的方案來說,跨主機通信的基本實現方案有以下幾種:
1)基于隧道的overlay網絡:按隧道類型來說,不同的公司或者組織有不同的實現方案。docker原生的overlay網絡就是基于vxlan隧道實現的。ovn則需要通過geneve或者stt隧道來實現的。flannel最新版本也開始默認基于vxlan實現overlay網絡。
2)基于包封裝的overlay網絡:基于UDP封裝等數據包包裝方式,在docker集群上實現跨主機網絡。典型實現方案有Weave、Flannel的早期版本。
3)基于三層實現SDN網絡:基于三層協議和路由,直接在三層上實現跨主機網絡,并且通過iptables實現網絡的安全隔離。典型的方案為?Calico。同時對不支持三層路由的環境,Calico還提供了基于IPIP封裝的跨主機網絡實現
Dokcer通過使用Linux橋接提供容器之間的通信,docker0橋接接口的目的就是方便Docker管理。當Docker daemon啟動時需要做以下操作:
->? 如果docker0不存在則創建
->? 搜索一個與當前路由不沖突的ip段
->? 在確定的范圍中選擇 ip
->? 綁定ip到 docker0
列出當前主機網橋
1 2 3 |
|
查看當前 docker0 ip
1 2 3 4 5 6 7 8 9 10 |
|
在容器運行時,每個容器都會分配一個特定的虛擬機口并橋接到docker0。每個容器都會配置同docker0 ip相同網段的專用ip 地址,docker0的IP地址被用于所有容器的默認網關。
一般啟動的容器中ip默認是172.17.0.1/24網段的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
那么能不能在創建容器的時候指定特定的ip呢?這是當然可以實現的!
注意:宿主機的ip路由轉發功能一定要打開,否則所創建的容器無法聯網!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
2.1、創建容器使用特定范圍的IP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
|
使用pipework給容器設置一個固定的ip
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
|
2.2、不同主機間的容器通信(pipework ?config docker container ip)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
|
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
其實除了上面使用的pipework工具還,還可以使用虛擬交換機(Open vSwitch)進行docker容器間的網絡通信,廢話不多說,下面說下Open vSwitch的使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
|
三、啟動容器測試
Server1和Server2上修改docker啟動的虛擬網卡綁定為kbr0,重啟docker進程
1)在Server1宿主機上啟動容器,然后登陸容器內查看ip,就會發現ip是上面設定額172.17.1.0/24網段的
1 |
|
2)在Server2宿主機上啟動容器,然后登陸容器內查看ip,就會發現ip是上面設定額172.17.2.0/24網段的
1 |
|
然后在上面啟動的容內互ping對方容器,發現是可以ping通的