参考:https://blog.csdn.net/xbw_linux123/article/details/81873490(超详细,推荐直接看这篇)

https://blog.csdn.net/sld880311/article/details/77650937(vet pair技术)

https://www.cnblogs.com/qwangxiao/p/8783379.html(docker 端口映射)

实验系统版本:centos 7


1.None网络模型

None网络模型从字面就很好理解,就是不分配网络给docker。这种模型没什么好说的,大家可能更好奇的是为什么会用到这种模型,那是因为有些情况下,网络是没有必要的,比如我建个docker把数据备份到本地磁盘某个路径下,这个时候网络是没必要的 ,数据备份完就可以直接退出了。

docker指定网络模型为none很简单,只需启动容器时--net none

[root@localhost ~]# docker run --name "none" --net none  -it  --rm  busybox 

/ # ifconfig -a

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:1 

          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

  

  

2.bridge模型


(1)现在让我们考虑下,两个容器之间的通信,之前我们了解到,容器之间实现了名称空间的隔离,其他就有网络名称空间隔离,所以两个容器之间在宿主机上看起来就类似两个互不相关的进程,要实现它们的通信,那就要给他们建立关联关系。我们知道,单单两个主机之间要互相通信,最简单的就是拿一根网线连接起来,而两个容器之间的通信,最简单的也类似这样。

在上述模型中,docker使用veth pair 技术为两个容器建立关联关系,所谓的veth pair技术,就是模拟出两个可以互相通信的接口,就像一根水管两头绑了两个吸水放水的设备,只要把设备放在不同的水池中,就可以把一头的水导到另一头,我们可以用ip命令模拟这个过程

pair.png

	ip netns add ns1   #创建一个名为ns1的名称空间
	ip netns add ns2
	ip link add veth1 type veth peer name veth2 #创建一对pair,一头叫veth1,一头叫veth2
	ip link set veth1 netns ns1	#veth1放到ns1名称空间中
	ip link set veth2 netns ns2
	ip netns exec ns1 ifconfig  veth1 192.168.1.1/24 up  #在ns1中,配置网卡veth1
	ip netns exec ns2 ifconfig  veth2 192.168.1.2/24 up  #在ns2中,配置veth2,”ip netns exec ns1“命令表示在ns1名称空间中执行后面的命令
	ip netns exec ns1 ifconfig -a	#查看配置
	ip netns exec ns1  ping 192.168.1.2  #在ns1中测试到ns2的联通性



题外话:有人可能会发现,有些时候ping不通自己的ip,比如在名称空间ns1中ping 192.168.1.1可能ping不通,这个是因为,192.168.1.1是私有地址,默认走lo接口,所以只需要把lo接口up起来就可以

ip netns exec ns1  ifconfig  lo up

ip netns exec ns1  ping 192.168.1.1



(2)我们已经解决了单独两个容器之间通信的问题,现在让我们考虑三个或者多个容器之间互相通信问题。显然,如果按照老方法,为所有容器两两创建接口,这是无法想象的,好在tcp/ip网络中已经有解决这类问题的经验,所以后来人也只需参考就好,tcp/ip用了一个网桥解决这个问题,相对应的,我们也可以虚拟出一个网桥,然后把不同容器各创建一对接口,一个放容器一个放网桥来解决这个问题。虚拟网桥在linux中是 bridge-utils包实现的,所以我们要先确认有装这个包

br0.png

        yum install  bridge-utils -y
	brctl addbr br0  #创建网桥br0
	ip netns add ns1   #创建一个名为ns1的名称空间
	ip netns add ns2
	ip link add v1 type veth peer name v11   #创建veth pair对
	ip link add v2 type veth peer name v22
	ip link set v1 netns ns1	#把接口一端v1放到ns1中,下面会把v11放到网桥br0上
	ip link set v2 netns ns2
	ip link set v11 master br0   
	brctl addif br0 v22			#把v22放到br0中,这里故意和上面不一样写法,两者作用相同,可以brctl show查看
	ip netns exec ns1 ifconfig  v1 192.168.1.1/24 up  (配置ns1中的v1)
	ip netns exec ns2 ifconfig  v2 192.168.1.2/24 up
	
	ifconfig br0 up		#下面这些都是比较容易不注意犯错的,端口一定要开启来,而且内核转发功能也要开
	ifconfig v11 up
	ifconfig v22 up
	echo 1 > /proc/sys/net/ipv4/ip_forward
	ip netns exec ns2 ping 192.168.1.1


        

(3)现在我们已经解决了,同一局域网多个容器的通信问题,那么问题来了,不同局域网之间应该如何操作呢?聪明的你应该想到了,在tcp/ip中,我们借用路由的功能,在这里我们也是一样的做法,linux内核开启ip_forward后,就可以当成路由器来用,所以这里,我们为网桥br0,br1分配ip当成三层交换机来用(这个说法不一定正确,因为虚拟网桥刚好在宿主机上,都是用内核的功能,所以我不知道具体实现是把网桥br0作为路由器,还是把宿主机作为路由器来用,姑且用三层交换机说法),为每个容器指定网关,实现不同局域网之间的通信。(其实也可以多做一步,为br0到宿主机,br1到宿主机各创建一对veth pair,然后宿主机当路由器,更方便理解,但是我测试这种方法在名称空间访问外网的时候会有问题,而且我们这里模拟的是docker,所以也按三层交换机的方法来)

br0br1.png

实验:接(2)的环境

	brctl addbr br1 	#新网桥,连接不同局域网
	ip netns add ns3
	ip link add v3 type veth peer name v33   
	ip netns exec ns3 ifconfig v3 10.0.0.3 up   
	ifconfig br1 10.0.0.254/24 up		#为br1配置ip作为网关
	ifconfig br0 192.168.1.254/24 up
	ip netns exec ns1 route add default gw 192.168.1.254  #为不同名称空间指定默认路由
	ip netns exec ns2 route add default gw 192.168.1.254
	ip netns exec ns3 route add default gw 10.0.0.254

[root@localhost ~]# ip netns exec ns1 ping 10.0.0.3

PING 10.0.0.3 (10.0.0.3) 56(84) bytes of data.

64 bytes from 10.0.0.3: icmp_seq=1 ttl=63 time=0.210 ms


(4)现在我们解决了,同一宿主机下,不同局域网的容器通信问题,那么不同主机里的容器该怎么通信?我们每个容器配置的都是局域网ip,显然直接访问是不行,好在我们网络中也已经有这方面的解决方法,而docker也正是用这种方法,那就是nat技术,要想容器能访问外网,实际上只要用iptables 做个POSTROUTING的地址伪装就可以了

实验:接(3)

[root@localhost ~]# iptables -t nat -I POSTROUTING -s 192.168.1.0/24  -j MASQUERADE

[root@localhost ~]# ip netns exec ns1 ping  1.1.1.1

PING 1.1.1.1 (1.1.1.1) 56(84) bytes of data.

64 bytes from 1.1.1.1: icmp_seq=2 ttl=53 time=193 ms

而外网要想访问容器,那就要做PREROUTING链的规则,这里我就不实验了,容器bridge网络模型,背后的基础原理做到这里已经足以说明,大数量容器的网络解决则已经超出我能力范围,有机会再补上

(5)在docker中使用bridge网络模型

bridge.png

实验:启动容器时,用--net指定即可,不指也可以,默认也是这个选项

[root@localhost ~]# docker run -it --name b1 --rm --net bridge  busybox   

/ # ip a

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1

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

121: eth0@if122: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue 

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

/ # route -n

Kernel IP routing table

Destination     Gateway         Genmask         Flags Metric Ref    Use Iface

0.0.0.0         172.17.0.1      0.0.0.0         UG    0      0        0 eth0

172.17.0.0      0.0.0.0         255.255.0.0     U     0      0        0 eth0

/ # 


用ip a可以看到docker 默认生成了docker0网桥,上面配置了172.17.0.1作为网关,进去容器也可以看到跟我们实验相同的配置,需要说明的是,容器默认认为两个局域网是隔离的,所以如果你自定义了一个新的网段,你会发现跟其他局域网是不能直接通信的,具体怎么隔离的,有待查明,用iptables -t nat -nvL 则可以看到对应的iptables nat规则


3.host网络模型

host.png

在bridge模型中,一个容器访问其他宿主机的容器,要经过容器ns1,网桥br0,路由转发,网桥br1,最后在到达容器ns2,显然这种模型,效率是个问题。所以host模型就应运而生了。所谓的host模型,简单地说,就是直接使用宿主机的网络名称空间,或者可以简单认为不进行网络隔离。所以优点和缺点都是显而易见的。

优点:效率高,不需要nat

缺点:没有自己独立的名称空间,隔离性差

实验:很简单,跟bridge类似,只不过--net 指定为host 

docker run -it --name b1 --rm --net host  busybox    

在里面ip a,可以看到跟宿主机一样的内容

4.container网络模型

container.png


懂了host模型,就很好理解container模型了,简单地说,就是使用另一个容器的网络名称空间,这种模型的应用场景,一般是两个容器之间有关联关系,比如lnmp中,nginx和mysqld就可以共用网络名称空间,通过lo套接字互相访问

实验:接3

docker run -it --name b2 --rm --net container:b1  busybox 


container:b1指明使用跟b1一样的网络名称空间