本文介绍了flannel网络在Kubernetes中的工做方式web
Kubernetes是用于大规模管理容器化应用程序出色的编排工具。可是,您可能知道,使用kubernetes并不是易事,尤为是后端网络实现。我在网络中遇到了许多问题,花了我不少时间弄清楚它是如何工做的。docker
在本文中,我想以最简单的实现为例,来解释kubernetes的网络工做。但愿本文能够为像我这样正在研究kubernetes的人们提供帮助。后端
Kubernetes网络模型
下图显示了kubernetes集群的简单图像:服务器
Kubernetes管理Linux机器集群(多是ECS之类的云VM或物理服务器),在每台主机上,kubernetes运行任意数量的Pod,在每一个Pod中能够有任意数量的容器。用户的应用程序正在这些容器之一中运行。微信
对于kubernetes,Pod是最小的管理单元,而且一个Pod中的全部容器共享相同的网络名称空间,这意味着它们具备相同的网络接口而且可使用*localhost
*相互链接网络
在官方文件[1]说kubernetes网络模式要求:架构
-
全部容器无需NAT便可与全部其余容器通讯 -
全部节点均可以与全部容器通讯(反之亦然),而无需NAT -
容器所看到的IP其余人所看到的IP同样
咱们能够按照上述要求将全部容器替换为Pod,由于容器与Pod网络共享。运维
基本上,这意味着全部Pod都应该可以与群集中的其余Pod自由通讯,即便它们位于不一样的主机中,而且它们也使用本身的IP地址相互识别,就像基础主机不存在同样。此外,主机也应该可以使用本身的IP地址与任何Pod通讯,而无需任何地址转换。编辑器
Kubernetes不提供任何默认的网络实现,而是仅定义模型,并由其余工具来实现。现在有不少实现,Flannel是其中之一,也是最简单的之一。在如下各节中,我将解释Flannel的UDP模式实现。工具
The Overlay Network
Flannel是由CoreOS建立的,用于Kubernetes网络,也能够用做其余目的的通用软件定义网络解决方案。
为了知足kubernetes的网络要求,flannel的想法很简单:建立另外一个在主机网络之上运行的扁平网络,这就是所谓的覆盖网络overlay network。在此覆盖网络中,全部容器(Pod)将被分配一个IP地址,它们经过直接调用彼此的IP地址来相互通讯。
为了帮助解释,我在AWS上使用了一个小型的测试kubernetes集群,该集群中有3个Kubernetes节点。网络以下所示:
此群集中有三个网络:
AWS VPC网络:全部实例都在一个VPC子网中172.20.32.0/19
。它们已经在此范围内分配了ip地址,全部主机均可以彼此链接,由于它们位于同一LAN中。
Flannel overlay network:flannel建立了另外一个网络100.96.0.0/16
,它是一个更大的网络,能够容纳65536个地址,而且遍布全部kubernetes节点,将在此范围内为每一个Pod分配一个地址,稍后咱们将看到flannel如何实现此目的。
主机内docker网络:在每一个主机内部,flannel为该主机中的全部pod分配了一个网络100.96.x.0/24
,它能够容纳256地址。docker桥接接口docker0
将使用此网络建立新容器。
经过这种设计,每一个容器都有其本身的IP地址,都属于覆盖子网100.96.0.0/16
。同一主机内的容器能够经过docker bridge网络接口Docker0
相互通讯,这很简单,所以在本文中我将跳过。为了在主机上与覆盖网络中的其余容器进行跨主机通讯,flannel使用内核路由表和UDP封装来实现该功能,如下各节对此进行了说明。
跨主机容器通讯
假设具备IP地址的节点1中的容器(咱们将其称为容器1)100.96.1.2
要使用IP地址链接到节点2中的容器(咱们将其称为容器2)100.96.2.3
,让咱们看看覆盖网络如何启用数据包经过。
第一个container-1使用建立一个IP数据包src: 100.96.1.2 -> dst: 100.96.2.3
,该数据包将做为容器的网关进入docker0网桥。
在每一个主机中,flannel运行一个名为的守护进程flanneld
,它在内核的路由表中建立一些路由规则,这是节点1的路由表的样子:
admin@ip-172-20-33-102:~$ ip route
default via 172.20.32.1 dev eth0
100.96.0.0/16 dev flannel0 proto kernel scope link src 100.96.1.0
100.96.1.0/24 dev docker0 proto kernel scope link src 100.96.1.1
172.20.32.0/19 dev eth0 proto kernel scope link src 172.20.33.102
如咱们所见,数据包的目标地址100.96.2.3
位于更大的覆盖网络中100.96.0.0/16
,所以它与第二条规则匹配,如今内核知道应该将数据包发送到flannel0
。
flannel0
TUN是由咱们的flanneld
守护进程建立的TUN设备,TUN是在Linux内核中实现的软件接口,它能够在用户程序和内核之间传递原始ip数据包。它在两个方向上起做用:
-
将IP数据包写入 flannel0
设备时,该数据包将直接发送到内核,内核将根据其路由表对数据包进行路由 -
当IP数据包到达内核,而且路由表说应该将其路由到 flannel0
设备时,内核会将数据包直接发送到建立该设备的flanneld
进程,该进程是守护进程。
当内核将数据包发送到TUN设备时,它将直接进入flanneld
进程,它看到目标地址为100.96.2.3
,尽管从图中能够看出该地址属于在Node 2上运行的容器,可是如何flanneld
知道呢?
Flannel碰巧将某些信息存储在名为etcd的键值存储服务中,若是您知道kubernetes,则不该感到惊讶。在flannel,咱们能够将其视为常规键值存储。
Flannel将子网映射信息存储到etcd服务中,咱们可使用如下etcdctl
命令查看它:
admin@ip-172-20-33-102:~$ etcdctl ls /coreos.com/network/subnets
/coreos.com/network/subnets/100.96.1.0-24
/coreos.com/network/subnets/100.96.2.0-24
/coreos.com/network/subnets/100.96.3.0-24
admin@ip-172-20-33-102:~$ etcdctl get /coreos.com/network/subnets/100.96.2.0-24
{"PublicIP":"172.20.54.98"}
所以,每一个flanneld
进程查询etcd都知道每一个子网属于哪一个主机,并将目标ip地址与etcd中存储的全部子网密钥进行比较。在本例中,该地址100.96.2.3
将与子网匹配100.96.2.0-24
,而且如咱们所见,存储在此键中的值表示Node ip为172.20.54.98
。
如今flanneld
知道了目的地址,而后将原始IP数据包包装到UDP数据包中,以其本身的主机ip做为源地址,而目标主机的IP做为目的地址。在每一个主机中,该flanneld
进程将侦听默认的UDP端口:8285
。所以,只须要将UDP数据包的目标端口设置为8285
,而后经过网络发送它。
UDP数据包到达目标主机后,内核的IP堆栈会将数据包发送到flanneld
进程,由于那是用户进程在UDP端口上侦听:8285
。而后flanneld
将得到UDP数据包的有效负载,该数据包是由原始容器生成的原始IP数据包,只需将其写入TUN设备flannel0
,而后该数据包将直接传递到内核,这就是TUN的工做方式。
与节点1相同,路由表将决定此数据包的去向,让咱们看一下节点2的路由表:
admin@ip-172-20-54-98:~$ ip route
default via 172.20.32.1 dev eth0
100.96.0.0/16 dev flannel0 proto kernel scope link src 100.96.2.0
100.96.2.0/24 dev docker0 proto kernel scope link src 100.96.2.1
172.20.32.0/19 dev eth0 proto kernel scope link src 172.20.54.98
IP数据包的目标地址是100.96.2.3
,内核将采用最精确的匹配,这是第三条规则。数据包将发送到docker0
设备。就像docker0
桥接设备同样,此主机中的全部容器都链接到该桥接器,最终目的地容器2将看到并接收到该数据包。
最终,咱们的数据包完成了一种传递到目标的方式,当contianer-2将数据包发送回容器1时,反向路由将以彻底相同的方式工做。这就是跨主机容器通讯的工做方式。
使用Docker网络进行配置
在以上解释中,咱们遗漏了一点。这就是咱们如何配置docker使用较小的子网100.96.x.0/24
?
碰巧flanneld
会将其子网信息写入主机中的文件中:
admin@ip-172-20-33-102:~$ cat /run/flannel/subnet.env
FLANNEL_NETWORK=100.96.0.0/16
FLANNEL_SUBNET=100.96.1.1/24
FLANNEL_MTU=8973
FLANNEL_IPMASQ=true
该信息将用于配置docker守护程序的选项,所以docker能够将FLANNEL_SUBNET
用做其桥接网络,而后主机容器网络将起做用:
dockerd --bip = $ FLANNEL_SUBNET --mtu = $ FLANNEL_MTU
数据包复制和性能
较新版本的flannel不建议将UDP封装用于生产,它表示仅应将其用于调试和测试目的。缘由之一是性能。
尽管flannel0
TUN设备提供了一种经过内核获取和发送数据包的简单方法,但它会下降性能:必须将数据包从用户空间来回复制到内核空间:
如上所述,必须从原始容器进程发送数据包,而后在用户空间和内核空间之间复制3次,这将显着增长网络开销,所以,若是能够的话,应避免在生产中使用UDP。
结论
Flannel是kubernetes网络模型的最简单实现之一。它使用现有的Docker网络和带有守护进程的额外Tun设备进行UDP封装。我解释了核心部分的详细信息:跨主机容器通讯,并简要提到了性能损失。我但愿本文能帮助人们理解kuberentes网络的基础知识,并以此为基础,您能够开始探索更有趣的kubernetes网络世界,并弄清楚?Calico,?WeaveNet,?Romana等许多高级实现。
https://blog.laputa.io/kubernetes-flannel-networking-6a1cb1f8ec7c
参考资料
kubernetes networking: https://kubernetes.io/docs/concepts/cluster-administration/networking/
本文分享自微信公众号 - 云原生生态圈(CloudNativeEcoSystem)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。