我们知道docker有四种网络模型,bridge(桥接),joincd(联盟式),open(开发式),none(不适用任何网络模式),那么,不管使用那种网络模式,在跨节点访问的时候都需要NAT机制来实现。
docker网络通信
任何一个pod(pod的ip是私有地址)在离开本机时候都需要做源地址转换,确保能够携物理地址向外转发,如下:
containerA具有虚拟的eth0网卡,另外veth一边关联到宿主机的docker0网卡上,container1会获取到一个172.16.0.0/16网段的一个ip地址。通过打开转发,这个ip通过宿主机的eth0可以访问外部网络资源。为了确保响应报文能够顺利送回本机,就需要报文在离开本机时候做源地址转换。
comtainerB想要被别的主机访问到,就需要在宿主机物理接口做DNAT,将服务暴露。对于客户端来讲,访问的是eth0接口,而后由eth0做目标地址转换comtainerB的eth0地址上来。
如果containerA与comtainerB通信,那就需两级转换,containerA离开host1的eth0就需要做SNAT,而后报文经过物理网络发送至host2的eth0,在做一次DNAT到containerB。containerB响应报文也是如此,先从本机SNAT出,到host1做DNAT到containerA。这个SNAT和DNAT过程是自动实现的,也是比不可少的,并且双方只通过宿主机的ip和端口访问,并不知道访问的对端到底是谁。这种方式的网络效率较低,且不能满足我们想要的网络模型
kubernetes网络通信
而kubernetes本身运行多个pod管理编排,就必须要解决这种网络问题。并且各个pod之间必须进行通信,大致存在一下几种:
- 容器间通信,pod内的多个容器间通信,如:log
- pod通信,pod与pod通信的网络直达,且均使用各自的pod ip,不经过任何地址转换
- pod与service通信,pod ip到service 的cluster ip通信(依赖于iptables或者ipvs)ipvs仅作为负载均衡,但是无法做nat转换。iptables则用来做nat转换等功能
- service与集群外部客户端的通信,如ingress,nodeport等
kuberntes的网络自己并不实现,依靠CNI接口的网络插件来接入使用满足以上几点的kubernetes的网络通信需求
其中有,flannel,canel,kube-router等。
- 虚拟网桥
通过软件的方式实现虚拟网桥,虚拟网卡链接每一个pod网络都有专用的网络接口。每一对网卡,一端pod,一段宿主机之上并接入网桥,以及物理网桥使用
- 多路复用
使用macVlan,基于mac创建vlan,为每个虚拟接口配置一个独有的mac地址,使一个物理网卡能承载多个容器使用。使用物理网卡,并基于物理网卡的macvlan机制进行跨节点之间通讯
- 硬件交换
使用SR-IOV,一个网卡支持在物理机虚拟出多个接口。称为单根I/O 虚拟化,单根I/O 虚拟化是创建虚拟设备很高性能的方式。一块物理网卡,虚拟硬件级别的多个网卡。
如果要使用CNI插件。对于kubeclt来讲,只需要通过/etc/cni/net.d/目录加载配置文件,因此将配置文件放在此目录即可加载。基本格式如下:
[root@linuxea ~]# cat /etc/cni/net.d/10-flannel.conflist
{
"name": "cbr0",
"plugins": [
{
"type": "flannel",
"delegate": {
"hairpinMode": true,
"isDefaultGateway": true
}
},
{
"type": "portmap",
"capabilities": {
"portMappings": true
}
}
]
}
这是一个叠加网络,且支持端口映射。其他的网络模式的配置文件也可放在此目录,就会被kubelet加载,并且必要时会创建一个pod,调用/etc/cni/net.d/下的插件的配置文件替代kubernetes生成接口,网络创建管理等功能。
在kubernetes中网络插件不仅仅为了实现网络分配,管理,更要实现网络策略,也就是说pod与pod中间可以被设置不能够互相访问,而flannel则不能支持网络策略。但是flannel部署简单,相对于flannel,calico部署更麻烦,不过calico不但支持网络分配和管理,更支持网络策略,并且支持BGP的方式支持二层转发三层路由功能,因此在性能上稍强。
并且,flannel的配置上非常简单的,我们可以使用flannel做网络分配和管理,而用calico来做网络策略分配
flannel vxlan
flannel基于vxlan(扩展的局域网),正常情况下两个pod自己桥接通讯就能基于物理网卡通讯。那么现在是叠加网络,在双方通讯的过程中都会在主机上封装一个叠加网络报文的隧道,通过此隧道进行通信。如下:
因此有额外的预留开销给隧道,所以的他的mtu是1450,而物理网卡的mtu是1500。
[root@linuxea ~]# ip a
5: flannel.1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN group default
link/ether 82:4f:25:3e:c1:36 brd ff:ff:ff:ff:ff:ff
inet 172.16.0.0/32 scope global flannel.1
valid_lft forever preferred_lft forever
inet6 fe80::804f:25ff:fe3e:c136/64 scope link
valid_lft forever preferred_lft forever
另外还有一个cnio的172.16.0.1被当前主机作为隧道协议的本地通信的接口。当创建pod后此接口就出现
6: cni0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP group default qlen 1000
link/ether 0a:58:ac:10:00:01 brd ff:ff:ff:ff:ff:ff
inet 1 72.16.0.1/24 scope global cni0
valid_lft forever preferred_lft forever
inet6 fe80::e8e0:1aff:fe5b:9240/64 scope link
valid_lft forever preferred_lft forever
flannel host-gw
host-gateway(主机网关)
对于每个节点的的pod都有一个各自的网段,并且把所在主机的网络接口当作网关使用,从而与接口和pod之间各自配置ip地址,并通过网关向外传输通讯
当向外传递时,先查看目标地址是否是本地,如果不是就转交给网关,到达网关后会查询本地的路由条目,路由条目中记录了到达主机的ip,而后经过物理网卡发送到目标主机物理网卡。如下:
报文经过路由表中的条目路由就到达主机,把主机本身节点当作网关,这样一来那么随着pod的增多,路由表中的路由条目会很大,而这种两层路由的方式将会比vxlan性能要好很多,但是默认并没使用这种方式。
当然,这种方式的缺陷在于各节点的ip必须工作在同一个二层网络之中,也就是说必须在同一个网段内。
- 所谓的二层网络,是不经过物理路由转发,且虚拟网络间路由。倘若此时在大型复杂网络中,多网络间的kubernetes集群,host-gw就不能被使用
flannel directrouting
上述,vxlan和host-gw都各有特点,vxlan有单独的隧道接口方式可以让不同网络中的pod互通,host-gw可以使同网络层中的ip走路由条目快速转发通信。此时若想使用vxlan并且仍然使用host-gw,那么可以尝试flannel的第二种网络directorouting(路由转发),相比较flannel第一种方式vxlan,directorouting要灵活很多。
如果源ip和目标ip在一个三层网络中直接使用host-gw,如果源ip和目标ip不再同一个网络中就是用vxlan(叠加隧道通信),这就是flannel的第二种网络模式:directorouting 。如下:
另外还有udp方式,在最早的是时候被采用。这种方式效率很低,可做最后的方式使用。