自從Docker容器出現以來,容器的網絡通信就一直是被關注的焦點,也是生産環境的迫切需求。容器的網絡通信又可以分為兩大方面:單主機容器上的互相通信,和跨主機的容器互相通信。下面将分别針對這兩方面,對容器的通信原理進行簡單的分析,幫助大家更好地使用docker。前面已經在Docker容器學習梳理--基礎知識(2)這一篇中詳細介紹了Docker的網絡配置以及pipework工具。
docker單主機容器通信
<code>基于對net namespace的控制,docker可以為在容器建立隔離的網絡環境,在隔離的網絡環境下,容器具有完全獨立的網絡棧,與主控端隔離,</code>
<code>也可以使容器共享主機或者其他容器的網絡命名空間,基本可以滿足開發者在各種場景下的需要。</code>
<code> </code>
<code>按docker官方的說法,docker容器的網絡有五種模式:</code>
<code>1)bridge模式,--net=bridge(預設)</code>
<code>這是dokcer網絡的預設設定,為容器建立獨立的網絡命名空間,容器具有獨立的網卡等所有單獨的網絡棧,是最常用的使用方式。</code>
<code>在docker run啟動容器的時候,如果不加--net參數,就預設采用這種網絡模式。安裝完docker,系統會自動添加一個供docker使用的網橋docker0,我們建立一個新的容器時,</code>
<code>容器通過DHCP擷取一個與docker0同網段的IP位址,并預設連接配接到docker0網橋,以此實作容器與主控端的網絡互通。</code>
<code>2)host模式,--net=host</code>
<code>這個模式下建立出來的容器,直接使用容器主控端的網絡命名空間。</code>
<code>将不擁有自己獨立的Network Namespace,即沒有獨立的網絡環境。它使用主控端的ip和端口。</code>
<code>3)none模式,--net=none</code>
<code>為容器建立獨立網絡命名空間,但不為它做任何網絡配置,容器中隻有lo,使用者可以在此基礎上,對容器網絡做任意定制。</code>
<code>這個模式下,dokcer不為容器進行任何網絡配置。需要我們自己為容器添加網卡,配置IP。</code>
<code>是以,若想使用pipework配置docker容器的ip位址,必須要在none模式下才可以。</code>
<code>4)其他容器模式(即container模式),--net=container:NAME_or_ID</code>
<code>與host模式類似,隻是容器将與指定的容器共享網絡命名空間。</code>
<code>這個模式就是指定一個已有的容器,共享該容器的IP和端口。除了網絡方面兩個容器共享,其他的如檔案系統,程序等還是隔離開的。</code>
<code>5)使用者自定義:docker 1.9版本以後新增的特性,允許容器使用第三方的網絡實作或者建立單獨的bridge網絡,提供網絡隔離能力。</code>
這些網絡模式在互相網絡通信方面的對比如下所示:
南北向通信指容器與主控端外界的通路機制,東西向流量指同一主控端上,與其他容器互相通路的機制。
host模式
<code>由于容器和主控端共享同一個網絡命名空間,換言之,容器的IP位址即為主控端的IP位址。是以容器可以和主控端一樣,使用主控端的任意網卡,實作和外界的通信。其網絡模型可以參照下圖</code>
<code>采用host模式的容器,可以直接使用主控端的IP位址與外界進行通信,若主控端具有公有IP,那麼容器也擁有這個公有IP。同時容器内服務的端口也可以使用主控端的端口,</code>
<code>無需額外進行NAT轉換,而且由于容器通信時,不再需要通過linuxbridge等方式轉發或者資料包的拆封,性能上有很大優勢。</code>
<code>當然,這種模式有優勢,也就有劣勢,主要包括以下幾個方面:</code>
<code>1)最明顯的就是容器不再擁有隔離、獨立的網絡棧。容器會與主控端競争網絡棧的使用,并且容器的崩潰就可能導緻主控端崩潰,在生産環境中,這種問題可能是不被允許的。</code>
<code>2)容器内部将不再擁有所有的端口資源,因為一些端口已經被主控端服務、bridge模式的容器端口綁定等其他服務占用掉了。</code>
bridge模式
<code>bridge模式是docker預設的,也是開發者最常使用的網絡模式。在這種模式下,docker為容器建立獨立的網絡棧,保證容器内的程序使用獨立的網絡環境,</code>
<code>實作容器之間、容器與主控端之間的網絡棧隔離。同時,通過主控端上的docker0網橋,容器可以與主控端乃至外界進行網絡通信。</code>
<code>其網絡模型可以參考下圖:</code>
<code>從上面的網絡模型可以看出,容器從原理上是可以與主控端乃至外界的其他機器通信的。同一主控端上,容器之間都是連接配接掉docker0這個網橋上的,它可以作為虛拟交換機使容器可以互相通信。</code>
<code>然而,由于主控端的IP位址與容器veth pair的 IP位址均不在同一個網段,故僅僅依靠veth pair和namespace的技術,還不足以使主控端以外的網絡主動發現容器的存在。為了使外界可以方位容器中的程序,docker采用了端口綁定的方式,也就是通過iptables的NAT,将主控端上的端口</code>
<code>端口流量轉發到容器内的端口上。</code>
<code>舉一個簡單的例子,使用下面的指令建立容器,并将主控端的3306端口綁定到容器的3306端口:</code>
<code>docker run -tid --name db -p 3306:3306 MySQL</code>
<code>在主控端上,可以通過iptables -t nat -L -n,查到一條DNAT規則:</code>
<code>DNAT tcp -- 0.0.0.0</code><code>/0</code> <code>0.0.0.0</code><code>/0</code> <code>tcp dpt:3306 to:172.17.0.5:3306</code>
<code>上面的172.17.0.5即為bridge模式下,建立的容器IP。</code>
<code>很明顯,bridge模式的容器與外界通信時,必定會占用主控端上的端口,進而與主控端競争端口資源,對主控端端口的管理會是一個比較大的問題。同時,由于容器與外界通信是基于三層上iptables NAT,性能和效率上的損耗是可以預見的。</code>
none模式
<code>在這種模式下,容器有獨立的網絡棧,但不包含任何網絡配置,隻具有lo這個loopback網卡用于程序通信。也就是說,none模式為容器做了最少的網絡設定,</code>
<code>但是俗話說得好“少即是多”,在沒有網絡配置的情況下,通過第三方工具或者手工的方式,開發這任意定制容器的網絡,提供了最高的靈活性</code>
其他容器(container)模式
<code>其他網絡模式是docker中一種較為特别的網絡的模式。在這個模式下的容器,會使用其他容器的網絡命名空間,其網絡隔離性會處于bridge橋接模式與host模式之間。</code>
<code>當容器共享其他容器的網絡命名空間,則在這兩個容器之間不存在網絡隔離,而她們又與主控端以及除此之外其他的容器存在網絡隔離。其網絡模型可以參考下圖:</code>
<code>在這種模式下的容器可以通過localhost來同一網絡命名空間下的其他容器,傳輸效率較高。而且這種模式還節約了一定數量的網絡資源,但它并沒有改變容器與外界通信的方式。</code>
<code>在一些特殊的場景中非常有用,例如,kubernetes的pod,kubernetes為pod建立一個基礎設施容器,同一pod下的其他容器都以其他容器模式共享這個基礎設施容器的網絡命名空間,</code>
<code>互相之間以localhost通路,構成一個統一的整體。</code>
使用者定義網絡模式
<code>在使用者定義網絡模式下,開發者可以使用任何docker支援的第三方網絡driver來定制容器的網絡。并且,docker 1.9以上的版本預設自帶了bridge和overlay兩種類型的自定義網絡driver。可以用于內建calico、weave、openvswitch等第三方廠商的網絡實作。</code>
<code>除了docker自帶的bridge driver,其他的幾種driver都可以實作容器的跨主機通信。而基于bdrige driver的網絡,docker會自動為其建立iptables規則,</code>
<code>保證與其他網絡之間、與docker0之間的網絡隔離。</code>
<code>例如,使用下面的指令建立一個基于bridge driver的自定義網絡:</code>
<code>docker network create bri1</code>
<code>則docker會自動生成如下的iptables規則,保證不同網絡上的容器無法互相通信。</code>
<code>-A DOCKER-ISOLATION -i br-8dba6df70456 -o docker0 -j DROP</code>
<code>-A DOCKER-ISOLATION -i docker0 -o br-8dba6df70456 -j DROP</code>
<code>除此之外,bridge driver的所有行為都和預設的bridge模式完全一緻。而overlay及其他driver,則可以實作容器的跨主機通信。</code>
docker跨主機容器通信
<code>早期大家的跨主機通信方案主要有以下幾種:</code>
<code>1)容器使用host模式:容器直接使用主控端的網絡,這樣天生就可以支援跨主機通信。雖然可以解決跨主機通信問題,但這種方式應用場景很有限,容易出現端口沖突,也無法做到隔離網絡環境,</code>
<code>一個容器崩潰很可能引起整個主控端的崩潰。</code>
<code>2)端口綁定:通過綁定容器端口到主控端端口,跨主機通信時,使用主機IP+端口的方式通路容器中的服務。顯而易見,這種方式僅能支援網絡棧的四層及以上的應用,并且容器與主控端緊耦合,</code>
<code>很難靈活的處理,可擴充性不佳。</code>
<code>3)docker外定制容器網絡:在容器通過docker建立完成後,然後再通過修改容器的網絡命名空間來定義容器網絡。典型的就是很久以前的pipework,容器以none模式建立,pipework通過進入容器</code>
<code>的網絡命名空間為容器重新配置網絡,這樣容器網絡可以是靜态IP、vxlan網絡等各種方式,非常靈活,容器啟動的一段時間内會沒有IP,明顯無法在大規模場景下使用,隻能在實驗室中測試使用。</code>
<code>4)第三方SDN定義容器網絡:使用Open vSwitch或Flannel等第三方SDN工具,為容器建構可以跨主機通信的網絡環境。這些方案一般要求各個主機上的docker0網橋的cidr不同,以避免出現IP沖突</code>
<code>的問題,限制了容器在主控端上的可擷取IP範圍。并且在容器需要對叢集外提供服務時,需要比較複雜的配置,對部署實施人員的網絡技能要求比較高。</code>
<code>上面這些方案有各種各樣的缺陷,同時也因為跨主機通信的迫切需求,docker 1.9版本時,官方提出了基于vxlan的overlay網絡實作,原生支援容器的跨主機通信。同時,還支援通過libnetwork的</code>
<code>plugin機制擴充各種第三方實作,進而以不同的方式實作跨主機通信。就目前社群比較流行的方案來說,跨主機通信的基本實作方案有以下幾種:</code>
<code>1)基于隧道的overlay網絡:按隧道類型來說,不同的公司或者組織有不同的實作方案。docker原生的overlay網絡就是基于vxlan隧道實作的。ovn則需要通過geneve或者stt隧道來實作的。flannel</code>
<code>最新版本也開始預設基于vxlan實作overlay網絡。</code>
<code>2)基于包封裝的overlay網絡:基于UDP封裝等資料包包裝方式,在docker叢集上實作跨主機網絡。典型實作方案有weave、flannel的早期版本。</code>
<code>3)基于三層實作SDN網絡:基于三層協定和路由,直接在三層上實作跨主機網絡,并且通過iptables實作網絡的安全隔離。典型的方案為Project Calico。同時對不支援三層路由的環境,Project Calico還提供了基于IPIP封裝的跨主機網絡實作</code>
Dokcer通過使用Linux橋接提供容器之間的通信,docker0橋接接口的目的就是友善Docker管理。當Docker daemon啟動時需要做以下操作
<code>a)如果docker0不存在則建立</code>
<code>b)搜尋一個與目前路由不沖突的ip段</code>
<code>c)在确定的範圍中選擇 ip</code>
<code>d)綁定ip到 docker0</code>
CentOS7安裝docker
<code>[root@localhost ~]</code><code># yum install docker bridge-utils -y</code>
<code>[root@localhost ~]</code><code># systemctl start docker;systemctl enable docker</code>
列出目前主機網橋
<code>[root@localhost ~]</code><code># brctl show</code>
<code>bridge name bridge </code><code>id</code> <code>STP enabled interfaces</code>
<code>docker0 8000.0242b1a59b05 no</code>
檢視目前 docker0 ip
<code>[root@localhost ~]</code><code># ifconfig docker0</code>
<code>docker0: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500</code>
<code> </code><code>inet 172.17.0.1 netmask 255.255.0.0 broadcast 0.0.0.0</code>
<code> </code><code>ether 02:42:b1:a5:9b:05 txqueuelen 0 (Ethernet)</code>
<code> </code><code>RX packets 0 bytes 0 (0.0 B)</code>
<code> </code><code>RX errors 0 dropped 0 overruns 0 frame 0</code>
<code> </code><code>TX packets 0 bytes 0 (0.0 B)</code>
<code> </code><code>TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0</code>
在容器運作時,每個容器都會配置設定一個特定的虛拟機口并橋接到docker0。每個容器都會配置同docker0 ip相同網段的專用ip 位址,docker0的IP位址被用于所有容器的預設網關。
一般啟動的容器中ip預設是172.17.0.1/24網段的。
<code>[root@localhost ~]</code><code># docker images</code>
<code>REPOSITORY TAG IMAGE ID CREATED SIZE</code>
<code>docker.io</code><code>/centos</code> <code>latest ff426288ea90 3 weeks ago 207.2 MB</code>
<code>[root@localhost ~]</code><code># </code>
<code>[root@localhost ~]</code><code># docker run -itd --name my-test centos /bin/bash</code>
<code>11607da07401f75dc481458934e8cd3353896ebaba418c2f2d436619ffbd8772</code>
<code>[root@localhost ~]</code><code># docker ps</code>
<code>CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES</code>
<code>11607da07401 centos </code><code>"/bin/bash"</code> <code>24 seconds ago Up 23 seconds my-</code><code>test</code>
<code>[root@localhost ~]</code><code># docker inspect 11607da07401 |grep -i ipaddr</code>
<code> </code><code>"SecondaryIPAddresses"</code><code>: null,</code>
<code> </code><code>"IPAddress"</code><code>: </code><code>"172.17.0.2"</code><code>,</code>
<code> </code><code>"IPAddress"</code><code>: </code><code>"172.17.0.2"</code><code>,</code>
<code>[root@localhost ~]</code><code>#</code>
那麼能不能在建立容器的時候指定特定的ip呢?這是當然可以實作的!
注意:主控端的ip路由轉發功能一定要打開,否則所建立的容器無法聯網!
<code>[root@localhost ~]</code><code># cat /proc/sys/net/ipv4/ip_forward</code>
<code>1</code>
<code>11607da07401 centos </code><code>"/bin/bash"</code> <code>4 minutes ago Up 4 minutes my-</code><code>test</code>
<code>[root@localhost ~]</code><code># docker run -itd --name=test1 centos </code>
<code>4fb496dac251f60bcffaceea340847687ed4d7c9db20a351296bc9143c73d74b</code>
<code>[root@localhost ~]</code><code># docker exec -it test1 /bin/bash </code>
<code>[root@4fb496dac251 /]</code><code># ping www.baidu.com</code>
<code>PING www.a.shifen.com (115.239.211.112) 56(84) bytes of data.</code>
<code>64 bytes from 115.239.211.112 (115.239.211.112): icmp_seq=1 ttl=48 </code><code>time</code><code>=12.4 ms</code>
<code>64 bytes from 115.239.211.112 (115.239.211.112): icmp_seq=2 ttl=48 </code><code>time</code><code>=11.1 ms</code>
<code>^C</code>
<code>--- www.a.shifen.com </code><code>ping</code> <code>statistics ---</code>
<code>2 packets transmitted, 2 received, 0% packet loss, </code><code>time</code> <code>1001ms</code>
<code>rtt min</code><code>/avg/max/mdev</code> <code>= 11.186</code><code>/11</code><code>.823</code><code>/12</code><code>.461</code><code>/0</code><code>.646 ms</code>
<code>[root@4fb496dac251 /]</code><code># </code>
<code>關閉ip路由轉發功能,容器即不能聯網</code>
<code>[root@localhost ~]</code><code># echo 0 > /proc/sys/net/ipv4/ip_forward</code>
<code>0</code>
<code>ping</code><code>: www.baidu.com: Name or service not known </code><code>//ping</code><code>不通~</code>
一、建立容器使用特定範圍的IP
<code>Docker 會嘗試尋找沒有被主機使用的ip段,盡管它适用于大多數情況下,但是它不是萬能的,有時候我們還是需要對ip進一步規劃。</code>
<code>Docker允許你管理docker0橋接或者通過-b選項自定義橋接網卡,需要安裝bridge-utils軟體包。操作流程如下:</code>
<code>a)確定docker的程序是停止的</code>
<code>b)建立自定義網橋</code>
<code>c)給網橋配置設定特定的ip</code>
<code>d)以-b的方式指定網橋</code>
<code> </code>
<code>具體操作過程如下(比如建立容器的時候,指定ip為10.10.172.202</code><code>/24</code><code>網段的):</code>
<code>[root@localhost ~]</code><code># service docker stop</code>
<code>[root@localhost ~]</code><code># ip link set dev docker0 down</code>
<code>[root@localhost ~]</code><code># brctl delbr docker0</code>
<code>[root@localhost ~]</code><code># brctl addbr br0</code>
<code>[root@localhost ~]</code><code># ip addr add 10.10.172.202/24 dev br0 //注意,這個10.10.172.202就是所建容器的網關位址。通過docker inspect container_id能檢視到</code>
<code>[root@localhost ~]</code><code># ip link set dev br0 up</code>
<code>[root@localhost ~]</code><code># ip addr show br0</code>
<code>[root@localhost ~]</code><code># vim /etc/sysconfig/docker //即将虛拟的橋接口由預設的docker0改為br0</code>
<code>将</code>
<code>OPTIONS=</code><code>'--selinux-enabled --log-driver=journald'</code>
<code>改為</code>
<code>OPTIONS=</code><code>'--selinux-enabled --log-driver=journald -b=br0'</code> <code>//</code><code>即添加-b=br0</code>
<code>[root@localhost ~]</code><code># service docker restart</code>
<code>--------------------------------------------------------------------------------------</code>
<code>上面是centos7下的操作步驟,下面提供下ubuntu下的操作步驟:</code>
<code>$ </code><code>sudo</code> <code>service docker stop</code>
<code>$ </code><code>sudo</code> <code>ip link </code><code>set</code> <code>dev docker0 down</code>
<code>$ </code><code>sudo</code> <code>brctl delbr docker0</code>
<code>$ </code><code>sudo</code> <code>brctl addbr br0</code>
<code>$ </code><code>sudo</code> <code>ip addr add 10.10.172.202</code><code>/24</code> <code>dev br0</code>
<code>$ </code><code>sudo</code> <code>ip link </code><code>set</code> <code>dev br0 up</code>
<code>$ ip addr show br0</code>
<code>$ </code><code>echo</code> <code>'DOCKER_OPTS="-b=br0"'</code> <code>>> </code><code>/etc/default/docker</code>
<code>$ </code><code>sudo</code> <code>service docker start</code>
<code>然後建立容器,檢視下容器ip是否為設定的10.10.172.202</code><code>/24</code><code>網段的</code>
<code>docker.io</code><code>/ubuntu</code> <code>latest 0458a4468cbc 7 days ago 111.7 MB</code>
<code>[root@localhost ~]</code><code># docker run -t -i --name test1 centos /bin/bash</code>
<code>[root@61a2cdea34f5 /]</code><code># </code>
<code>[root@localhost ~]</code><code># docker run -t -i --name test2 docker.io/ubuntu /bin/bash</code>
<code>[root@0514266c80e0 /]</code><code>#</code>
<code>0514266c80e0 docker.io</code><code>/ubuntu</code> <code>"/bin/bash"</code> <code>47 minutes ago Up 5 minutes test2</code>
<code>61a2cdea34f5 centos </code><code>"/bin/bash"</code> <code>46 minutes ago Up 46 minutes test1</code>
<code>[root@localhost ~]</code><code># docker inspect --format='{{.NetworkSettings.IPAddress}}' 0514266c80e0</code>
<code>10.10.172.16</code>
<code>[root@localhost ~]</code><code># docker inspect --format='{{.NetworkSettings.IPAddress}}' 61a2cdea34f5</code>
<code>10.10.172.15</code>
<code> </code>
<code>br0 8000.005056866833 no eth0</code>
<code> </code><code>veth0pl20732</code>
<code> </code><code>veth0pl21198</code>
<code> </code>
使用pipework給容器設定一個固定的ip
<code>可以利用pipework為容器指定一個固定的ip,操作方法非常簡單,如下:</code>
<code>[root@localhost ~]</code><code># ip addr add 10.10.172.202/24 dev br0 //這個ip相當于br0網橋的網關ip,可以随意設定。</code>
<code>[root@localhost ~]</code><code># docker run -ti -d --net=none --name=my-test1 docker.io/nginx /bin/bash</code>
<code>[root@localhost ~]</code><code># pipework br0 -i eth0 my-test1 10.10.172.190/[email protected]</code>
<code>[root@localhost ~]</code><code># docker exec -ti my-test1 /bin/bash</code>
<code>root@7fe423b47afb:/</code><code># apt-get update </code>
<code>root@7fe423b47afb:/</code><code># apt-get install net-tools -y</code>
<code>root@7fe423b47afb:/</code><code># ifconfig </code>
<code>eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500</code>
<code> </code><code>inet 10.10.172.190 netmask 255.255.255.0 broadcast 10.10.172.255</code>
<code> </code><code>inet6 fe80::e4fe:e6ff:fedb:b4bc prefixlen 64 scopeid 0x20<link></code>
<code> </code><code>ether e6:fe:e6:db:b4:</code><code>bc</code> <code>txqueuelen 1000 (Ethernet)</code>
<code> </code><code>RX packets 5656 bytes 10813699 (10.3 MiB)</code>
<code> </code><code>TX packets 4849 bytes 336703 (328.8 KiB)</code>
<code>lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536</code>
<code> </code><code>inet 127.0.0.1 netmask 255.0.0.0</code>
<code> </code><code>inet6 ::1 prefixlen 128 scopeid 0x10<host></code>
<code> </code><code>loop txqueuelen 0 (Local Loopback)</code>
<code>再啟動一個容器</code>
<code>[root@localhost ~]</code><code># docker run -ti -d --net=none --name=my-test2 docker.io/nginx /bin/bash</code>
<code>[root@localhost ~]</code><code># pipework br0 -i eth0 my-test2 10.10.172.191/[email protected]</code>
<code>這樣,my-test1容器和my-test2容器在同一個主控端上,是以它們固定後的ip是可以互相</code><code>ping</code><code>通的。</code>
二、不同主機間的容器通信(pipework config docker container ip)
<code>我的centos7測試機上的docker是yum安裝的,預設自帶pipework工具,是以就不用在另行安裝它了。</code>
<code>-----------------------------------------------------------------------------------------------</code>
<code>如果沒有pipework工具,可以安裝下面步驟進行安裝:</code>
<code># git clone https://github.com/jpetazzo/pipework.git</code>
<code># sudo cp -rp pipework/pipework /usr/local/bin/</code>
<code> </code>
<code>安裝相應依賴軟體(網橋)</code>
<code># yum install bridge-utils -y</code>
<code> </code>
<code>檢視Docker主控端上的橋接網絡</code>
<code> </code>
<code>有兩種方式做法:</code>
<code>1)可以選擇删除docker0,直接把docker的橋接指定為br0;</code>
<code>2)也可以選擇保留使用預設docker0的配置,這樣單主機容器之間的通信可以通過docker0;</code>
<code> </code><code>跨主機不同容器之間通過pipework将容器的網卡橋接到br0上,這樣跨主機容器之間就可以通信了。</code>
<code>如果保留了docker0,則容器啟動時不加--net=none參數,那麼本機容器啟動後就是預設的docker0自動配置設定的ip(預設是172.17.1.0</code><code>/24</code><code>網段),它們之間是可以通信的;</code>
<code>跨主控端的容器建立時要加--net=none參數,待容器啟動後通過pipework給容器指定ip,這樣跨主控端的容器ip是在同一網段内的同網段位址,是以可以通信。</code>
<code> </code>
<code>一般來說:最好在建立容器的時候加上--net=none,防止自動配置設定的IP在區域網路中有沖突。若是容器建立後自動擷取ip,下次容器啟動會ip有變化,可能會和實體網段中的ip沖突</code>
<code>---------------------------------------------------------------------------------------------------</code>
<code>執行個體說明如下:</code>
<code>主控端資訊</code>
<code>ip:10.10.172.202 (網卡裝置為eth0)</code>
<code>gateway:10.10.172.1</code>
<code>netmask:255.255.255.0</code>
<code>作業系統:</code>
<code>CentOS Linux release 7.2.1511 (Core)</code>
<code>1)删除虛拟橋接卡docker0的配置</code>
<code>[root@localhost ~]</code><code># ip link set dev br0 up </code>
<code>[root@localhost ~]</code><code># ip addr del 10.10.172.202/24 dev eth0 //删除主控端網卡的IP(如果是使用這個位址進行的遠端連接配接,這一步操作後就會斷掉;如果是使用外網位址連接配接的話,就不會斷開)</code>
<code>[root@localhost ~]</code><code># ip addr add 10.10.172.202/24 dev br0 //将宿主主機的ip設定到br0</code>
<code>[root@localhost ~]</code><code># brctl addif br0 eth0 //将主控端網卡挂到br0上</code>
<code>[root@localhost ~]</code><code># ip route del default //删除預設的原路由,其實就是eth0上使用的原路由10.10.172.1(這步小心,注意删除後要保證機器能遠端連接配接上,最好是通過外網ip遠端連的。别删除路由後,遠端連接配接不上,中斷了)</code>
<code>[root@localhost ~]</code><code># ip route add default via 10.10.172.1 dev br0 //為br0設定路由,此時CRT就可以遠端連接配接伺服器了。</code>
<code>[root@localhost ~]</code><code># vim /etc/sysconfig/docker //即将虛拟的橋接口由預設的docker0改為br0</code>
<code>[root@localhost ~]</code><code># service docker start</code>
<code>啟動一個手動設定網絡的容器</code>
<code>[root@localhost ~]</code><code># docker run -itd --net=none --name=my-test1 docker.io/centos</code>
<code>[root@localhost network-scripts]</code><code># docker ps</code>
<code>ba99871b4c45 docker.io</code><code>/centos</code> <code>"/bin/bash"</code> <code>7 minutes ago Up 9 seconds my-test1</code>
<code>為my-test1容器設定一個與橋接實體網絡同位址段的ip(如下,</code><code>"ip@gateway"</code><code>)</code>
<code>預設不指定網卡裝置名,則預設添加為eth0。可以通過-i參數添加網卡裝置名</code>
<code>[root@localhost ~]</code><code># pipework br0 -i eth0 my-test1 10.10.172.190/[email protected] //網關為主控端的IP位址或者是主控端的網關位址</code>
<code>同理,在其他機器上啟動容器,并類似上面用pipework設定一個同網段類的ip,這樣跨主機的容器就可以互相</code><code>ping</code><code>通了!</code>
<code>--------------------------------------------------------------------------------------------------</code>
<code>2)保留預設虛拟橋接卡docker0的配置</code>
<code>[root@localhost ~]</code><code># cd /etc/sysconfig/network-scripts/</code>
<code>[root@localhost network-scripts]</code><code># cp ifcfg-eth0 ifcfg-eth0.bak</code>
<code>[root@localhost network-scripts]</code><code># cp ifcfg-eth0 ifcfg-br0</code>
<code>[root@localhost network-scripts]</code><code># vim ifcfg-eth0 //增加BRIDGE=br0,删除IPADDR,NETMASK,GATEWAY,DNS的設定</code>
<code>......</code>
<code>BRIDGE=br0</code>
<code>[root@localhost network-scripts]</code><code># vim ifcfg-br0 //修改DEVICE為br0,Type為Bridge,把eth0的網絡設定設定到這裡來(裡面應該有ip,網關,子網路遮罩或DNS設定)</code>
<code>TYPE=Bridge</code>
<code>DEVICE=br0</code>
<code>[root@localhost network-scripts]</code><code># service network restart</code>
<code>[root@localhost network-scripts]</code><code># service docker restart</code>
<code>開啟一個容器并指定網絡模式為none(這樣,建立的容器就不會通過docker0自動配置設定ip了,而是根據pipework工具自定ip指定)</code>
<code>[root@localhost network-scripts]</code><code># docker images</code>
<code>[root@localhost network-scripts]</code><code># docker run -itd --net=none --name=my-centos docker.io/centos /bin/bash</code>
<code>ed945a089b689e1fee01225ea957172bf0a38e4c243d870d009891871872a450</code>
<code>接着給容器配置網絡</code>
<code>[root@localhost network-scripts]</code><code># pipework br0 -i eth0 my-centos 10.10.172.191/[email protected]</code>
<code>[root@localhost network-scripts]</code><code># docker attach ed945a089b68</code>
<code>[root@localhost network-scripts]</code><code># docker exec -it ed945a089b68 /bin/bash #推薦使用exec連接配接容器</code>
<code>[root@ed945a089b68 /]</code><code># ifconfig eth0 //若沒有ifconfig指令,可以yum安裝net-tools工具 </code>
<code> </code><code>inet 10.10.172.191 netmask 255.255.255.0 broadcast 10.10.172.255</code>
<code> </code><code>inet6 fe80::b8e8:27ff:fe7f:ce43 prefixlen 64 scopeid 0x20<link></code>
<code> </code><code>ether ba:e8:27:7f:ce:43 txqueuelen 1000 (Ethernet)</code>
<code> </code><code>RX packets 7060 bytes 13523313 (12.8 MiB)</code>
<code> </code><code>TX packets 5139 bytes 359449 (351.0 KiB)</code>
<code>[root@ed945a089b68 /]</code><code># route -n</code>
<code>Kernel IP routing table</code>
<code>Destination Gateway Genmask Flags Metric Ref Use Iface</code>
<code>0.0.0.0 10.10.172.1 0.0.0.0 UG 0 0 0 eth0</code>
<code>10.10.172.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0</code>
<code>另外pipework不能添加靜态路由,如果有需求則可以在run的時候加上--privileged=</code><code>true</code> <code>權限在容器中手動添加,但這種方法安全性有缺陷。</code>
<code>除此之外,可以通過ip netns(--help參考幫助)添加靜态路由,以避免建立容器使用--privileged=</code><code>true</code><code>選項造成一些不必要的安全問題:</code>
<code>如下擷取指定容器的pid</code>
<code>[root@localhost network-scripts]</code><code># docker inspect --format="{{ .State.Pid }}" ed945a089b68</code>
<code>19532</code>
<code>[root@localhost network-scripts]</code><code># ln -s /proc/19532/ns/net /var/run/netns/19532</code>
<code>[root@localhost network-scripts]</code><code># ip netns exec 19532 ip route add 192.168.21.0/24 dev eth0 via 10.10.172.1</code>
<code>[root@localhost network-scripts]</code><code># ip netns exec 19532 ip route //添加成功</code>
<code>192.168.21.0</code><code>/24</code> <code>via 10.10.172.1 dev eth0</code>
<code>同理,在其它主控端進行相應的配置,建立容器并使用pipework添加虛拟網卡橋接到br0,如此建立的容器間就可以互相通信了。 </code>
-----------------------------------------------幾個報錯出來---------------------------------------------
<code>1)重新開機網卡報錯如下:</code>
<code># systemctl restart network</code>
<code>Nov 23 22:09:08 hdcoe02 systemd[1]: network.service: control process exited, code=exited status=1 </code>
<code>Nov 23 22:09:08 hdcoe02 systemd[1]: Failed to start LSB: Bring up</code><code>/down</code> <code>networking. </code>
<code>Nov 23 22:09:08 hdcoe02 systemd[1]: Unit network.service entered failed state.<</code><code>/span</code><code>> </code>
<code>解決辦法:</code>
<code># systemctl enable NetworkManager-wait-online.service</code>
<code># systemctl stop NetworkManager</code>
<code># systemctl restart network.service</code>
<code>2)建立容器,出現下面告警</code>
<code>WARNING: IPv4 forwarding is disabled. Networking will not work.</code>
<code>#vim /usr/lib/sysctl.d/00-system.conf</code>
<code>添加如下代碼:</code>
<code>net.ipv4.ip_forward=1</code>
<code>執行以下指令,讓其配置立即生效</code>
<code># sysctl -p</code>
-----------------------------------------------------------------------------------------------------------------------------------
其實除了上面使用的pipework工具還,還可以使用虛拟交換機(Open vSwitch)進行docker容器間的網絡通信,廢話不多說,下面說下Open vSwitch的使用:
<code>一、在Server1和Server2上分别安裝openvswitch</code>
<code>[root@Slave1 ~]</code><code># yum install docker bridge-util wget openssl-devel kernel-devel -y </code>
<code>[root@Slave1 ~]</code><code># yum groupinstall "Development Tools" -y </code>
<code>[root@Slave1 ~]</code><code># adduser ovswitch</code>
<code>[root@Slave1 ~]</code><code># su - ovswitch</code>
<code>[ovswitch@Slave1 ~]$ wget http:</code><code>//openvswitch</code><code>.org</code><code>/releases/openvswitch-2</code><code>.3.0.</code><code>tar</code><code>.gz</code>
<code>[ovswitch@Slave1 ~]$ </code><code>tar</code> <code>-zxvpf openvswitch-2.3.0.</code><code>tar</code><code>.gz</code>
<code>[ovswitch@Slave1 ~]$ </code><code>mkdir</code> <code>-p ~</code><code>/rpmbuild/SOURCES</code>
<code>[ovswitch@Slave1 ~]$ </code><code>sed</code> <code>'s/openvswitch-kmod, //g'</code> <code>openvswitch-2.3.0</code><code>/rhel/openvswitch</code><code>.spec > openvswitch-2.3.0</code><code>/rhel/openvswitch_no_kmod</code><code>.spec</code>
<code>[ovswitch@Slave1 ~]$ </code><code>cp</code> <code>openvswitch-2.3.0.</code><code>tar</code><code>.gz rpmbuild</code><code>/SOURCES/</code>
<code>[ovswitch@Slave1 ~]$ rpmbuild -bb --without check ~</code><code>/openvswitch-2</code><code>.3.0</code><code>/rhel/openvswitch_no_kmod</code><code>.spec</code>
<code>[ovswitch@Slave1 ~]$ </code><code>exit</code>
<code>[root@Slave1 ~]</code><code># yum localinstall /home/ovswitch/rpmbuild/RPMS/x86_64/openvswitch-2.3.0-1.x86_64.rpm</code>
<code>[root@Slave1 ~]</code><code># mkdir /etc/openvswitch</code>
<code>[root@Slave1 ~]</code><code># setenforce 0</code>
<code>[root@Slave1 ~]</code><code># systemctl start openvswitch.service</code>
<code>[root@Slave1 ~]</code><code># chkconfig openswitch on</code>
<code>[root@Slave1 ~]</code><code># systemctl status openvswitch.service -l</code>
<code>二、在Slave1和Slave2上建立OVS Bridge并配置路由</code>
<code>1)在Slave1主控端上設定docker容器内網ip網段172.17.1.0</code><code>/24</code>
<code>[root@Slave1 ~]</code><code># vim /proc/sys/net/ipv4/ip_forward</code>
<code>[root@Slave1 ~]</code><code># ovs-vsctl add-br obr0</code>
<code>[root@Slave1 ~]</code><code># ovs-vsctl add-port obr0 gre0 -- set Interface gre0 type=gre options:remote_ip=10.10.172.204</code>
<code>[root@Slave1 ~]</code><code># brctl addbr kbr0</code>
<code>[root@Slave1 ~]</code><code># brctl addif kbr0 obr0</code>
<code>[root@Slave1 ~]</code><code># ip link set dev docker0 down</code>
<code>[root@Slave1 ~]</code><code># ip link del dev docker0</code>
<code>[root@Slave1 ~]</code><code># brctl show </code>
<code>kbr0 8000.f24f08040741 no obr0</code>
<code>編輯</code><code>/usr/lib/systemd/system/docker</code><code>.service檔案,添加docker啟動選項</code><code>"--bip=172.17.1.1/24"</code><code>來指定docker0位址即可</code>
<code>[root@Slave1 ~]</code><code># cat /usr/lib/systemd/system/docker.service </code>
<code>[Unit]</code>
<code>Description=Docker Application Container Engine</code>
<code>Documentation=http:</code><code>//docs</code><code>.docker.com</code>
<code>After=network.target rhel-push-plugin.socket registries.service</code>
<code>Wants=docker-storage-setup.service</code>
<code>Requires=docker-cleanup.timer</code>
<code>[Service]</code>
<code>Type=notify</code>
<code>NotifyAccess=all</code>
<code>EnvironmentFile=-</code><code>/run/containers/registries</code><code>.conf</code>
<code>EnvironmentFile=-</code><code>/etc/sysconfig/docker</code>
<code>EnvironmentFile=-</code><code>/etc/sysconfig/docker-storage</code>
<code>EnvironmentFile=-</code><code>/etc/sysconfig/docker-network</code>
<code>Environment=GOTRACEBACK=crash</code>
<code>Environment=DOCKER_HTTP_HOST_COMPAT=1</code>
<code>Environment=PATH=</code><code>/usr/libexec/docker</code><code>:</code><code>/usr/bin</code><code>:</code><code>/usr/sbin</code>
<code>ExecStart=</code><code>/usr/bin/dockerd-current</code> <code>\</code>
<code> </code><code>--add-runtime docker-runc=</code><code>/usr/libexec/docker/docker-runc-current</code> <code>\</code>
<code> </code><code>--default-runtime=docker-runc \</code>
<code> </code><code>--</code><code>exec</code><code>-opt native.cgroupdriver=systemd \</code>
<code> </code><code>--userland-proxy-path=</code><code>/usr/libexec/docker/docker-proxy-current</code> <code>\</code>
<code> </code><code>--bip=172.17.1.1</code><code>/24</code> <code>\</code>
<code> </code><code>$OPTIONS \</code>
<code> </code><code>$DOCKER_STORAGE_OPTIONS \</code>
<code> </code><code>$DOCKER_NETWORK_OPTIONS \</code>
<code> </code><code>$ADD_REGISTRY \</code>
<code> </code><code>$BLOCK_REGISTRY \</code>
<code> </code><code>$INSECURE_REGISTRY\</code>
<code> </code><code>$REGISTRIES </code>
<code>ExecReload=</code><code>/bin/kill</code> <code>-s HUP $MAINPID</code>
<code>LimitNOFILE=1048576</code>
<code>LimitNPROC=1048576</code>
<code>LimitCORE=infinity</code>
<code>TimeoutStartSec=0</code>
<code>Restart=on-abnormal</code>
<code>MountFlags=slave</code>
<code>KillMode=process</code>
<code>[Install]</code>
<code>WantedBy=multi-user.target</code>
<code>[root@Slave1 ~]</code><code>#</code>
<code>[root@Slave1 ~]</code><code># vim /etc/sysconfig/network-scripts/route-eth0</code>
<code>172.17.2.0</code><code>/24</code> <code>via 10.10.172.204 dev eth0</code>
<code>[root@Slave1 ~]</code><code># systemctl restart network.service</code>
檢視主控端網卡資訊
<code>[root@dockerserver1 ~]</code><code># ip addr</code>
<code>1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN </code>
<code> </code><code>link</code><code>/loopback</code> <code>00:00:00:00:00:00 brd 00:00:00:00:00:00</code>
<code> </code><code>inet 127.0.0.1</code><code>/8</code> <code>scope host lo</code>
<code> </code><code>valid_lft forever preferred_lft forever</code>
<code> </code><code>inet6 ::1</code><code>/128</code> <code>scope host </code>
<code>2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP qlen 1000</code>
<code> </code><code>link</code><code>/ether</code> <code>00:50:56:86:3e:d8 brd ff:ff:ff:ff:ff:ff</code>
<code> </code><code>inet 10.10.172.203</code><code>/24</code> <code>brd 10.10.172.255 scope global eth0</code>
<code> </code><code>inet6 fe80::250:56ff:fe86:3ed8</code><code>/64</code> <code>scope link </code>
<code>4: ovs-system: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN </code>
<code> </code><code>link</code><code>/ether</code> <code>06:19:20:ae:f6:61 brd ff:ff:ff:ff:ff:ff</code>
<code>10: obr0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop master kbr0 state DOWN </code>
<code> </code><code>link</code><code>/ether</code> <code>f2:4f:08:04:07:41 brd ff:ff:ff:ff:ff:ff</code>
<code>11: kbr0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN </code>
<code>12: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN </code>
<code> </code><code>link</code><code>/ether</code> <code>02:42:ca:6c:84:a0 brd ff:ff:ff:ff:ff:ff</code>
<code> </code><code>inet 172.17.1.1</code><code>/24</code> <code>scope global docker0</code>
<code>[root@dockerserver1 ~]</code><code>#</code>
<code>2)在Slave2主控端上設定docker容器内網ip網段172.17.2.0</code><code>/24</code>
<code>[root@Slave2 ~]</code><code># vim /proc/sys/net/ipv4/ip_forward</code>
<code>[root@Slave2 ~]</code><code># ovs-vsctl add-br obr0</code>
<code>[root@Slave2 ~]</code><code># ovs-vsctl add-port obr0 gre0 -- set Interface gre0 type=gre options:remote_ip=10.10.172.203</code>
<code>[root@Slave2 ~]</code><code># brctl addbr kbr0</code>
<code>[root@Slave2 ~]</code><code># brctl addif kbr0 obr0</code>
<code>[root@Slave2 ~]</code><code># ip link set dev docker0 down</code>
<code>[root@Slave2 ~]</code><code># ip link del dev docker0</code>
<code>[root@Slave2 ~]</code><code># brctl show </code>
<code>kbr0 8000.f6ff0062b849 no obr0</code>
<code>編輯</code><code>/usr/lib/systemd/system/docker</code><code>.service檔案,添加docker啟動選項</code><code>"--bip=172.17.2.1/24"</code><code>來指定docker0位址即可</code>
<code>[root@Slave2 ~]</code><code># cat /usr/lib/systemd/system/docker.service</code>
<code> </code><code>--bip=172.17.2.1</code><code>/24</code> <code>\</code>
<code> </code><code>$REGISTRIES</code>
<code>[root@Slave2 ~]</code><code># </code>
<code>[root@Slave2 ~]</code><code># vim /etc/sysconfig/network-scripts/route-eth0</code>
<code>172.17.1.0</code><code>/24</code> <code>via 10.10.172.203 dev eth0</code>
<code>[root@Slave2 ~]</code><code># systemctl restart network.service</code>
檢視主控端網卡資訊:
<code>[root@dockerserver2 ~]</code><code># ip addr</code>
<code> </code><code>link</code><code>/ether</code> <code>00:50:56:86:22:d8 brd ff:ff:ff:ff:ff:ff</code>
<code> </code><code>inet 10.10.172.204</code><code>/24</code> <code>brd 10.10.172.255 scope global eth0</code>
<code> </code><code>inet6 fe80::250:56ff:fe86:22d8</code><code>/64</code> <code>scope link </code>
<code> </code><code>link</code><code>/ether</code> <code>be:d4:64:ee:cb:29 brd ff:ff:ff:ff:ff:ff</code>
<code>8: obr0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop master kbr0 state DOWN </code>
<code> </code><code>link</code><code>/ether</code> <code>f6:ff:00:62:b8:49 brd ff:ff:ff:ff:ff:ff</code>
<code>9: kbr0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN </code>
<code>10: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN </code>
<code> </code><code>link</code><code>/ether</code> <code>02:42:03:28:ae:db brd ff:ff:ff:ff:ff:ff</code>
<code> </code><code>inet 172.17.2.1</code><code>/24</code> <code>scope global docker0</code>
<code>[root@dockerserver2 ~]</code><code>#</code>
<code>三、啟動容器測試</code>
<code>Server1和Server2上修改docker啟動的虛拟網卡綁定為kbr0,重新開機docker程序</code>
<code>1)在Server1主控端上啟動容器,然後登陸容器内檢視ip,就會發現ip是上面設定值172.17.1.0</code><code>/24</code><code>網段的。</code>
<code>[root@Slave1 ~]</code><code># docker run -idt --name my-server1 docker.io/centos /bin/bash</code>
<code>[root@Slave1 ~]</code><code># docker exec my-server1 ifconfig eth0</code>
<code> </code><code>inet 172.17.1.2 netmask 255.255.255.0 broadcast 0.0.0.0</code>
<code> </code><code>inet6 fe80::42:acff:fe11:102 prefixlen 64 scopeid 0x20<link></code>
<code> </code><code>ether 02:42:ac:11:01:02 txqueuelen 0 (Ethernet)</code>
<code> </code><code>RX packets 6555 bytes 13556099 (12.9 MiB)</code>
<code> </code><code>TX packets 5456 bytes 423669 (413.7 KiB)</code>
<code>2)在Server2主控端上啟動容器,然後登陸容器内檢視ip,就會發現ip是上面設定值172.17.2.0</code><code>/24</code><code>網段的</code>
<code>[root@Slave2 ~]</code><code># docker run -idt --name my-server1 docker.io/centos /bin/bash</code>
<code>[root@Slave2 ~]</code><code># docker exec my-server1 ifconfig eth0 </code>
<code> </code><code>inet 172.17.2.2 netmask 255.255.255.0 broadcast 0.0.0.0</code>
<code> </code><code>inet6 fe80::42:acff:fe11:202 prefixlen 64 scopeid 0x20<link></code>
<code> </code><code>ether 02:42:ac:11:02:02 txqueuelen 0 (Ethernet)</code>
<code> </code><code>RX packets 5979 bytes 13412345 (12.7 MiB)</code>
<code> </code><code>TX packets 5136 bytes 366985 (358.3 KiB)</code>
<code>[root@Slave2 ~]</code><code># docker exec -it my-server1 /bin/bash </code>
<code>[root@857e1de96467 /]</code><code># ping 172.17.1.2</code>
<code>PING 172.17.1.2 (172.17.1.2) 56(84) bytes of data.</code>
<code>64 bytes from 172.17.1.2: icmp_seq=1 ttl=62 </code><code>time</code><code>=0.926 ms</code>
<code>--- 172.17.1.2 </code><code>ping</code> <code>statistics ---</code>
<code>1 packets transmitted, 1 received, 0% packet loss, </code><code>time</code> <code>0ms</code>
<code>rtt min</code><code>/avg/max/mdev</code> <code>= 0.926</code><code>/0</code><code>.926</code><code>/0</code><code>.926</code><code>/0</code><code>.000 ms</code>
<code>[root@857e1de96467 /]</code><code># exit</code>
<code>exit</code>
<code>然後在上面啟動的容内互</code><code>ping</code><code>對方容器,發現是可以</code><code>ping</code><code>通的。</code>
注:docker主控端IP轉發功能要啟用,不然docker容器不能正常上網的。
本文轉自 dengaosky 51CTO部落格,原文連結:http://blog.51cto.com/dengaosky/2067727,如需轉載請自行聯系原作者