Docker系列学习(九)——Docker网络介绍

   docker网络主要作用是使得不同容器之间可以进行网络访问,我们前文已经提到了不同容器之间是相互隔离的,但是我们可以使用添加容器卷的方式(参考Docker系列学习(六)——容器数据卷)使得不同容器可以对同一个文件夹中的数据进行操作。更多的情况是不同容器之间我们需要能够通过ip进行相互访问,比如我在一个容器中A启动了一个web应用,另一个容器B中启动了mysql,现在我需要让容器A中的web应用能够访问到容器B中的mysql数据库,能不能直接访问到呢?该怎样访问呢?这时候就需要Docker网络的知识了。

docker0介绍

  我们使用ip addr命令,看一下在启动容器前我们的主机中网络配置状况。

  可以看到我们当前主机中有3个网络,前两个网络都分别是内部循环ip,内网ip,而第三个网络就是我们今天的主角docker0,我可以看到docker0的ip是172.18.0.1(记住这个ip)。

  接下来我们先启动一个容器(就拿之前文章中制作的一个my_ubuntu镜像),进入到容器中,并使用ip addr查看容器内部网络情况(如果报错未找到ip命令,则需要先在容器中使用apt-get install iproute2 iproute2-doc inetutils-ping安装)。

   注意到此时我们容器内部出现了一个ip为172.18.0.2的一个网络连接,与此同时我们再来看看主机中现在的网络状况(启动了一个容器之后)。

   当启动一个容器之后我们主机的网络中出现了一个新的网络而这个网络编号129:128刚好和容器中的128:129是匹配的。因此我们可以知道docker采用的是veth-pair的方式构建了一个虚拟的网络连接。我们再启动一个容器2来试一试。

   这时再来看看主机的网络变化(此时我们有两个容器正在运行)

   好了现在明白了,每当我们启动一个容器的时候,主机和容器之间将会创建一个veth-pair,进行网络桥接通信,ip地址默认会从docker0的172.18.0.1开始往后。

  我们可以试下容器1与容器2之间,主机与容器之间能不能ping通(能不能通信)。

 

   可以看到容器2是可以ping通容器1,主机也可以ping通容器内部的,这意味着容器之间是可以相互使用ip进行通信的。

--link 容器互联

  前面我们知道了容器之间可以通过ip进行通信,但是当我们的ip过多时,例如有多个数据库连接时就使用ip来连接就比较麻烦了,因此docker提供了一个使用容器别名来进行网络连接的方法。

  我们使用以下命令使得新启动的my_ubuntu03能够通过ping 容器别名的方式来ping通网络。

 docker run -it --link my_ubuntu02 --link my_ubuntu01 --name my_ubuntu03 my_ubuntu

  这里的--link后跟的是已经启动的容器的别名,这个命令的意思是从镜像my_ubuntu中启动一个叫my_ubuntu03的容器,并且将03和02,01进行连接。

   上面的图片可以看到,我们可以使用ping my_ubuntu01(这是我们之前启动过的一个容器别名)ping通了吧,同样的由于我们也link了my_ubuntu02,我们同样可以使用别名ping通my_ubuntu02。

  注意:此时我们是无法从my_ubuntu01或者my_ubuntu02通过别名来ping通03的,因此这个--link可以理解为单向的连接。

   是可以的吧,其实我们大致可以猜到,--link就是把我们容器01和容器02的ip写进了容器03的/etc/hosts文件中,这样我们就能使用别名来ping了。

  我们已经知道--link的深层含义,但是--link是有缺点的,并不推荐使用,这个我们需要先从docker自动分配ip说起。上一小节中我们知道每当启动一个容器docker就会自动按顺序为其分配一个ip,例如容器1分配的是172.18.0.2,容器2分配的是172.18.0.3,容器3分配的是172.18.0.4.......但是当你的容器停止或者被删除时,你分配的ip就会被释放,举个例子,当你容器1停止时172.18.0.2这个ip就会被释放,如果有新的容器4在这个时候启动,那么这个新的容器4的IP就会是172.18.0.2,如果容器1在容器4之后启动,那么容器1的ip就会是172.18.0.5了(因为172.18.0.2被容器4占用了)。这时候就出现问题了,因为我们再my_ubuntu03这个容器中注册的是my_ubuntu01对应的ip是172.18.0.2,如果我们继续使用ping my_ubuntu01,此时其实我们ping的就是容器4(因为容器4的ip是172.18.0.2)而不是容器1了,这显然与我们的想法是不一样的。

自定义网络

  在上一节末尾我们知道,就是当我们的容器重启的时候(停止又启动)此时该容器的ip地址会被释放,在一些情况下重启之后的容器ip会和重启之前不一样的(大家可以试下当停止这个容器的时候,该容器的veth-pair也会消失,由于ip地址的分配是顺序分配的,如果有空缺就会分配,所以当重新启动该容器之前有一个新的容器占领了之前释放的ip地址,则该会分配到新的veth-pair和ip地址)。这个问题就会导致,本来我web服务连接好mysql,但是因为一些原因mysql容器被重启了(并且释放的ip地址被其他的容器占用了),此时web服务中的ip就失效了无法连接重启后的mysql,这就导致我们又得进web服务中修改,这非常麻烦,特别是在多个mysql共存的情况下,非常容易出现错误。为了解决这个问题我们就需要采用自定义网络来配置我们容器之间的连接了。

  我们可以使用docker network ls查看docker网络配置。

docker network ls

docker网络模式介绍:

桥接模式(bridge):容器之间通讯采用一个中间者来传输,容器之间不直接连接,而是使用把信息传递给中间者,让中间者转发。

none:不配置网络

host:与宿主机共享网络

  在平常使用docker run 的时候其实有个默认参数是--net bridge换句话说平常默认使用的是docker0的网络也就是172.18.0.x这个字段,所以我们前面启动的容器的时候自动分配的ip都是在172.18.0.x这里面的。docker0的网络(172.18.0.x)是有缺陷的,无法直接访问的。因为我们需要创建一个新的网络。

自定义网络:

  主要是使用docker network create命令,主要的参数有

常用参数
--driver  创建的网络连接是什么模式,默认是bridge(桥接)
--subnet 配置子网
--gateway 配置网关地址

  接下来我们试一试完整命令:

dorcker network create --driver bridge --subnet 180.168.0.0/16 --gateway 180.168.0.1 my_net

  从命令可以知道我们创建的ip地址为180.168.0.x,其中网关式180.168.0.1,命名为my_net,我们可以使用docker network ls命令看到,我们创建的my_net网络已经出现了。

   接下来我们就使用这个网络来启动容器,这样容器的ip就会是以180.168.0.x开始了。尝试一下。

docker run -it --name ubuntu_my_net --net my_net my_ubuntu

   可以看到此时的ubuntu_my_net容器,的ip地址为180.168.0.2了而不是之前默认的(172.18.0.x),我们再使用这种方式创建多个容器,分别为ubuntu_my_net02,ubuntu_my_net03,所以我们现在已经通过这种方式出现了3个容器了分别是ubuntu_my_net,ubuntu_my_net02,ubuntu_my_net03,有趣的是这三个网络就可以自动使用别名通信了(我并没有使用--link指定),相互之间可以使用ping 容器名的方式进行通信了,很显然这种方式比--link更加高级(/etc/hosts中并没有加入任何信息)。

  推荐使用这样的方式来进行容器间的联网,每个集群一个网络,这样保证了集群之间没有影响,更加安全,并且开头提到的问题也可以解决了,使用别名的方式来连接,由内部docker内部自动维护ip对应关系,即使你容器3重新启动,只要是我使用容器名来连接,那么必然是连接到你的指定的容器的,不会出现ip混乱的情况。

网络联通:

  前面已经解决单个集群之间内部的通信了,但是集群与集群之间又该怎么通信呢?

  我们已经在my_net网络下生成了三个容器,接下来我们在默认网络下(docker0)下也生成两个容器分别叫ubuntu_01和ubuntu_02,这样我们就可以得到如下示意图:

   我们可以在ubuntu_01中尝试一下,能不能通过ip与my_net的容器中就进行通信。

   可以看出在不同的两个网络集群中是无法ping通的,因为单个网络集群中都是通过该集群中的某一个ip来进行转发信息的(例如docker0网络中是172.168.0.1来进行转发该集群下不同的信息的),而集群之间如果没有联系,那么是无法使得不同集群之间得到网络交流。

  那我们应该如何操作呢?难道是连接两个网络么?(如果是直接打通两个网络那干嘛要分成两个网络啊)我们是将容器连接进网络,从而实现不同集群之间的容器互通的,看示意图。

   主要使用的命令是:

docker network connect 网络名 容器名

  比如此处我将把ubuntu_01连接到my_net网络中从而使得其可以ping通my_net网络下的其他容器(前文已经试过如果不联通网络,ubuntu_01是无法通过ip来联通my_net网络下的其他容器的),命令如下

docker network connect my_net ubuntu_01

  

   其实我们通过查看my_net网络的详细信息(docker network inspect my_net)

  居然发现了my_net下居然出现了ubuntu_01的另一种ip地址(生成ubuntu_01时是采用默认的docker0的ip的,也就是172.18.0.x),这就是同一个容器两个ip地址!

  我们在ubuntu_01下查看网络设置(ip addr)

   可以发现确实存在两个ip的分别是172.18.0.2和180.168.0.5.

拓展:

  在VScode中可以在右侧查看到所有的network信息,并且可以通过右键点击查看不同网络的inspect,相当方便。

 

 

posted @ 2022-04-21 17:36  Circle_Wang  阅读(667)  评论(0编辑  收藏  举报