We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
几天前,为了解决日常在本地进行日常工作和开发测试之间的矛盾,利用docker在Windows系统中搭建了基于Linux的测试环境:借助Docker,在win10下编码,一键在Linux下测试。在这边文章里主要介绍了如何在本地通过docker构建与生产环境基本一致的环境并一键运行、测试我们的代码。Docker官方建议每个容器中只运行一个服务[1],但是我们的项目可能是由多个服务组成,在服务中可能会需要mysql、redis等中间件的支持,所以通常我们将一个项目的不同服务划分到不同容器中。这种做法虽然具有低耦合、高隔离性等优点,但是也增加了服务之间通信的复杂度。
Docker容器间的通信方式根据媒介可以分为:volume共享通信、网络通信等;根据通信范围也可以分为:同主机通信和跨主机通信等。而本文主要针对容器间的网络通信方法进行讨论。
volume共享通信
网络通信
同主机通信
跨主机通信
Docker的网络驱动模型分类:
以上是Docker支持的几种网络驱动模型,它们都具有独特的特点和应用范围,为了更加详细地了解Docker的网络运行原理,下面挑选几种较为重要的网络模型进行研究。
如上图所示为Docker中bridge驱动模式的示意图,其中蓝色的模块表示主机上的网卡。当Docker启动时会自动在主机上创建一个虚拟网桥docker0,使用默认网络模式创建docker容器时会自动创建一对儿veth pair接口,一端连接在docker容器中(如图容器中的eth0),一端连接在虚拟网桥docker0上(如图veth)。这种veth pair是一种虚拟网络设备,主要用于不同namespace中(意味着网络隔离)的网络通信,它总是成对存在的。在这里可以把它想象成一对儿靠虚拟网线连接起来的两个虚拟网卡,一端连接着docker容器,一端连接着虚拟网桥docker0。
bridge
docker0
veth pair
eth0
veth
通过这种方式,不同docker容器之间可以通过ip地址互相通信,也可以通过虚拟网桥访问主机上的网络eth0(添加iptables规则,将docker容器对目标地址发出的访问通过地址伪装的方式修改为主机对目标地址进行访问)。
如果想要外界网络访问docker容器时,需要在docker容器启动时加上参数'-p [主机端口]:[容器端口]'进行端口映射,原理也是通过修改iptables规则将访问[主机端口]的数据转发到docker容器的[容器端口]中,但是这种做法也存在着占用主机有限的端口资源的缺点。
在主机上通过命令docker network ls可以查看docker中存在的网络:
docker network ls
docker network ls # 输出结果: NETWORK ID NAME DRIVER SCOPE e79b7548b225 bridge bridge local 666d5f1f459d host host local d0d785cf4794 none null local
然后通过命令docker network inspect bridge查看bridge网络的详细配置:
docker network inspect bridge
docker network inspect bridge # 输出结果: [ { "Name": "bridge", "Id": "e79b7548b225c3c80d0f70d0de0b5911ed70a7f39ac20f75a8ae71c5cef05b3a", "Created": "2019-05-17T13:01:27.6581642Z", "Scope": "local", "Driver": "bridge", # bridge驱动模式 "EnableIPv6": false, "IPAM": { "Driver": "default", "Options": null, "Config": [ { "Subnet": "172.17.0.0/16", # 子网,docker容器的IP范围 "Gateway": "172.17.0.1" # 网关,即docker0 } ] }, ... 省略 ... # 两个docker容器(一个mysql容器,一个Web服务容器)的网络配置 "Containers": { # mysql容器 "d6f33e9bbd60e10d02dd2eebea424a7fc129d9646c96742ec3fe467833017679": { "Name": "mysqld5.7", "EndpointID": "32e900f33367e3570c416c43a5618bd7a742cf94f36799e92895951ed1784736", "MacAddress": "02:42:ac:11:00:02", "IPv4Address": "172.17.0.2/16", # 容器的IP "IPv6Address": "" }, # 基于tornado的Web服务容器 "dd292d535d16bbfe5382e29486756f4dddfea8e9b10af769db61618d739c5c4e": { "Name": "test_demo", "EndpointID": "eaf8e294f7b54aa50c6e6b30ac91f63b1a0ccbc5b56d6fbdcfeacd0471b15eb3", "MacAddress": "02:42:ac:11:00:03", "IPv4Address": "172.17.0.3/16", # 容器的IP "IPv6Address": "" } }, "Options": { "com.docker.network.bridge.default_bridge": "true", "com.docker.network.bridge.enable_icc": "true", "com.docker.network.bridge.enable_ip_masquerade": "true", "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0", "com.docker.network.bridge.name": "docker0", "com.docker.network.driver.mtu": "1500" }, "Labels": {} } ]
由上述配置信息可以看出,docker网桥的网关的IP为"172.17.0.1",也即docker0,而docker的子网为"172.17.0.0/16",docker将会为容器在"172.17.0.0/16"中分配IP,如其中的mysql容器的IP为"172.17.0.2/16"、test_demo容器的IP为"172.17.0.3/16"。由于不同容器通过veth pair连接在虚拟网桥docker0上,所以容器之间可以通过IP互相通信,但是无法通过容器名进行通信:
mysql容器
test_demo容器
# 在test_demo容器中访问mysqld5.7容器(通过IP) ping 172.17.0.2 -c 3 # 输出结果: PING 172.17.0.2 (172.17.0.2) 56(84) bytes of data. 64 bytes from 172.17.0.2: icmp_seq=1 ttl=64 time=0.147 ms 64 bytes from 172.17.0.2: icmp_seq=2 ttl=64 time=0.185 ms 64 bytes from 172.17.0.2: icmp_seq=3 ttl=64 time=0.209 ms --- 172.17.0.2 ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 2057ms rtt min/avg/max/mdev = 0.147/0.180/0.209/0.027 ms # 在test_demo容器中访问mysqld5.7容器(通过容器名) ping mysqld5.7 # 输出结果: ping: mysqld5.7: Name or service not known
所以,默认的网桥bridge上的容器只能通过IP互连,无法通过DNS解析名称或别名。假如我们在container1中部署了Web服务,在container2中部署了mysql,container1中的Web服务往往需要连接container2的mysql,这是只能靠IP进行连接,但是docker也无法保证容器重启后的IP地址不变,所以更好的方式是通过别名进行互联,在网络中加入DNS服务器,将容器名与IP地址进行匹配,省去了手动修改Web服务中连接mysql的IP的过程。
为了实现不同容器通过容器名或别名的互连,docker提供了以下几种:
在启动docker容器时加入--link参数,但是目前已经被废弃,废弃的主要原因是需要在连接的两个容器上都创建--link选项,当互连的容器数量较多时,操作的复杂度会显著增加;
--link
启动docker容器后进入容器并修改/etc/host配置文件,缺点是手动配置较为繁杂;
/etc/host
用户自定义bridge网桥,这是目前解决此类问题的主要方法;
用户自定义bridge相对于使用默认bridge的主要优势:
更好的隔离效果
更好的互通性
80
创建用户自定义bridge:
docker network create my-net # 创建了一个名为"my-net"的网络
将Web服务容器和mysql服务容器加入到"my-net"中,并观察变化:
docker network connect my-net test_demo # 将Web服务加入my-net网络中 docker network connect my-net mysqld5.7 # 将mysql服务加入my-net网络中
# 查看my-net的网络配置 docker network inspect my-net # 输出结果(省略部分内容): [ { "Name": "my-net", "Scope": "local", "Driver": "bridge", "EnableIPv6": false, "IPAM": { "Config": [ { "Subnet": "172.18.0.0/16", # my-net的子网 "Gateway": "172.18.0.1" # my-net的网关 } ] }, "Containers": { "d6f33e9bbd60e10d02dd2eebea424a7fc129d9646c96742ec3fe467833017679": { "Name": "mysqld5.7", "EndpointID": "7d0e8d70bb523cceb4d2d8d4e3f8231fc68332c70f7f9b4e5d4abccce2b31a65", "MacAddress": "02:42:ac:12:00:03", "IPv4Address": "172.18.0.3/16", # mysql服务容器的IP,与之前不同 "IPv6Address": "" }, "dd292d535d16bbfe5382e29486756f4dddfea8e9b10af769db61618d739c5c4e": { "Name": "test_demo", "EndpointID": "f7802f1af81e258f77e227609dfdcdf66c49f19776381eb8b0dca6d9e794ccad", "MacAddress": "02:42:ac:12:00:02", "IPv4Address": "172.18.0.2/16", # Web服务容器的IP,与之前不同 "IPv6Address": "" } }, } ]
如上所示,用户自定义网络my-net的子网为"172.18.0.0/16",所以两docker容器在my-net网络中的IP分别为:"172.18.0.3/16"、"172.18.0.2/16",与之前的"172.17.0.2/16"、"172.17.0.3/16"不同。
my-net
通过容器名或别名互连: 进入到Web服务器container1中连接container2:
ping mysqld5.7 -c 3 # 输出结果: PING mysqld5.7 (172.18.0.3) 56(84) bytes of data. 64 bytes from mysqld5.7.my-net (172.18.0.3): icmp_seq=1 ttl=64 time=0.066 ms 64 bytes from mysqld5.7.my-net (172.18.0.3): icmp_seq=2 ttl=64 time=0.145 ms 64 bytes from mysqld5.7.my-net (172.18.0.3): icmp_seq=3 ttl=64 time=0.143 ms --- mysqld5.7 ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 2048ms rtt min/avg/max/mdev = 0.066/0.118/0.145/0.036 ms
如上所示,我们已经可以通过容器名"mysqld5.7"连接mysql服务器了。
断开网络: 由于我们的容器仍然连接着默认bridgedocker0,而现在我们已经不需要它,所以应该将容器与docker0的连接断开,执行以下操作:
# 断开容器与docker0的连接 docker network disconnect bridge test_demo docker network disconnect bridge mysqld5.7
关键字:跨主机通信、集群
The text was updated successfully, but these errors were encountered:
No branches or pull requests
几天前,为了解决日常在本地进行日常工作和开发测试之间的矛盾,利用docker在Windows系统中搭建了基于Linux的测试环境:借助Docker,在win10下编码,一键在Linux下测试。在这边文章里主要介绍了如何在本地通过docker构建与生产环境基本一致的环境并一键运行、测试我们的代码。Docker官方建议每个容器中只运行一个服务[1],但是我们的项目可能是由多个服务组成,在服务中可能会需要mysql、redis等中间件的支持,所以通常我们将一个项目的不同服务划分到不同容器中。这种做法虽然具有低耦合、高隔离性等优点,但是也增加了服务之间通信的复杂度。
Docker容器间的通信方式根据媒介可以分为:
volume共享通信
、网络通信
等;根据通信范围也可以分为:同主机通信
和跨主机通信
等。而本文主要针对容器间的网络通信方法进行讨论。1. Docker的网络驱动模型
Docker的网络驱动模型分类:
以上是Docker支持的几种网络驱动模型,它们都具有独特的特点和应用范围,为了更加详细地了解Docker的网络运行原理,下面挑选几种较为重要的网络模型进行研究。
2. bridge
2.1 docker的默认网桥
如上图所示为Docker中
bridge
驱动模式的示意图,其中蓝色的模块表示主机上的网卡。当Docker启动时会自动在主机上创建一个虚拟网桥docker0
,使用默认网络模式创建docker容器时会自动创建一对儿veth pair
接口,一端连接在docker容器中(如图容器中的eth0
),一端连接在虚拟网桥docker0
上(如图veth
)。这种veth pair
是一种虚拟网络设备,主要用于不同namespace中(意味着网络隔离)的网络通信,它总是成对存在的。在这里可以把它想象成一对儿靠虚拟网线连接起来的两个虚拟网卡,一端连接着docker容器,一端连接着虚拟网桥docker0
。通过这种方式,不同docker容器之间可以通过ip地址互相通信,也可以通过虚拟网桥访问主机上的网络
eth0
(添加iptables规则,将docker容器对目标地址发出的访问通过地址伪装的方式修改为主机对目标地址进行访问)。如果想要外界网络访问docker容器时,需要在docker容器启动时加上参数'-p [主机端口]:[容器端口]'进行端口映射,原理也是通过修改iptables规则将访问[主机端口]的数据转发到docker容器的[容器端口]中,但是这种做法也存在着占用主机有限的端口资源的缺点。
在主机上通过命令
docker network ls
可以查看docker中存在的网络:然后通过命令
docker network inspect bridge
查看bridge网络的详细配置:由上述配置信息可以看出,docker网桥的网关的IP为"172.17.0.1",也即
docker0
,而docker的子网为"172.17.0.0/16",docker将会为容器在"172.17.0.0/16"中分配IP,如其中的mysql容器
的IP为"172.17.0.2/16"、test_demo容器
的IP为"172.17.0.3/16"。由于不同容器通过veth pair
连接在虚拟网桥docker0
上,所以容器之间可以通过IP互相通信,但是无法通过容器名进行通信:所以,默认的网桥
bridge
上的容器只能通过IP互连,无法通过DNS解析名称或别名。假如我们在container1中部署了Web服务,在container2中部署了mysql,container1中的Web服务往往需要连接container2的mysql,这是只能靠IP进行连接,但是docker也无法保证容器重启后的IP地址不变,所以更好的方式是通过别名进行互联,在网络中加入DNS服务器,将容器名与IP地址进行匹配,省去了手动修改Web服务中连接mysql的IP的过程。为了实现不同容器通过容器名或别名的互连,docker提供了以下几种:
在启动docker容器时加入
--link
参数,但是目前已经被废弃,废弃的主要原因是需要在连接的两个容器上都创建--link
选项,当互连的容器数量较多时,操作的复杂度会显著增加;启动docker容器后进入容器并修改
/etc/host
配置文件,缺点是手动配置较为繁杂;用户自定义
bridge
网桥,这是目前解决此类问题的主要方法;2.2 用户自定义bridge
用户自定义bridge相对于使用默认bridge的主要优势:
更好的隔离效果
是针对外界网络,而更好的互通性
则是指同一bridge
下的不同容器之间。还是以之前的分别部署了Web服务和mysql服务的两个容器container1、container2为例,container1只需要对外界网络暴露Web服务的80
端口,而负责后端的container2只需与container1互连,不需要对外暴露,有效地保护了后端容器的安全性,提高了容器对外的隔离效果。而同属于用户自定义bridge
的容器container1、container2之间自动将所有端口暴露,方便容器间进行无障碍的通信,而不会遭受到外界的意外访问。2.2.1 如何使用用户自定义bridge
创建用户自定义bridge:
docker network create my-net # 创建了一个名为"my-net"的网络
将Web服务容器和mysql服务容器加入到"my-net"中,并观察变化:
如上所示,用户自定义网络
my-net
的子网为"172.18.0.0/16",所以两docker容器在my-net
网络中的IP分别为:"172.18.0.3/16"、"172.18.0.2/16",与之前的"172.17.0.2/16"、"172.17.0.3/16"不同。通过容器名或别名互连: 进入到Web服务器container1中连接container2:
如上所示,我们已经可以通过容器名"mysqld5.7"连接mysql服务器了。
断开网络: 由于我们的容器仍然连接着默认bridge
docker0
,而现在我们已经不需要它,所以应该将容器与docker0
的连接断开,执行以下操作:# 断开容器与docker0的连接 docker network disconnect bridge test_demo docker network disconnect bridge mysqld5.7
3. overlay
关键字:跨主机通信、集群
【待续。。】
The text was updated successfully, but these errors were encountered: