参考资料:

  https://serverfault.com/questions/55611/loopback-to-forwarded-public-ip-address-from-local-network-hairpin-nat

  https://www.jianshu.com/p/4a358a120983

  书接上回,我通过docker部署了一个gitlab容器,并且想用它来实现我所有地方代码的版本控制(这是可行的,因为我部署gitlab的服务器有宝贵的公网IP)。我先画一下网络拓扑图吧,这个拓扑图的信息量也很大:

docker内的python 获取宿主机ip docker获取宿主机ip地址_git

           这个网络拓扑为什么这么画我先按下不表,先说一下我碰到的问题,毕竟出现问题才是我研究这些玩意儿的动机。

  1. 按照上一篇随笔配置好gitlab之后,其他机器,包括PC都能够正常使用git:

docker内的python 获取宿主机ip docker获取宿主机ip地址_docker_02

  2. 但是!!!同一个IP地址下面的业务容器却不能使用git,如图中的业务容器A和业务容器B,在访问10.0.0.1:45008的时候,会被拒绝

docker内的python 获取宿主机ip docker获取宿主机ip地址_IP_03

  这件事情就比较离谱,因为我访问的是一个公网IP,虽然涉及的两个容器(业务容器A和gitlab容器)都在一个内网内,但是用公网应该也是可以访问的吧?比如访问10.0.0.1:45008,通过端口映射,业务容器A应该是可以找到gitlab容器的80端口的啊。但是事实却并不是如此,具体原因就比较复杂了,可以参考参考资料一里面的解释,这个涉及到路由规则,路由器我是万万没有权限碰的...

  更糟糕的是,同一个内网之下的业务容器B,由于路由的原因,也是无法访问gitlab容器的。也就是说,现在的情况是,10.0.0.1 IP下面的容器都访问不了gitlab容器,但是其他IP下的客户端都能访问。这简直离了大谱,你们是最亲的才对啊!高速的局域网让你们亲密无间啊!怎么窝里斗!

  眼看着前功尽弃,我也不甘心啊,多方查找资料后我了解了docker的组网机制。

  默认的docker容器都是都是通过桥接方式和宿主机共享网络的,我个人理解就是宿主机虚构了一堆网卡,然后组了一个虚拟的小局域网172.17.0.0/16。网关是宿主机172.17.0.1,容器各自分配虚拟的IP,当容器需要访问因特网时,就把宿主机172.17.0.1当成路由器了。

  3. 既然如此,我就不访问10.0.0.1:45008了,宿主机是172.17.0.1,我作为和gitlab容器同处一个虚拟网下的业务容器A,我访问172.17.0.1:45008不就行了吗?正好映射到172.17.0.5:80。很遗憾,还是不行:

docker内的python 获取宿主机ip docker获取宿主机ip地址_docker_04

  这个情况则更加离谱,我是可以ping通172.17.0.1的,但是访问172.17.0.1的端口就不行。会提示No route to host

  4. 灵光一现,既然业务容器A和gitlab本来就亲密无间穿同一条裤子挂在虚拟网段172.17.0.0/16上,我直接访问172.17.0.5:80会怎么样?

docker内的python 获取宿主机ip docker获取宿主机ip地址_docker_05

  成功... 属实不容易啊我去... 业务容器A搞定了,业务容器B怎么办???

  5. 又灵光一闪,两个服务器既然同一个公网IP,那么两个服务应该在同一个局域网下面吧?通过ifconfig查看二者的IP地址,然后利用局域网IP进行访问

docker内的python 获取宿主机ip docker获取宿主机ip地址_git_06

  成功... 泪目了


  总结一下:

  1. 如果宿主机有一个公网IP,那么同一个IP下的容器在访问宿主机的其他容器时应当使用虚拟子网的IP地址而不是公网IP,否则将造成路由错误

  2. 两个有相同公网IP的不同物理机,在同一个局域网应当使用局域网地址而不是公网IP,否则将造成路由错误

  再补充一下如何查看docker的虚拟子网:

  1) 使用docker network list列出所有的虚拟子网

  2)使用docker network inspect (bridge) 查看虚拟子网下面的容器和其IP