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網路的詳細配置