【Docker 那些事儿】容器网络(上篇)

🌟 前言
当企业开始大规模使用 Docker 时,工程师就需要掌握很多关于网络的知识。 Docker 作为目前流行的轻量级容器技术,有很多令人称道的功能,如前面介绍的 Docker 镜像管理。 然而,Docker 也有很多不完善的地方,网络方面就是 Docker 比较薄弱的部分。 作为初学者的我们来说很有必要深入了解 Docker 的网络知识,以适应更高的网络需求。
1. 容器网络概述
🍑 容器网络概述
容器网络主要用于容器与容器、容器与外网、容器与宿主机之间的通信及互联。
宿主机接通外网,再与容器之间搭建网桥,使容器与宿主机网络联通,以达到容器连接外网的目的,如图所示👇

用户可以创建一个或多个网络,一个容器可以加入一个或多个网络。
同一个网络中的容器可以相互通信,不同网络中的容器相互隔离。
在创建容器之前,用户可以先创建网络,然后再将容器添加到网络,即创建容器与创建网络是分开的。
Docker 的本地网络实现是利用了 Linux 的 Network Namespace 和虚拟网络设备(主要是 Virtual Ethernet Pair),在本地主机和容器内分别创建一个虚拟接口,并使它们彼此连通。
Docker 使用 Linux 的 Namespace 技术来进行资源隔离,例如,PID Namespace 隔离进程、Mount Namespace 隔离文件系统、Network Namespace 隔离网络等。
Network Namespace 为容器提供了独立的网络环境。
Virtual Ethernet Pair 简称 VETH Pair,是一对端口,所有从这对端口进入的数据包都将从另一端出来,反之也是一样。
🍑 查看容器网络
用户要对容器网络进行配置,首先要熟练掌握命令,下面详细介绍容器网络管理命令。
查看容器网络的命令格式为:

参数介绍如下👇
-f
,--filter filter
过滤条件。
--format string
格式化打印结果。
--no-trunc
不缩略显示。
-q
,--quiet
只显示网络对象的 ID。
首先,查看所有容器网络,示例代码如下:

如以上示例所示,容器网络也有其固定的 ID 号,DRIVER 表示容器网络的驱动程序,SCOPE 表示容器网络的作用域。
默认情况下,容器创建完成后,会自动创建 bridge、host、none 三种网络模式。
接着,通过在命令中添加过滤条件查看容器网络,示例代码如下:

以上示例通过 -f 参数添加了 'driver=host'
过滤条件,成功过滤出了 host 网络。
通常 Docker 所显示的容器 ID 号、镜像 ID 号等都是通过缩略之后的结果,只要在命令中添加对应的参数即可显示完整 ID 号。
通过在命令中添加参数,以不缩略的形式列出所有容器网络,示例代码如下:

以上示例在命令中添加了 --no-trunc
参数,以不缩略的形式列出了所有容器网络。
最后,通过在命令中添加参数,列出所有网络的 ID 号,示例代码如下:

以上示例在命令中添加了 -q 参数,列出了所有容器网络的 ID 号。
🍑 创建容器网络
创建网络的命令格式为:

参数介绍如下👇
-d
,--driver string
指定网络模式(默认为 bridge)。
--subnet strings
指定子网网段。
--ip-range strings
指定容器的 IP 地址范围,格式同 subnet 参数。
--gateway strings
子网的 IPV4 或 IPV6 网关。
创建一个容器网络,并指定它的网络模式,示例代码如下:

以上示例创建了一个命名为 test-bridge 的新容器网络,并指定它的网络模式为 bridge 网络。
其余网络属性配置方式与之相同,这里不再赘述。
🍑 删除容器网络
下面将讲解删除容器网络的操作,在生产环境中建议再三确认之后再执行删除操作。
删除容器网络与删除容器同样都需要在命令中添加 rm 参数。
删除容器网络的命令格式为:

首先,查看是否有需要删除的容器网络,示例代码如下:

接着,将需要删除的容器网络进行删除,示例代码如下:

最后,查看容器网络是否被删除,示例代码如下:

从以上示例中,可以看到容器网络 test-bridge 已经成功删除。
🍑 容器网络详细信息
查看容器网络详细信息的命令格式为:

参数介绍如下👇
-f
,--format string
使用给定的模板格式化输出。
示例代码如下:

以上示例查看了 none 网络的详细信息,包括容器网络名称、容器网络 ID 容器网络创建时间等。
🍑 配置容器网络
配置容器网络就是为用户创建的容器添加网络配置,只需要在运行容器的命令中添加指定的网络参数。
配置容器网络的命令格式为:

示例代码如下:

以上示例通过在运行容器中添加 --network
参数,指定了容器的 host 网络模式。
其中,反斜杠 "\"
表示命令没有输入完,上下两行为同一条命令,这种命令输入方式适合在输入长命令时使用。
下面查看容器信息,验证该容器网络是否为 host 模式,示例代码如下:

以上示例通过 docker inspect 命令中添加 grep 参数过滤出了该容器的网络模式为 host 的信息。
下面运行一个不指定网络模式的容器,并查看其网络模式信息,示例代码如下:

由以上示例可见,不指定网络模式,Docker 会默认使用 bridge 网络模式。
🍑 容器网络连接与断开
容器网络连接与断开的命令格式为:

下面通过示例讲解容器网络的连接与断开,首先创建一个容器,示例代码如下:

以上示例运行了一个连接 host 网络的容器。
然后将该容器与 host 网络断开,示例代码如下:

以上示例使该容器与 host 网络断开。
在命令中使用 ID 号即可,并且断开网络之前需要先关闭该容器。
下面再次执行断开网络命令,观察执行结果,示例代码如下:

以上示例中,再次执行断开命令时发生报错,提示网络已断开。
下面将该容器添加到 none 网络中,示例代码如下:

以上示例中,供执行了两次连接 none 网络的命令。
第一次执行命令使容器成功连接了 none 网络,再次执行命令连接时报错,提示容器已在 none 网络中。
2. none 网络
顾名思义,none 网络就是什么都没有的网络。
在这种模式下,Docker 容器网络拥有自己的网络命名空间,但并不为 Docker 容器进行任何网络配置。
也就是说,这个模式下的容器除了本地环回接口,没有其他任何 网卡、IP、路由 等信息,如图所示👇

没有网络配置的情况下,Docker 开发者才能在此基础上做其他无限多可能的网络定制开发。
用户可以自己为 Docker 容器添加网卡、配置 IP 地址等。
这也正体现了 Docker 开放的设计理念。
下面使用默认网络模式创建一个 BusyBox 容器。示例代码如下:

以上示例运行了一个被命名为 test-default 的 BusyBox 容器,并且此时已进入容器。
下面在容器终端中查看容器 IP 地址,示例代码如下:

从以上示例中可以看到,Docker 为容器分配了 IP 地址。
下面通过 ping 命令测试容器网络是否能够联通外网,示例代码如下:

以上示例中,通过 ping 协议连通了外网,说明容器能够正常上网,。
作为对比验证,再运行一个 BusyBox 容器,将网络设置为 none 模式,示例代码如下:

通过以上示例的对比验证,可以很清晰地理解 Docker 容器 none 网络就是没有网络。
没有网络意味着安全性非常高,这样的网络模式通常可以用在一些对安全性要求较高,并且不需要联网的应用中,如用来产生随机密码的应用。
3. host 网络
一个 Docker 容器一般会被分配一个独立的网络命名空间,但如果启动容器时选择了 host 模式,那么这个容器将不会分配一个独立的网络命名空间,而是和宿主机共享同一个网络命名空间。
容器将使用宿主机的 IP 地址和 端口号,如图所示👇

下面在宿主机上运行一个网络模式为 host 的 Web 应用容器,示例代码如下:

上述示例运行了一个命名为 test-host 的 Nginx 容器,并指定网络模式为 host,使其在后台运行。
下面查看容器信息,示例代码如下:

从以上示例中,可以看出网络模式为 host。
下面再查看宿主机的 80 端口状态,示例代码如下:

从以上示例中可以看出 80 端口被 Nginx 进程占用。
创建容器时并没有指定端口映射,容器默认使用了宿主机的 80 端口,证明在 host 模式下宿主机与容器共用端口。
下面通过 curl 工具测试容器网站是否能够被访问,示例代码如下:

下面是浏览器的访问测试结果,如图所示👇

4. bridge 网络
在 bridge 模式下,Docker 守护进程创建了一个虚拟以太网桥 Docker0,附加在其上的网卡之间能自动转发数据包。
默认情况下,守护进程会创建一对对等接口,将其中一个接口设置为容器的 eth0 接口,另一个接口 veth 放置在宿主机的命名空间中,从而将宿主机上的所有容器都连接到这个内部网络上。
同时,守护进程还会从网桥的私有地址空间中分配一个 IP 地址和子网给该容器。如图所示👇

下面通过 brctl show 命令可以查看容器网桥信息,在这之前需要安装网桥工具管理包,否则命令不生效,示例代码如下:

安装完成之后,即可查看容器网桥信息,示例代码如下:

下面运行一个网络模式为 bridge 的容器,示例代码如下:

以上示例在后台运行了一个命名为 test-nginx 的 Nginx 容器,并指定它的网络模式为 bridge,将宿主机的 8000 端口映射到了容器的 80 端口。
下面接着查看容器网桥信息,示例代码如下:

从以上示例中可以看到,网桥 Docker0 上挂载了网络接口,vethaaf8a5e 就是新创建的 Nginx 容器的虚拟网卡,名称以 veth 开头。
下面运行一个 BusyBox 容器,并设置网络模式为 bridge,查看其网卡信息,示例代码如下:

上述示例中,veth70a4bee 就是 BusyBox 容器的虚拟网卡,名称同样以 veth 开头。
Docker 采用 NAT(Network Address Translation,网络地址转换)方式,将容器内部服务监听的端口与宿主机的某一个端口进行映射,使宿主机以外的网络可以将网络报文发送至容器内部。
访问容器时,需要访问宿主机的 IP 及端口。
因为增加了网络层,所以会影响网络的传输效率,如图所示👇

在同一个服务器中,可能运行着多个业务,若这些业务都使用默认端口将会产生冲突,这时就需要容器映射不同的端口。
下面在宿主机中安装 Apache 服务,示例代码如下:

安装完成之后,启动 Apache 服务,示例代码如下:

以上示例在宿主机中启动了一个 Apache 服务。
下面通过查看端口的形式验证 Apache 是否正常运行,示例代码如下:

以上示例查看宿主机 80 端口,可以看到 80 端口被 Apache 占用,服务正常运行。
下面创建一个 Nginx 容器,并配置端口映射,示例代码如下:

以上示例在后台运行了一个命名为 test-nginx 的 Nginx 容器,并将容器 80 端口映射到宿主机的 8000 端口。
下面测试用户通过宿主机的 8000 端口是否能够访问容器中的 Nginx 服务,示例代码如下:

从以上示例可以看出,通过宿主机的 8000 端口可以正常访问 Nginx 容器。
下面再访问宿主机的 Apache 服务,示例代码如下:

从以上示例可以看出,通过宿主机 80 端口可以正常访问 Apache 服务。
有了端口映射,容器与容器、容器与宿主机的业务就不会出现冲突,保证了业务都能够被正常访问。
版权声明: 本文为 InfoQ 作者【飞向星的客机】的原创文章。
原文链接:【http://xie.infoq.cn/article/bb4f3b40cb252a18af4c9be76】。文章转载请联系作者。
评论