一.实验环境(rhel7.3版本)

1.selinux和firewalld状态为disabled

2.各主机信息如下:

主机

ip

server1(安装好docker)

172.25.83.1

server2(安装好docker)

172.25.83.2

 

二.添加docker的bridge自定义网络

 

1.使用自动分配的ip地址和网关地址

 

  • 添加bridge自定义网络(自动分配的ip地址和网关地址)之前的网络:bridge,host和none

docker重置自定义网络_自定义

 

[root@server1 ~]# docker network create --driver bridge my_net1   #添加bridge的自定义网络,名字为my_net1,使用自定义的ip地址和网关地址
  • 添加bridge自定义网络(自动分配的ip地址和网关地址)之后的网络:

docker重置自定义网络_自定义_02

  • 添加bridge自定义网络(自动分配的ip地址和网关地址)之后的网桥:

docker重置自定义网络_ip地址_03

  • "ip  addr  show",查看bridge自定义网络(自动分配的ip地址和网关地址)的网关地址

docker重置自定义网络_自定义_04

  • "docker  network  inspect  my_net1"查看bridge自定义网络(自动分配的ip地址和网关地址)的信息

docker重置自定义网络_docker重置自定义网络_05

 

2.使用自定义的ip地址和网关地址

 

  • 添加bridge自定义网络(自定义的ip地址和网关地址)之前的网络:bridge,host和none

docker重置自定义网络_ip地址_06

 

[root@server1 ~]# docker network create --driver bridge --subnet 172.20.0.0/24 --gateway 172.20.0.1 my_net2   #添加bridge的自定义网络,名字为my_net2,指定相应的ip地址和网关地址
  • 添加bridge自定义网络(自定义的ip地址和网关地址)之后的网络:

docker重置自定义网络_ip地址_07

  • 添加bridge自定义网络(自定义的ip地址和网关地址)之后的网桥:

docker重置自定义网络_ip地址_08

  • "ip  addr  show",查看bridge自定义网络(自定义的ip地址和网关地址)的网关地址

docker重置自定义网络_docker重置自定义网络_09

  • "docker  network  inspect  my_net2"查看bridge自定义网络(自定义的ip地址和网关地址)的信息

docker重置自定义网络_docker_10

 

3.创建两个容器,并桥接到不同的网桥(只要是网桥就可以,可以是系统自带的网桥,也可以是自定义的网桥)上,彼此是不通信的

 

[root@server1 ~]# docker run -it --name vm1 --net my_net1 ubuntu   #创建容器vm1,并桥接到my_net1上
root@23c7c3f81f06:/# ip a
6: eth0@if7: <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

[root@server1 ~]# docker run -it --name vm2 --net my_net2 ubuntu   #创建容器vm2,并桥接到my_net2上
root@fa248fa44b1c:/# ip a    
8: eth0@if9: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:ac:14:00:02 brd ff:ff:ff:ff:ff:ff
    inet 172.20.0.2/24 brd 172.20.0.255 scope global eth0
       valid_lft forever preferred_lft forever

root@fa248fa44b1c:/# ping 172.18.0.2   #ping不同vm1的IP地址,因为vm1和vm2不在同一网桥上
PING 172.18.0.2 (172.18.0.2) 56(84) bytes of data.
^C
--- 172.18.0.2 ping statistics ---
2 packets transmitted, 0 received, 100% packet loss, time 999ms

 

4.使用--ip参数可以指定容器ip地址,但必须是在自定义网桥上(自定义的ip地址和网关地址),默认的bridge模式不支持,同一网桥上的容器是可以通信的

 

[root@server1 ~]# docker run -it --name vm3 --net my_net2 --ip 172.20.0.10 ubuntu   #创建容器vm3,并桥接到my_net2上,并指定ip地址为172.20.0.10
root@bc85e6a878d2:/# ip a
10: eth0@if11: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:ac:14:00:0a brd ff:ff:ff:ff:ff:ff
    inet 172.20.0.10/24 brd 172.20.0.255 scope global eth0
       valid_lft forever preferred_lft forever
root@bc85e6a878d2:/# ping 172.20.0.2   #可以ping通vm3的ip地址(因为vm3和vm2在同一网桥上)
PING 172.20.0.2 (172.20.0.2) 56(84) bytes of data.
64 bytes from 172.20.0.2: icmp_seq=1 ttl=64 time=0.090 ms
^C
--- 172.20.0.2 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.090/0.090/0.090/0.000 ms

 

5.docker在设计上就是要隔离不同network的

 

[root@server1 ~]# brctl show
bridge name	bridge id		STP enabled	interfaces
br-0c08e7c8a0a4		8000.024215d293db	no		vethd7bc020
br-3633ad685362		8000.024200370255	no		veth2d976f5
							veth6a7c9ab
docker0		8000.0242306eeb7b	no

 

6.那么如何使两个不同网桥的容器通信呢?

 

使用docker  network  connect  命令为vm1添加一块my_net2的网卡(使得vm1可以ping通vm2的ip地址)。或者为vm2添加一块my_net1的网卡(使得vm2可以ping通vm1的ip地址)。(这里以为vm1添加一块my_net2的网卡的方法为例)


值的注意的是:

  1. docker的bridge自定义网络之间:双方可以随便添加对方的网卡
  2. docker的bridge自定义网络与系统自带的网桥之间:只能是,系统自带的网桥对应的容器添加bridge自定义网络对应的容器的网卡。而反过来会报错
  3. 但是docker的系统自带的网桥之间:是可以通信的,因为是在一个网络桥接上。

[root@server1 ~]# docker network connect my_net2 vm1   #为vm1添加一块my_net2的网卡
[root@server1 ~]# docker attach vm1   #连接容器vm1
root@23c7c3f81f06:/# ip a
6: eth0@if7: <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
12: eth1@if13: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:ac:14:00:03 brd ff:ff:ff:ff:ff:ff
    inet 172.20.0.3/24 brd 172.20.0.255 scope global eth1
       valid_lft forever preferred_lft forever

root@23c7c3f81f06:/# ping 172.20.0.2   #能ping通vm2的IP地址(因为vm1上添加了一块my_net2的网卡)
PING 172.20.0.2 (172.20.0.2) 56(84) bytes of data.
64 bytes from 172.20.0.2: icmp_seq=1 ttl=64 time=0.102 ms
64 bytes from 172.20.0.2: icmp_seq=2 ttl=64 time=0.071 ms
^C
--- 172.20.0.2 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 999ms
rtt min/avg/max/mdev = 0.071/0.086/0.102/0.018 ms

docker重置自定义网络_ip地址_11

 

7.docker的bridge的自定义网络之间(或者是与系统自带的网桥)默认是有域名解析的

 


值的注意的是:

  1. docker的bridge自定义网络之间默认是有域名解析的;
  2. docker的bridge自定义网络与系统自带的网桥之间默认是有解析的;
  3. 但是docker的系统自带的网桥之间默认是没有解析的。

[root@server1 ~]# docker attach vm1   #连接容器vm1
root@23c7c3f81f06:/# 
root@23c7c3f81f06:/# ping vm2   #能ping通vm2,正如能ping通vm2的ip地址一样
PING vm2 (172.20.0.2) 56(84) bytes of data.
64 bytes from vm2.my_net2 (172.20.0.2): icmp_seq=1 ttl=64 time=0.079 ms
^C
--- vm2 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.079/0.079/0.079/0.000 ms

[root@server1 ~]# docker attach vm2   #连接容器vm2
root@fa248fa44b1c:/# ping vm1   #能ping通vm1,虽然ping不同vm1的ip地址,但是能ping通vm2的容器名。(这是因为自定义的网络之间默认是有解析的)
PING vm1 (172.20.0.3) 56(84) bytes of data.
64 bytes from vm1.my_net2 (172.20.0.3): icmp_seq=1 ttl=64 time=0.055 ms
^C
--- vm1 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.055/0.055/0.055/0.000 m

 

8.容器访问外网是通过iptables的SNAT实现的

 

[root@server1 ~]# iptables -t nat -nL
Chain POSTROUTING (policy ACCEPT)
target     prot opt source               destination         
MASQUERADE  all  --  172.20.0.0/24        0.0.0.0/0           
MASQUERADE  all  --  172.18.0.0/16        0.0.0.0/0           
MASQUERADE  all  --  172.17.0.0/16        0.0.0.0/0

 

docker重置自定义网络_自定义_12

 

三.外网是如何访问容器的?

 

外网访问容器用到了docker-proxy和iptables的DNAT

  1. 宿主机访问本机容器使用的是iptables的DNAT;
  2. 外部主机访问容器或容器之间的访问是docker-proxy实现的。

 

[root@server1 ~]# docker run -d --name test -p 80:80 nginx
[root@server1 ~]# netstat -antulpe | grep 80
tcp6       0      0 :::80                   :::*                    LISTEN      0          39814      4564/docker-proxy 
[root@server1 ~]# iptables -t nat -nL
DNAT       tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:80 to:172.17.0.2:80

docker重置自定义网络_docker重置自定义网络_13

 

 

四.跨主机网络解决方案有哪些?

 

跨主机网络解决方案:

  1. docker原生的macvlan和overlay;
  2. 第三方的flannel,weave和calico。

众多网络方案是如何与docker集成在一起的?

  1. libnetwork         docker容器网络库
  2. CNM(Container  Network  Model)这个模型对容器网络进行了抽象

CNM分三类组件:

  1. Sandbox:容器网络栈。包括容器接口,dns,路由表。(namespace)
  2. Endpoint:作用是将sanbox接入network(veth  pair)
  3. Network:包含一组endpoint,同一network的endpoint可以通信。

 

五.添加docker的macvlan自定义网络

 

macvlan 网络方案的实现

  • Linux  kernel提供的一种网卡虚拟化技术。
  • 无需Linux  bridge,直接使用 物理接口,性能机号。

 

1.使用一个网卡,创建一个macvlan网络

 

<1>在两台docker主机上,打开网卡eth0的混杂模式

 

macvlan本身是linxu kernel的模块,本质上是一种网卡虚拟化技术。其功能是允许在同一个物理网卡上虚拟出多个网卡,通过不同的MAC地址在数据链路层进行网络数据的转发,一块网卡上配置多个 MAC 地址(即多个 interface),每个interface可以配置自己的IP,Docker的macvlan网络实际上就是使用了Linux提供的macvlan驱 动。

因为多个MAC地址的网络数据包都是从同一块网卡上传输,所以需要打开网卡的混杂模式ip link set eth0 promisc on。

 

#配置server1
[root@server1 ~]# ip link set eth0 promisc on   #在server1上打开网卡eth0的混杂模式
[root@server1 ~]# ip addr show eth0   #看到PROMISC,表示成功
2: eth0: <BROADCAST,MULTICAST,PROMISC,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 52:54:00:c5:33:5c brd ff:ff:ff:ff:ff:ff
    inet 172.25.83.1/24 brd 172.25.83.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::5054:ff:fec5:335c/64 scope link 
       valid_lft forever preferred_lft forever

#server2的操作同server1

注意 : 如果不开启混杂模式,会导致macvlan网络无法访问外界,具体在不使用vlan时,表现为无法ping通路由,无法ping通同一网络内其他主机。


 

<2>在两台主机上各创建macvlan网络:

 

创建macvlan网络不同于桥接模式,需要指定网段和网关(因为要保证跨主机上网段和网关是相同的),并且都得是真实存在的,例如docker network create -d macvlan --subnet=172.183.1.0/24  --gateway=172.183.1.1  -o parent=eth0  mac_net1。

 

#配置server1
[root@server1 ~]# docker network create --driver macvlan --subnet 172.183.1.0/24 --gateway 172.183.1.1 -o parent=eth0 mac_net1

#server2的操作同server1
  • 查看宿主机server1上的网络

docker重置自定义网络_自定义_14

 

<3>在两台主机上分别使用创建的网络mac_net1运行一个容器

 

#配置server1
[root@server1 ~]# docker run -it --name vm4 --net mac_net1 --ip 172.183.1.10 ubuntu
root@03290f0f87b6:/# ip a
40: eth0@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default 
    link/ether 02:42:ac:b7:01:0a brd ff:ff:ff:ff:ff:ff
    inet 172.183.1.10/24 brd 172.183.1.255 scope global eth0
       valid_lft forever preferred_lft forever

#配置server2
[root@server1 ~]# docker run -it --name vm5 --net mac_net2 --ip 172.183.2.10 ubuntu
root@29fdbe4ccd9d:/# ip a
6: eth0@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default 
    link/ether 02:42:ac:b7:01:0b brd ff:ff:ff:ff:ff:ff
    inet 172.183.1.11/24 brd 172.183.1.255 scope global eth0
       valid_lft forever preferred_lft forever

root@29fdbe4ccd9d:/# ping 172.183.1.10   #能够ping同宿主机server1创建的容器vm4的ip地址,实现了跨主机的通信
PING 172.183.1.10 (172.183.1.10) 56(84) bytes of data.
64 bytes from 172.183.1.10: icmp_seq=1 ttl=64 time=0.272 ms
^C
--- 172.183.1.10 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.272/0.272/0.272/0.000 ms
root@29fdbe4ccd9d:/# ping 172.183.1.1   #但是ping不同网关,这是因为当前的网络是macvlan的网络,而不是bridge的网络。
PING 172.183.1.1 (172.183.1.1) 56(84) bytes of data.
^C
--- 172.183.1.1 ping statistics ---
2 packets transmitted, 0 received, 100% packet loss, time 999ms
  • macvlan模式不依赖网桥,所以brctl show查看并没有创建新的bridge,但是查看容器的网络,会看到虚拟网卡对应了一个interface是2。

docker重置自定义网络_docker重置自定义网络_15

  • 查看宿主机的网络,2正是虚机的网卡

docker重置自定义网络_docker_16

  • 可见,容器的 eth0 就是宿主机的eth0通过macvlan虚拟出来的interface。容器的interface直接与主机的网卡连接,这种方案使得容器无需通过NAT和端口映射就能与外网直接通信(只要有网关),在网络上看起来与其他独立主机没有区别。当前网络结构如图所示:

docker重置自定义网络_ip地址_17

 


有些人做实验时,可能会遇到这样的问题,所有的步骤都是正确的,但是两个宿主机之间的容器就是不能通信,这是为什么呢?

答:

因为两个宿主机的网卡eth0需要打开混杂模式,也就是说两个宿主机之间能够通信依赖的就是网卡eth0。而物理机和虚拟机之间的连接是通过桥接,所以虚拟机和物理机是有联系的。因此,这时你需要检查真机的防火墙,虚拟机server1的防火墙,虚拟机server2的防火墙是否已经关闭。如果没有关闭,则需要关闭。


 

macvlan会独占主机的网卡,也就是说一个网卡只能创建一个macvlan 网络,否则会报错,如果要创建多个macvlan网络,则需要添加多块网卡。

 

2.使用第二个网卡,创建另外一个macvlan网络(该过程与1的过程完全相同)

 

<1>在两台docker主机上添加一块新网卡,打开网卡eth0的混杂模式

 

#配置server1
[root@server1 ~]# ip addr show eth1
41: eth1: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN qlen 1000
    link/ether 52:54:00:af:02:0d brd ff:ff:ff:ff:ff:ff
[root@server1 ~]# ip link set up eth1   #因为网卡eth1的状态是DOWN,所以需要先激活网卡eth1
[root@server1 ~]# ip link set eth1 promisc on   #打开网卡eth1的混杂模式
[root@server1 ~]# ip addr show eth1   #看到网卡eth1的状态是UP,并且显示PROMISC,表示前两步配置成功
41: eth1: <BROADCAST,MULTICAST,PROMISC,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 52:54:00:af:02:0d brd ff:ff:ff:ff:ff:ff
    inet6 fe80::5054:ff:feaf:20d/64 scope link 
       valid_lft forever preferred_lft forever

#server2的操作同server1

 

 

<2>在两台主机上各创建macvlan网络:

 

#配置server1
[root@server1 ~]# docker network create --driver macvlan --subnet 172.183.2.0/24 --gateway 172.183.2.1 -o parent=eth1 mac_net2

#server2的操作同server1

 

<3>在两台主机上分别使用创建的网络mac_net2运行一个容器

 

#配置server1
[root@server1 ~]# docker run -it --name vm5 --net mac_net2 --ip 172.183.2.10 ubuntu

#配置server2
[root@server2 ~]# docker run -it --name vm2 --net mac_net2 --ip 172.183.2.11 ubuntu
root@c24820bb2b64:/# ping 172.183.2.10   #能够ping同宿主机server1创建的容器vm4的ip地址,实现了跨主机的通信
PING 172.183.2.10 (172.183.2.10) 56(84) bytes of data.
64 bytes from 172.183.2.10: icmp_seq=1 ttl=64 time=0.395 ms
^C
--- 172.183.2.10 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.395/0.395/0.395/0.000 ms
root@c24820bb2b64:/# ping 172.183.2.1   #但是ping不同网关,这是因为当前的网络是macvlan的网络,而不是bridge的网络。
PING 172.183.2.1 (172.183.2.1) 56(84) bytes of data.
^C
--- 172.183.2.1 ping statistics ---
2 packets transmitted, 0 received, 100% packet loss, time 999ms=

 

我们可以看到,多个网卡可以实现多个macvlan网络的创建。但是,如果需要创建的macvlan的网络有很多。那添加的网卡数也会特别多,会特别麻烦,那也就引入了下面的子接口的方法。

macvlan会独占主机的网卡,也就是说一个网卡只能创建一个macvlan 网络,否则会报错,为了能支持更多的macvlan网络,macvlan不仅可以连接到interface(如eth0),也可以连接到网卡的子接口 sub-interface(如eth0.xxx)。

 

VLAN 是现代网络常用的网络虚拟化技术,它可以将物理的二层网络划分成多达 4094 个逻辑网络,这些逻辑网络在二层上是隔离的,每个逻辑网络(即 VLAN)由 VLAN ID 区分,VLAN ID 的取值为 1-4094。Linux 的网卡也能支持 VLAN(apt-get install vlan),同一个 interface可以收发多个VLAN 的数据包,不过前提是要创建 VLAN的 sub-interface。比如希望 eth0同时支持 VLAN10 和 VLAN20,则需创建 sub-interface eth0.10 和 eth0.20。

 

3.使用网卡的字接口,创建macvlan网络(该过程与1的类似,只是网卡接口不同而已)

 

<1>在两台docker主机上,打开网卡eth0的混杂模式——>1中已经做过,不需要再做

 

<2>在两台主机上各创建macvlan网络:

 

#配置server1
[root@server1 ~]# docker network create --driver macvlan --subnet 172.183.3.0/24 --gateway 172.183.3.1 -o parent=eth0.1 mac_net3

#server2的操作同server1
  • 创建的子接口是可以看到的,如下:

docker重置自定义网络_docker重置自定义网络_18

 

<3>在两台主机上分别使用创建的网络mac_net3运行一个容器

 

#配置server1
[root@server1 ~]# docker run -it --name vm6 --net mac_net3 --ip 172.183.3.10 ubuntu

#配置server2
[root@server2 ~]# docker run -it --name vm3 --net mac_net3 --ip 172.183.3.11 ubuntu
root@a3739073c1a6:/# ping 172.183.3.10
PING 172.183.3.10 (172.183.3.10) 56(84) bytes of data.
64 bytes from 172.183.3.10: icmp_seq=1 ttl=64 time=0.457 ms
^C
--- 172.183.3.10 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.457/0.457/0.457/0.000 ms