1. 程式人生 > >docker的host、bridge網路型別

docker的host、bridge網路型別

1.1 host模式

眾所周知,Docker使用了LinuxNamespaces技術來進行資源隔離,如PID Namespace隔離程序,Mount Namespace隔離檔案系統,Network Namespace隔離網路等。一個Network Namespace提供了一份獨立的網路環境,包括網絡卡、路由、Iptable規則等都與其他的Network Namespace隔離。一個Docker容器一般會分配一個獨立的Network Namespace。但如果啟動容器的時候使用host模式,那麼這個容器將不會獲得一個獨立的Network Namespace,而是和宿主機共用一個Network Namespace。容器將不會虛擬出自己的網絡卡,配置自己的

IP等,而是使用宿主機的IP和埠,Docker Container可以和宿主機一樣,使用宿主機的eth0,實現和外界的通訊。換言之,Docker Container的IP地址即為宿主機eth0的IP地址。

例如,我們在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網路模式。

可以說,Docker Container的網路模式中,host模式是bridge橋接模式很好的補充。採用host模式的Docker Container,可以直接使用宿主機的IP地址與外界進行通訊,若宿主機的eth0是一個公有IP,那麼容器也擁有這個公有IP。同時容器內服務的埠也可以使用宿主機的埠,無需額外進行NAT轉換。當然,有這樣的方便,肯定會損失部分其他的特性,最明顯的是Docker Container網路環境隔離性的弱化,即容器不再擁有隔離、獨立的網路棧。另外,使用host模式的Docker Container雖然可以讓容器內部的服務和傳統情況無差別、無改造的使用,但是由於網路隔離性的弱化,該容器會與宿主機共享競爭網路棧的使用;另外,容器內部將不再擁有所有的埠資源,原因是部分埠資源已經被宿主機本身的服務佔用,還有部分埠已經用以bridge網路模式容器的埠對映。

1.2 bridge模式

bridge模式是Docker預設的網路設定,此模式會為每一個容器分配Network Namespace、設定IP等,並將一個主機上的Docker容器連線到一個虛擬網橋上。

Brdige橋接模式為Docker Container建立獨立的網路棧,保證容器內的程序組使用獨立的網路環境,實現容器間、容器與宿主機之間的網路棧隔離。另外,Docker通過宿主機上的網橋(docker0)來連通容器內部的網路棧與宿主機的網路棧,實現容器與宿主機乃至外界的網路通訊。

Docker Containerbridge橋接模式可以參考下圖:

建立容器測試:(由於是預設設定,這裡沒指定網路--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