一.实验环境(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
[root@server1 ~]# docker network create --driver bridge my_net1 #添加bridge的自定义网络,名字为my_net1,使用自定义的ip地址和网关地址
- 添加bridge自定义网络(自动分配的ip地址和网关地址)之后的网络:
- 添加bridge自定义网络(自动分配的ip地址和网关地址)之后的网桥:
- "ip addr show",查看bridge自定义网络(自动分配的ip地址和网关地址)的网关地址
- "docker network inspect my_net1"查看bridge自定义网络(自动分配的ip地址和网关地址)的信息
2.使用自定义的ip地址和网关地址
- 添加bridge自定义网络(自定义的ip地址和网关地址)之前的网络:bridge,host和none
[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地址和网关地址)之后的网络:
- 添加bridge自定义网络(自定义的ip地址和网关地址)之后的网桥:
- "ip addr show",查看bridge自定义网络(自定义的ip地址和网关地址)的网关地址
- "docker network inspect my_net2"查看bridge自定义网络(自定义的ip地址和网关地址)的信息
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的网卡的方法为例)
值的注意的是:
- docker的bridge自定义网络之间:双方可以随便添加对方的网卡
- docker的bridge自定义网络与系统自带的网桥之间:只能是,系统自带的网桥对应的容器添加bridge自定义网络对应的容器的网卡。而反过来会报错。
- 但是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
7.docker的bridge的自定义网络之间(或者是与系统自带的网桥)默认是有域名解析的
值的注意的是:
- docker的bridge自定义网络之间默认是有域名解析的;
- docker的bridge自定义网络与系统自带的网桥之间默认是有解析的;
- 但是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-proxy和iptables的DNAT
- 宿主机访问本机容器使用的是iptables的DNAT;
- 外部主机访问容器或容器之间的访问是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原生的macvlan和overlay;
- 第三方的flannel,weave和calico。
众多网络方案是如何与docker集成在一起的?
- libnetwork docker容器网络库
- CNM(Container Network Model)这个模型对容器网络进行了抽象
CNM分三类组件:
- Sandbox:容器网络栈。包括容器接口,dns,路由表。(namespace)
- Endpoint:作用是将sanbox接入network(veth pair)
- 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上的网络
<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。
- 查看宿主机的网络,2正是虚机的网卡
- 可见,容器的 eth0 就是宿主机的eth0通过macvlan虚拟出来的interface。容器的interface直接与主机的网卡连接,这种方案使得容器无需通过NAT和端口映射就能与外网直接通信(只要有网关),在网络上看起来与其他独立主机没有区别。当前网络结构如图所示:
有些人做实验时,可能会遇到这样的问题,所有的步骤都是正确的,但是两个宿主机之间的容器就是不能通信,这是为什么呢?
答:
因为两个宿主机的网卡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
- 创建的子接口是可以看到的,如下:
<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