1. 程式人生 > 實用技巧 >Docker容器技術之容器虛擬化網路(4)

Docker容器技術之容器虛擬化網路(4)

目錄

1. 虛擬化網路


Network Namespace 是 Linux 核心提供的功能,是實現網路虛擬化的重要功能,它能建立多個隔離的網路空間,它們有獨自網路棧資訊。不管是虛擬機器還是容器,執行的時候彷彿自己都在獨立的網路中。而且不同Network Namespace的資源相互不可見,彼此之間無法通訊。

假如我們的物理機有4塊物理網絡卡,我們要建立4個名稱空間,而這些裝置是可以單獨關聯至某個單獨的名稱空間使用的

如上圖所示,把第一塊網絡卡分配給第一個名稱空間,第二塊分給第二個名稱空間,第三塊分給第三個名稱空間,第四塊分給第四個名稱空間。此時其它名稱空間都是看不見當前所在名稱空間的,因為一個裝置只能屬於一個名稱空間。

這種方式使得每一個名稱空間都能配置IP地址,並且與外部網路直接通訊,因為它們使用的是物理網絡卡。

但如果我們所擁有的名稱空間數量超過物理網絡卡數量呢?此時我們可以使用虛擬網絡卡裝置,用純軟體的方式來模擬一組裝置來使用。Linux核心級支援2種級別裝置的模擬,一種是二層裝置,一種是三層裝置。

Linux核心模擬的二層裝置,每個網路介面裝置是成對出現的,可以模擬為一根網線的兩端,其中一端模擬主機的虛擬網絡卡,另一端模擬虛擬交換機,就相當於讓一個主機連到一個交換機上去。Linux核心原生支援二層虛擬網橋裝置,即用軟體虛擬交換機的功能。如下圖所示:

那麼此時如果再有一個名稱空間,它有建立了一對虛擬網絡卡,一端連線名稱空間,一端連線虛擬交換機,此時就相當於兩個名稱空間連線到了同一個交換機網路中,此時如果兩個名稱空間的網絡卡地址配置在同一網段,那麼很顯然他們之間是可以互相通訊的。如下圖所示:


從網路通訊的物理裝置到網絡卡都是用純軟體的方式來實現,這種實現方式就叫做虛擬化網路。

2. 單節點容器間通訊


如果在同一個物理機上的兩個容器想通訊,我們的辦法就是在這臺主機上建立一個虛擬交換機,而後讓兩個容器各自用純軟體的方式建立一對虛擬網絡卡,一半在容器上,一半在虛擬交換機上,從而實現通訊。如下圖所示:

這就是單節點上兩個容器間的通訊方式。單節點上兩個容器之間的通訊也有一些複雜情況,比如我們期望構建的容器要跨交換機通訊呢?


我們做兩個虛擬交換機,兩個交換機上各自連線不同的容器,如上圖所示,此時如果要C1和C3通訊又該如何實現呢?其實我們可以通過名稱空間建立一對網絡卡,一端連SW1,另一端連SW2,這樣一來兩個交換機就連起來了,照理說這樣一來C1和C3這兩個處於不同交換機的容器就可以實現通訊了,但是這樣一來又存在另一個問題,那就是如果C1和C3在不同網路呢?如果不在同一網路我們就必須要通過路由轉發才能使其通訊,也就是我們得在兩臺交換機之間加一個路由器,其實Linux核心本身就是支援路由轉發的,只需要我們將路由轉發功能開啟即可。此時我們可以再啟動一個容器,這個容器裡面就跑一個核心,並將其轉發功能開啟,這樣一來就模擬了一臺路由器,通過這臺路由器來實現路由轉發。

3. 不同節點容器間通訊



如上圖所示,此時如果C1要與C5進行通訊又該如何實現呢?如果我們採用橋接的方式,很容易產生廣播風暴,因此,在大規模的虛擬機器或容器的場景中,使用橋接的方式無疑是自取滅亡,所以我們不應該使用橋接的方式來實現通訊。

如果一來,我們既不能橋接,又需要與外部來實現通訊,那就只能使用NAT技術了。通過DNAT將容器的埠暴露到宿主機上,通過訪問宿主機的埠來實現訪問容器內部的目的,而在請求端我們需要做SNAT將資料包通過宿主機的真實網絡卡轉發出去。但這樣做的話,因為要進行兩次NAT轉換,所以效率會比較低。

此時我們可以採用一種叫做Overlay Network(疊加網路)的技術來實現不同節點間容器的相互通訊功能。

Overlay Network會將報文進行隧道轉發,也就是在報文發出去之前要為其新增一個IP首部,也就是上圖的1.1和1.2這部分,這裡的1.1是源,1.2是目標,當宿主機2收到報文後解封裝發現要找的目標容器是C2,於是把包轉發給C2。

4. docker容器網路


Docker在安裝後自動提供3種網路,可以使用docker network ls命令檢視

[root@node02 ~]# docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
1b093120d2bb        bridge              bridge              local
7362e537cc7b        host                host                local
910a5741bb77        none                null                local

Docker使用Linux橋接,在宿主機虛擬一個Docker容器網橋(docker0),Docker啟動一個容器時會根據Docker網橋的網段分配給容器一個IP地址,稱為Container-IP,同時Docker網橋是每個容器的預設閘道器。因為在同一宿主機內的容器都接入同一個網橋,這樣容器之間就能夠通過容器的Container-IP直接通訊。

5. docker的4種網路模式


網路模式 配置 說明
host --network host 容器和宿主機共享Network namespace
container --network container:NAME_OR_ID 容器和另外一個容器共享Network namespace
none --network none 容器有獨立的Network namespace,
但並沒有對其進行任何網路設定,
如分配veth pair 和網橋連線,配置IP等
bridge --network bridge 預設模式

5.1 bridge模式

當Docker程序啟動時,會在主機上建立一個名為docker0的虛擬網橋,此主機上啟動的Docker容器會連線到這個虛擬網橋上。虛擬網橋的工作方式和物理交換機類似,這樣主機上的所有容器就通過交換機連在了一個二層網路中。

docker0子網中分配一個IP給容器使用,並設定docker0的IP地址為容器的預設閘道器。在主機上建立一對虛擬網絡卡veth pair裝置,Docker將veth pair裝置的一端放在新建立的容器中,並命名為eth0(容器的網絡卡),另一端放在主機中,以vethxxx這樣類似的名字命名,並將這個網路裝置加入到docker0網橋中。可以通過brctl show命令檢視。

bridge模式是docker的預設網路模式,不寫--network引數,就是bridge模式。使用docker run -p時,docker實際是在iptables做了DNAT規則,實現埠轉發功能。可以使用iptables -t nat -vnL檢視。

bridge模式如下圖所示:

演示

[root@node02 ~]# docker run -d nginx
Unable to find image 'nginx:latest' locally
latest: Pulling from library/nginx
bf5952930446: Pull complete 
cb9a6de05e5a: Pull complete 
9513ea0afb93: Pull complete 
b49ea07d2e93: Pull complete 
a5e4a503d449: Pull complete 
Digest: sha256:b0ad43f7ee5edbc0effbc14645ae7055e21bc1973aee5150745632a24a752661
Status: Downloaded newer image for nginx:latest
13452140347d7365d07d87e563c5ef9dc13e4a1c679a95006bb357bed6692e94
[root@node02 ~]# docker ps
CONTAINER ID        IMAGE                     COMMAND                  CREATED             STATUS              PORTS               NAMES
13452140347d        nginx                     "/docker-entrypoint.…"   11 seconds ago      Up 10 seconds       80/tcp              lucid_shamir
68bf73ee67f9        dragonyear22/nginx:v0.2   "/bin/httpd -f -h /l…"   13 hours ago        Up 13 hours                             naughty_bohr
48379c69153b        busybox                   "sh"                     14 hours ago        Up 14 hours                             eager_goldwasser
[root@node02 ~]# docker inspect 13452140347d
...........
                    "Gateway": "172.17.0.1",
                    "IPAddress": "172.17.0.4",
...........

[root@node02 ~]# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    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
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 00:0c:29:dd:54:b6 brd ff:ff:ff:ff:ff:ff
    inet 192.168.159.161/24 brd 192.168.159.255 scope global noprefixroute dynamic ens33
       valid_lft 1791sec preferred_lft 1791sec
    inet6 fe80::2d92:6f17:5128:80f9/64 scope link noprefixroute 
       valid_lft forever preferred_lft forever
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:06:c5:29:42 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:6ff:fec5:2942/64 scope link 
       valid_lft forever preferred_lft forever
5: veth1c4e358@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default 
    link/ether 56:17:7a:17:60:b4 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet6 fe80::5417:7aff:fe17:60b4/64 scope link 
       valid_lft forever preferred_lft forever
11: veth710559c@if10: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default 
    link/ether a2:f8:89:83:4b:55 brd ff:ff:ff:ff:ff:ff link-netnsid 1
    inet6 fe80::a0f8:89ff:fe83:4b55/64 scope link 
       valid_lft forever preferred_lft forever
13: veth664302c@if12: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default 
    link/ether 16:b2:4d:f7:f6:28 brd ff:ff:ff:ff:ff:ff link-netnsid 2
    inet6 fe80::14b2:4dff:fef7:f628/64 scope link 
       valid_lft forever preferred_lft forever

[root@node02 ~]# ping 172.17.0.4
PING 172.17.0.4 (172.17.0.4) 56(84) bytes of data.
64 bytes from 172.17.0.4: icmp_seq=1 ttl=64 time=0.111 ms
64 bytes from 172.17.0.4: icmp_seq=2 ttl=64 time=0.083 ms
64 bytes from 172.17.0.4: icmp_seq=3 ttl=64 time=0.037 ms
64 bytes from 172.17.0.4: icmp_seq=4 ttl=64 time=0.039 ms
^C
--- 172.17.0.4 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 2999ms
rtt min/avg/max/mdev = 0.037/0.067/0.111/0.032 ms

Docker網橋是宿主機虛擬出來的,並不是真實存在的網路裝置,外部網路是無法定址到的,這也意味著外部網路無法通過直接Container-IP訪問到容器。如果容器希望外部訪問能夠訪問到,可以通過對映容器埠到宿主主機(埠對映),即docker run建立容器時候通過 -p 或 -P 引數來啟用,訪問容器的時候就通過[宿主機IP]:[容器埠]訪問容器。

使用docker run -p時,docker實際是在iptables做了DNAT規則,實現埠轉發功能。可以使用iptables -t nat -vnL檢視。

[root@node02 ~]# docker run -d -p 80 --name t2 nginx
064251a9b7a3b84e532d47dd993405efed8bc2ea28270937a9b311f1a70ab521
[root@node02 ~]# iptables -t nat -vnL |tail -1
    0     0 DNAT       tcp  --  !docker0 *       0.0.0.0/0            0.0.0.0/0            tcp dpt:32768 to:172.17.0.5:80
[root@node02 ~]# ss -antl |grep 32768
LISTEN     0      128       [::]:32768                 [::]:*

5.2 container模式

這個模式指定新建立的容器和已經存在的一個容器共享一個 Network Namespace,而不是和宿主機共享。新建立的容器不會建立自己的網絡卡,配置自己的 IP,而是和一個指定的容器共享 IP、埠範圍等。同樣,兩個容器除了網路方面,其他的如檔案系統、程序列表等還是隔離的。兩個容器的程序可以通過 lo 網絡卡裝置通訊。

container模式如下圖所示:

演示
在一個終端,使用bridge網路模式啟動容器b1

[root@node02 ~]# docker run --name b1 -it --rm busybox:latest
/ # ifconfig
eth0      Link encap:Ethernet  HWaddr 02:42:AC:11:00:06  
          inet addr:172.17.0.6  Bcast:172.17.255.255  Mask:255.255.0.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:6 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:516 (516.0 B)  TX bytes:0 (0.0 B)

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)
/ # echo "hello world b1" > /tmp/index.html
/ # httpd -h /tmp/       在b1上啟動httpd服務
/ # 
/ # netstat -nutl
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       
tcp        0      0 :::80                   :::*                    LISTEN

在另一個終端使用Container 網路模式建立容器b2

[root@node02 ~]# docker run --name b2 -it --network container:b1 --rm busybox:latest
/ # ifconfig -a
eth0      Link encap:Ethernet  HWaddr 02:42:AC:11:00:06  
          inet addr:172.17.0.6  Bcast:172.17.255.255  Mask:255.255.0.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:8 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:656 (656.0 B)  TX bytes:0 (0.0 B)

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)
/ # wget -O - -q 127.0.0.1      b1啟動的httpd服務,在b2上直接訪問
hello world b1
/ # ls /tmp/      但是檔案系統並不共享,只共享網路

5.3 host模式

如果啟動容器的時候使用host模式,那麼這個容器將不會獲得一個獨立的Network Namespace,而是和宿主機共用一個Network Namespace。容器將不會虛擬出自己的網絡卡,配置自己的IP等,而是使用宿主機的IP和埠。但是,容器的其他方面,如檔案系統、程序列表等還是和宿主機隔離的。

使用host模式的容器可以直接使用宿主機的IP地址與外界通訊,容器內部的服務埠也可以使用宿主機的埠,不需要進行NAT,host最大的優勢就是網路效能比較好,但是docker host上已經使用的埠就不能再用了,網路的隔離性不好。

Host模式如下圖所示:

演示

[root@node02 ~]# docker run --name b2 -it --network host --rm busybox:latest
/ # ifconfig -a
docker0   Link encap:Ethernet  HWaddr 02:42:06:C5:29:42  
          inet addr:172.17.0.1  Bcast:172.17.255.255  Mask:255.255.0.0
          inet6 addr: fe80::42:6ff:fec5:2942/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:14 errors:0 dropped:0 overruns:0 frame:0
          TX packets:19 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:974 (974.0 B)  TX bytes:1484 (1.4 KiB)

ens33     Link encap:Ethernet  HWaddr 00:0C:29:DD:54:B6  
          inet addr:192.168.159.161  Bcast:192.168.159.255  Mask:255.255.255.0
          inet6 addr: fe80::2d92:6f17:5128:80f9/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:117652 errors:0 dropped:0 overruns:0 frame:0
          TX packets:14693 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:170532211 (162.6 MiB)  TX bytes:1278297 (1.2 MiB)

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:64 errors:0 dropped:0 overruns:0 frame:0
          TX packets:64 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:5568 (5.4 KiB)  TX bytes:5568 (5.4 KiB)

veth1c4e358 Link encap:Ethernet  HWaddr 56:17:7A:17:60:B4  
          inet6 addr: fe80::5417:7aff:fe17:60b4/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:15 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:0 (0.0 B)  TX bytes:1186 (1.1 KiB)

veth3b5c82d Link encap:Ethernet  HWaddr 1A:B1:44:EB:27:D9  
          inet6 addr: fe80::18b1:44ff:feeb:27d9/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:0 (0.0 B)  TX bytes:656 (656.0 B)

veth664302c Link encap:Ethernet  HWaddr 16:B2:4D:F7:F6:28  
          inet6 addr: fe80::14b2:4dff:fef7:f628/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:6 errors:0 dropped:0 overruns:0 frame:0
          TX packets:14 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:476 (476.0 B)  TX bytes:1132 (1.1 KiB)

veth710559c Link encap:Ethernet  HWaddr A2:F8:89:83:4B:55  
          inet6 addr: fe80::a0f8:89ff:fe83:4b55/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:8 errors:0 dropped:0 overruns:0 frame:0
          TX packets:17 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:694 (694.0 B)  TX bytes:1260 (1.2 KiB)

通過檢視IP,可以發現和宿主機是一樣的

5.4 none模式

使用none模式,Docker容器擁有自己的Network Namespace,但是,並不為Docker容器進行任何網路配置。也就是說,這個Docker容器沒有網絡卡、IP、路由等資訊。需要我們自己為Docker容器新增網絡卡、配置IP等。

這種網路模式下容器只有lo迴環網路,沒有其他網絡卡。none模式可以在容器建立時通過--network none來指定。這種型別的網路沒有辦法聯網,封閉的網路能很好的保證容器的安全性。

應用場景:

  • 啟動一個容器處理資料,比如轉換資料格式
  • 一些後臺的計算和處理任務

none模式如下圖所示:

演示

[root@node02 ~]# docker run --name b1 -it --network none --rm busybox:latest 
/ # ifconfig
lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

/ # route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
docker network inspect bridge   #檢視bridge網路的詳細配置