容器和宿主機、容器之間以及誇主機容器如何通信呢?這就須要使用到Docker網絡。docker
在前面的介紹中咱們在Dockerfile中經過EXPOSE參數來設置容器暴露的端口,讓在docker run中使用-p來設置宿主機端口到容器端口的映射,這只是最簡單的宿主機和容器通信,一樣使用宿主機IP:PORT方式可讓其餘容器和該容器通信,可是這樣有個問題,首先應用程序須要對IP進行硬編碼,其次容器每次重啓IP都會變化,顯然在生產環節中應該作到的儘量的解耦,下面咱們先看一下Docker網絡的構成。
數據庫
查看網絡設置bootstrap
啓動docker服務就會產生一個docker0的虛擬網橋(多端口虛擬交換機)設備。veth*這個是啓動一個容器就會產生一個這樣的設備該設備與容器內的eth0虛擬網卡對應,這個veth*你能夠理解爲某個網橋上的接口,因此它只有MAC地址而沒有IP地址,畢竟二層交換機網口是沒有IP地址的,這個接口的另一端就插在容器的網卡上,這個網卡有IP也有MAC(也能夠理解爲一根連在docker0上的網線,畢竟容器內的網卡和容器外的是一對,460就是容器外ID,459就是容器內網卡的ID)網橋在內核層連通了其餘物理或者虛擬網卡。bash
下面看一下docker run命令中和網絡有關的參數:服務器
--dns=IP #指定DNS服務器 --dns-search=DOMAIN #指定搜索域 -h HOSTNAME #設置容器的主機名稱 --link=容器名:別名 #啓動該容器時與指定的容器進行連接,這樣容器間能夠經過名稱來訪問 -p #映射主機端口 --net=bridge #默認配置,爲容器建立獨立的網絡命名空間,分配網卡、IP地址並經過veth接口 #將容器掛到docker0虛擬網橋上。 --net=none #爲容器建立獨立的網絡命名空間,但不進行網絡設置,容器沒有網卡和IP --net=host #容器和宿主機共享網絡設置,在容器中看到的網絡信息都與宿主機同樣,也就是 #不爲容器建立獨立的網絡命名空間。 --net=user_defined_network #用戶自行使用network建立一個網絡,同一個網絡內的容器彼 #此可見。相似於vmware中你能夠建立多個網絡通道好比vmnet1 #、vmnet2等。 --net=container:容器名稱或者ID #表示該容器共享指定容器的網絡命名空間。
容器的DNS配置:
網絡
容器中的主機名和DNS設置是經過/etc/resolv.conf、/etc/hostname和/etc/hosts三個文件來維護的,以下圖:架構
/etc/resolv.conf文件在建立容器時,默認和宿主機上的同樣;/etc/hosts文件默認只有一條容器本身的記錄;/etc/hostname記錄了容器本身的主機名。你能夠直接修改容器的這三個文件,可是容器一旦重啓就失效了。負載均衡
因此在運行docker run的時候使用--dns=IP來在容器中的/etc/rsovle.conf文件以後添加額外的DNS服務器地址;less
--hostname=HOSTNAME來指定主機名稱。socket
容器的訪問控制:
外部要想訪問Docker容器或者Docker容器訪問外部網絡那麼,下面的參數須要爲1,表示啓用IPV4的轉發
sysctl net.ipv4.ip_forward
另外還須要注意一下幾點:
另外默認狀況下容器能夠訪問外部網絡,可是外部網絡沒法訪問容器
容器是經過SNAT方式出去並訪問外部網絡的
外部訪問容器還取決於宿主機和容器內的防火牆,若是容許訪問在在docker run中加入-P或者-p參數指定宿主機到容器的端口映射
流量進入到宿主機網卡,先進入到PREROUTING鏈中,而後將流量引入到DOCKER鏈,經過DNAT把32768流量修改地址爲docker容器地址和端口。
libnetwork
這是Docker種的一個插件化網絡功能。這個模型結構很簡潔。它包括三個基於元素:
沙盒:表明一個容器,也能夠理解爲網絡命名空間
接入點:表明能夠掛載容器的接口,會分配IP地址
網絡:能夠連通多個接入點的子網
首先驅動註冊本身的到網絡控制器,網絡控制器建立網絡,而後在在網絡上建立接入點,最後把容器鏈接到接入點上。刪除過程則是反向操做,先把容器從接口上卸載,而後刪除接入點,最後刪除網絡。目前支持的驅動類型有4種:
類型 | 說明 |
Null | 不提供網絡服務,容器若是接入到這兒類型的網絡接入點上,則沒有網絡鏈接 |
Briage | 網橋,根Docker0同樣,使用傳統的Linux網橋和Iptables來實現,經過NAT容器能夠和宿主機以及宿主機之外進行通信。 |
Overlay | 使用vxlan隧道實現跨主機通訊,這個和軟件定義網絡中是一個概念,軟件定義網絡中也有多種實現方式其中就有vxlan,另一個比較經常使用的是GRE,其實這些都是創建隧道。GRE是創建二層點到點隧道,vxlan能夠看作是vlan的升級,由於傳統vland經過打標籤的方式來區分不一樣網絡,可是標籤有限最多4096個VLAN網絡;而vxlan的標籤是24位因此網絡個數大大提高,另外它是L2 over UDP的形式,這樣能夠跨越三層。這種SDN在公有云平臺使用較多由於規模大,多租戶。 |
host | 主機模式,容器使用宿主機的網絡命名空間,也就是使用宿主機網卡IP對外通訊(顧名思義它們兩個是同一IP)。但這種方式沒有獨立的網絡協議棧,容器會和宿主機競爭使用網絡協議棧,並且宿主機上的不少服務使用的端口在容器中就不能使用。 |
Remote | 擴展類型,預留給其餘方案 |
經過上面的描述能夠看到容器的這些網絡類型和虛擬化裏面的及其類似。下面說一下經常使用命令
列出網絡:
docker network ls [options] # -f driver=NAME 列出特定驅動類型的網絡
建立網絡:
docker network create [options] NETWORK-NAME # -d 驅動類型 # --gateway IP 網關地址 # --internal 禁止外部對該網絡訪問 # --ip-range IP 分配IP地址的範圍 # --subnet VALUE 設置子網掩碼 # --ipam-driver STRING IP地址管理的插件類型 # --ipam-opt VALUE IP地址管理插件的選項 # --ipv6 是否支持IPV6 # --lable VALUE 爲網絡添加標籤信息 # --o VALUE 網絡驅動選項
看下面的例子
docker network create -d bridge --gateway 172.16.200.254 --subnet 172.16.200.0/24 vmnet01
我這裏創建一個172.16.200.0的網絡,並指定了默認網關的IP,使用網橋模式。
查看內部細節
網橋驅動的一些選項:
選項 | 說明 |
com.docker.network.bridge.name | 網橋名稱,就是在ifconfig中看到的,建議加上這個選項,不然網卡多了不容易識別。默認的docker0就是默認網絡bridge的別名,針對docker0配置IP就至關於給虛擬交換機配置管理IP,同時這個IP也是容器網關地址。 |
com.docker.network.bridge.enable_ip_masquerade | 是否啓用ip_masquerade,這是地址假裝,相似SNAT,可是有點區別,它用將發送數據的網卡IP替換源IP,容器每次啓動IP地址都發生變化,那麼如何確保咱們用相同的地址或者域名訪問這個容器呢?總不可能每次啓動容器都修改iptables。使用ip_masquerade這樣每次容器的IP發生變化,會自動獲取IP並修改iptables。 |
com.docker.network.bridge.enable_icc | 該網橋是否容許容器間通訊。默認網橋docker0的icc是true,因此也就是默認容許容器間通訊。容器建立或者鏈接到網橋後就會使用網橋上配置的這些參數,若是該網橋的icc是false則,容器間沒法通訊,當仍然能夠經過容器連接的方式設置容器間通訊。 |
com.docker.network.bridge.host_binding_ipv4 | 綁定容器端口時的默認IP是什麼,默認網橋docker0的配置是0.0.0.0也就是接受主機來自全部網絡接口上的流量。 |
com.docker.network.driver.mtu | 設置容器的MTU |
com.docker.network.bridge.default_bridge | 是否爲默認網橋,容器建立默認會使用的網橋,除非使用--net參數來設置鏈接到哪一個網橋。 |
docker network -d -o "com.docker.network.bridge.name"="XX" |
注意:建立網橋網絡若是不知道子網則會按照默認的172.16.0.0/16日後排1七、18....。同時會爲這個網橋設置一個IP一般是這個子網的第一個IP,那麼ifconfig中看到的名字和建立網絡是的名字不一樣,名字是隨機的,因此這就是爲何須要使用上面的-o參數中的com.docker.network.bridge.name來指定一個名字。其實經過docker命令建立網絡後臺實際上是建立的Linux網橋。
將容器鏈接到網絡:
該命令是把正在運行的容器進行切換網絡,若是是建立容器的話,須要在docker run命令中使用--net參數來指定網絡,若是不指定,則使用默認網橋。
docker network connect [options] NETWORK-NAME CONTAINER # --ip IP 爲容器手動分配一個地址,若是不指定則自動分配 # --alias VALUE 爲容器添加一個別名 # --link VALUE 添加連接到另外一個容器 # --link-local-ip VALUE 爲容器添加一個連接地址
看下面的例子,咱們就把一個正在運行的容器鏈接到咱們上面建立的網絡上。
docker network connect vmnet01 jspSrv01
鏈接到其餘網絡以後並不影響外網訪問。
從網絡中卸載容器:
docker network disconnect [options] NETWORK-NAME CONTAINER # -f 強制把容器從網絡接口上卸載
看下面的例子,咱們把剛纔的容器從vmnet01上卸載
docker network disconnect -f vmnet01 jspSrv01
卸載後你發現還有網絡信息,其實它是又回到默認網橋上了
刪除一個網絡:
當網絡不存在接入點時,刪除成功。
docker network rm NETWORK-NAME
查看網絡內部:
docker network inspect [options] NETWORK-NAME # -f STRING 對指定字符進行格式化輸出
跨主機網絡通訊:
跨主機通訊的方式有三種:
容器使用host模式,直接使用host主機的IP,可是這樣端口容易出現衝突。使用場景有限。
端口映射,也就是咱們以前一直使用的,經過網橋模式的網絡,經過DNAT來實現外部訪問,但缺乏靈活度。
直接路由,你可使用默認的Docker0,也能夠新建一個網橋。在Docker主機上添加一條靜態路由實現。這個方案的問題是雖然跨了主機,可是不一樣主機上的容器必須鏈接到相同網橋這就意味着IP段同樣,因此這就有很大侷限性。
使用SDN方式,好比Flanneldocker1.9之後原生支持的Overlay網絡或者Open vSwitch
下面咱們就基於docker自帶的overlay來實現,須要注意使用overlay網絡有2中模式一個是swarm模式,一個是非swarm模式,在非swarm模式下使用則須要藉助服務發現第三方組件。
docker實現跨主機通訊須要的網絡驅動類型爲overlay,可是同時還須要一個鍵值型的服務發現和配置共享軟件,好比Zookeeper、Doozerd、Etcd、Consul等。Zookeeper、Doozerd、Etcd在架構上很相似,只提供原始的鍵值存儲,要求程序開發人員本身提供服務發現功能,而Consul則內置了服務發現,只要用戶註冊服務並經過DNS或HTTP接口執行服務發現便可,同時它還具備健康檢查功能。咱們這裏使用Consul來做爲服務發現工具。先說一下環境:
計算機名稱 | IP | 功能 |
dockerothsrv | eth0:192.168.124.139 eth1:172.16.100.10 |
Docker私有倉庫、Consul服務 |
Docker01 | eth0:192.168.124.138 eth1:172.16.100.20 |
Docker容器服務器 |
Docker02 | eth0:192.168.124.141 eth1:172.16.100.30 |
Docker容器服務器 |
創建一個Consul服務:
這個你能夠直接在系統中搭建,也能夠運行一個容器來完成,咱們這裏經過運行容器來實現由於畢竟是講跨主機通信,咱們在dockerothsrv服務器上安裝
docker run -d -p 8500:8500 -h consul progrium/consul -server -bootstrap
配置docker主機:
這裏就須要修改dockerd的配置文件,兩臺Docker服務器都增長,增長一些內容,以下圖:
參數 | 說明 |
cluster-store | 指向鍵值存儲的地址,在consul中註冊,它得存儲形式就是鍵值。 |
cluster-advertise |
這是一個主機網絡接口或者IP地址加端口的組合,也就是本主機中dockerd實例在consul集羣中的地址,遠程dockerd服務鏈接本dockerd服務時所使用的值,也是Docker01和Docker02服務器互通的端口。它可使用IP:PORT形式,也能夠是INTERFACE:PORT形式。這個端口是你的dockerd服務以daemon形式運行時指定的端口。入上圖tcp://0.0.0.0後面的5555。有些文檔中你會看到2376或者2375這樣的端口,這是由於Docker官方文檔中寫的是這個,由於dockerd默認是本地socket運行不接受網絡遠程鏈接,因此若是須要則須要指定-H tcp選項中的IP:PORT,2375是不加密端口,2376是加密端口。 |
重啓dockerd服務
systemctl restart docker
查看一下
創建overlay驅動類型網絡:
docker network create -d overlay oNet
你只須要在一臺主機上創建,而後在另一臺主機上就能夠看到。以下圖:
說明:docker_gwbridge網絡這是一個本地網橋,它會在2中狀況下自動創建,一個是初始化或者加入一個swarm集羣時;另外是
測試:
新建2個容器使用自動的oNet網絡
Docker01
Docker02
查看一下網絡詳情
#關閉2臺宿主機上的防火牆,不然通不了 systemctl stop firewalld
跨主機通訊原理:
由於後面咱們要查看網絡命名空間,而docker的網絡命名空間不是創建在/var/run/netns下,因此咱們把docker的網絡命名空間連接到那裏,這樣就能夠經過ip命令進行查看
ln -s /var/run/docker/netns/ /var/run/netns #經過IP netns查看網絡命名空間
經過命令查看容器內的網卡和宿主機上的哪一個網卡是一對兒
ip addr docker exec aaa ethtool -S eth0/1
發現容器內的eth0也就是10.0.0.0網段的這個網卡沒有關聯到宿主機上的任何虛擬網卡,那它去哪裏了?反而容器中eth1關聯到496,而496走的是docker_gwbridge網橋,可這個網橋是172.18.0.0/16網段,那究竟是如何和10.0.0.0網段通信呢?咱們查看一下命名空間?
至因而哪一個命名空間你只能逐一查看,4-cd這個命名空間中是10.0.0.0網段,容器中的eth0關聯到這個命名空間中的494上,而這個494則鏈接到了br0網橋上。其中vxlan1是VXLAN隧道端點,它是VXLAN網絡的設備邊緣,用於VXLAN報文非封包和解包,包括ARP請求報文和正常的VXLAN數據報文,封裝好之後報文經過隧道向另外一端的VTEP也就是VXLAN隧道端點發送,另外一端的VTEP收到後解開報文。
經過下圖能夠看到這個命名空間的路由。
能夠經過下面的命令查看vxlan1的VLANID是多少。須要知道每創建一個網絡都有一個獨立的網絡命名空間。
ip netns exec 4-cd7565b196 ip -d link show vxlan1
兩臺主機上的vxlanX中的X不同,可是同一個overlay網絡中的vxlan的VLANID是同樣的。看一下分解圖:
通訊過程是這樣:
宿主機A的容器01 ping 10.0.0.4 經過該容器的eth0發送出去,並經過路由表得知發往br0,br0至關於虛擬交換機,若是目標主機在同一宿主機,則直接經過br0通訊,若是不在則經過vxlan
br0收到請求會把請求交給vxlan1,這裏你PING一下,而後經過下面的命令能夠看出來
ip netns exec 網絡命名空間 ip neigh
vxlan中保存有MAC地址表(docker守護進程經過gossip協議在concul數據庫中學來的),並經過宿主機A的eth1發送出去
報文到達宿主機B,拆包發現是vxlan報文,獲取IP,則交給它上面的vxlan設備(同一ID的設備)
宿主機B上vxlan拆包,交給br0,而後br0根據MAC表完成最後的投遞。
overlay的不足:
因爲overlay網絡和宿主機默認網絡不在同一網絡下,因此爲了解決和宿主機通信問題,docker爲宿主機和容器額外添加一個網卡,爲宿主機添加的就是docker_gwbridge爲容器添加的就是eth1,這兩個網卡是一個IP段。但這樣會形成使用上的不方便,容器間使用一套IP,外網訪問容器使用另一套IP
容器對外提供仍然須要經過端口綁定來實現,外界沒法經過IP直接訪問容器
overlay必須依賴docker進程和鍵值數據庫來通訊。
docker原生的overlay性能損耗比較大,生產中不建議使用
不管你採起什麼方案,都須要面臨一些問題:
跨主機通訊(不一樣主機上的容器要能夠相互通訊並且還能夠在整個IDC環境內被訪問到)
容器漂移
跨主機容器IP分配,要避免IP衝突
網絡性能對比:
由此看出Bridge的性能損耗在10%左右,docker原生overlay性能損耗最大。思科的Calico overlay性能幾乎接近Bridge性能。
擴展只知識:
爲何須要服務發現?
寫一個程序調用某個服務,若是這個服務在其餘服務器上按照傳統方法的話,咱們要先獲得那個服務器的IP以及那個服務所使用的端口,不管哪一個服務所在服務器是物理機仍是虛擬機都同樣它們的IP地址相對靜態,咱們只須要把這個信息寫在代碼中或者代碼程序所依賴的配置文件中便可。但如今的狀況不同了,尤爲是雲計算的微服務架構好比容器技術,服務運行在容器中,不管這個容器是在物理機仍是在虛擬機總之這個獲取這個容器的地址很麻煩,由於容器或者稱該服務的網絡地址是動態分配的,並且由於服務容量(運行該容器的實例個數)因此動態伸縮因此致使也沒法用靜態地址,那要想調用服務就須要使用新的方式進行服務發現。
服務發現有兩種模式,客戶端發現和服務器端發現,經常使用的是服務器端發現由於相對於客戶端發現來講,服務器端發現把Service Registry抽離出來。Service Registry就是服務註冊,服務實例會在這裏進行註冊,當不用的時候會註銷。客戶端經過負載均衡器向服務發起請求,負載均衡器在Service Registry中查找並將請求發送給可用服務實例。全部的服務發現工具中都有Service Registry這樣一個角色,它是核心,它是一個K/V的數據庫裏面包含服務實例的網絡地址。一般Service Registry必須具有高可用,並且要時刻保持更新。
服務發現的註冊方式:有自注冊和第三方註冊
自注冊:本身註冊本身,其實就是服務實例本身去Service Registry中註冊和註銷,在必要的狀況下還會發送心跳信息給Registry。優勢是簡單不須要其餘組件、缺點是服務實例和Service Registry是耦合的,開發人員必需要爲服務單獨寫註冊代碼。
第三方註冊:顧名思義註冊和註銷不是由服務實例本身完成,是由一個叫作服務註冊器來作,它對運行中的服務實例進行監聽跟蹤,當它發現服務可用時就會自動爲其註冊。優勢是解耦,註冊由統一組件完成,開發人員不用單獨寫註冊器;缺點是部署環境中必須有註冊器,若是沒有就得本身來設置。
什麼是Overlay?
overlay就是網絡疊加,一個數據包或者幀封裝在另一個數據包或者幀裏面。被封裝的包轉發到隧道另外一端時進行解封裝。Vxlan或者GRE/NVGRE都是一種疊加網絡技術,屬於2層overlay技術,思路就是將以太網報文放在某種隧道上傳輸,只是構建隧道的方式或者協議不一樣。隧道有2層和3層之分,2層常見的就是PPTP或者L2TP等。三層常見的GRE、IPsec等。
overlay是在現有網絡之上構建虛擬網絡,上層應用只和虛擬網絡有關。