您好,登錄后才能下訂單哦!
容器和宿主機、容器之間以及夸主機容器如何通訊呢?這就需要使用到Docker網絡。
在前面的介紹中我們在Dockerfile中通過EXPOSE參數來設置容器暴露的端口,讓在docker run中使用-p來設置宿主機端口到容器端口的映射,這只是最簡單的宿主機和容器通訊,同樣使用宿主機IP:PORT方式可以讓其他容器和該容器通訊,但是這樣有個問題,首先應用程序需要對IP進行硬編碼,其次容器每次重啟IP都會變化,顯然在生產環節中應該做到的盡可能的解耦,下面我們先看一下Docker網絡的構成。
查看網絡設置
啟動docker服務就會產生一個docker0的虛擬網橋(多端口虛擬交換機)設備。veth*這個是啟動一個容器就會產生一個這樣的設備該設備與容器內的eth0虛擬網卡對應,這個veth*你可以理解為某個網橋上的接口,所以它只有MAC地址而沒有IP地址,畢竟二層交換機網口是沒有IP地址的,這個接口的另外一端就插在容器的網卡上,這個網卡有IP也有MAC(也可以理解為一根連在docker0上的網線,畢竟容器內的網卡和容器外的是一對,460就是容器外ID,459就是容器內網卡的ID)網橋在內核層連通了其他物理或者虛擬網卡。
下面看一下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服務器地址;
--hostname=HOSTNAME來指定主機名稱。
容器的訪問控制:
外部要想訪問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,使用網橋模式。
注意:如果不使用-d指定網絡驅動類型,則默認為網橋。
查看內部細節
網橋驅動的一些選項:
選項 | 說明 |
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往后排17、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 eth2:172.16.100.10 | Docker私有倉庫、Consul服務 |
Docker01 | eth0:192.168.124.138 eth2:172.16.100.20 | Docker容器服務器 |
Docker02 | eth0:192.168.124.141 eth2: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
查看一下網絡詳情
兩個容器相互Ping一下看看
#關閉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網段的這個網卡沒有關聯到宿主機上的任何虛擬網卡,那它去哪里了?反而容器中eth2關聯到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的eth2發送出去
報文到達宿主機B,拆包發現是vxlan報文,獲取IP,則交給它上面的vxlan設備(同一ID的設備)
宿主機B上vxlan拆包,交給br0,然后br0根據MAC表完成最后的投遞。
overlay的不足:
由于overlay網絡和宿主機默認網絡不在同一網絡下,所以為了解決和宿主機通訊問題,docker為宿主機和容器額外添加一個網卡,為宿主機添加的就是docker_gwbridge為容器添加的就是eth2,這兩個網卡是一個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是在現有網絡之上構建虛擬網絡,上層應用只和虛擬網絡有關。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。