一、概述
就目前Docker自身默认的网络来说,单台主机上的不同Docker容器可以借助docker0网桥直接通信,这没毛病,而不同主机上的Docker容器之间只能通过在主机上用映射端口的方法来进行通信,有时这种方式会很不方便,甚至达不到我们的要求,因此位于不同物理机上的Docker容器之间直接使用本身的IP地址进行通信很有必要。再者说,如果将Docker容器起在不同的物理主机上,我们不可避免的会遭遇到Docker容器的跨主机通信问题。
二、情景
如下图所示,我们有两个物理主机1和主机2,我们在各自宿主机上启动一个centos容器,启动成功之后,两个容器分别运行在两个宿主机之上,默认的IP地址分配如图所示,这也是Docker自身默认的网络。
此时两台主机上的Docker容器如何直接通过IP地址进行通信?
一种直接想到的方案便是通过分别在各自主机中 添加路由
三、方案原理分析
由于使用容器的IP进行路由,就需要避免不同主机上的容器使用了相同的IP,为此我们应该为不同的主机分配不同的子网来保证。于是我们构造一下两个容器之间通信的路由方案,如下图所示。
各项配置如下:
- 主机1的IP地址为:192.168.91.128
- 主机2的IP地址为:192.168.91.129
- 为主机1上的Docker容器分配的子网:10.0.128.0/24
- 为主机2上的Docker容器分配的子网:10.0.129.0/24
这样配置之后,两个主机上的Docker容器就肯定不会使用相同的IP地址从而避免了IP冲突。
我们接下来 定义两条路由规则
- 所有目的地址为10.0.128.0/24的包都被转发到主机1上
- 所有目的地址为10.0.129.0/24的包都被转发到主机2上
综上所述,数据包在两个容器间的传递过程如下:
- 从container1 发往 container2 的数据包,首先发往container1的“网关”docker0,然后通过查找主机1的路由得知需要将数据包发给主机2,数据包到达主机2后再转发给主机2的docker0,最后由其将数据包转到container2中;反向原理相同,不再赘述。
四、实际试验
操作系统服务器地址Dockerd地址ubuntu-16.04.5-server-amd64192.168.91.12810.0.128.2ubuntu-16.04.5-server-amd64192.168.91.12910.0.129.2
请确保已经安装好了docker,这里是使用以下命令安装的
修改docker0
1、第一种方案 :直接修改 /etc/default/docker 文件,添加 DOCKER_OPTS 参数即可
分别对主机1和主机2上的docker0进行配置
主机1
编辑主机1上的 /etc/default/docker 文件,最后一行添加
DOCKER_OPTS="--bip 10.0.128.1/24"
特别注意,DOCKER_OPTS参数后面必须有引号。--bip后面的ip就是docker0的ip地址,一般从第一个ip开始!
不要修改 /etc/docker/daemon.json 文件添加bip,我测试了一些,重启docker会报错!
主机2
编辑主机1上的 /etc/default/docker 文件,最后一行添加
DOCKER_OPTS="--bip 10.0.129.1/24"
2、第二种方案 :直接修改 /etc/docker/daemon.json下的文件(没有,则创建此文件)
主机1
{
"bip": "10.0.128.1/24"
}
主机2
{
"bip": "10.0.129.1/24"
}
重启docker服务
主机1和主机2上均执行如下命令重启docker服务以使修改后的docker0网段生效
systemctl restart docker
查看主机1上docker0的ip地址
root@ubuntu:~# ifconfig docker0
docker0 Link encap:Ethernet HWaddr 02:42:8a:46:e2:eb
inet addr:10.0.128.1 Bcast:0.0.0.0 Mask:255.255.255.0
UP BROADCAST MULTICAST MTU:1500 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:0
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
查看主机2上docker0的ip地址
root@ubuntu:~# ifconfig docker0
docker0 Link encap:Ethernet HWaddr 02:42:22:b1:25:66
inet addr:10.0.129.1 Bcast:0.0.0.0 Mask:255.255.255.0
UP BROADCAST MULTICAST MTU:1500 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:0
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
发现默认的网段已经改变了!
添加路由规则
主机1
查看路由表
root@ubuntu:~# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 192.168.91.2 0.0.0.0 UG 0 0 0 ens32
10.0.128.0 0.0.0.0 255.255.255.0 U 0 0 0 docker0192.168.91.0 0.0.0.0 255.255.255.0 U 0 0 0 ens32
默认只有自己本身的路由,如果需要访问 10.0.129.0/24 网段,需要添加路由
主机1上添加路由规则如下:
-- 临时路由
route add -net 10.0.129.0/24 gw 192.168.91.129
-- 永久路由,在/etc/rc.d/rc.local 文件下
vim /etc/rc.d/rc.local
#增量添加
route add -net 10.0.129.0/24 gw 192.168.91.129
# 保存
给文件赋权限
chmod +x /etc/rc.d/rc.local
gw 表示下一跳地址,这里的地址就是主机2的ip地址
再次查看路由,发现已经添加上了,重启也生效
root@ubuntu:~# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 192.168.91.2 0.0.0.0 UG 0 0 0 ens32
10.0.128.0 0.0.0.0 255.255.255.0 U 0 0 0 docker0
10.0.129.0 192.168.91.129 255.255.255.0 UG 0 0 0 ens32
192.168.91.0 0.0.0.0 255.255.255.0 U 0 0 0 ens32
主机2
于上,同理,主机2上添加路由规则如下:
route add -net 10.0.128.0/24 gw 192.168.91.128
启动微服务应用
注意:对台服务器情况下,设置静态路由相对较为繁琐
解决方案为:
如果有10台host都需要这样配置的话,每台host上就要敲九条路由信息 如果批量部署的情况下是自寻死路,根本无法管理。所以我想到了使用“动态路由”来让主机与主机之间互相学习对方的网段。
Linux上安装路由器的软件名为Quagga,它可以配置很多种企业级的动态路由协议。我使用的是RIPV2和OSPF同时开启,但安装Quagga的方法和配置命令,太过于繁琐。所以我将Quagga Docker化了。
下载quagga路由器
docker pull index.alauda.cn/georce/router
启动路由器
首先请确定你的所有的docker0网桥不在同一个网段!!!
如:
VM1#ifconfig docker0 1.1.1.1/24
VM2#ifconfig docker0 2.2.2.2/24
VM1#docker run -itd --name=router --privileged --net=host index.alauda.cn/georce/router
VM2#docker run -itd --name=router --privileged --net=host index.alauda.cn/georce/router
就算过几分钟后你也看不出任何变化,但请你查看你的路由表。
VM1或VM2#route -n或者ip route
是不是出现了别的docker0的路由信息?请让容器1 ping下容器2,发现ping成功。
局域网里的Windows平台也直接访问容器1和容器2