|
|
Docker 网络基础介绍
|
|
作者:Michael Hausenblas 来源:CSDN 发布于 2016-5-25 |
|
|
当你开始扩大 Docker 的应用范围时,忽然发现,你需要了解很多关于网络的知识。作为 Docker 网络的入门介绍,本文将从小处着手,首先你要考虑如何管理容器之间的连接。Docker 容器需要有个主机才能运行,该主机既可以是实体机器(例如:企业数据中心的裸机服务器),也可以是 on-prem 或云中的虚拟机。如图一所示,主机上会运行 Docker 后台程序与 Docker 客户端。一方面,你可以与 Docker 注册表交互(pull 或 push Docker 图片);另一方面,也可以启动、停止或监视容器。
图1. 简化版 Docker 架构(单主机)
主机与容器之间的关系为 1:N。也就是说,一个主机上通常会运行着多个容器。例如,Facebook 报告称,取决于机器的规模,每个主机上平均运行着10到40个容器。此外,Mesosphere 对裸机进行的多项负载测试结果显示,在每个主机上运行250以内的容器是可能的。
不过,无论是单主机部署模式,还是采用了机器集群,你都不可避免地需要面对网络:
对大多数单主机部署而言,问题可以归结为通过共享卷组(shared volume)进行数据交换还是通过网络(基于 HTTP 或其他协议)进行数据交换。尽管 Docker 数据卷组简单易用,但也会导致紧密耦合,这意味着更难将单主机部署转变为多主机部署。通常,共享卷组的优势在于数据处理速度。
在多主机部署中,你需要考虑两个方面:单个主机内的容器如何相互交流?不同主机之间的容器又如何交流?性能考虑与安全要素都将影响你的设计决定。当单主机的容量无法满足需求(请参考前文关于单主机承载的平均容器数及最大容器数的讨论),或打算使用诸如 Apache Spark, HDFS 或 Cassandra 之类的分布式系统时,多主机部署就变得必不可少了。
分布式系统与数据局部性
使用分布式系统(进行计算或存储)的主要好处通常来自并发处理,后者常常伴随数据局部性。此处,数据局部性是指将代码发布至数据所处位置的原则,而不是传统的那样倒过来。试思考以下情况:如果你的数据集大小以 TB 计,而代码量以 MB 计,在集群中移动代码其实比将多个 TB 的数据移至中央处理区更加高效。除了获得并发处理的优势,分布式系统还具备更好的容错性,因为系统的一些部分仍或多或少保留了独立运转的能力。
简而言之,Docker 网络是 Docker 提供的原生容器 SDN 解决方法。概括来说,Docker 网络提供了四种可选模式:桥接模式、主机模式、容器模式以及无网络模式。本文将逐一讨论这四种模式与单主机配置相关的内容,并在文末总结时提及诸如安全之类的一般话题。
桥接模式网络
在此模式下(参考图2),Docker 后台程序会创建 docker0,一个虚拟的以太网桥,用于自动转发与之连接的任意网络接口间的数据包。默认情况下,该后台进程会创建一对对等接口(peer interface),分派其一为容器的 eth0 接口,另一个则分派在主机的命名空间下,同时从私有 IP 地址范围中为该桥接分配一个 IP 地址或子网络,由此,同一主机上的所有容器都连接到此内网中。
实例1. 运行中的 Docker 桥接模式网络
$ docker run -d -P --net=bridge nginx:1.9.1$ docker ps
CONTAINER ID IMAGE COMMAND CREATED
STATUS PORTS NAMES17d447b7425d nginx:1.9.1 nginx -g 19 seconds ago
Up 18 seconds 0.0.0.0:49153->443/tcp, 0.0.0.0:49154->80/tcp trusting_feynman |
注意
由于桥接模式是 Docker 默认的连接模式,在示例1中,你也可以使用docker run -d -P nginx:1.9.1。如果你没有使用 -P 参数(用于发布容器的所有公开端口),也没有使用 -p host_port:container_port(用于发布特定的端口),IP 数据包在主机之外就无法路由至容器。
图2. 桥接模式网络设置
主机模式网络
该模式能有效禁止 Docker 容器的网络隔离。因为容器共享了主机的网络命名空间,它会直接暴露于公网。因此,你需要通过端口映射(port mapping)开展协调工作。
实例2. 运行中的 Docker 主机模式网络
$ docker run -d --net=host ubuntu:14.04 tail -f
/dev/null$ ip addr | grep -A 2 eth0:2: eth0:
<BROADCAST,MULTICAST,UP,LOWER_UP>
mtu 9001 qdisc mq state UP group default qlen 1000
link/ether 06:58:2b:07:d5:f3 brd ff:ff:ff:ff:ff:ff
inet **10.0.7.197**/22 brd 10.0.7.255 scope global dynamic eth0
$ docker ps CONTAINER ID IMAGE
COMMAND CREATED STATUS
PORTS
NAMES b44d7d5d3903
ubuntu:14.04 tail -f 2 seconds ago Up 2 seconds
jovial_blackwell $ docker exec -it b44d7d5d3903 ip addr2: eth0:
<BROADCAST,MULTICAST,UP,LOWER_UP>
mtu 9001 qdisc mq state UP group default qlen 1000
link/ether 06:58:2b:07:d5:f3 brd ff:ff:ff:ff:ff:ff
inet **10.0.7.197**/22 brd 10.0.7.255 scope global dynamic eth0 |
正如实例2所示,容器与主机的 IP 地址是一致的,同为 10.0.7.197。
在图3中,在使用主机模式时,容器直接继承了其所在主机的 IP 地址。该模式相比桥接模式运转更迅速(因为不存在路由的开销),但是它直接将容器暴露于公网中,而后者包含了诸多安全隐患。
图3. Docker 主机模式网络设置
容器模式网络
在此模式下,Docker 会重用其他容器的网络命名空间。通常,如果你想提供客制化的网络堆栈,该模式最为有用。该模式也为Kubernetes网络所采用。
实例3. 运行中的 Docker 容器模式网络
$ docker run -d -P --net=bridge nginx:1.9.1$ docker ps CONTAINER ID IMAGE
COMMAND CREATED
STATUS PORTS
NAMES eb19088be8a0 nginx:1.9.1 nginx -g 3 minutes ago
Up 3 minutes0.0.0.0:32769->80/tcp,0.0.0.0:32768->443/tcp
admiring_engelbart $ docker exec -it admiring_engelbart ip addr8:
eth0@if9: <BROADCAST,MULTICAST,UP,LOWER_UP>
mtu 9001 qdisc noqueue state UP group default
link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff
inet **172.17.0.3**/16 scope global eth0
$ docker run -it --net=container:admiring_engelbart ubuntu:14.04 ip addr...8:
eth0@if9: <BROADCAST,MULTICAST,UP,LOWER_UP>
mtu 9001 qdisc noqueue state UP group default
link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff
inet **172.17.0.3**/16 scope global eth0 |
结果(如实例3所示)正如我们所想:通过 –net=container 启动的第二个容器与第一个容器拥有相同的 IP 地址,即自动分配的admiring_engelbart,也即 172.17.0.3。
无网络模式
该模式将容器置于其网络堆栈之内,但并不进行配置。由此,断绝了网络连接,主要适用于以下两种情况:不需要网络的容器(例如:向磁盘卷组进行写入的批量作业),以及当你想配置自定义网络时。
实例4. 运行中的 Docker 无网模式
$ docker run -d -P --net=none nginx:1.9.1$ docker ps
CONTAINER ID IMAGE COMMAND CREATED
STATUS PORTS NAMES
d8c26d68037c nginx:1.9.1 nginx -g 2 minutes ago
Up 2 minutes grave_perlman
$ docker inspect d8c26d68037c | grep IPAddress "IPAddress": "", "SecondaryIPAddresses": null, |
正如实例4所见,并未配置网络连接——这正是我们所期望的。
你可以在此Docker 文档中了解有关网络以及配置选项的更多知识。
注意
本文提及的所有 Docker 指令都在 CoreOS 环境中实践过,Docker 客户度与主机端的版本号均为 1.7.1。
总结
除了上文提及的四种 Docker 单主机网络模式,你还应了解以下几个方面的内容(这些内容也都与多主机部署密切相关)。
分配 IP 地址
当容器数量变化频繁且涉及的数量较多时,手动分配 IP 地址是不可行的。桥接模式在一定程度上能解决该问题。为了避免本地网络中的 ARP (Address Resolution Protocol,地址解析协议)冲突,Docker 后台进程会根据分配的 IP 地址随机生成 MAC 地址。
分配端口
你会发现自己不是处于固定端口分配阵营,就是动态端口分配阵营。这可以是随服务或应用而变的策略,也可以是全局策略,但是,你必须下定决心。记住,在桥接模式下,Docker 能自动分配可路由的(UDP 或 TCP)端口。
网络安全
默认情况下,Docker 启用了容器间通讯(这意味着默认设置为 –icc=true),也就是说,同一主机上的容器可以毫无限制地相互交流,这有可能导致拒绝服务(DOS)攻击。此外,Docker 通过 –ip_forward 与 –iptables 标记控制容器与外部世界的交流。你应该学习这些标记的默认值,并让公司安全团队也有所了解,同时在 Docker 后台进程的设置中体现你的认知。对了,由 StackEngine 的 Boyd Hemphill 完成的 Docker 安全分析也是很好的学习材料。
另一个需要考虑的网络安全问题是在线加密。然而,在撰写本文时,针对这一问题的解决方案非常有限。只有两个系统提供了开箱即用的解决方案:使用 NaCl 的 Weave 以及包含基于 TLS 设置的 OpenVPN。据笔者从 Docker 安全主管 Diogo Mónica 处得到的消息,在线加密可能从 1.9版本后开始提供。
最后,建议阅读 Adrian Mouat 撰写的《Docker 使用入门》,该文档详细介绍了有关网络安全的方方面面。 |
|
|
|
|