Docker网络连接探索


【编者的话】本文作者分别从两个方面解释了Docker的网络连接,包括主机间的网络通讯和主机内部的网络通讯。总结一下,默认情况下,主机间的Docker容器通讯只能通过端口映射的方式进行,而主机内部的容器通讯可以通过连接的方式进行。当然,不要忘记ICC参数。

我最近的几篇博客一直在讨论Docker,但是却一直没有提到过Docker如何管理网络。我们知道Docker可以通过端口映射的方式来暴露容器服务,但这一方式也随之带来了一些新的挑战。

讨论与网络相关的话题,我们首先要理解网络的相关知识,并且要知道容器的连接方式。本文主要是回顾Docker基本的网络设置。

所以让我们先从最基本的设置开始吧。在本文中,我们将会使用两个Docker容器:docker1和docker2,它们的网络结构如下图所示。
image1.png

看起来并不复杂,只有两个主机(译者注:作者把两个Docker分别安装到了两台主机上)与一个非常简单的网络设置。假设你已经安装了Docker,并以默认的网络配置运行,如果你需要安装的详细说明,请看这里。在这个环境里,我所做的就是在每一个主机上配置一个静态IP、配置DNS服务器、运行yum update,然后安装Docker。

主机间的网络通讯

接下来我们分别在每一个Docker主机上启动一个Docker容器,然后看看会发生什么。我们将通过以下命令在Docker主机上运行一个busybox容器:
docker run -it --rm busybox

image2.png

因为我们本地并没有busybox镜像,所以Docker会从远程仓库中下载该镜像。容器运行后就会进入一个Shell,让我们来看看相应的网络配置。如下图所示。
image3.png

嗯,如你所看到的,两个Docker主机上的容器拥有相同的IP地址。这一点非常重要,也就是说,默认情况下,所有的容器网络都会隐藏在实际网络之下。如果我们退出容器,并检查每一个主机上的防火墙规则,那我们会发现:
image4.png

这里是一个对所有容器有效的策略路由规则(隐藏在NAT下)。这个规则允许所有的容器都可以和外部网络(实际网络)通讯。但是我们不允许外界规则返回到容器内。如前所述,这可以通过端口映射的方式实现点到点的网络接口。举个例子,我们可以通过下面的命令实现在Docker主机上映射8080端口到busybox容器的80端口。
docker run -it –rm –p 8080:80 busybox

如果我们运行了刚才的命令,那么我们将会看到,防火墙创建了一个关联NAT的规则,完成了从容器的80端口到主机的8080端口的功能。
image5.png

因此在默认模式下,如果在docker1主机上运行的busybox容器,想要与docker2上的容器进行通讯,它只能将容器内的端口映射到主机上端口。在这种情况下,网络拓扑如下图所示.......
image6.png

图中有三个不同的网络区域,包括10.20.30/24的物理网络以及两个容器相关的网络。两个容器网络都基于相应主机的docker0的网桥接口。默认情况下,该网络将介于172.17.0.0/16。docker0桥接接口自身的IP地址为172.17.42.1。我们可以通过ifconfig命令查看整个Docker的网络情况。
image7.png

这里我们可以看到docker0网桥,eth0网口的地址为0.20.30.0/24和本地回环信息。

主机内部网络通讯

任何基于同一个docker0网桥的容器都可以通过IP地址直接通讯,
这并不需要用NAT方式或者其它的网络通讯手段来实现。还有一个有趣的实现方法,那就是容器连接。让我们看下面的场景例子,如下图所示。
image8.png

让我们假设我们要运行的容器都运行于同一个Docker主机上。假定这两个容器之间需要进行网络通讯。我不知道你有没有注意到,Docker并没有为容器指定一个固定的IP地址(注:可能有一些方法可以做到这点,但是据我所知,没有哪一个原生Docker支持这种做法),现在只能动态分配IP地址,这使得在某个容器上创建对外的服务是一件非常困难的事情。这个时候,就是容器连接的用武之地了。

为了从一个容器连接到另一个容器,你只需要在run命令中使用link参数即可。举个例子,让我们先启动一个名为busybox1的容器:
docker run --name busybox1 -p 8080:8080 -it busybox

上面的命令将会启动一个运行在busybox的镜像上的名为busybox1的容器,它通过主机的8080端口映射到容器的8080端口,这没什么新鲜的。现在,我们启动第二个名为busybox2的容器。命令如下:
docker run --name busybox2 --link busybox1:busybox1 -it busybox

请注意,命令中我使用了--link参数,它将告诉busybox2容器连接到busybox1容器。现在busybox2容器已经启动,让我们看看在容器上到底发生了什么?我们注意到通过busybox1这个名字我们可以直接ping通busybox1容器。
image9.png

如果我们查看/etc/hosts,我们将会发现Docker创建了一个主机条目,里面是从busybox1容器到busybox2容器的正确的IP地址。是不是很酷?如果我们检查下容器环境变量,我们又将发现一些有趣的东西,如下图所示。
image10.png

有意思。我们注意到图中很多的环境变量,它们都与busybox1上的端口映射信息有关。你可以在这里阅读更多的关于容器连接的资料。

最后,我想谈谈ICC参数。正如我前面提到的,如果有两个容器,它们都在同一个Docker主机内,那它们默认就可以进行通讯。这是真的,因为Docker默认设置了ICC参数并且将它的值设置为true。ICC是标准的容器通讯。如果我们设置将它的值设置为false,那容器只有通过连接或者暴露出的端口才能进行通讯。让我们看下面这个例子。


注意:在主机上改变 ICC的参数必须基于一个运行的Docker。目前我运行在CentOS 6.5上。
首先,让我们在docker1主机上把ICC标识设置为false。为此,我们编辑/etc/sysconfig/docker文件。这看起来像是我们第一次编辑它。
image11.png

改变other_args所在行的值为-ice=false
image12.png

保存该文件并重启Docker服务(service docker restart)。一旦服务启动成功,ICC将不可见,然后我们继续测试。

在docker1主机上,我将要下载并运行一个Apache容器,同时将容器的80端口映射到主机的8080端口上。
image13.png

这里我们可以看到我通过守护进程的方式启动了一个名为web的容器,并且映射了端口。现在让我们启动第二个名叫DB的容器并连接到刚才的web容器上。
image14.png


注意:刚才的DB容器并不能ping通web容器。让我们试着允许web容器在暴露的端口上访问。
image15.png
哈哈!正如我期望的那样现在起作用了,我只能连接容器到暴露的端口上。

原文链接:Docker Networking 101 – The defaults(翻译:隋鑫 审校:郭蕾)

1 个评论

try weave if you just need nat to expose certain services.

要回复文章请先登录注册