docker中的网络通信方式

前言

在前面的文章中相信大家对docker的使用有了一个基本的了解,但实际在docker上部署我们的应用的时候不禁要思考以下问题:

1.在同一台机器启动了多个容器,每个容器上运行着不同的业务,而这些业务相互之间如何互相访问吗?
2.在不同机器上启动不同容器,这两个不同容器之间又应该如何访问?

为了解决上面几个问题,本文来探讨一下docker中的网络通信方式。

Docker的网络模式

常用的docker网络模式有5种,每种模式有各自使用的情景,可以再ocker run创建 Docker 容器时,可以用 –net 选项指定容器的网络模式。

host模式
使用 –net=host 指定。与宿主机共享网络,此时容器没有使用网络的namespace,宿主机的所有设备,如Dbus会暴露到容器中,因此存在安全隐患。
如果启动容器的时候使用 host 模式,那么这个容器将不会获得一个独立的 Network Namespace,而是和宿主机共用一个 Network Namespace。容器将不会虚拟出自己的网卡,配置自己的 IP 等,而是使用宿主机的 IP 和端口。

container模式
使用 –net=container:NAME_or_ID 指定。指定与某个容器实例共享网络。
这个模式指定新创建的容器和已经存在的一个容器共享一个 Network Namespace,而不是和宿主机共享。新创建的容器不会创建自己的网卡,配置自己的 IP,而是和一个指定的容器共享 IP、端口范围等。同样,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。两个容器的进程可以通过 lo 网卡设备通信。

none模式
使用 –net=none 指定。不设置网络,相当于容器内没有配置网卡,用户可以手动配置。
这个模式和前两个不同。在这种模式下,Docker 容器拥有自己的 Network Namespace,但是,并不为 Docker容器进行任何网络配置。也就是说,这个 Docker 容器没有网卡、IP、路由等信息。需要我们自己为 Docker 容器添加网卡、配置 IP 等。

bridge模式
使用 –net=bridge 指定,默认设置。此时docker引擎会创建一个veth对,一端连接到容器实例并命名为eth0,另一端连接到指定的网桥中(比如docker0),因此同在一个主机的容器实例由于连接在同一个网桥中,它们能够互相通信。容器创建时还会自动创建一条SNAT规则,用于容器与外部通信时。如果用户使用了-p或者-Pe端口端口,还会创建对应的端口映射规则。

bridge 模式是 Docker 默认的网络设置,此模式会为每一个容器分配 Network Namespace、设置 IP 等,并将一个主机上的 Docker 容器连接到一个虚拟网桥上。

当 Docker server 启动时,会在主机上创建一个名为 docker0 的虚拟网桥,此主机上启动的 Docker 容器会连接到这个虚拟网桥上。虚拟网桥的工作方式和物理交换机类似,这样主机上的所有容器就通过交换机连在了一个二层网络中。

接下来就要为容器分配 IP 了,Docker 会从 RFC1918 所定义的私有 IP 网段中,选择一个和宿主机不同的IP地址和子网分配给 docker0,连接到 docker0 的容器就从这个子网中选择一个未占用的 IP 使用。如一般 Docker 会使用 172.17.0.0/16 这个网段,并将 172.17.42.1/16 分配给 docker0 网桥(在主机上使用 ifconfig 命令是可以看到 docker0 的,可以认为它是网桥的管理接口,在宿主机上作为一块虚拟网卡使用)

当创建一个 Docker 容器的时候,同时会创建了一对 veth pair 接口(当数据包发送到一个接口时,另外一个接口也可以收到相同的数据包)。这对接口一端在容器内,即 eth0;另一端在本地并被挂载到 docker0 网桥,名称以 veth 开头(例如 vethAQI2QT)。通过这种方式,主机可以跟容器通信,容器之间也可以相互通信。Docker 就创建了在主机和所有容器之间一个虚拟共享网络。

自定义模式
使用自定义网络,可以使用docker network create创建,并且默认支持多种网络驱动,用户可以自由创建桥接网络或者overlay网络,通过overlay网络以实现在不同宿主机的容器之间需要互相通信的情景。

了解了docker网络之后我们来尝试解以上问题。

同主机不同容器之间通信

这里同主机不同容器之间通信主要使用Docker桥接(Bridge)模式。该bridge接口在本地一个单独的Docker宿主机上运行,并且它是我们后面提到的所有三种连接方式的背后机制。

先看宿主机的ip地址

运行一个centos镜像, 查看ip地址得到:172.17.0.2

以同样的命令再起一个容器,查看ip地址得到:172.17.0.4

连接方式
– 方式一:可以通过使用容器的IP地址来通信。这种方式会导致IP地址的硬编码,不方便迁移,并且容器重启后IP地址可能会改变,除非使用固定的IP地址。
– 方式二:可以通过宿主机的IP加上容器暴露出的端口号来通信。这种方式比较单一,只能依靠监听在暴露出的端口的进程来进行有限的通信。
– 方式三:可以使用容器名,通过docker的link机制通信。这种方式通过docker的link机制可以通过一个name来和另一个容器通信,link机制方便了容器去发现其它的容器并且可以安全的传递一些连接信息给其它的容器。使用name给容器起一个别名,方便记忆和使用。即使容器重启了,地址发生了变化,不会影响两个容器之间的连接。
– 方式四:创建bridge网络

方式一:使用容器的IP地址来通信

方式二:使用宿主机的IP加上容器暴露出的端口号来通信

这两种方式必须知道每个容器的ip,在实际使用中并不实用。

方式三:使用docker的link机制通信

实际上–link机制就是在Docker容器中的/etc/hosts文件中添加了一个centos-1容器的名称解析。有了这个名称解析后就可以不使用IP来和目标容器通信了,除此之外当目标容器重启,Docker会负责更新/etc/hosts文件,因此可以不用担心容器重启后IP地址发生了改变,解析无法生效的问题。

centos-1容器的/etc/hosts文件

centos-2容器的/etc/hosts文件

当docker引入网络新特性后,link机制变的有些多余,但是为了兼容早期版本,–link机制在默认网络上的功能依旧没有发生变化,docker引入网络新特性后,内置了一个DNS Server,但是只有用户创建了自定义网络后,这个DNS Server才会起作用。

方式四:创建bridge网络
步骤1:运行如下命令创建bridge网络:docker network create testnet
查询到新创建的bridge testnet。

能看到上面创建的网络就表示创建成功了。

步骤2:运行容器连接到testnet网络
使用方法:docker run -it –name <容器名> —network –network-alias <网络别名> <镜像名>

步骤3.从一个容器ping另外一个容器,测试结果如下:
[root@fafe2622f2af /]# ping centos-1
PING centos-1 (172.20.0.2) 56(84) bytes of data.
64 bytes from centos-1.testnet (172.17.0.2): icmp_seq=1 ttl=64 time=0.158 ms
64 bytes from centos-1.testnet (172.17.0.2): icmp_seq=2 ttl=64 time=0.108 ms
64 bytes from centos-1.testnet (172.17.0.2): icmp_seq=3 ttl=64 time=0.112 ms
64 bytes from centos-1.testnet (172.17.0.2): icmp_seq=4 ttl=64 time=0.113 ms

推荐使用这种方法,自定义网络,因为使用的是网络别名,可以不用顾虑ip是否变动,只要连接到docker内部bright网络即可互访。bridge也可以建立多个,隔离在不同的网段。

跨主机不同容器之间通信

机器A容器访问机器B本地应用

直接访问,没有什么特殊,机器A容器中访问机器B的IP地址和应用的端口即可。

机器A本地应用访问机器B容器

只要机器B中的容器使用host网络或者将端口映射到宿主机(机器B)端口,就可以通过宿主机IP加端口访问。

机器A容器访问机器B容器

机器A的容器无需特殊配置,机器B中的容器使用host网络或者将端口映射到宿主机(机器B)端口,就可以通过宿主机IP加端口访问。

发表评论

电子邮件地址不会被公开。 必填项已用*标注