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
    复制代码

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

查看所有标签

猜你喜欢:

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

The Haskell School of Expression

The Haskell School of Expression

Paul Hudak / Cambridge University Press / 2000-01 / USD 95.00

Functional programming is a style of programming that emphasizes the use of functions (in contrast to object-oriented programming, which emphasizes the use of objects). It has become popular in recen......一起来看看 《The Haskell School of Expression》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具