docker的host、bridge網路型別
1.1 host模式
眾所周知,Docker使用了Linux的Namespaces技術來進行資源隔離,如PID Namespace隔離程序,Mount Namespace隔離檔案系統,Network Namespace隔離網路等。一個Network Namespace提供了一份獨立的網路環境,包括網絡卡、路由、Iptable規則等都與其他的Network Namespace隔離。一個Docker容器一般會分配一個獨立的Network Namespace。但如果啟動容器的時候使用host模式,那麼這個容器將不會獲得一個獨立的Network Namespace,而是和宿主機共用一個Network Namespace。容器將不會虛擬出自己的網絡卡,配置自己的
例如,我們在10.10.101.105/24的機器上用host模式啟動一個含有web應用的Docker容器,監聽tcp80埠。當我們在容器中執行任何類似ifconfig命令檢視網路環境時,看到的都是宿主機上的資訊。而外界訪問容器中的應用,則直接使用10.10.101.105:80即可,不用任何NAT轉換,就如直接跑在宿主機中一樣。但是,容器的其他方面,如檔案系統、程序列表等還是和宿主機隔離的。
Docker Container的host網路模式可以參考下圖:上圖最左側的Docker Container,即採用了host網路模式,而其他兩個Docker Container依然沿用brdige橋接模式,兩種模式同時存在於宿主機上並不矛盾。
Docker Container的host網路模式在實現過程中,由於不需要額外的網橋以及虛擬網絡卡,故不會涉及docker0以及veth pair。上文namespace的介紹中曾經提到,父程序在建立子程序時,如果不使用CLONE_NEWNET這個引數標誌,那麼創建出的子程序會與父程序共享同一個網路namespace。Docker就是採用了這個簡單的原理,在建立程序啟動容器的過程中,沒有傳入CLONE_NEWNET引數標誌,實現Docker Container與宿主機共享同一個網路環境,即實現host網路模式。
1.2 bridge模式
bridge模式是Docker預設的網路設定,此模式會為每一個容器分配Network Namespace、設定IP等,並將一個主機上的Docker容器連線到一個虛擬網橋上。
Brdige橋接模式為Docker Container建立獨立的網路棧,保證容器內的程序組使用獨立的網路環境,實現容器間、容器與宿主機之間的網路棧隔離。另外,Docker通過宿主機上的網橋(docker0)來連通容器內部的網路棧與宿主機的網路棧,實現容器與宿主機乃至外界的網路通訊。
Docker Container的bridge橋接模式可以參考下圖:
建立容器測試:(由於是預設設定,這裡沒指定網路--net="bridge"。另外可以看到容器內建立了eth0)
[[email protected] ~]# docker run -i -t mysql:latest /bin/bash
[email protected]:/usr/local/mysql# ip addr
1: lo: mtu 65536 qdisc noqueue state UNKNOWN
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
75: eth0: mtu 1500 qdisc pfifo_fast state UP qlen 1000
link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.2/16 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::42:acff:fe11:2/64 scope link
valid_lft forever preferred_lft forever
容器與Host網路是連通的:
[email protected]:/usr/local/mysql# ping 186.100.8.117
PING 186.100.8.117 (186.100.8.117): 48 data bytes
56 bytes from 186.100.8.117: icmp_seq=0 ttl=64 time=0.124 ms
eth0實際上是veth pair的一端,另一端(vethb689485)連在docker0網橋上:
[[email protected] ~]# ethtool -S vethb689485
NIC statistics:
peer_ifindex: 75
[[email protected] ~]# brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.56847afe9799 no vethb689485
通過Iptables實現容器內訪問外部網路:
[[email protected] ~]# iptables-save |grep 172.17.0.*
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
-A FORWARD -d 172.17.0.2/32 ! -i docker0 -o docker0 -p tcp -m tcp --dport 5000 -j ACCEPT
Bridge橋接模式的實現步驟主要如下: (1) Docker Daemon利用veth pair技術,在宿主機上建立兩個虛擬網路介面裝置,假設為veth0和veth1。而veth pair技術的特性可以保證無論哪一個veth接收到網路報文,都會將報文傳輸給另一方。 (2) Docker Daemon將veth0附加到Docker Daemon建立的docker0網橋上。保證宿主機的網路報文可以發往veth0; (3) Docker Daemon將veth1新增到Docker Container所屬的namespace下,並被改名為eth0。如此一來,保證宿主機的網路報文若發往veth0,則立即會被eth0接收,實現宿主機到Docker Container網路的聯通性;同時,也保證Docker Container單獨使用eth0,實現容器網路環境的隔離性。 Bridge橋接模式,從原理上實現了Docker Container到宿主機乃至其他機器的網路連通性。然而,由於宿主機的IP地址與veth pair的 IP地址均不在同一個網段,故僅僅依靠veth pair和namespace的技術,還不足以是宿主機以外的網路主動發現Docker Container的存在。為了使得Docker Container可以讓宿主機以外的世界感知到容器內部暴露的服務,Docker採用NAT(Network Address Translation,網路地址轉換)的方式,讓宿主機以外的世界可以主動將網路報文傳送至容器內部。 具體來講,當Docker Container需要暴露服務時,內部服務必須監聽容器IP和埠號port_0,以便外界主動發起訪問請求。由於宿主機以外的世界,只知道宿主機eth0的網路地址,而並不知道Docker Container的IP地址,哪怕就算知道Docker Container的IP地址,從二層網路的角度來講,外界也無法直接通過Docker Container的IP地址訪問容器內部應用。因此,Docker使用NAT方法,將容器內部的服務監聽的埠與宿主機的某一個埠port_1進行“繫結”。 如此一來,外界訪問Docker Container內部服務的流程為: (1) 外界訪問宿主機的IP以及宿主機的埠port_1; (2) 當宿主機接收到這樣的請求之後,由於DNAT規則的存在,會將該請求的目的IP(宿主機eth0的IP)和目的埠port_1進行轉換,轉換為容器IP和容器的埠port_0; (3) 由於宿主機認識容器IP,故可以將請求傳送給veth pair; (4) veth pair的veth0將請求傳送至容器內部的eth0,最終交給內部服務進行處理。 使用DNAT方法,可以使得Docker宿主機以外的世界主動訪問Docker Container內部服務。那麼Docker Container如何訪問宿主機以外的世界呢。以下簡要分析Docker Container訪問宿主機以外世界的流程: (1) Docker Container內部程序獲悉宿主機以外服務的IP地址和埠port_2,於是Docker Container發起請求。容器的獨立網路環境保證了請求中報文的源IP地址為容器IP(即容器內部eth0),另外Linux核心會自動為程序分配一個可用源埠(假設為port_3); (2) 請求通過容器內部eth0傳送至veth pair的另一端,到達veth0,也就是到達了網橋(docker0)處; (3) docker0網橋開啟了資料報轉發功能(/proc/sys/net/ipv4/ip_forward),故將請求傳送至宿主機的eth0處; (4) 宿主機處理請求時,使用SNAT對請求進行源地址IP轉換,即將請求中源地址IP(容器IP地址)轉換為宿主機eth0的IP地址; (5) 宿主機將經過SNAT轉換後的報文通過請求的目的IP地址(宿主機以外世界的IP地址)傳送至外界。 在這裡,很多人肯定會問:對於Docker Container內部主動發起對外的網路請求,當請求到達宿主機進行SNAT處理後發給外界,當外界響應請求時,響應報文中的目的IP地址肯定是Docker宿主機的IP地址,那響應報文回到宿主機的時候,宿主機又是如何轉給Docker Container的呢?關於這樣的響應,由於port_3埠並沒有在宿主機上做相應的DNAT轉換,原則上不會被髮送至容器內部。為什麼說對於這樣的響應,不會做DNAT轉換呢。原因很簡單,DNAT轉換是針對容器內部服務監聽的特定埠做的,該埠是供服務監聽使用,而容器內部發起的請求報文中,源埠號肯定不會佔用服務監聽的埠,故容器內部發起請求的響應不會在宿主機上經過DNAT處理。 其實,這一環節的內容是由iptables規則來完成,具體的iptables規則如下: iptables -I FORWARD -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT 這條規則的意思是,在宿主機上發往docker0網橋的網路資料報文,如果是該資料報文所處的連線已經建立的話,則無條件接受,並由Linux核心將其傳送到原來的連線上,即回到Docker Container內部。 以上便是Docker Container中bridge橋接模式的簡要介紹。可以說,bridger橋接模式從功能的角度實現了兩個方面:第一,讓容器擁有獨立、隔離的網路棧;第二,讓容器和宿主機以外的世界通過NAT建立通訊。 然而,bridge橋接模式下的Docker Container在使用時,並非為開發者包辦了一切。最明顯的是,該模式下Docker Container不具有一個公有IP,即和宿主機的eth0不處於同一個網段。導致的結果是宿主機以外的世界不能直接和容器進行通訊。雖然NAT模式經過中間處理實現了這一點,但是NAT模式仍然存在問題與不便,如:容器均需要在宿主機上競爭埠,容器內部服務的訪問者需要使用服務發現獲知服務的外部埠等。另外NAT模式由於是在三層網路上的實現手段,故肯定會影響網路的傳輸效率。http://www.superwu.cn/?p=1809
http://blog.csdn.net/halcyonbaby/article/details/42112141
http://www.dataguru.cn/thread-549095-1-1.html