你一直在Kubernetes集群中运行一系列服务并已从中获益,或者你正打算这么做。尽管有一系列工具能帮助你建立并管理集群,你仍困惑于集群底层是如何工作的,以及出现问题该如何处理。我曾经就是这样的。
诚然Kubernetes对初学者来说已足够易用,但我们仍然不得不承认,它的底层实现异常复杂。Kubernetes由许多部件组成,如果你想对失败场景做好应对准备,那么你必须知道各部件是如何协调工作的。其中一个最复杂,甚至可以说是最关键的部件就是网络。因此我着手精确理解Kubernetes网络是如何工作的。我阅读了许多文章,看了很多演讲,甚至浏览了代码库。以下就是我的所得。
Kubernetes网络模型核心点是,Kubernetes网络有一个重要的基本设计原则:每个Pod拥有唯一的IP。这个Pod IP被该Pod内的所有容器共享,并且其它所有Pod都可以路由到该Pod。你可曾注意到,你的Kubernetes节点上运行着一些"pause"容器?
它们被称作“沙盒容器(sandbox containers)",其唯一任务是保留并持有一个网络命名空间(netns),该命名空间被Pod内所有容器共享。
通过这种方式,即使一个容器死掉,新的容器创建出来代替这个容器,Pod IP也不会改变。这种IP-per-pod模型的巨大优势是,Pod和底层主机不会有IP或者端口冲突。我们不用担心应用使用了什么端口。这点满足后,Kubernetes唯一的要求是,这些Pod IP可被其它所有Pod访问,不管那些Pod在哪个节点。
节点内通信第一步是确保同一节点上的Pod可以相互通信,然后可以扩展到跨节点通信、internet上的通信,等等。
Kubernetes Node(root network namespace)在每个Kubernetes节点(本场景指的是Linux机器)上,都有一个根(root)命名空间(root是作为基准,而不是超级用户)--root netns。最主要的网络接口 eth0 就是在这个root netns下。
Kubernetes Node(pod network namespace)类似的,每个Pod都有其自身的netns,通过一个虚拟的以太网对连接到root netns。这基本上就是一个管道对,一端在root netns内,另一端在Pod的nens内。我们把Pod端的网络接口叫 eth0,这样Pod就不需要知道底层主机,它认为它拥有自己的根网络设备。另一端命名成比如 vethxxx。你可以用ifconfig 或者 ip a 命令列出你的节点上的所有这些接口。
Kubernetes Node(linux network bridge)节点上的所有Pod都会完成这个过程。这些Pod要相互通信,就要用到linux的以太网桥 cbr0 了。Docker使用了类似的网桥,称为docker0。你可以用 brctl show 命令列出所有网桥。
Kubernetes Node(same node pod-to-pod communication)假设一个网络数据包要由pod1到pod2。
它由pod1中netns的eth0网口离开,通过vethxxx进入root netns。
然后被传到cbr0,cbr0使用ARP请求,说“谁拥有这个IP”,从而发现目标地址。
vethyyy说它有这个IP,因此网桥就知道了往哪里转发这个包。
数据包到达vethyyy,跨过管道对,到达pod2的netns。这就是同一节点内容器间通信的流程。
当然也可以用其它方式,但是无疑这是最简单的方式,同时也是Docker采用的方式。节点间通信正如我前面提到,Pod也需要跨节点可达。Kubernetes不关心如何实现。我们可以使用L2(ARP跨节点),L3(IP路由跨节点,就像云提供商的路由表),Overlay网络,或者甚至信鸽。无所谓,只要流量能到达另一个节点的期望Pod就好。每个节点都为Pod IPs分配了唯一的CIDR块(一段IP地址范围),因此每个Pod都拥有唯一的IP,不会和其它节点上的Pod冲突。大多数情况下,特别是在云环境上,云提供商的路由表就能确保数据包到达正确的目的地。我们在每个节点上建立正确的路由也能达到同样的目的。许多其它的网络插件通过自己的方式达到这个目的。
这里我们有两个节点,与之前看到的类似。每个节点有不同的网络命名空间、网络接口以及网桥。
Kubernetes Nodes with route table(cross node pod-to-pod communication)假设一个数据包要从pod1到达pod4(在不同的节点上)。它由pod1中netns的eth0网口离开,通过vethxxx进入root netns。然后被传到cbr0,cbr0通过发送ARP请求来找到目标地址。
本节点上没有Pod拥有pod4的IP地址,因此数据包由cbr0 传到 主网络接口 eth0。数据包的源地址为pod1,目标地址为pod4,它以这种方式离开node1进入电缆。路由表有每个节点的CIDR块的路由设定,它把数据包路由到CIDR块包含pod4的IP的节点。因此数据包到达了node2的主网络接口eth0。现在即使pod4不是eth0的IP,数据包也仍然能转发到cbr0,因为节点配置了IP forwarding enabled。节点的路由表寻找任意能匹配pod4 IP的路由。它发现了 cbr0 是这个节点的CIDR块的目标地址。你可以用route -n命令列出该节点的路由表,它会显示cbr0的路由,类型如下:
网桥接收了数据包,发送ARP请求,发现目标IP属于vethyyy。数据包跨过管道对到达pod4。这就是Kubernetes网络的基础。下次你碰到问题,务必先检查这些网桥和路由表。前面我们漫谈了Kubernetes的网络模型。
我们观察了数据包是如何在同一节点上的pod 间和跨节点的 pod 间流动的。我们也注意到了Linux网桥和路由表在这个过程中所扮演的角色。现在我们将进一步阐述这些概念,并阐述Overlay网络是如何工作的。我们也将理解Kubernetes千变万化的Pod是如何从运行的应用中抽象出来,并在幕后处理的。
Overlay 网络Overlay网络不是默认必须的,但是它们在特定场景下非常有用。比如当我们没有足够的IP空间,或者网络无法处理额外路由,抑或当我们需要Overlay提供的某些额外管理特性。一个常见的场景是当云提供商的路由表能处理的路由数是有限制时。例如,AWS路由表最多支持50条路由才不至于影响网络性能。因此如果我们有超过50个Kubernetes节点,AWS路由表将不够。这种情况下,使用Overlay网络将帮到我们。
本质上来说,Overlay就是在跨节点的本地网络上的包中再封装一层包。你可能不想使用Overlay网络,因为它会带来由封装和解封所有报文引起的时延和复杂度开销。通常这是不必要的,因此我们应当在知道为什么我们需要它时才使用它。
为了理解Overlay网络中流量的流向,我们拿Flannel做例子,它是CoreOS 的一个开源项目。
Kubernetes Node with route table(cross node pod-to-pop Traffic flow with flannel overlay network)这里我们注意到它和之前我们看到的设施是一样的,只是在root netns中新增了一个虚拟的以太网设备,称为flannel0。它是虚拟扩展网络Virtual Extensible LAN(VXLAN)的一种实现,但是在Linux上,它只是另一个网络接口。从pod1到pod4(在不同节点)的数据包的流向类似如下:
1、它由pod1中netns的eth0网口离开,通过vethxxx进入root netns。
2、然后被传到cbr0,cbr0通过发送ARP请求来找到目标地址。
3
- a、由于本节点上没有Pod拥有pod4的IP地址,因此网桥把数据包发送给了flannel0,因为节点的路由表上flannel0被配成了Pod网段的目标地址。
- b、flanneld daemon和Kubernetes apiserver或者底层的etcd通信,它知道所有的Pod IP,并且知道它们在哪个节点上。因此Flannel创建了Pod IP和Node IP之间的映射(在用户空间)。flannel0取到这个包,并在其上再用一个UDP包封装起来,该UDP包头部的源和目的IP分别被改成了对应节点的IP,然后发送这个新包到特定的VXLAN端口(通常是8472)。
- Packet-in-packet encapsulation(notice the packet is encapsulated from 3c to 6b in previous diagram)尽管这个映射发生在用户空间,真实的封装以及数据的流动发生在内核空间,因此仍然是很快的。
- c、封装后的包通过eth0发送出去,因为它涉及了节点间的路由流量。
4、包带着节点IP信息作为源和目的地址离开本节点。
5、云提供商的路由表已经知道了如何在节点间发送报文,因此该报文被发送到目标地址node2。
6
- a、包到达node2的eth0网卡,由于目标端口是特定的VXLAN端口,内核将报文发送给了 flannel0。
- b、flannel0解封报文,并将其发送到 root 命名空间下。从这里开始,报文的路径和我们之前在Part 1 中看到的非Overlay网络就是一致的了。
- c、由于IP forwarding开启着,内核按照路由表将报文转发给了cbr0。
7、网桥获取到了包,发送ARP请求,发现目标IP属于vethyyy。
8、包跨过管道对到达pod4。这就是Kubernetes中Overlay网络的工作方式,虽然不同的实现还是会有细微的差别。有个常见的误区是,当我们使用Kubernetes,我们就不得不使用Overlay网路。事实是,这完全依赖于特定场景。因此请确保在确实需要的场景下才使用。在前一部分我们学习了Kubernetes网络的基础知识。现在我们知道了Overlay网络是如何工作的。在下一部分,我们将看到Pod创建和删除过程中网络变化是如何发生的,以及出站和进站流量是如何流动的。
总体而言我对网络概念仍然是个新手,因此我非常期待能得到大家的反馈,特别是某些不清晰或者错误的地方。