参考: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命令模拟这个过程
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包实现的,所以我们要先确认有装这个包
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,所以也按三层交换机的方法来)
实验:接(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网络模型
实验:启动容器时,用--net指定即可,不指也可以,默认也是这个选项
[root@localhost ~]# docker run -it --name b1 --rm --net bridge busybox
/ # ip a
1: lo:
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:
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网络模型
在bridge模型中,一个容器访问其他宿主机的容器,要经过容器ns1,网桥br0,路由转发,网桥br1,最后在到达容器ns2,显然这种模型,效率是个问题。所以host模型就应运而生了。所谓的host模型,简单地说,就是直接使用宿主机的网络名称空间,或者可以简单认为不进行网络隔离。所以优点和缺点都是显而易见的。
优点:效率高,不需要nat
缺点:没有自己独立的名称空间,隔离性差
实验:很简单,跟bridge类似,只不过--net 指定为host
docker run -it --name b1 --rm --net host busybox
在里面ip a,可以看到跟宿主机一样的内容
4.container网络模型
懂了host模型,就很好理解container模型了,简单地说,就是使用另一个容器的网络名称空间,这种模型的应用场景,一般是两个容器之间有关联关系,比如lnmp中,nginx和mysqld就可以共用网络名称空间,通过lo套接字互相访问
实验:接3
docker run -it --name b2 --rm --net container:b1 busybox
container:b1指明使用跟b1一样的网络名称空间