青云QingCloud
作者青云QingCloud2021-01-02 20:31
技术经理, 青云QingCloud

一文读懂 Kubernets 网络

字数 8429阅读 264评论 0赞 0

文章转自https://sookocheff.com/post/kubernetes/understanding-kubernetes-networking-model/#kubernetes-basic,加上一些注释便于理解

Kubernetes的四种网络

  1. Container-to-Container networking:容器到容器的网络
  2. Pod-to-Pod networking:pod到pod的网络
  3. Pod-to-Service networking:pod到service的网络
  4. Internet-to-Service networking:internet到service的网络

1. Container-to-Container networking

​ 在这里讲解的容器到容器的网络,指得是同一个pod的容器之间的通讯。容器与容器之间的通讯直接使用localhost

​ 如果之前了解过kubernetes应该都知道,在kubernetes里面最小单元是pod。而每一个pod里面必须有pause容器。这个pause容器所负责的之一就是容器与容器之间通讯。

​ 想要了解pause容器,就必须先知道linux namespace。实际上docker所实现的虚拟化服务就是建立在namespace之上的。docker所使用的隔离就是namespace的隔离。kubernetes通过调整容器的network namespace,实现同一个pod中容器间的相互通讯。

namespace 的小知识

1.linux 中提供了7中namespace(network namespace是其中之一),namespace之间相互隔离。

2.linux 启动的时候会分配一个root namespace。可以通过命令ll /proc/$$/ns查看当前所在的namespace

3.在pid namespace里面可以创建子pid namespace,同时在父pid namespace可以看到子pid namespace的进程。

4.pid namespace的1号进程挂掉之后,该pid namespace其他进程将会kill,系统1号进程是不会被杀掉。

5.1号进程(当前nameapace中pid 为1的进程)还有一个能力,就是能够接管孤儿进程,成为孤儿进程的父进程。这一点很重要,否则系统里就会出现非常多的僵死进程了。

​ 在启动pod的时候,kubernetes会最先启动pause容器,在pause 容器中构建一个新的network namespace,然后kubernetes会启动业务容器,将业务容器的network namespace设置为pause 容器的network namespace。这样所有的业务容器的通讯都是使用pause的network namespace。

在docker run 命令中使用 –name pause 设置容器名称,–net=container:pause设置容器的network namespace

2.Pod-to-Pod networking

​ 在Kubernetes中,每个Pod都有一个真实的IP地址,并且每个Pod使用该IP地址与其他Pod通信。当前的章节是了解Kubernetes如何使用真实IP启用Pod到Pod的通信,在这里我们先解决Pod是部署在集群中的同一物理节点。以避免通过内部网络进行跨节点通信的复杂性。

​ 从Pod的角度来看,它存在于自己的network namespace(由pause建立) 中,该namespace需要与同一节点上的其他网络namespace进行通信。我们可以直接使用一对veth—–由两个可以在多个namespace中分布的虚拟接口组成。

​ 要实现接Pod namespace之间的通讯,我们可以将一对veth的一侧分配给root network namespace,另一侧分配给Pod的network namespace。有多少个的Pod就有多少对veth设备。下图显示了将每个Pod连接到root network namespace的veth对(eth0-veth0 组成一对veth设备)。

​ 至此,我们已经将Pod设置了自己的network namespace。同时他们认为自己具有自己的eth0设备和IP地址,并且它们已连接到Node的root namespace。现在,我们希望Pods通过root namespace相互通信,为此,我们使用了bridge(网桥)。如下图。

​ Linux bridge是一种虚拟的第2层网络设备,用于将两个或多个网段结合在一起。bridge透明地将两个网络连接在一起。bridge维护源和目的地之间转发表。bridge通过检查转发表确定数据包的目的地并确定是否将数据包传递到连接到bridge的其他网段。 bridge通过查看网络中每个以太网设备唯一的MAC地址来决定是桥接数据还是丢弃数据。

​ Bridge实现 ARP协议来发现IP地址和MAC地址,并关联P地址和MAC地址。当在Bridge处接收到数据帧时,Bridge会将帧广播到所有连接的设备(原始发送方除外),并且将对帧进行响应的设备存储在查找表中。具有相同IP地址的将来流量将使用查找表来发现正确的MAC地址,以将数据包转发到该MAC地址。

2.1同节点Pod-to-Pod networking

​ 给定将每个Pod隔离到其自己的网络堆栈的network namespace,将每个network namespace连接到root network namespace的虚拟以太网设备。这样我们终于可以在同一节点上的Pod之间发送流量了。如下图所示。

数据包流程

  • Pod 1将数据包发送到其自己的以太网设备eth0,该设备可用作Pod的默认设备。
  • 在Pod 1,eth0通过虚拟以太网设备连接到根名称空间veth0 。
  • Bridge的 cbr0器配置有veth0连接的网段。数据包到达网桥后,网桥解析正确的网络段,使用ARP协议发现veth1。
  • 将数据包发送至veth1。当数据包到达虚拟设备时veth1,它将直接转发到Pod 2的名称空间以及该名称空间中的eth0设备,至此完成数据的交互。

​ Kubernetes的网络模型要求Pod必须通过其跨节点的IP地址才能访问。也就是说,一个Pod的IP地址始终对网络中的其他Pod可见,并且每个Pod都将自己的IP地址视为与其他Pod看到的IP地址相同。现在,我们转向在不同节点上的Pod之间路由流量的问题。

2.2 跨节点Pod-to-Pod networking2.2 跨节点Pod-to-Pod networking

​ 在确定了如何在同一节点上的Pod之间路由数据包之后。我们继续进行路由,以在不同节点上的Pod之间路由数据包。Kubernetes网络模型要求Pod IP在整个网络上都是可访问的,但是它没有指定必须如何完成。实际上,这是特定于网络的,但是Kubernetes已经建立了一些模式来简化此过程。

​ 通常,群集中的每个节点都分配有一个CIDR块(一个ip地址段,用于分配pod地址)。一旦发往CIDR块的流量到达节点,则节点有责任将流量转发到正确的Pod。下图说明了两个节点之间的流量流,假设网络可以将CIDR块中的流量路由到正确的节点

  • 数据包首先通过Pod 1的eth0设备发送,该设备与root namespace中的veth0设备配对。最终,数据包最终到达root namespace的网络bridge
  • ARP将在bridge上查询失败,因为没有任何设备连接到网桥,且该设备的数据包的MAC地址正确。失败时,网桥将数据包发送给root network namespace的eth0设备。此时,数据包离开节点并进入网络。
  • 现在我们假设网络可以根据CIDR块将数据包路由到正确的节点
  • 数据包进入目标节点的root namespace(eth0在node 2上),在此它通过bridge路由到正确的veth
  • 最后,通过数据流向Pod4的network namespace

1.这段话省略的一个地方(就是上面第三步假设的处理)。我们常常说的网络插件(flannel,calico)。他们的作用就是帮助跨节点的pod与pod通讯。在上图中并没有标示出网络插件,是因为每一个插件的作用是不同的。

2.为了方便调用网络插件。kubernets使用CNI 标准(可以看作是网络的统一调用接口)。这样kubernets在调用的时候就不用关心底层是flannel还是calico。大家可以看看flannel和calico的实现方式有啥异同点

3.CNI配置/etc/cni/net.d/。网络插件地址/opt/cni/bin

3.Pod-to-Service Networking

Service是kubernets资源类型之一,常用于负载均衡。

​ 我们已经展示了如何在Pod与pod之间的通讯。到目前为止,这个方案非常有效,但事实往往是出人意料的。Pod IP地址不是静态的。应用程序崩溃或节点重启后ip就不是原来的ip了。这些突发事件中的每一个都可以使Pod IP地址更改却不会发出警告。Kubernetes中内置的Service就是用来解决此问题。

​ Kubernetes Service管理一组Pod的状态。通过Service,你可以监控一组pod的ip地址。Service充当对一组Pod抽象,并为这组Pod IP地址分配一个虚拟IP地址。发送到Service的虚拟IP的所有流量都将被转发到与虚拟IP关联的Pod集。这允许该service内的Pod集的ip地址随时更改。客户端只需要记住Service的虚拟IP即可。

​ 创建新的Kubernetes Service时,将代表您创建一个新的虚拟IP(也称为cluster IP)。群集中任何地方,寻址到该IP的流量都将负载均衡到与该Service关联的一组支持Pod。实际上,Kubernetes会自动创建并维护一个分布式集群内负载均衡器,该负载均衡器会将流量分配给与服务相关联的健康Pod。让我们仔细看看它是如何工作的。

3.1 netfilter and iptables

​ 为了在集群中执行负载平衡,Kubernetes依赖于Linux内置的网络框架netfilter。Netfilter是Linux提供的框架,它允许以自定义处理程序的形式实现各种与网络相关的操作。

​ Netfilter提供了用于数据包过滤,网络地址转换和端口转换的各种功能和操作,这些功能和操作提供了通过网络定向数据包所需的功能,并提供了禁止数据包到达计算机网络内敏感位置的功能。 iptables是一个用户空间程序,它提供一个基于表的系统,用于定义使用netfilter框架处理和转换数据包的规则。

​ 在Kubernetes中,kube-proxy控制器通过访问Kubernetes API服务器来配置iptables规则。当Service或Pod的ip地址更新时。kube-proxy将同步的更新iptables规则,以便将定向到service的流量正确路由到Pod。iptables规则监视发往Service的虚拟IP的流量,并在匹配项中从可用Pod的集合中选择一个随机的Pod IP地址,并且iptables规则将数据包的目标IP地址从Service的虚拟IP更改为选定的Pod的ip。

​ 在返回数据包时,该IP地址来自目标Pod。 在这种情况下,iptables再次重写IP标头,用Service的IP替换Pod IP。原Pod认为它一直在与ServiceIP进行的通信。

3.2 IPVS

​ Kubernetes的最新版本(1.11)新添加了负载均衡的选项:IPVS。 IPVS(IP Virtual Server)也建立在netfilter之上,并作为Linux内核的一部分实现传输层负载平衡。 IPVS被集成到Linux中,在此服务器上运行,并充当真实服务器群集之前的负载平衡器。 IPVS可以将对基于TCP和UDP的服务的请求定向到真实服务器,并使真实服务器的服务在单个IP地址上显示为虚拟服务。这使得IPVS非常适合Kubernetes Services。

​ 在声明Kubernetes服务时,您可以指定是否要使用iptables或IPVS完成集群内负载平衡。 IPVS专为负载平衡而设计,并使用更有效的数据结构(哈希表),与iptables相比,几乎可以无限扩展。创建使用IPVS平衡的服务负载时,会发生三件事:在节点上创建虚拟IPVS接口,将服务的IP地址绑定到虚拟IPVS接口,并为每个服务IP地址创建IPVS服务器。

​ 现在,让我们看一下通过集群内负载平衡服务的数据包的周期。

3.3pod 到service数据包的过程

  • 数据包首先通过连接到Pod网络名称空间的eth0接口离开Pod
  • 然后,它通过虚拟以太网设备(weth pair)到达bridge
  • bridge上运行的ARP协议无法定位Service的ip,因此它通过默认路由eth0将数据包传输出去
  • 在这里,发生了一些不同的事情。 在被eth0接受之前,该数据包已通过iptables进行过滤。 收到数据包后,iptables会修改数据包,将目标的Service的ip修改成pod的ip。
  • 最终数据包流向的事真实的pod ip,而不是service ip

3.4Service 到pod数据包的过程

  • 接收到此数据包的Pod将做出响应,将源IP标识为自己的IP,将目标IP标识为最初发送该数据包的Pod
  • 进入节点后,数据包流经iptables,后者使用conntrack记住其先前所做的选择,并将数据包的源重写为service 的IP而非Pod的IP。
  • 数据包从此处通过网桥流到与Pod的namespace 配对的虚拟以太网设备,再流到我们之前看到的Pod的以太网设备。

3.5使用DNS

​ Kubernetes可以选择使用DNS(域名),以避免必须将服务的群集IP地址硬编码到您的应用程序中。Kubernetes DNS实际上就是kubernets的一个service。Kubernetes DNS通过kubelete的配置来设置域名。每一个service都会定义一个域名(包括自己)

​ 在最新的Kubernetes 中,使用coredns作为DNS服务器。CoreDNS 使用Caddy作为底层的 Web Server,Caddy 是一个轻量、易用的Web Server,它支持 HTTP、HTTPS、HTTP/2、GRPC 等多种连接方式。所有 coreDNS 可以通过四种方式对外直接提供 DNS 服务,分别是 UDP、gRPC、HTTPS 和 TLS

​ CoreDNS 的大多数功能都是由插件来实现的,插件和服务本身都使用了 Caddy 提供的一些功能,所以项目本身也不是特别的复杂。

原文档DNS使用的是kubedns,但是kubesphere使用的是coredns。从使用方面来说coredns更优于kubedns,所以这里讲解coredns。具体文章可以看<> https://zhuanlan.zhihu.com/p/80141656?from_voters_page=true>。实际上> ,DNS的实现就是一个service。

4.Internet-to-Service Networking

​ 到目前为止,我们已经研究了如何 Kubernetes集群中路由流量。一切都很好,但不幸的是,您的应用程序与外界隔离,无法实现任何业务目标。有时您将需要向外部流量公开您的服务。这种需求突出了两个相关的问题:(1)将来自Kubernetes服务的流量引出到Internet(2)将来自Internet的流量引到您的Kubernetes服务。本节依次处理这些问题。

​ 一般情况下,从service流向internet的流量是不会遇到阻碍的。如果有网关限制的话,可能需要设置iptables。下面我们只讲解internet 访问service的情况。

4.1InEgress,Internet 流向 service

​ 入口(将流量引入群集)是一个非常棘手的问题。同样,这是特定于您正在运行的网络的。Ingress分为两个可在网络堆栈的不同部分上运行的解决方案:(1)Service LoadBalancer(2)Ingress控制器。

4.2metallb

metallb提供两种服务,1.address allocation(地址分配) 2.external announcement.(外部通知)

4.2metallb的地址分配

​ 在启用了云的Kubernetes集群中,您需要一个负载均衡器,并且您的云平台会为您分配一个IP地址。在裸机集群中,MetalLB负责该分配。

​ MetalLB无法凭空创建IP地址,因此您必须为它提供可以使用的IP地址池。当服务启动和脱机时,MetalLB将负责分配和取消分配单个地址,但是它只会分发作为其已配置池一部分的IP。

​ 如何获取MetalLB的IP地址池取决于您的环境。如果您在主机托管设施中运行裸机群集,则托管服务提供商可能会提供IP地址进行租赁。在这种情况下,您将租用/26个IP空间(64个地址),并将该范围提供给MetalLB以用于群集服务。

​ 另外,您的群集可能完全是私有的,为附近的LAN提供服务,但不暴露于Internet。在这种情况下,您可以从一个专用地址空间(所谓的RFC1918地址)中选择一个IP范围,并将其分配给MetalLB。这样的地址是免费的,并且只要您仅向LAN提供群集服务就可以正常工作。或者,您可以两者都做!MetalLB使您可以定义任意数量的地址池,而不管您为它提供什么样的地址。

4.2metallb的外部公告

​ 一旦MetalLB为服务分配了外部IP地址,它就需要使群集之外的网络意识到该IP在群集中“存在”。MetalLB使用标准路由协议来实现此目的:ARP,NDP或BGP。

​ 在第2层模式下,群集中的一台机器拥有服务的所有权,并使用标准地址发现协议(用于IPv4的ARP, 用于IPv6的NDP)使这些IP在本地网络上可访问。从LAN的角度来看,通告机仅具有多个IP地址。

​ 在BGP模式下,群集中的所有计算机都 与您控制的附近路由器建立BGP对等会话,并告诉这些路由器如何将流量转发到服务IP。借助BGP的策略机制,使用BGP可以在多个节点之间实现真正的负载平衡,并实现细粒度的流量控制。

4.3Ingress

​ 实际上,ingress相当于一个7层的负载均衡器,是k8s对反向代理的一个抽象。大概的工作原理也确实类似于Nginx,可以理解成在 Ingress 里建立一个个映射规则 , ingress Controller 通过监听 Ingress这个api对象里的配置规则并转化成 Nginx 的配置(kubernetes声明式API和控制循环) , 然后对外部提供服务。ingress包括:ingress controlleringress resources

​ ingress controller:核心是一个deployment,实现方式有很多,比如nginx, Contour, Haproxy, trafik, Istio,需要编写的yaml有:Deployment, Service, ConfigMap, ServiceAccount(Auth),其中service的类型可以是NodePort或者LoadBalancer。

​ ingress resources:这个就是一个类型为Ingress的k8s api对象了,这部分则是面向开发人员。

来源https://www.jianshu.com/p/97dd4d59ac5a

LoadBalancer 目前没有集成到kubernets,目前有一个开源的> metallb> ,原文档使用的是aws的LB,这里我只介绍了metallb。

总结,service是对pod的负载均衡,ingress是对service的负载均衡。

如果觉得我的文章对您有用,请点赞。您的支持将鼓励我继续创作!

0

添加新评论0 条评论

Ctrl+Enter 发表

容器云管理平台选型优先顺序调查

发表您的选型观点,参与即得50金币。