Docker容器间通信方法

栏目: 编程工具 · 发布时间: 5年前

内容简介:几天前,为了解决日常在本地进行日常工作和开发测试之间的矛盾,利用docker在Windows系统中搭建了基于Linux的测试环境:Docker容器间的通信方式根据媒介可以分为:Docker的网络驱动模型分类:

几天前,为了解决日常在本地进行日常工作和开发测试之间的矛盾,利用 docker 在Windows系统中搭建了基于 Linux 的测试环境: 借助Docker,在win10下编码,一键在Linux下测试 。在这边文章里主要介绍了如何在本地通过docker构建与生产环境基本一致的环境并一键运行、测试我们的代码。Docker官方建议每个容器中只运行一个服务,但是我们的项目可能是由多个服务组成,在服务中可能会需要 mysqlredis 等中间件的支持,所以通常我们将一个项目的不同服务划分到不同容器中。这种做法虽然具有低耦合、高隔离性等优点,但是也增加了服务之间通信的复杂度。

Docker容器间的通信方式根据媒介可以分为: volume共享通信网络通信 等;根据通信范围也可以分为: 同主机通信跨主机通信 等。而本文主要针对容器间的网络通信方法进行讨论。

1. Docker的网络驱动模型

Docker的网络驱动模型分类:

  • bridge :Docker中默认的网络驱动模型,在启动容器时如果不指定则默认为此驱动类型;
  • host :打破Docker容器与宿主机之间的网络隔离,直接使用宿主机的网络环境,该模型仅适用于Docker17.6及以上版本;
  • overlay :可以连接多个docker守护进程或者满足集群服务之间的通信;适用于不同宿主机上的docker容器之间的通信;
  • macvlan :可以为docker容器分配MAC地址,使其像真实的物理机一样运行;
  • none :即禁用了网络驱动,需要自己手动自定义网络驱动配置;
  • plugins :使用第三方网络驱动插件;

以上是Docker支持的几种网络驱动模型,它们都具有独特的特点和应用范围,为了更加详细地了解Docker的网络运行原理,下面挑选几种较为重要的网络模型进行研究。

2. bridge

2.1 docker的默认网桥

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 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
# 输出结果:
[
    {
        "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互相通信,但是无法通过容器名进行通信:

# 在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提供了以下几种:

  1. 在启动docker容器时加入 --link 参数,但是目前已经被废弃,废弃的主要原因是需要在连接的两个容器上都创建 --link 选项,当互连的容器数量较多时,操作的复杂度会显著增加;

  2. 启动docker容器后进入容器并修改 /etc/host 配置文件,缺点是手动配置较为繁杂;

  3. 用户自定义 bridge 网桥,这是目前解决此类问题的主要方法;

2.2 用户自定义bridge

用户自定义bridge相对于使用默认bridge的主要优势:

  • 用户自定义bridge可以在容器化的应用程序提供更好的 隔离效果 和更好的 互通性 : 这段话看似有些矛盾,但是其中 更好的隔离效果 是针对外界网络,而 更好的互通性 则是指同一 bridge 下的不同容器之间。还是以之前的分别部署了Web服务和mysql服务的两个容器container1、container2为例,container1只需要对外界网络暴露Web服务的 80 端口,而负责后端的container2只需与container1互连,不需要对外暴露,有效地保护了后端容器的安全性,提高了容器对外的隔离效果。而同属于用户自定义 bridge 的容器container1、container2之间自动将所有端口暴露,方便容器间进行无障碍的通信,而不会遭受到外界的意外访问。
  • 用户自定义bridge在容器之间提供了自动DNS解析: 这也是本文讨论的重点,不同于默认bridge只能通过IP互连的限制,用户自定义的bridge自动提供了容器间的DNS解析功能,容器间可以通过容器名或别名进行通信。

2.2.1 如何使用用户自定义bridge

  1. 创建用户自定义bridge:

    docker network create my-net        # 创建了一个名为"my-net"的网络
    复制代码
  2. 将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"不同。

  3. 通过容器名或别名互连: 进入到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服务器了。

  4. 断开网络: 由于我们的容器仍然连接着默认bridge docker0 ,而现在我们已经不需要它,所以应该将容器与 docker0 的连接断开,执行以下操作:

    # 断开容器与docker0的连接
    docker network disconnect bridge test_demo
    docker network disconnect bridge mysqld5.7
    复制代码

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

深入理解C#(第3版)

深入理解C#(第3版)

斯基特 (Jon Skeet) / 姚琪琳 / 人民邮电出版社 / 2014-4-1 / 99.00元

本书是世界顶级技术专家“十年磨一剑”的经典之作,在C#和.NET领域享有盛誉。与其他泛泛介绍C#的书籍不同,本书深度探究C#的特性,并结合技术发展,引领读者深入C#的时空。作者从语言设计的动机出发,介绍支持这些特性的核心概念。作者将新的语言特性放在C#语言发展的背景之上,用极富实际意义的示例,向读者展示编写代码和设计解决方案的最佳方式。同时作者将多年的C#开发经验与读者分享,读者可咀其精华、免走弯......一起来看看 《深入理解C#(第3版)》 这本书的介绍吧!

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

html转js在线工具
html转js在线工具

html转js在线工具