当我们开始大规模使用Docker时,会发现需要了解很多关于网络的知识。Docker容器需要运行在一台宿主机上,可以是一台物理机,也可以是云上的一台虚拟机。无论是在单主机上进行部署,还是在集群上部署,总得和网络打交道。
对于大多数单主机部署来说,问题归结于是使用共享卷进行数据交换,还是使用网络(基于HTTP或者其他的)进行数据交换。尽管Docker数据卷很容易使用,但也引入了紧耦合,这意味着很难将单主机部署转化为多主机部署。自然地,共享卷的优势是速度。
在多主机部署中,你需要考虑两个方面:单主机上的容器之间如何通信和多主机之间的通信路径是怎样的。性能考量和安全方面都有可能影响你的设计决定。多主机部署通常是很有必要的,原因是单主机的能力有限,也可能是因为需要部署分布式系统,例如HGDW。
1. Docker 网络概况
用一张图来说明 Docker 网络的基本概况:
2. 四种单节点网络模式
2.1 bridge 模式
Docker 容器默认使用 bridge 模式的网络。其特点如下:
a.要访问宿主机的 IP 以及宿主机的端口port;
b.NAT 模式由于是在三层网络上的实现手段,故肯定会影响网络的传输效率。使用一个 linux bridge,默认为docker0;
c.使用 veth 对,一头在容器的网络 namespace 中,一头在 docker0 上;
d.该模式下Docker容器不具有公有IP,因为宿主机的IP地址与veth对的 IP地址不在同一个网段内;
e.Docker采用NAT方式,将容器内部的服务监听的端口与宿主机的某一个端口port ;进行“绑定”,使得宿主机以外的世界可以主动将网络报文发送至容器内部;
f. 外界访问容器内的服务时,需容器拥有独立、隔离的网络栈;让容器和宿主机以外的世界通过NAT建立通信;
iptables 的 SNTA 规则,使得从容器离开去外界的网络包的源 IP 地址被转换为 Docker 主机的IP地址:
效果是这样的:
示意图:
2.2 Host 模式
Host 模式并没有为容器创建一个隔离的网络环境。而之所以称之为host模式,是因为该模式下的 Docker 容器会和 host 宿主机共享同一个网络 namespace,故 Docker Container可以和宿主机一样,使用宿主机的eth0,实现和外界的通信。换言之,Docker Container的 IP 地址即为宿主机 eth0 的 IP 地址。其特点包括:
a. 这种模式下的容器没有隔离的 network namespace;
b. 容器的 IP 地址同Docker host 的 IP 地址;
c. 需要注意容器中服务的端口号不能与 Docker host 上已经使用的端口号相冲突;
d. host 模式能够和其它模式共存;
示意图:
2.3 container 模式
Container 网络模式是 Docker 中一种较为特别的网络的模式。处于这个模式下的 Docker 容器会共享其他容器的网络环境,因此,至少这两个容器之间不存在网络隔离,而这两个容器又与宿主机以及除此之外其他的容器存在网络隔离。
示意图:
注意:因为此时两个容器要共享一个network namespace,因此需要注意端口冲突情况,否则第二个容器将无法被启动。
2.4 none 模式
网络模式为 none,即不为 Docker 容器构造任何网络环境。一旦Docker 容器采用了none 网络模式,那么容器内部就只能使用loopback网络设备,不会再有其他的网络资源。Docker Container的none网络模式意味着不给该容器创建任何网络环境,容器只能使用127.0.0.1的本机网络。
3. 多节点 Docker 网络
Docker 多节点网络模式可以分为两类,一类是 Docker 在 1.19 版本中引入的基于VxLAN的对跨节点网络的原生支持;另一种是通过插件(plugin)方式引入的第三方实现方案,比如Flannel,Calico等等。
3.1 Docker 原生overlay 网络
Docker 1.19 版本中增加了对overlay网络的原生支持。Docker 支持 Consul, Etcd, 和 ZooKeeper 三种分布式key-value 存储。其中,etcd 是一个高可用的分布式 k/v存储系统,使用etcd的场景默认处理的数据都是控制数据,对于应用数据,只推荐数据量很小,但是更新访问频繁的情况。
3.1.1安装配置
准备三个节点:
a. devstack 192.168.1.18
b. docker1 192.168.1.21
c. docker2 192.168.1.19
在 devstack上使用Docker 启动 etcd 容器:
使用 Docker 启动etcd 请参考 https://coreos.com/etcd/docs/latest/docker_guide.html。不过,应该是因为制造镜像所使用的Dockerfile 原因,官网上的命令因为少了上面红色字体部分而会造成启动失败:
添加红色部分后,容器可以被正确创建:
在docker1 和docker2 节点上修改 /etc/default/docker,添加:
DOCKER_OPTS="--cluster-store=etcd://192.168.1.18:2379
--cluster-advertise=192.168.1.20:2379"
然后分别重启 docker deamon。注意,要使用IP地址;要是使用 hostname 的话,docker 服务将启动失败:
root@docker2:/home/sammy# docker ps
An error occurred trying to connect: Get http://%2Fvar%2Frun%2Fdocker.sock/v1.24/containers/json: read unix @->/var/run/docker.sock: read: connection reset by peer
3.1.2使用 Docker overlay 网络
1)在docker1上运行下面的命令创建一个 overlay 网络
在 docker2 上你也会看到这个网络,说明通过 etcd网络数据是分布式而不是本地的了。
2)在网络中创建容器
a.在 docker2 上,运行 docker run -d --name over2 --network overlaynet1
training/webapp python app.py
b.在 docker1 上,运行 docker run -d --name over1 --network overlaynet1
training/webapp python app.py
进入容器 over2,发现它有两块网卡:
其中 eth1 的网络是一个内部的网段,其实它走的还是普通的 NAT 模式;而 eth0 是overlay 网段上分配的IP地址,也就是它走的是overlay 网络,它的 MTU 是 1450 而不是 1500。
进一步查看它的路由表,你会发现只有同一个 overlay 网络中的容器之间的通信才会通过eth0,其它所有通信还是走eth1。
先看此时的网络拓扑图:
得出以下结论:
a.Docker 在每个节点上创建了两个 linux bridge,一个用于 overlay 网络(ov-000100-1de98),一个用于非 overlay 的 NAT 网络(docker_gwbridge)
b.容器内的到overlay 网络的其它容器的网络流量走 overlay 网卡(eth0),其它网络流量走 NAT 网卡(eth1)
c.当前Docker 创建vxlan 隧道的ID范围为 256~1000,因而最多可以创建745个网络,因此,本例中的这个 vxlan 隧道使用的 ID 是256
d.Docker vxlan 驱动使用 4789 UDP 端口
e.overlay网络模型底层需要类似 consul 或 etcd 的 KV 存储系统进行消息同步
f.Docker overlay 不使用多播
g.Overlay 网络中的容器处于一个虚拟的大二层网络中
3. 网络性能对比
3.1 网上摘录的对比数据
1)使用 iperf 工具检查测试了一下性能并做对比:
两台主机是同一个物理机上的两个虚机,因此,结果的绝对值其实没多少意义,相对值有一定的参考性。
2)文章 Testing Docker multi-host network performance 对比了多种网络模式下的性能,结果如下:
3.3 关于Docker 网络模式选择的简单结论
a.Bridge 模式的性能损耗大概为10%
b.原生 overlay 模式的性能损耗非常高,甚至达到了56%,因此在生产环境下使用这种模式需要非常谨慎。
c.如果一定要使用 overlay 模式的话,可以考虑使用 Cisco 发起的 Calico 模式,它的性能和 bridge 相当。
d.Weave overlay 模式的性能数据非常可疑,按理说应该不可能这么差。