Docker下的网络模式
前言
在docker常用指令详解一文中介绍了docker的常用指令, 在构建容器的时候使用了 --net anyesu_net
这个选项, 意思是让容器使用自定义的网络 anyesu_net
, 本文就 Docker
下的网络模式做一个简单介绍。
四种网络模式的选择
bridge
这是 docker
默认使用的模式, Docker Daemon
启动时默认会创建 docker0
这个网桥, 网段为 172.17.0.0/16
, 宿主机ip为 172.17.0.1
, 作为这个虚拟子网的网关。
当然, 也可以新建一个名为 anyesu_net
网段为 172.18.0.0
的网桥(docker默认创建的网段为 172.17.0.0
)
docker network create --subnet=172.18.0.0/16 anyesu_net
启动容器时指定 --net anyesu_net
即可
docker network 命令相关资料:Docker命令行参考(29) – docker network create创建一个网络
host
容器共用宿主机的网络(ip和端口)。使用docker有相当一部分目的是为了隔离宿主机和容器, 使用 host
模式就违背了这一点, 不是很好。另外有很多镜像如 tomcat
默认监听8080端口的, 使用host模式后开多个容器就会端口冲突, 而不得不修改 tomcat 的监听端口。
none
这种模式下, 创建的容器拥有自己的 Network Namespace
, 但是没有任何网络配置, 所以默认是没有网络的, 可以自己对容器的网卡, ip进行配置, 适合用来配置比默认设置更加复杂的网络环境。
container
类似于 host
模式, 不过这种模式是共享已存在容器的网络。
给容器分配固定ip
默认应该是按容器创建或启动顺序依次分配的, 所以容器重启后ip就可能会变化, 这对于一些需要定向访问容器的功能来说就比较麻烦了。一种解决办法是使用link来连接容器, 原理就是动态配置hosts, 不过这种方式启动顺序有依赖关系, 因此本人不习惯使用。还有一种方法, 是启动容器的时候使用自定义网络, 如 anyesu_net
, 并指定 --ip
选项来固定ip。
当然, 网上还有很多教程借助 pipework
、nsenter
等工具实现固定ip的配置, 甚至可以分配到宿主机所在物理网段上的ip。不过, 步骤都比较复杂, 有兴趣的小伙伴可以参考下面的几篇文章自己尝试哦。
- Docker为容器分配指定物理网段的静态IP
- Docker 配置固定IP及桥接的实现方法
- 分配局域网固定ip
- 为Docker容器指定自定义网段的固定IP/静态IP地址
- Docker 使用物理网络IP地址 及四种网络模式
关于容器中hosts文件的修改
启动容器的时候指定 --link
或 --add-host
选项修改 hosts
文件内容, 但都是追加内容而无法覆盖已有内容, 比如我要重设localhost指向宿主机的ip 172.17.0.1
而不是默认的 127.0.0.1
(可能也就我吃饱了撑着要这么做吧)就不能用这种方法了。
了解到, 容器的 hosts
文件是容器启动时宿主机上动态创建后挂载到容器上的(源文件位于宿主机的 /var/lib/docker/containers/[容器id]
目录下), 因此, 容器重启之后还会重新创建, 即所作的修改都没了。于是想到在容器的 启动命令 中动态修改, 但是 hosts
文件是不允许直接修改的, 于是采用下面的办法:
# 拷贝hosts内容
cp /etc/hosts /etc/hosts.tmp
# 替换字符串
sed -i 's$127.0.0.1$172.17.0.1$' /etc/hosts.tmp
# 覆盖hosts
cat /etc/hosts.tmp > /etc/hosts
# CMD参数中 使用 sh -c "... && ... && ..." 的方式来运行多个指令
个人觉得这也不是一种好办法, 就换了一种更简单粗暴的方法: -v /etc/hosts:/etc/hosts
, 使用宿主机文件映射容器的 hosts
文件(或者新建一个hosts文件专门给容器用而不影响宿主机的hosts)达到覆盖的目的, 再配合容器固定ip配置, 多个容器共用一套hosts文件, 将所有容器的机器名和ip配置进去, 容器之间也可以通过机器名或子网ip直接访问。
相关文章:
Docker下使用dubbo
公司项目中使用了 dubbo
来搭建分布式系统, 本地开发环境统一使用 localhost
来连接 ZooKeeper
, 绑定不同端口实现伪分布式。后来就在测试环境上使用 Jenkins + Svn + Maven + Docker
完成自动化构建, 将每个服务部署在单独的容器之中, 使用默认的 bridge
网络模式。
遇到的问题
1. 访问宿主机上ZooKeeper
简单的方法是在项目配置文件中使用环境变量或hostname ZkServer
作为注册中心地址, 相应地对容器进行环境变量和 hosts 的配置即可。不过,作为一个懒人, 项目中已经写好了使用 localhost 但不想改成 ZkServer
, 方法也简单, 就是使用上面的方法替换 localhost 的ip为172.17.0.1, 即宿主机ip。
2. 服务提供者注册ip问题
容器内的服务默认注册到 zookeeper
上的地址是其ip(如 172.17.0.2
), 是一个内网地址, 对于宿主机和其上容器之外的其他机器来说是不可访问的。如果要使服务对其他机器可用的话就要另辟蹊径了, 主要方法有:
- 容器使用
host
网络模式或设置为宿主机物理网段上的ip- 对宿主机和服务消费者之间的网络设置路由规则, 使消费者可访问容器内网ip
- 修改
dubbo
源码来指定服务注册ip
上面的方法实践起来有点复杂, 也不是很可靠, 还得另寻方法。
查了下 dubbo
的源码, 在 com.alibaba.dubbo.config.ServiceConfig
类的 doExportUrlsFor1Protocol 方法中, 如果配置中没有指定 host 属性或者使用了回环地址则调用 InetAddress.getLocalHost().getHostAddress()
来获取本机可用ip。InetAddress.getLocalHost
这个方法在Windows下应该是取所有网卡中第一个可用的ip, 在Linux下应该是取主机名对应的ip(先去 /etc/hosts
中找, 找不到再去 dns
服务器找, 需要注意这有可能会得到一个错误的ip), 因此, 在Linux下针对上面的问题只需修改 hosts
文件, 指定主机名对应的ip为宿主机的局域网ip。另外测试了下, 设置的 host
在Linux下只用于提供给服务注册中心而不用于端口绑定, 即可设置任意ip; 而在Windows下设置了非本机ip会无法绑定端口。
hosts文件改完还有一个问题:tomcat
默认 shutdown
命令绑定的端口是 localhost:8005
, 由于 localhost
已经不再是容器的IP, 因此端口绑定会失败。简单处理就是修改 tomcat/conf/server.xml
中 Server 节点的属性, 添加属性 address="127.0.0.1"
, 如下所示:
<Server address="127.0.0.1" port="8005" shutdown="SHUTDOWN">
总结下最终的方案步骤:
- 创建自定义网络(
172.18.0.0/16
网段) - 容器指定hostname、自定义网络、固定ip
- 宿主机(局域网ip:
192.168.1.100
)新建文件/etc/hosts2
挂载到容器的/etc/hosts
上(-v /etc/hosts2:/etc/hosts
) , 内容如下, 所有容器共用此hosts
文件
172.18.0.1 localhost
192.168.1.100 docker_host1
192.168.1.100 docker_host2
...
- 宿主机到容器的端口映射
-p 20880:20880
最终结构图如下所示:
dubbo容器结构2017-12-25追加
目前dubbo自身已提供环境变量的方式注册ip和端口,详见官方示例
- 当前2.5.9版本有 bug , 监听端口与注册端口不一致会导致找不到对应服务
配置优先级:
系统环境变量 -> java命令参数-D -> 配置文件host属性 -> /etc/hosts中hostname-ip映射关系 -> 默认联通注册中心地址的网卡地址 -> 第一个可用的网卡地址