当前位置: 首页 > Linux

【Kubernetes系列】Part10网络原理解析(下)

时间:2023-04-06 04:19:58 Linux

3.OverlayNetwork-OverlayNetwork覆盖网络(overlaynetwork)是一种将TCP数据包裹在另一个网络数据包中进行路由、转发和通信的技术。默认情况下不需要覆盖网络,但它们在某些情况下非常有用。例如当我们没有足够的IP空间,或者网络无法处理额外的路由时,或者当我们需要Overlay提供的一些额外的管理功能时。一个常见的场景是当云提供商的路由表可以处理的路由数量有限时。例如,AWS路由表最多支持50条路由,这样网络性能就不会受到影响。所以如果我们有超过50个Kubernetes节点,AWS路由表就不够用了。在这种情况下,使用Overlay网络会对我们有所帮助。Overlay本质上就是在本地网络跨节点的报文中再封装一层报文。您可能不想使用覆盖网络,因为它会带来延迟和封装和解封装所有数据包引起的复杂性开销。通常这是不必要的,所以我们应该只在知道为什么需要它时才使用它。为了理解流量在Overlay网络中的流向,我们以Flannel为例,Flannel是CoreOS的一个开源项目。Flannel通过为每个主机分配一个子网来为容器提供一个虚拟网络。它基于LinuxTUN/TAP,使用UDP封装IP数据包创建覆盖网络,并借助etcd维护网络的分布。在这里我们注意到它与我们之前看到的设施相同,只是在rootnetns中添加了一个名为flannel0的新虚拟以太网设备。它是虚拟可扩展局域网(VXLAN)的实现,但在Linux上,它只是另一个网络接口。pod1到pod4(不同节点)的数据包流向类似如下:1、从pod1中netns的eth0网口离开,通过vethxxx进入rootnetns。2.然后传递给cbr0,cbr0通过发送ARP请求找到目标地址。3.数据封装*a.由于本节点上没有Pod有pod4的IP地址,所以网桥将数据包发送到flannel0,因为flannel0在该节点的路由表中配置为Pod网段的目标地址。*乙。flanneld守护进程与Kubernetesapiserver或底层etcd通信。它知道所有PodIP以及它们所在的节点。因此Flannel在PodIP和节点IP之间创建了一个映射(在用户空间中)。flannel0获取此数据包并将其封装在UDP数据包中。UDP包头的源IP和目的IP都改为对应节点的IP,然后将新包发送到特定的VXLAN端口(一般为8472)。虽然这个映射发生在用户空间,但是数据的实际封装和流动发生在内核空间,所以还是很快的*c。封装后的数据包通过eth0发送出去,因为涉及到节点间的路由流量。4.数据包以节点IP信息作为源地址和目的地址离开节点。5.云提供商的路由表已经知道如何在节点之间发送消息,所以将消息发送到目标地址node2。6.数据解包*a.包到达node2的eth0网卡,由于目标端口是特定的VXLAN端口,内核将包发送到flannel0。*乙。flannel0解封装数据包并将其发送到根命名空间。从这里开始,数据包的路径与我们在第1部分中看到的非Overlay网络相同。*c。因为启用了IP转发,所以内核根据路由表将数据包转发到cbr0。7.网桥拿到数据包,发送ARP请求,发现目标IP属于vethyyy。8.包穿过管道对到达pod4。这就是Overlay网络在Kubernetes中的工作方式,尽管不同的实现之间仍然存在细微差别。一个常见的误解是,我们使用Kubernetes时,必须使用Overlay网络。事实是,这完全取决于特定情况。因此,请确保仅在真正需要时才使用它。4.动态集群由于Kubernetes(更一般地说是分布式系统)固有的变化特性,它的Pod(以及Pod的IP)总是在变化。更改的原因可能是滚动升级和扩展以响应不可预测的pod或节点崩溃。这使得PodIP不能直接用于通信。我们来看看KubernetesService,它是一个虚拟IP,伴随着一组PodIP作为Endpoints(由标签选择器识别)。它们充当虚拟负载均衡器,它们的IP保持不变,而后端PodIP可能会不断变化。整个虚拟IP的实现实际上是一套iptables(最新版本有使用IPVS的选项,那是另外讨论)规则,由Kubernetes组件kube-proxy管理。现在这个名字实际上具有误导性。它在v1.0之前确实是一个代理,并且由于它的实现是内核空间和用户空间之间的不断复制,它变得非常占用资源并且速度很慢。现在,它只是一个控制器,就像Kubernetes中的许多其他控制器一样,监视api服务器端点的变化并相应地更新iptables规则。使用这些iptables规则,每当数据包以服务IP为目的地时,它都会执行DNAT(DNAT=目标网络地址转换)操作,这意味着目标IP从服务IP更改为端点之一-PodIP-通过iptables随机选择。这确保负载在后端pod之间均匀分布。当这个DNAT发生时,这个信息被存储在conntrack——Linux连接跟踪表中(iptables规则5元组转换和完整存储:protocol,srcIP,srcPort,dstIP,dstPort)。这样当请求返回时,它可以取消DNAT,这意味着将源IP从PodIP更改为ServiceIP。这样,客户端就不必关心数据包流在后台是如何处理的。因此,通过使用Kubernetes服务,我们可以使用相同的端口而不会发生任何冲突(因为我们可以将端口重新映射到端点)。这使得服务发现变得非常容易。我们可以使用内部DNS并对服务主机名进行硬编码。我们甚至可以使用Kubernetes提供的服务主机和端口环境变量来完成服务发现。专家建议:采用第二种方式,可以省去不必要的DNS调用,但是由于环境变量创建顺序的限制(后面创建的服务不包含在环境变量中),建议使用DNS服务名称解析。4.1出站流量到目前为止我们讨论的Kubernetes服务在集群中工作。然而,在大多数实际情况下,应用程序需要访问一些外部api/网站。通常,节点可以同时拥有私有IP和公共IP。对于互联网访问,这些公共和私有IP存在某种1:1NAT,尤其是在云环境中。对于一个节点到某个外部IP的正常通信,出站数据包的源IP由节点的私有IP变为其公有IP,入站响应数据包则相反。但是,当Pod发出与外部IP的连接时,源IP是PodIP,云提供商的NAT机制不知道它。因此它会丢弃源IP不是节点IP的数据包。所以你可能也猜对了,我们将使用更多的iptables!这些规则也是由kube-proxy添加的,进行SNAT(SourceNetworkAddressTranslation),即IPMASQUERADE(IPMasquerading)。它告诉内核使用发送此数据包的网络接口的IP而不是源PodIP,同时保留反SNAT操作的conntrack条目。4.2入站流量到目前为止一切顺利。Pod可以相互交谈,也可以访问互联网。但我们仍然缺少一个关键部分——服务用户请求流量。到目前为止,主要有两种方法可以做到这一点:*NodePort/CloudLoadBalancer(L4-IP和端口)将服务类型设置为NodePort将默认为服务分配30000-33000范围内的nodePort。即使没有在该特定节点上运行的pod,该节点端口也会在每个节点上打开。此NodePort上的入站流量将再次使用iptables发送到其中一个Pod(Pod甚至可能位于不同的节点上!)。云环境中的LoadBalancer服务类型将在所有节点前面创建一个云负载均衡器(例如ELB),命中相同的节点端口。*Ingress(L7-HTTP/TCP)许多不同的工具,如Nginx、Traefik、HAProxy等,保持http主机名/路径和各自后端的映射。通常这是基于负载均衡器和节点端口的流量入口点,优点是我们可以用一个入口点处理所有服务的入站流量,而不是多个节点端口和负载均衡器。4.3网络策略将其视为Pod的安全组/ACL。NetworkPolicy规则允许/拒绝跨Pod的流量。确切的实现取决于网络层/CNI,但大多数只使用iptables。结论现在就这些了。在前面的部分中,我们了解了Kubernetes网络的基础知识以及覆盖网络的工作原理。现在我们知道服务抽象如何在动态集群中工作,并使服务发现变得非常容易。我们还介绍了出站和入站流量的工作原理以及网络策略如何促进集群内的安全性。参考文章:图解KubernetesNetworking指南-part1An图解KubernetesNetworking指南-part2An图解KubernetesNetworking指南-part3