这个问题最终在github提了issue后得到了解决,本质上还是我没有弄清楚docker的网络配置。出现了类似问题需要寻找解决方案的同学可以直接拉到文末。


这又是一个神坑了,我甚至不知道算bug还是特性。

之前写了一篇博客,介绍了一些docker中的坑,但是这个坑太大了,让我不得不重新搞篇文章来写。

一般情况下网段冲突都可以通过这篇文章第3小节中介绍的方法来解决,但是在使用docker-compose的时候,偶尔会有一种情况居然无法解决。

注意,我这里说的是“偶尔”,因为我也只遇到了一次,还是在一个运行了大半年的程序突然出现的。所以我并不知道发生的具体原因是什么,但是经过摸索,大致知道了解决方案。

问题的现象很简单,docker-compose中的容器由于网段冲突导致无法访问,修改了docker0的网段后也不行(配置文件中并未涉及任何网络的配置,全是默认)。

解决方案其实很简单,就是:重启容器!不要删除容器,直接重启,一直重启!后面再解释为什么。

首先,为了便于理解,介绍一下docker的 network,docker其实就是个装虚拟机的容器,是虚拟机就会涉及到网络通信,比如搞vmware的时候经常就要选NAT、host-only还是桥接模式(bridge)。docker 默认的网络模式就是桥接,关于桥接模式,可以参考这张图。桥接模式下,虚拟机可以算作与物理机在同一个子网下的实体。

使用指令 docker network ls 可以查看自己电脑上的docker 网络信息。

docker在启动容器的时候,会默认以桥接模式新建一个子网,其名称就是应用名+defautlt(如图中那些后缀名为deafult的网络),这样做的好处很多,比如之前一篇文章中提到docker-compose中访问redis不需要填ip,只需要写redis:6379,因为“redis”在相当于该子网中的一个域名(没错,这里还附带了DNS的功能)。docker在新建网络的时候,默认使用的网络驱动是bridge,所以,这会使新建的网络与物理机保持在同一个网段。因此,在大多数网段冲突的情况下,修改docker的默认网桥docker0就能生效。所以,我无法理解的地方就在这里!

我明明已经修改了docker0的网段,如图:

但是,当我启动容器时,在显示创建了子网之后,我马上就与该服务器断开了连接(由于网段冲突导致ssh被挤掉了)….

没关系,我再换一台与它网段不冲突的服务器,通过该服务器连上这个服务器,

这个时候,使用指令docker network inspect onlinejudgedeploy_default 查看该网桥的信息,

这里的网段还是127.17,与我宿舍的网段冲突,导致无法访问!

为什么这里的新建的子网网段与bridge不一致呢?我实在无法理解,这种模式反而与NAT模式一样,新建的虚拟机网络成了docker0下面的一个子网,这个子网的ip又与docker默认的网段相同。但如果是这样的话,虚拟机的网络根本就不会在外界暴露出来,也就不会导致和其它的网段冲突……

然而,更搞笑的是这个问题的解决方式,只需要重启一遍容器就行了,重启之后,网段会从127.17变成127.18,每重启一次就会+1……注意重启得用docker-compose restart或docker-compose stop,不能是docker-compose down,因为docker-compose down会销毁容器并移除网络,然后再启动之后网段又从127.17开始计数……这就好像docker的作者已经预料到了这种问题,并且预料了用户会不断的重启,所以加了这个特性。。。

另外,docker-compose 是可以在配置文件中指定ip或网段的,我尝试了新建网络并指定网段,

docker network create -d bridge --subnet=192.168.1.0/24 --gateway=192.168.1.254 test

使用inspect查看时一切正常,也可以将单个的容器连接到该网络,但是在使用docker-compose up时又出现了新的问题,

Creating network "onlinejudgedeploy_default" with driver "test"
ERROR: plugin "test" not found

在centOS 和MacOS上试过了都这样,也没找到合理的解决方案。

右边的两个窗口分别是启动容器ubuntu前后的情况,可以发现container里确实出现了该容器,网段也是正常的

上图也可以说明新建的网络确实对单个容器生效了,使用docker-compose时却失败了。

这应该算是一个bug吧,当然也很可能是我操作不当……


2020-01-08更新,针对这个问题,我在github的docker-compose 库下提了issue,得到了回复,果然是我搞错了。

地址:https://github.com/docker/compose/issues/7114

复制一下官方人员的回复:

Seems you get confused with the driver attribute of network configuation. This one is used to select the engine’s network plugin used to create networks, not to select an existing network.

driver:test makes no sense, and using driver: bridge doesn’t mean compose will use existing docker0 bridge (that you have previously reconfigured) but create a new bridge network for your compose stack

If you want the compose-managed networks to use specific IPs, you’ll need to investigate driver options to customize the created network. Alternatively you can configure compose to not manage network and consider you’re in charge for this by making network external: true

翻译一下,大意就是我搞混了driver的属性,driver: bridge是指使用桥接方式在宿主机上创建一个新网络,这个新网络与docker0网桥并没有关系,所以即使修改了docker0的网段也没有用……

而正确的方式也比较简单,只是我搞错了配置文件的属性,比如我之前错误的写法是:

version: '2'

services:
  web:
    build: .
    ports:
     - "80:5000"
    volumes:
     - .:/qqzone_spider
    depends_on:
     - redis
  redis:
    image: redis
    command: redis-server
networks:
  default:
    driver: bridge

而正确的写法是

version: '2'

services:
  web:
    build: .
    ports:
     - "80:5000"
    volumes:
     - .:/qqzone_spider
    depends_on:
     - redis
  redis:
    image: redis
    command: redis-server
networks:
  default:
    external:
      name: test

其中test是我自己创建并指定ip的新网桥。

此外,我在一些博客上看到似乎是可以通过修改docker0的网段来实现修改全部docker的默认网段的,比如这篇,但是我前面已经大篇幅说明了,并没有实验成功,所以有些奇怪。因为如果不能修改默认网段,就意味着我每一个容器的配置文件都要改,这对大量容器的运维简直是灾难。。。

 

点击量:29904

分类: 开发笔记

发表评论

电子邮件地址不会被公开。 必填项已用*标注