Docker 跨主机容器间网络通信实现的工具有 Pipework、Flannel、Weave、Open vSwitch(虚拟交换机)、Calico, 其中 Pipework、Weave、Flannel,三者的区别是:
在每个宿主机上布置一个特殊的 route 的容器,不同宿主机的 route 容器连接起来。 route拦截所有普通容器的 ip 请求,并通过 udp 包发送到其他宿主机上的普通容器。这样在跨机的多个容器端看到的就是同一个扁平网络。 weave 解决了网络问题,不过部署依然是单机的。
Flannel 是 CoreOS 团队针对 Kubernetes 设计的一个网络规划服务,简单来说,它的功能是让集群中的不同节点主机创建的 Docker 容器都具有全集群唯一的虚拟 IP 地址。但在默认的Docker 配置中,每个节点上的 Docker 服务会分别负责所在节点容器的 IP 分配。这样导致的一个问题是,不同节点上容器可能获得相同的内外 IP 地址。并使这些容器之间能够之间通过 IP 地址相互找到,也就是相互 ping 通。Flannel 设计目的就是为集群中所有节点重新规划 IP 地址的使用规则,从而使得不同节点上的容器能够获得"同属一个内网"且"不重复的"IP地址,并让属于不同节点上的容器能够直接通过内网 IP 通信。
Flannel 实质上是一种"覆盖网络(overlay network)",即表示运行在一个网上的网(应用层网络),并不依靠 ip 地址来传递消息,而是采用一种映射机制,把 ip 地址和 identifiers 做映射来资源定位。也就是将 TCP 数据包装在另一种网络包里面进行路由转发和通信,目前已经支持 UDP、VxLAN、AWS VPC 和 GCE 路由等数据转发方式。
Flannel 使用 etcd 存储配置数据和子网分配信息。flannel 启动之后,后台进程首先检索配置和正在使用的子网列表,然后选择一个可用的子网,然后尝试去注册它。etcd 也存储这个每个主机对应的 ip。flannel 使用 etcd 的 watch 机制监视/coreos.com/network/subnets 下面所有元素的变化信息,并且根据它来维护一个路由表。为了提高性能,flannel优化了UniversalTAP/TUN 设备,对 TUN 和 UDP 之间的 ip 分片做了代理。
每个主机配置一个 ip 段和子网个数。例如,可以配置一个覆盖网络使用 10.100.0.0/16 段,每个主机/24 个子网。因此主机 a 可以接受 10.100.5.0/24,主机 B 可以接受 10.100.18.0/24的包。flannel 使用 etcd 来维护分配的子网到实际的 ip 地址之间的映射。对于数据路径,flannel 使用 udp 来封装 ip 数据报,转发到远程主机。选择 UDP 作为转发协议是因为他能穿透防火墙。例如,AWS Classic 无法转发 IPoIP or GRE 网络包,是因为它的安全组仅仅支持 TCP/UDP/ICMP。 Flannel 工作原理流程图如下 (默认的节点间数据通信方式是 UDP 转发;flannel 默认使用 8285 端口作为 UDP 封装报文的端口,VxLan 使用 8472 端口)
对上图的简单说明 (Flannel 的工作原理可以解释如下):
-> 数据从源容器中发出后,经由所在主机的 docker0 虚拟网卡转发到flannel0 虚拟网卡,这是个 P2P 的虚拟网卡,flanneld 服务监听在网卡的另外一端。
-> Flannel 通过 Etcd 服务维护了一张节点间的路由表,该张表里保存了各个节点主机的子网网段信息。
-> 源主机的 flanneld 服务将原本的数据内容 UDP 封装后根据自己的路由表投递给目的节点的 flanneld 服务,数据到达以后被解包,然后直接进入目的节点的 flannel0 虚拟网卡,然后被转发到目的主机的 docker0 虚拟网卡,最后就像本机容器通信一样的由 docker0 路由到达目标容器。
1) UDP 封装是怎么回事?
在 UDP 的数据内容部分其实是另一个 ICMP(也就是 ping 命令)的数据包。原始数据是在起始节点的 Flannel 服务上进行 UDP 封装的,投递到目的节点后就被另一端的 Flannel 服务还原成了原始的数据包,两边的 Docker 服务都感觉不到这个过程的存。
2) 为什么每个节点上的 Docker 会使用不同的 IP 地址段?
这个事情看起来很诡异,但真相十分简单。其实只是单纯的因为 Flannel 通过Etcd 分配了每个节点可用的 IP 地址段后,偷偷的修改了 Docker 的启动参数。在运行了 Flannel 服务的节点上可以查看到 Docker 服务进程运行参数(ps aux|grep docker|grep "bip"),例如“--bip=182.48.25.1/24”这个参数,它限制了所在节点容器获得的 IP 范围。这个 IP 范围是由 Flannel 自动分配的,由 Flannel 通过保存在 Etcd 服务中的记录确保它们不会重复。
3) 为什么在发送节点上的数据会从 docker0 路由到 flannel0 虚拟网卡,在目的节点会从 flannel0 路由到 docker0 虚拟网卡?
例如现在有一个数据包要从 IP 为 172.17.18.2 的容器发到 IP 为 172.17.46.2的容器。根据数据发送节点的路由表,它只与 172.17.0.0/16 匹配这条记录匹配,因此数据从 docker0出来以后就被投递到了 flannel0。同理在目标节点,由于投递的地址是一个容器,因此目的地址一定会落在 docker0 对于的 172.17.46.0/24 这个记录上,自然的被投递到了 docker0 网卡
pipework 是一个单机的工具,组合了 brctl 等工具,可以认为 pipework 解决的是宿主机上的设置容器的虚拟网卡、网桥、ip 等,可以配合其他网络使用。
如果容器数量不多,想简单的组一个大的 3 层网络,可以考虑 weave
如果容器数量很多,而且你们的环境复杂,需要多个子网,可以考虑 openvswitch 或者 fannel
weave 的总体网络性能表现欠佳, flannel VXLAN 能满足要求,一般推荐用 flannel
1)机器环境(centos7 系统),使用两台服务器做测试(docker 安装请参照另外一篇文件:centos 7 install docker)
192.168.14.139 部署 etcd,flannel,docker 主控端(通过 etcd)
192.168.14.138 部署 flannel,docker 被控端
如果还有其他节点,直接加入即可
systemctl disable firewalld.service
systemctl stop firewalld.service
iptables -P FORWARD ACCEPT
yum install etcd flannel -y
1,配置修改 etcd 配置文件:
cp /etc/etcd/etcd.conf /etc/etcd/etcd.conf.bak
vim /etc/etcd/etcd.conf
ETCD_LISTEN_PEER_URLS="http://192.168.14.139:2380"
ETCD_LISTEN_CLIENT_URLS=http://192.168.14.139:2379,http://127.0.0.1:2379
ETCD_NAME="default"
ETCD_INITIAL_ADVERTISE_PEER_URLS="http://192.168.14.139:2380"
ETCD_ADVERTISE_CLIENT_URLS=http://192.168.14.139:2379
保存,退出
2,启动 ectd :
systemctl enable etcd
systemctl start etcd
3,设置 host 主机的 subnet
vim /root/etcd.sh
{ "Network": "10.10.0.0/16","SubnetLen": 24,"Backend": {"Type":"vxlan"} }
说明:Network:定义 host 主机的 IP 地址池为 10.10.0.0/16,注:由于 etcd并不是动态保存 host 上 flannel 网络的,比如:当有节点被删除后,etcd 中的关于这个节点的 subnet 网络并不会被删除,所以使用 10.X.X.X 的网络,保证有足够的网络可用SubnetLen:指定每个主机分配的 subnet 大小为 24 位,即 10.10.x.0/24Backend 为 vxlan,即主机之间通过 vxlan 通信,我们主要讨论 vxlan 和 host-gw 这两种方式
4,将配置保存到 etcd
etcdctl --endpoints=http://192.168.14.139:2379 set /usr/local/bin/network/config < /root/etcd.sh
说明:--endpoints=http://192.168. 14.139:22379 指定 etcd 的 url
通过 etcdctl get /usr/local/bin/network/config 在 etcd 本机上获取 key
说明:/usr/local/bin/network/config 保存 key 的地方,flanneld 会读取这个配置,保证自己能获取到 subnet,这个 key 可以任意指定,当 host 主机分配到 subnet 后,key 会修改 docker 的启动参数
5,配置 flannel
cp /etc/sysconfig/flanneld /etc/sysconfig/flanneld.bak
vim /etc/sysconfig/flanneld
FLANNEL_ETCD_ENDPOINTS=http://192.168.14.139:2379
FLANNEL_ETCD_PREFIX="/usr/local/bin/network"
启动 flannel:
systemctl enable flanneld
systemctl start flanneld
如果启动 flanneld 时出现一下错误:etcd 集群配置错误,拒绝链接解决办法是:在 etcd 的服务器上的/etc/etcd/etcd.conf 中将集群:ETCD_LISTEN_PEER_URLS="http://localhost:2380"这个注释去掉然后重启 docker
systemctl restart docker
6,查看配置后的网络
可以在主控端看到 flanneld1.1 的网络了
yum install flannel
cp /etc/sysconfig/flanneld /etc/sysconfig/flanneld.bak
vim /etc/sysconfig/flannel
FLANNEL_ETCD_ENDPOINTS=http://192.168.14.139:2379
FLANNEL_ETCD_PREFIX="/usr/local/bin/network"
启动 flannel
systemctl enable flannel
systemct start flannel
现在我们可以在被控端上看见一个 flanneld1.1 的网络了
然后在重启 docker
systemctl restart docker
不过可以看到我的主控制端和被控制端各有一个 flannel1.1,但不在同一个网段,主控制端的 flanneld1.1 的 IP 为:10.10.54.0,被控制端的 flanneld1.1的 IP 为:10.10.41.0,怎么进行通信呢,再来看一下容器的网络(docker1 为主控制端,docker2 为被控制端)。
两个容器使用的是同一个 IP,肯定是没法访问对方
1,编辑 docker 的配置文件(所有节点都需要操作):
/usr/lib/systemd/system/docker.service
添加:--bip= 和 –mtu=
vim /usr/lib/systemd/system/docker.service
以上 这两个参数要参考 /run/flannel/subnet.env,必须与其保持一致
PS:以上只是举例,具体请以实际节点的 IP 为准
重启 docker 服务
systemctl daemon-reload
systemctl restart docker
查看 docker0 的变化
验证容器的连通性
用 ping 命令测试一下
通了
以上用的 flannel 网络模式为:vxlan ,除了这种方式,还有一种比较好的网络模式:host-gw
说明:
vxlan 和 host-gw 的区别;
1.host-gw 把每个主机都配置成网关,主机知道其他主机的 subnet 和转发地址。
vxlan 则是在主机之间创建隧道,不同的主机的容器都在一个大的网段内,比如:10.2.0.0/16
2.虽然 vxlan 与 host-gw 使用不同的机制,但对于容器之间还是可以相互通信的,并且外网其他的服务器也可以直接访问容器 IP,不需要在映射端口到宿主机上面来访问,不过需要在外网的服务器添加一条路由:
如果外网服务器为 Windows,添加静态路由:
route add 10.10.62.0 mask 255.255.255.0 192.168.14.139
route add 10.10.52.0 mask 255.255.255.0 192.168.14.138
如果外网服务器为 Linux,添加静态路由:
route add -net 10.10.62.0 netmask 255.255.255.0 gw 192.168.14.139
route add -net 10.10.52.0 netmask 255.255.255.0 gw 192.168.14.138
3.由于 vxlan 需要对数据进行打包和拆包,性能可能稍逊于 host-gw
下面我们用 host-gw 方式来部署一下
部署的方式和 vxlan 差不多,唯一改变的地方在:设置 host 主机的 subnet
vim /root/etcd.sh
{ "Network": "10.10.0.0/16","SubnetLen": 24,"Backend": {"Type":"host-
gw"} }
其他的操作都是相同的,不过要注意一下,这种模式下面,用 ifconfig 命令查看宿主机网卡信息,flannel 可能没有 flannel1.1 网卡