Docker的原生網絡與自定義網絡、容器的通信

實驗環境:
server1 172.25.254.1
server2 172.25.254.2

Docker的三種原生網絡

簡介

docker 在安裝好時會有 3 種網絡模型:bridge(默認),host,none

[root@server1 ~]# docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
672f5543b2d3        bridge              bridge              local
e181fb352854        host                host                local
7261b2daa122        none                null                local
  1. bridge模式:
    bridge模式是Docker默認的網絡設置,此模式會爲每一個容器分配Network Namespace、設置IP等,並將並將一個主機上的Docker容器連接到一個虛擬網橋上。當Docker server啓動時,會在主機上創建一個名爲docker0的虛擬網橋,此主機上啓動的Docker容器會連接到這個虛擬網橋上。虛擬網橋的工作方式和物理交換機類似,這樣主機上的所有容器就通過交換機連在了一個二層網絡中。接下來就要爲容器分配IP了,Docker會從RFC1918所定義的私有IP網段中,選擇一個和宿主機不同的IP地址和子網分配給docker0,連接到docker0的容器就從這個子網中選擇一個未佔用的IP使用。如一般Docker會使用172.17.0.0/16這個網段,並將172.17.42.1/16分配給docker0網橋(在主機上使用ifconfig命令是可以看到docker0的,可以認爲它是網橋的管理端口,在宿主機上作爲一塊虛擬網卡使用)。

  2. host模式:
    如果啓動容器的時候使用host模式,那麼這個容器將不會獲得一個獨立的Network Namespace,而是和宿主機共用一個Network Namespace,共享網絡。容器將不會虛擬出自己的網卡,配置自己的IP等,而是使用宿主機的IP和端口。

  3. none模式:
    在none模式下,Docker容器擁有自己的Network Namespace,但是,並不爲Docker容器進行任何網絡配置。也就是說,這個Docker容器沒有網卡、IP、路由等信息。需要我們自己爲Docker容器添加網卡、配置IP等。這種模式主要用於數據庫等重要的服務。就是通過網絡無法訪問到。

bridge模式

bridge模式是Docker默認的網絡設置,當Docker server啓動時,會在主機上創建一個名爲docker0的虛擬網橋.

3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:1a:46:fc:82 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:1aff:fe46:fc82/64 scope link 
       valid_lft forever preferred_lft forever

[root@server1 ~]# docker run -d --name nginx nginx				#開啓這個容器
7dbb267f5ecd7e0e75e2b2dd7372bd15fd8c90cbd76764ea1ccc853dc4a36d68
[root@server1 ~]# brctl show
bridge name	bridge id		STP enabled	interfaces
docker0		8000.02421a46fc82	no		veth27dc0b7			# 這個容器橋接在docker0上。
[root@server1 ~]# docker inspect nginx
                    "Gateway": "172.17.0.1",
                    "IPAddress": "172.17.0.2",				#開啓容器後它自動獲取了這個ip地址
                    
[root@server1 ~]# curl 172.17.0.2			#嘗試訪問
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
	# 是可以訪問到的,這裏nginx開放的是80端口,它開在容器中,在我們的主機上時沒有開放的:
[root@server1 ~]# netstat -tnlp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      2991/sshd           
tcp        0      0 127.0.0.1:25            0.0.0.0:*               LISTEN      3101/master         
tcp6       0      0 :::22                   :::*                    LISTEN      2991/sshd           
tcp6       0      0 ::1:25                  :::*                    LISTEN      3101/master	

橋接模式下容器沒有公共 ip,只有宿主機可以訪問,外部主機不可見,容器要訪問外網必須通過宿主機的 NAT 規則,所以我們如果要想通過訪問本機的80端口去訪問容器,就需要做端口映射,將容器的80映射到server1主機上的80端口才行。而且橋接模式下容器是沒有對外的ip的,在企業中我們的server1主機ip應該是能進入到公網的IP,所以我們打開ip_forward的功能,讓容器可以通過server1訪問到外網。

物理主機上開啓內核路由功能,讓server1可以上網。
[root@rhel7host network-scripts]# sysctl -a |grep ip_forward
net.ipv4.ip_forward = 1
net.ipv4.ip_forward_use_pmtu = 0

這樣在容器開啓的時候,就會生成網絡對,相當於一根網線,連接容器與docker0,docker0在通過內核路由功能轉發到我們的真實網卡上面,然後在出去,訪問到公網。

Host模式

host模式下容器會和宿主機共享網絡。

[root@server1 ~]# docker run -it --name vm1 --network host ubuntu
root@server1:/# 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: ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 52:54:00:e4:9b:44 brd ff:ff:ff:ff:ff:ff
    inet 172.25.254.1/24 brd 172.25.254.255 scope global ens3
       valid_lft forever preferred_lft forever
    inet6 fe80::5054:ff:fee4:9b44/64 scope link 
       valid_lft forever preferred_lft forever
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default 
    link/ether 02:42:1a:46:fc:82 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:1aff:fe46:fc82/64 scope link 
       valid_lft forever preferred_lft forever			#我們可以看到的信息和宿主機是一樣的
root@server1:/# exit			# 退出容器
[root@server1 ~]# 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: ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 52:54:00:e4:9b:44 brd ff:ff:ff:ff:ff:ff
    inet 172.25.254.1/24 brd 172.25.254.255 scope global ens3
       valid_lft forever preferred_lft forever
    inet6 fe80::5054:ff:fee4:9b44/64 scope link 
       valid_lft forever preferred_lft forever
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default 
    link/ether 02:42:1a:46:fc:82 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:1aff:fe46:fc82/64 scope link 
       valid_lft forever preferred_lft forever

這樣我們就可以直接通過宿主機的網絡來和外界通信, 但是如果開啓了成百上千個容器的話,就容易產生衝突,比如端口的衝突,也缺失了容器的隔離性。

none模式

[root@server1 ~]# docker run -it --name vm2 --network none ubuntu
root@cac3293e4f47:/# 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		#只有一個迴環接口
root@cac3293e4f47:/#	

自定義Docker網絡

三種網絡驅動:1.bridge(自定義的bridge自帶DNS解析功能。直接使用 vm1 vm2 這種容器名去訪問,不一定要使用 ip。)
2.overlay
3.macvlan,
後面兩種用於跨主機的網絡,也就是集羣等

bridge網絡驅動

# 創建自定義橋接
[root@server1 ~]# docker network create -d bridge my_net1
043a6d4b3291e5ddb67b1814f22529f6d2242ed2577dd8cc0775f3e96cb1a4a3

# 查看,可以看出我們的自定義網絡加進去了
[root@server1 ~]# docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
672f5543b2d3        bridge              bridge              local
e181fb352854        host                host                local
043a6d4b3291        my_net1             bridge              local
7261b2daa122        none                null                local

# 用自定義網絡運行
[root@server1 ~]# docker run -it --name vm1 --network my_net1 ubuntu
WARNING: IPv4 forwarding is disabled. Networking will not work.
root@ce31d9f09ee8:/# ip addr
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
11: eth0@if12: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:ac:12:00:02 brd ff:ff:ff:ff:ff:ff
    inet 172.18.0.2/16 brd 172.18.255.255 scope global eth0
       valid_lft forever preferred_lft forever
## 發現網段變成了172.18網段了,說明自定義網絡的時候網段是遞增的,和開啓容器時ip遞增一樣。

## 我們還可以自定義網關和網段:
[root@server1 ~]# docker network create -d bridge --subnet 172.22.0.0/24 --gateway 172.22.0.1 my_net2
3779005fd53e06b5d9238c1568381f5e362f210977c7a785581f799bd4c29ae4
[root@server1 ~]# docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
672f5543b2d3        bridge              bridge              local
e181fb352854        host                host                local
043a6d4b3291        my_net1             bridge              local
3779005fd53e        my_net2             bridge              local
7261b2daa122        none                null                local
[root@server1 ~]# docker run -it --name vm2 --network my_net2 ubuntu
WARNING: IPv4 forwarding is disabled. Networking will not work.
root@93c5a4ebb2bf:/# 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
14: eth0@if15: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:ac:16:00:02 brd ff:ff:ff:ff:ff:ff
    inet 172.22.0.2/24 brd 172.22.0.255 scope global eth0		#確實變成了172.22網段
       valid_lft forever preferred_lft forever

##自定義IP
[root@server1 ~]# docker run -it --name vm3 --network my_net2  --ip 172.22.0.100 ubuntu
root@221518b54527:/# 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
18: eth0@if19: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:ac:16:00:64 brd ff:ff:ff:ff:ff:ff
    inet 172.22.0.100/24 brd 172.22.0.255 scope global eth0
       valid_lft forever preferred_lft forever
root@221518b54527:/# 
root@221518b54527:/# ping 172.22.0.2				#可以ping同剛纔那個VM2容器,因爲橋接在同一網絡上
PING 172.22.0.2 (172.22.0.2) 56(84) bytes of data.
64 bytes from 172.22.0.2: icmp_seq=1 ttl=64 time=0.051 ms
。。。

[root@server1 ~]# brctl show
bridge name	bridge id		STP enabled	interfaces
br-043a6d4b3291		8000.02423f6daeee	no			## my_net1只橋接了一個網卡
br-3779005fd53e		8000.02425938ed4e	no		veth688058b
							veth834b548			## my_net2橋接了兩個
docker0		8000.02421a46fc82	no		
# 橋接在不同網橋上的容器,彼此之間不能通信:
[root@server1 ~]# iptables -S
-A DOCKER-ISOLATION-STAGE-2 -o br-3779005fd53e -j DROP
-A DOCKER-ISOLATION-STAGE-2 -o br-043a6d4b3291 -j DROP
-A DOCKER-ISOLATION-STAGE-2 -o docker0 -j DROP
可以看到三個網橋對互相之間的數據都是DROP
都是互相丟棄的所以不同網橋之間是無法通信的。

那我們如何讓不同網橋之間進行通信:

## 給VM1添加新網絡接口並橋接在my_net2上
[root@server1 ~]# docker network connect my_net2 vm1
[root@server1 ~]# docker attach vm1
root@ce31d9f09ee8:/# 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
22: eth0@if23: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:ac:12:00:02 brd ff:ff:ff:ff:ff:ff
    inet 172.18.0.2/16 brd 172.18.255.255 scope global eth0
       valid_lft forever preferred_lft forever
24: eth1@if25: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:ac:16:00:03 brd ff:ff:ff:ff:ff:ff
    inet 172.22.0.3/24 brd 172.22.0.255 scope global eth1			# 多了172.22的網卡
       valid_lft forever preferred_lft forever
       
root@ce31d9f09ee8:/# ping vm1
PING vm1 (172.18.0.2) 56(84) bytes of data.
64 bytes from ce31d9f09ee8 (172.18.0.2): icmp_seq=1 ttl=64 time=0.017 ms

root@ce31d9f09ee8:/# ping vm2
PING vm2 (172.22.0.2) 56(84) bytes of data.
64 bytes from vm2.my_net2 (172.22.0.2): icmp_seq=1 ttl=64 time=0.050 ms

root@ce31d9f09ee8:/# ping vm3
PING vm3 (172.22.0.100) 56(84) bytes of data.
64 bytes from vm3.my_net2 (172.22.0.100): icmp_seq=1 ttl=64 time=0.051 ms

## 這次就都能ping通了
root@ce31d9f09ee8:/# cat /etc/host
cat: /etc/host: No such file or directory
root@ce31d9f09ee8:/# cat /etc/hosts
127.0.0.1	localhost
::1	localhost ip6-localhost ip6-loopback
fe00::0	ip6-localnet
ff00::0	ip6-mcastprefix
ff02::1	ip6-allnodes
ff02::2	ip6-allrouters
172.18.0.2	ce31d9f09ee8
172.22.0.3	ce31d9f09ee8		##這就是dns的自動解析

overlay 和macvlan在我們學習了容器之間的通信之後在進行學習。

容器之間的通信

  1. 使用內嵌的 DNS server.通過容器名去自動解析容器的IP地址。
[root@server1 ~]# docker run -it --name vm1 --network my_net1 ubuntu
root@eaa57a7f3c63:/#
[root@server1 ~]# docker run -it --name vm2 --network my_net1 ubuntu
root@6b12261d3b90:/# ping vm1
PING vm1 (172.18.0.2) 56(84) bytes of data.
64 bytes from vm1.my_net1 (172.18.0.2): icmp_seq=1 ttl=64 time=0.053 ms
  1. 使用joined 方式(類似於默認的 host 模式,容器之間共享網絡)
[root@server1 ~]# docker run -it --name vm1  ubuntu
root@5ac2f9bb5edf:/# ip a
1: lo:
34: eth0@if35: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever
root@5ac2f9bb5edf:/
# [root@server1 ~]# docker run -it --name vm2  --network container:vm1 ubuntu
														# 使用container的方式和vm1共享網絡
root@5ac2f9bb5edf:/# ip a
1: lo: 
34: eth0@if35: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever		## 發現兩個容器的網絡信息是一樣的

這樣做的用處在於,兩個容器之間可以使用 localhost(迴環接口)進行快速通信,適用於 web 服務器和應用服務器。

  1. 使用–link 來鏈接兩個容器
[root@server1 ~]# docker run -d nginx		# 不加--name時系統會自動分配一個容器名
doa8cc285f34e968bdc01e96d18591befabbd7829af4137d34e014fbcd35076a4f
c	[root@server1 ~]# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
a8cc285f34e9        nginx               "nginx -g 'daemon of…"   3 seconds ago       Up 1 second         80/tcp              unruffled_montalcini		#這就是分配的容器名
[root@server1 ~]# docker run -it --name vm1 --link unruffled_montalcini:web ubuntu
		# 使用--link連接這個容器,併爲它起個別名web,因爲系統給的太難記了
root@b83a76c4062d:/# ping web
PING web (172.17.0.2) 56(84) bytes of data.
64 bytes from web (172.17.0.2): icmp_seq=1 ttl=64 time=0.065 ms
# 發現是可以通信的,這時因爲我們的容器做了兩件事:

## 第一,寫了解析
root@b83a76c4062d:/# cat /etc/hosts
127.0.0.1	localhost
::1	localhost ip6-localhost ip6-loopback
fe00::0	ip6-localnet
ff00::0	ip6-mcastprefix
ff02::1	ip6-allnodes
ff02::2	ip6-allrouters
172.17.0.2	web a8cc285f34e9 unruffled_montalcini		## 這裏
172.17.0.3	b83a76c4062d

## 第二設置了環境變量
root@b83a76c4062d:/# env
。。。
WEB_NAME=/vm1/web
WEB_PORT_80_TCP_PROTO=tcp
WEB_PORT_80_TCP_ADDR=172.17.0.2

[root@server1 ~]# docker stop unruffled_montalcini 		#關閉剛開始的nginx容器
unruffled_montalcini
[root@server1 ~]# docker run -d nginx		#重新開一個
a0fde5256cedce1ca5ed106b1f861518526df2243b3b52dd6214c7aefe057945
[root@server1 ~]# docker inspect  tender_blackburn
              		 "Gateway": "172.17.0.1",
                    "IPAddress": "172.17.0.2",  #它的ip地址也是172.17.0.2
[root@server1 ~]# docker start unruffled_montalcini 		#再打開第一個nginx容器
unruffled_montalcini
[root@server1 ~]# docker inspect unruffled_montalcini
                   "Gateway": "172.17.0.1",
                    "IPAddress": "172.17.0.4",	# 它的ip地址變成了0.4了
說明容器關閉後其獲得的ip地址會被釋放。

root@b83a76c4062d:/# cat /etc/hosts 
127.0.0.1	localhost
::1	localhost ip6-localhost ip6-loopback
fe00::0	ip6-localnet
ff00::0	ip6-mcastprefix
ff02::1	ip6-allnodes
ff02::2	ip6-allrouters
172.17.0.4	web a8cc285f34e9 unruffled_montalcini
VM1上的本地解析也變了。

容器訪問外網

‘容器訪問外網是通過 iptables 的 SNAT 實現的,容器和 docker0 是橋接的方式,docker0 是
容器的網關,到達 docker0 後,通過 linux 內核的路由功能(ip_forward),然後防火牆會做
僞裝,也就是 SNAT,然後通過物理網卡接口到外網’

[root@server1 ~]# iptables -t nat -S
-P PREROUTING ACCEPT
-P INPUT ACCEPT
-P OUTPUT ACCEPT
-P POSTROUTING ACCEPT
-N DOCKER
-A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER
-A OUTPUT ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER
-A POSTROUTING -s 172.22.0.0/24 ! -o br-3779005fd53e -j MASQUERADE
-A POSTROUTING -s 172.18.0.0/16 ! -o br-043a6d4b3291 -j MASQUERADE		#爲容器的ip做了地址僞裝
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
-A DOCKER -i br-3779005fd53e -j RETURN
-A DOCKER -i br-043a6d4b3291 -j RETURN
-A DOCKER -i docker0 -j RETURN

在物理機開啓ip_forward讓server1可以上網;

[root@rhel7host ~]# iptables -t nat -I POSTROUTING -s 172.25.254.0/24 -j MASQUERADE 
[root@rhel7host ~]# sysctl -a |grep ip_forward
net.ipv4.ip_forward = 1
net.ipv4.ip_forward_use_pmtu = 0


# 測試:
[root@server1 ~]# ping baidu.com
PING baidu.com (220.181.38.148) 56(84) bytes of data.
64 bytes from 220.181.38.148 (220.181.38.148): icmp_seq=1 ttl=46 time=29.5 ms
## server1可以上網了。

##然後我們在進入ubuntu容器測試:
[root@server1 ~]# docker start vm1 
vm1
[root@server1 ~]# docker attach vm1
root@b83a76c4062d:/# ping baidu.com
PING baidu.com (220.181.38.148) 56(84) bytes of data.
64 bytes from 220.181.38.148: icmp_seq=1 ttl=45 time=27.7 ms		
## 這樣容器就也可以上網了。

外網訪問容器

[root@server1 ~]# docker run -d --name vm1 -p 80:80 nginx
07edbf644899bdeb49625dbc4a66265295e076965cf1e4316462333afa3d58c3
## 做端口映射(冒號後的是容器內部的端口)

[root@server1 ~]# docker port vm1		##查看容器端口映射情況
80/tcp -> 0.0.0.0:80			# 容器的80端口對server1主機的80端口

[root@server1 ~]# iptables -t nat -S ##查看防火牆策略
-A DOCKER ! -i docker0 -p tcp -m tcp --dport 80 -j DNAT --to-destination
172.17.0.3:80
說明訪問的流程時這樣的
外部主機訪問時--> 訪問到宿主機的 eth0(172.25.254.1:80)--> 然後火牆做了DNAT轉換 --> 訪問172.17.0.3(容器地
址)

瀏覽器訪問:
在這裏插入圖片描述
就可以通過外網訪問容器了。

[root@server1 ~]# netstat -tnlp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      2991/sshd           
tcp        0      0 127.0.0.1:25            0.0.0.0:*               LISTEN      3101/master         
tcp6       0      0 :::80                   :::*                    LISTEN      19911/docker-proxy 

docker-proxy 打開了80端口,我們每打開一個容器,都會開啓一個docker-proxy 進程

[root@server1 ~]# docker run -d --name vm2 -p 8080:80 nginx
dd444d0f4776c60b48001c53afca504e7aa72904e4cdccc6390571a08ecb6c22
[root@server1 ~]# ps ax |grep docker-proxy
19911 ?        Sl     0:00 /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 80 -container-ip 172.17.0.2 -container-port 80				## vm1
20440 ?        Sl     0:00 /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 8080 -container-ip 172.17.0.3 -container-port 80			## vm2
20507 pts/1    S+     0:00 grep --color=auto docker-proxy

這裏就需要注意:

  • 宿主機訪問本機容器使用 iptables 的 DNAT,外部主機訪問容器或者容器之間訪問是
    docker-proxy 實現的。

流程:

‘外部主機 --> 宿主機 ens3(我這裏是ens3) --> docker-proxy(外部訪問容器時通過 docker-proxy 處理數據
包,不是防火牆) --> docker0(172.17.0.1) --> 容器’

跨主機容器網絡

解決方案:

  1. docker 原生:overlay 和 macvlan
  2. 第三方:flannel、weave、calico

這些解決方案怎麼和 docker 集成在一起?
是通過 libnetwork ,docker 容器網絡庫,其中 CNM(Container Network Model)是它的核心,它對容器網絡進行了抽象。

CNM 由 3 部分組成:

  1. Sandbox:容器的網絡棧。包含容器的 interface、路由表和 DNS 等
  2. Endpoint:作用是將 Sandbox 接入 Network。(生成veth pair,虛擬網絡對,相當於網線的兩端連接沙盒和network)
  3. Network:包含一組 Endpoint,同一 Network 的 Endpoint 可以直接通信

我們主要通過macvlan進行演示。

macvlan

macvlan 網絡方案實現使用的是 linux 內核虛擬化技術,無需橋接,直接使用物理接口,
性能極好

  • 清理掉之前的容器,刪除自定義網絡
[root@server1 ~]# docker network prune 
WARNING! This will remove all networks not used by at least one container.
Are you sure you want to continue? [y/N] y
Deleted Networks:
my_net1
my_net2

[root@server1 ~]# docker rm -f vm1
^[[Avm1
[root@server1 ~]# docker rm -f vm2
vm2

給兩臺主機都添加上一塊新的虛擬網卡。
在這裏插入圖片描述

[root@server2 ~]# 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: ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 52:54:00:68:5c:65 brd ff:ff:ff:ff:ff:ff
    inet 172.25.254.2/24 brd 172.25.254.255 scope global ens3
       valid_lft forever preferred_lft forever
    inet6 fe80::5054:ff:fe68:5c65/64 scope link 
       valid_lft forever preferred_lft forever
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default 
    link/ether 02:42:af:4a:61:17 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
4: eth0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
    link/ether 52:54:00:8b:37:59 brd ff:ff:ff:ff:ff:ff
## 現在有ens3 和eth0這兩塊網卡。

激活這兩塊網卡,並開啓混雜模式:

[root@server1 ~]# ip link set up eth0
[root@server1 ~]# ip link set eth0 promisc on

[root@server2 ~]# ip link set up eth0
[root@server2 ~]# ip link set eth0 promisc on
[root@server2 ~]# ip a
能看到PROMISC 就會說明開啓了
4: eth0: <BROADCAST,MULTICAST,PROMISC,UP,LOWER_UP> 

創建macvlan 網絡模型;
server1和server2上

[root@server1 ~]# docker network create -d macvlan --subnet 172.20.0.0/24 --gateway 172.20.0.1 -o parent=eth0 macvlan1
[root@server2 ~]# docker network create -d macvlan --subnet 172.20.0.0/24 --gateway 172.20.0.1 -o parent=eth0 macvlan1
-o 指定使用的物理接口 起名爲 macvlan1
[root@server1 ~]# docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
672f5543b2d3        bridge              bridge              local
e181fb352854        host                host                local
4c5448255098        macvlan1            macvlan             local		## 這裏
7261b2daa122        none                null                local
[root@server1 ~]# docker run -it --name vm1 --network macvlan1 --ip 172.20.0.11 ubuntu
[root@server2 ~]# docker run -it --name vm2 --network macvlan1 --ip 172.20.0.12 ubuntu
#運行容器,macvlan 模型這裏必須手動指定 ip 地址,如果不指定會自動分配,單調遞增,兩臺主機可能會衝突。

root@a59b31a27751:/# ping 172.20.0.11
PING 172.20.0.11 (172.20.0.11) 56(84) bytes of data.
64 bytes from 172.20.0.11: icmp_seq=1 ttl=64 time=0.030 ms
64 bytes from 172.20.0.11: icmp_seq=2 ttl=64 time=0.027 ms

root@c3af04bec7fa:/# ping 172.20.0.11
PING 172.20.0.11 (172.20.0.11) 56(84) bytes of data.
64 bytes from 172.20.0.11: icmp_seq=1 ttl=64 time=0.662 ms
64 bytes from 172.20.0.11: icmp_seq=2 ttl=64 time=0.374 ms
##兩臺主機就可以互相通信了。

[root@server2 ~]# brctl show
bridge name	bridge id		STP enabled	interfaces
docker0		8000.0242af4a6117	no
[root@server1 ~]# brctl show
bridge name	bridge id		STP enabled	interfaces
docker0		8000.02421a46fc82	no
##並且兩臺主機的網絡都沒有橋接到docker0上。而是直接佔用了物理網卡。無需 NAT 或端口映射

這樣就就出現了一個問題每次創建一個網絡就需要一塊新的網卡,這是不現實的,
我們可以:

使用 vlan 子接口實現多 macvlan 網絡

vlan 可以將物理二層網絡劃分爲 4094 個邏輯網絡,彼此隔離,取值爲 1~4094,這樣就極大的提升了網卡的複用性.

[root@server1 ~]# docker network create -d macvlan --subnet 172.21.0.0/24 --gateway 172.21.0.1 -o parent=eth0.1 macvlan2
[root@server2 ~]# docker network create -d macvlan --subnet 172.21.0.0/24 --gateway 172.21.0.1 -o parent=eth0.1 macvlan2
81bd9c8e1951e76a7b7c4c15d86803976b2573d94bfc8e1fe8dd4e85d29bed66
[root@server2 ~]# docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
62adb4558a7e        bridge              bridge              local
bfe9ba62d903        host                host                local
123424e654e3        macvlan1            macvlan             local
81bd9c8e1951        macvlan2            macvlan             local
0f50d5bf6719        none                null                local
[root@server2 ~]# ip a
4: eth0: <BROADCAST,MULTICAST,PROMISC,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default 
7: eth0.1@eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
## 就出現了eth0.1這個網卡

[root@server1 ~]# docker run -it --name vm3 --network macvlan2 --ip 172.21.0.11 ubuntu
[root@server2 ~]# docker run -it --name vm4 --network macvlan2 --ip 172.21.0.12 ubuntu
root@45f205287186:/# 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
8: eth0@if7: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default 
    link/ether 02:42:ac:15:00:0c brd ff:ff:ff:ff:ff:ff
    inet 172.21.0.12/24 brd 172.21.0.255 scope global eth0		#自定義的ip地址
       valid_lft forever preferred_lft forever
root@45f205287186:/# ping 172.20.0.11
PING 172.20.0.11 (172.20.0.11) 56(84) bytes of data.
^C		## ping不通
3 packets transmitted, 0 received, 100% packet loss, time 1999ms

root@45f205287186:/# ping 172.21.0.11
PING 172.21.0.11 (172.21.0.11) 56(84) bytes of data.
64 bytes from 172.21.0.11: icmp_seq=1 ttl=64 time=0.378 ms     ## 通

# 我們ping macvlan1網絡的容器是不能ping通的,同一個macvlan的容器纔可以互相通信

‘注意:macvlan 網絡在二層是隔離的,所以不同 macvlan 容器不能通信,可以在三層通過網關連接(加路由),docker 本身不做任何限制,像傳統 vlan 那樣管理即可。網絡選型時如果對網絡比較熟悉,選 macvlan較好,因爲只需要把網絡設備調整好,docker 方面基本不用做什麼調整’

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章