如何解决docker-compose网段路由冲突,docker-compose自定义网络
作者:头秃敲代码
问题复现
网上这个问题出现的挺多的,我也总结一下,因为里面涉及到的网络基础知识比较多的,正好我一直不懂这一块,需要大补
问题
openvpn挂vpn连接远程主机,发现无法ping通远程主机IP:172.20.2.52,以前一直可以远程登录
问题排查
先route
一波查看一下路由表,看一下主机访问172.20.2.52的路由路径
root@ubuntu:/etc/openvpn# route Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface default bogon 0.0.0.0 UG 600 0 0 wlp2s0 link-local 0.0.0.0 255.255.0.0 U 1000 0 0 br-3b2a750ddc03 172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0 172.18.0.0 0.0.0.0 255.255.0.0 U 0 0 0 br-e2cde023b4a7 172.19.0.0 0.0.0.0 255.255.0.0 U 0 0 0 br-94d6396c6c12 172.20.0.0 0.0.0.0 255.255.0.0 U 0 0 0 br-3b2a750ddc03 192.168.43.0 0.0.0.0 255.255.255.0 U 600 0 0 wlp2s0
发现和目标IP 172.20.2.52相似的目标网段为172.20.0.0
,看后面的接口为br-3b2a750ddc03
,也即是说数据被转发到这个接口下,这貌似是个docker创建的网桥,怀疑是docker网桥占用了172.20网段,查看以下docker网络
root@ubuntu:/etc/openvpn# docker network ls NETWORK ID NAME DRIVER SCOPE 22021490da82 bridge bridge local e2cde023b4a7 harbor_harbor bridge local e4aa16aea5df host host local 3b2a750ddc03 milvus bridge local 2d89edc384b1 none null local 94d6396c6c12 test_default bridge local
看到3b2a750ddc03为命名为milvus的docker网络,原来是milvus的容器创建的网桥,进一步检查网络内部信息
root@ubuntu:/etc/openvpn# docker network inspect 3b2a750ddc03
可以看到所属的容器id和容器名
"Containers": { "22b638b8d2f05f3f8b17c62c63c00cd67ec60887fe20be19cc8f6bfb278c123b": { "Name": "milvus-standalone", "EndpointID": "e14a0676adba99908ab113a866650752e0e3ca21381d54ea43f6f4a33aca0fe0", "MacAddress": "02:42:ac:19:00:04", "IPv4Address": "172.20.0.4/16", "IPv6Address": "" }, "39ef60cd098c20efc62f6668375988786d1874a0f1711c15b947e46bd4da80be": { "Name": "milvus-etcd", "EndpointID": "34104d54ae9d3e4834fafb8d7c475d5690acff08955e7d08686999c78ccb220f", "MacAddress": "02:42:ac:19:00:03", "IPv4Address": "172.20.0.3/16", "IPv6Address": "" }, "d55dc8c8e4cce63ceb386e24ee05e27099826ec0c892aa77bb4998a99e9a67a6": { "Name": "milvus-minio", "EndpointID": "4eb0c415c175b7780a94ed72d09a574bad9797039a122a942bcac0e391186cfa", "MacAddress": "02:42:ac:19:00:02", "IPv4Address": "172.20.0.2/16", "IPv6Address": "" } },
可见这个网络接口下目标主机只有172.20.0.2,172.20.0.3,172.20.0.4三台主机IP,以及172.20.0.1网关,因此ping 172.20.2.52根本ping不通。
milvus的三个容器虚拟IP以及网关服务占用了172.20网段,openvpn创建的网段也是172.20.0.0,因此在路由中产生网段冲突
,无法转发到openvpn的对应网络接口,同理从172.17、18、19、20都是docker创建出来的网络接口和网段,如果有其他网络服务处在相同网段可能导致无法路由到
解决方案
(1)路由表添加细路由
在路由寻址时会根据目标IP在路由表中相同网络ID(网段)的记录,根据该记录将数据转发给下一个路由器,如果路由表中存在多条相同网络ID的记录,根据最长匹配算则最吻合的一个,因此增加一个172.20.2.0/24
的目标网段即可,使用route add
进行添加,添加目的地网段为172.20.2.0/24,网络接口为tun0
root@ubuntu:/etc/openvpn# route add -net 172.20.2.0/24 dev tun0
tun0是openvpn创建的网络接口,使用ifconfig查看
root@ubuntu:/etc/openvpn# ifconfig tun0: flags=4305<UP,POINTOPOINT,RUNNING,NOARP,MULTICAST> mtu 1500 inet 10.0.1.65 netmask 255.255.255.255 destination 10.0.1.66 inet6 fe80::7778:a3ae:badc:d759 prefixlen 64 scopeid 0x20<link> unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00 txqueuelen 100 (UNSPEC) RX packets 0 bytes 0 (0.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 1 bytes 48 (48.0 B) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
再看一下路由已经成功添加了一条细路由
root@ubuntu:/etc/openvpn# route Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface default bogon 0.0.0.0 UG 600 0 0 wlp2s0 link-local 0.0.0.0 255.255.0.0 U 1000 0 0 br-3b2a750ddc03 172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0 172.18.0.0 0.0.0.0 255.255.0.0 U 0 0 0 br-e2cde023b4a7 172.19.0.0 0.0.0.0 255.255.0.0 U 0 0 0 br-94d6396c6c12 172.20.0.0 0.0.0.0 255.255.0.0 U 0 0 0 br-3b2a750ddc03 172.20.2.0 0.0.0.0 255.255.0.0 U 0 0 0 tun0 192.168.43.0 0.0.0.0 255.255.255.0 U 600 0 0 wlp2s0
此时再ping 172.20.2.52已经可以ping通
root@ubuntu:/etc/openvpn# ping 172.20.2.52 PING 172.20.2.52 (172.20.2.52) 56(84) bytes of data. 64 bytes from 172.20.2.52: icmp_seq=1 ttl=126 time=45.2 ms 64 bytes from 172.20.2.52: icmp_seq=2 ttl=126 time=43.1 ms
(2)删除docker-compose网桥路由
另一种方法是找到docker网桥对应的容器应用之后直接删除容器,则对应的网络接口和路由都会删除,进入milvus工程目录使用docker-compose停止和删除容器即可
root@ubuntu:~/docker/compose/milvus# docker-compose down Removing milvus-standalone ... done Removing milvus-minio ... done Removing milvus-etcd ... done Removing network milvus
查看route路由表172.20.0.0网段已经消失,ifconfig,docker network ls都已经查不到对应的网络接口,此时再重启openvpn即可
(3)修改docker-compose.yml配置网络
docker-compose默认会给每个应用从172.18.0.0依次往后匹配网段,只要容器没有被删除则一直占用网络,如果删除后重启则依次采用新的网段之前的不再使用,因此很容易造成路由冲突,可以在单个docker-compose.yml文件中增加networks配置,设置网段为10.103.0.0/16
networks: default: name: milvus ipam: driver: default config: - subnet: 10.103.0.0/16
重启docker-compose,此时路由和docker网络都切换为了10.103.0.0/16网段
root@ubuntu:~/docker/compose/milvus# docker network ls|grep milvus 4a772f696084 milvus bridge local root@ubuntu:~/docker/compose/milvus# route |grep br-4a772f696084 10.103.0.0 0.0.0.0 255.255.0.0 U 0 0 0 br-4a772f696084
root@ubuntu:~/docker/compose/milvus# docker network inspect 4a772f696084 "Containers": { "71248b9185a5279bfd6a1c67d2a318554217beb94b8f5c0d9f837fc8480911dc": { "Name": "milvus-minio", "EndpointID": "2c39a39329eca0389eb6204e64f67869dedd787ea17d067fc23c9dd0f179dafa", "MacAddress": "02:42:0a:67:00:03", "IPv4Address": "10.103.0.3/16", "IPv6Address": "" }, "78adbd5868226361033c74532ea57d4fcbb5ef13f81fdd4e4fec531c19a9b629": { "Name": "milvus-standalone", "EndpointID": "711bb663135135189852e1ba39ab7c58b2acf84f5a5bb53fca00425efd08a234", "MacAddress": "02:42:0a:67:00:04", "IPv4Address": "10.103.0.4/16", "IPv6Address": "" }, "9d221d4fb00ff8a1dd8d95e3ed8198f134f2177ccccbb32f393595a29e1d884b": { "Name": "milvus-etcd", "EndpointID": "79bc5d0ffacb5a124ee5a2452c33346c871f6564bc446c21ba59516d080cfd19", "MacAddress": "02:42:0a:67:00:02", "IPv4Address": "10.103.0.2/16", "IPv6Address": "" }
(4)修改docker全局配置文件
修改全局docker网络配置,docker自动分配的网段使用12.11.0.0/16,每个子网掩码划分为 255.255.255.0
root@ubuntu:~/docker/compose/milvus# cat /etc/docker/daemon.json { "insecure-registries": ["http://192.168.1.28:80"], "default-address-pools" : [ { "base" : "12.11.0.0/16", "size" : 24 } ] }
重启docker
root@ubuntu:~/docker/compose/milvus# systemctl daemon-reload root@ubuntu:~/docker/compose/milvus# systemctl restart docker
再重新启动docker-compose,项目切换为指定的网段
root@ubuntu:~/docker/compose/milvus# docker-compose up -d Creating network "milvus" with the default driver Creating milvus-etcd ... done Creating milvus-minio ... done Creating milvus-standalone ... done root@ubuntu:~/docker/compose/milvus# docker network inspect "Config": [ { "Subnet": "12.11.2.0/24", "Gateway": "12.11.2.1" } ] }, "ConfigOnly": false, "Containers": { "335d4fb5fb97e3daaf4246dd76c6a90809ac841c36c05360baeab2e20b0ef0a1": { "Name": "milvus-standalone", "EndpointID": "5c51dd328f3baa31183512b40872ae3e281b6a339c702ac6ccdda893320299df", "MacAddress": "02:42:0c:0b:02:04", "IPv4Address": "12.11.2.4/24", "IPv6Address": "" }, "7311706aaaa23143297632afcb482f0585afcf5de8abc3af33e737c29af2b659": { "Name": "milvus-minio", "EndpointID": "10068465e847919b212089d0057ea3b860ffc00df817768ebe1e7f7305e9180f", "MacAddress": "02:42:0c:0b:02:03", "IPv4Address": "12.11.2.3/24", "IPv6Address": "" }, "a3e69162650f63b12e491e44fc8268768bb6ef252cc80ccb9636cc7f621980da": { "Name": "milvus-etcd", "EndpointID": "610ebfc3e0f090edea8b1d076b5930ce25770e227d1db4071a26d21ee5715e50", "MacAddress": "02:42:0c:0b:02:02", "IPv4Address": "12.11.2.2/24", "IPv6Address": "" } },
不仅是docker-compose,docker run启动的容器的虚拟IP也变更为指定的网段
相关知识
(1)网络基础知识(网关、路由器)
网关:网关是一个逻辑概念,网关是一个结点,是一个网络连接到另一个网络的关口,实质上是一个网络通向其他网络的IP地址。
两个不同网段的网络结点之间通信需要使用网关,如果网络A的主机发现目标主机不在本地网络中就会把数据包发送到A的网关,再转发给网络B的网关,再转发给网络B。
路由器:路由器是物理设备,路由器可以作为网关使用,通常指的网关就是路由器的IP。
主机1.1要发送数据包给主机4.1,因为IP地址不再同一网段,主机会将数据包发送给本网段的网关路由器A。
路由器A接收到数据包,查看数据包IP中的目标IP地址,在查找自己的路由表,数据包的目标IP地址是4.1,属于4.0网段,路由器A在路由表中查到4.0网段转发的接口是SO接口。
于是,路由表A将数据包从SO接口转发出去。到达了网关路由器B,用同样的转发方法,从EO口转发出去,4.1主机接收发这个数据包。
(2)docker网桥模式
(3)docker-compose网络模式
docker-compose中可以设置网络,如果不显示设置网络这些容器都会被加入app_default网络,比如工程目录为test,则启动后的网络名为test_default,使用docker network ls
可以查看网络列表,docker network inspect <network id>
可以查看对应网络的配置。
如果想要工程有特有的网段以及容器有特有的IP则需要在docker-compose.yml中设置自定义要网络,例子如下
version: '3' services: mysql: image: mysql restart: always volumes: - ./mysql:/home environment: - MYSQL_ROOT_PASSWORD=gp123456 networks: mynet1: ipv4_address: 172.100.0.5 flask: build: ./flask volumes: - ./flask:/home links: - mysql:mysql environment: - MYSQL_USER=xiaogp - MYSQL_PASSWORD=gp123456 - MYSQL_DB=pira ports: - "5000:5000" networks: mynet1: ipv4_address: 172.100.0.6 networks: mynet1: ipam: config: - subnet: 172.100.0.0/16
具体的写法是在最下面设置一个工程全局的网络,并且在各个容器服务的最下面引用,在全局下使用subnet设置网段,各容器使用ipv4_address设置IP。
重新启动后docker网络已经固定为172.100.0.0/16网段,各容器IP也改为自定义IP
root@ubuntu:~/docker/docker-compose/test_1# docker network inspect 393d21db53d0 "IPAM": { "Driver": "default", "Options": null, "Config": [ { "Subnet": "172.100.0.0/16" } ] }, "Internal": false, "Attachable": true, "Ingress": false, "ConfigFrom": { "Network": "" }, "ConfigOnly": false, "Containers": { "b2a5571aadaca0e52812b2016967b20e79d02dea6d70c422c8746ee510256c64": { "Name": "test_1_flask_1", "EndpointID": "e9d73b5841b24d01fd03e74dbbb5b40e9c9b884b2497073fd1b482d08aa4c89c", "MacAddress": "02:42:ac:64:00:06", "IPv4Address": "172.100.0.6/16", "IPv6Address": "" }, "e57d400253c9e97c5f8816a1832e4c105329c72611b691d2d4bd0ce1e8626cb3": { "Name": "test_1_mysql_1", "EndpointID": "f306029d9ab4c67014cbfd665523735ed968cca984b62a0bb00946635ee82d27", "MacAddress": "02:42:ac:64:00:05", "IPv4Address": "172.100.0.5/16", "IPv6Address": "" } },
(4)路由寻址规则
当TCP/IP需要向某个IP地址发起通信时,它会对路由表进行评估,以确定如何发送数据包。评估过程如下:
1.目的IP地址和路由表中每一个路由项的网络掩码进行相与计算,如果相与后的结果匹配对应路由项的网络地址,则记录下此路由项
2.当计算完路由表中所有的路由项后,择优选择其中一条路由规则
- 最长匹配路由:TCP/IP选择记录下的路由项中的最长匹配路由(网络掩码中具有最多“1”位的路由项)来和此目的IP地址进行通信。
- 最低跃点数:如果存在多个最长匹配路由,那么选择具有最低跃点数的路由项。
- 优先级高低:如果存在多个具有最低跃点数的最长匹配路由,那么:均根据最长匹配路由所对应的网络接口在网络连接的高级设置中的绑定优先级来决定(一般有线(eth0) > 无线 (wlan0) > 移动信号(4G))
(5)Linux route路由操作
每一个linux系统中都具有IP路由表,它存储了本地计算机可以到达的网络目的地址范围和如何到达的路由信息,本地计算机上的任何TCP/IP通信都受到路由表的控制,linux通过route 命令查看 Linux 内核的路由表。
字段 | 操作符 |
---|---|
Destination | 目标网络或目标主机。Destination 为 default(0.0.0.0)时,表示这个是默认网关,所有数据都发到这个网关 |
Gateway | 网关地址,0.0.0.0 表示当前记录对应的 Destination 跟本机在同一个网段,通信时不需要经过网关。如果没有就显示星号(*) |
Genmask | U: 该路由可以使用。 |
H: 该路由是到一个主机 | |
G: 该路由是到一个网关(路由器)。如果没有设置该标志,说明目的地 是直接相连的 | |
R: 恢复动态路由产生的表项 | |
D: 该路由是由改变路由(redirect)报文创建的 | |
M: 该路由已被改变路由报文修改 | |
!:这个路由将不会被接受。 | |
Metric | 路由距离,到达指定网络所需的中转数 |
Ref | 路由项引用次数 |
Use | 此路由项被路由软件查找的次数 |
Iface | 网卡名字,例如 eth0 |
(6)docker网络操作
docker network ls
:查看docker网络列表docker network inspect <network id/name>
:查看某个docker网络详情docker network rm
:删除docker网络docker network disconnect
:断开docker网络docker network prune
:清楚无用的网络
使用案例:
(1)查看所有driver=bridge的docker网络
root@ubuntu:~# docker network ls --filter driver=bridge NETWORK ID NAME DRIVER SCOPE 33868f4f5f1c bridge bridge local e2cde023b4a7 harbor_harbor bridge local 4fab000d4ed4 milvus bridge local 64634f55cf1b test_1_default bridge local 393d21db53d0 test_1_mynet1 bridge local
(2)删除已经无效的网络
root@ubuntu:~# docker network rm 64634f55cf1b 64634f55cf1b
如果哟啊删除正在使用的网络,需要先断开连接再删除网络
docker network disconnect <network> <container>
# 先使用inspect查到网络名和容器名 root@ubuntu:~# docker network disconnect test_1_mynet1 test_1_mysql_1 root@ubuntu:~# docker network ls NETWORK ID NAME DRIVER SCOPE 33868f4f5f1c bridge bridge local e2cde023b4a7 harbor_harbor bridge local e4aa16aea5df host host local 4fab000d4ed4 milvus bridge local 2d89edc384b1 none null local 393d21db53d0 test_1_mynet1 bridge local root@ubuntu:~# docker network rm test_1_mynet1 test_1_mynet1
(3)清理无效网络,想通过这种方法清除历史曾经使用过的网段,但是没有达到预期效果,以后再研究
root@ubuntu:~# docker network prune -f
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。