K8s网络模型
Posted 技术原始积累
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了K8s网络模型相关的知识,希望对你有一定的参考价值。
一、前言
k8s对Pods之间如何进行组网通信提出了要求,k8s对集群的网络有以下要求:
每个Pod自己看到的自己的ip和其他Pod看到的一致
鉴于上面这些要求,我们需要解决四个不同的网络问题::
Docker容器和Docker容器之间的网络
Pod与Pod之间的网络
Pod与Service之间的网络
Internet与Service之间的网络
下面我们一一进行讨论每种网络问题,以及如何解决。
二、容器和容器之间的网络
在k8s中每个Pod中管理着一组Docker容器,这些Docker容器共享同一个网络命名空间。
container模式指定新创建的Docker容器和已经存在的一个容器共享一个网络命名空间,而不是和宿主机共享。新创建的Docker容器不会创建自己的网卡,配置自己的 IP,而是和一个指定的容器共享 IP、端口范围等
每个Pod容器有有一个pause容器其有独立的网络命名空间,在Pod内启动Docker容器时候使用 –net=container就可以让当前Docker容器加入到Pod容器拥有的网络命名空间(pause容器)
三、Pod与Pod之间的网络
在同一个Node上,从Pod的视角看,它存在于自己的网络命名空间中,并且需要与该Node上的其他网络命名空间上的Pod进行通信。
那么是如何做到的?这多亏了使用linux虚拟以太网设备或者说是由两个虚拟接口组成的veth对使不同的网络命名空间链接起来,这些虚拟接口分布在多个网络命名空间上(这里是指多个Pod上)。
为了让多个Pod的网络命名空间链接起来,我们可以让veth对的一端链接到root网络命名空间(宿主机的),另一端链接到Pod的网络命名空间。
每对Veth就像一根接插电缆,连接两侧并允许流量在它们之间流动;这种veth对可以推广到同一个Node上任意多的Pod上,如上图这里展示使用veth对链接每个Pod到虚拟机的root网络命名空间。
下面我们看如何使用网桥设备来让通过veth对链接到root命名空间的多个Pod进行通信。
如上图显示了两个Pod通过veth对链接到root网络命名空间,并且通过网桥进行通信
3.1 同一个Node中的Pod之间的一次通信
鉴于每个Pod有自己独立的网络命名空间,我们使用虚拟以太网设备把多个Pod的命名空间链接到了root命名空间,并且使用网桥让多个Pod之间进行通信,下面我们看如何在两个pod之间进行通信:
通过网桥这里把veth0和veth1组成为一个以太网,他们直接是可以直接通信的,另外这里通过veth对让pod1的eth0和veth0、pod2的eth0和veth1关联起来,从而让pod1和pod2相互通信。
Pod 1通过自己默认的以太网设备eth0发送一个数据包,eth0把数据传递给veth0,数据包到达网桥后,网桥通过转发表把数据传递给veth1,然后虚拟设备veth1直接把包传递给Pod2网络命名空间中的虚拟设备eth0.
3.2 不同Node中的Pod之间通信
如上图Node1(vm1)上的Pod1与Node2(vm2)上Pod4之间进行交互。
数据包到达Node2后,首先会被root命名空间的eth0设备,然后通过网桥cbr0把数据路由到虚拟设备veth1,最终数据表会被流转到与veth1配对的另外一端(pod4的eth0)
每个Node都知道如何把数据包转发到其内部运行的Pod,当一个数据包到达Node后,其内部数据流就和Node内Pod之间的流转类似了。
对于如何来配置网络,k8s在网络这块自身并没有实现网络规划的具体逻辑,而是制定了一套CNI(Container Network Interface)接口规范,开放给社区来实现。
例如AWS,亚马逊为k8s维护了一个容器网络插件,使用CNI插件来让亚马逊VPC(虚拟私有云)环境中的Node与Node直接进行交互.
CoreOS的Flannel是k8s中实现CNI规范较为出名的一种实现。
3.2.1 VXLAN
UDP帧格式
vlan
vxlan协议格式 VXLAN 全称是 Virtual eXtensible Local Area Network,虚拟可扩展的局域网。它是一种 overlay 技术,通过三层的网络来搭建虚拟的二层网络,其帧格式:
从这个报文中可以看到三个部分: 1.最外层的 UDP 协议报文用来在底层网络上传输,也就是 vtep 之间互相通信的基础 2.中间是 VXLAN 头部,vtep 接受到报文之后,去除前面的 UDP 协议部分,根据这部分来处理 vxlan 的逻辑,主要是根据 VNI 发送到最终的虚拟机 3.最里面是原始的报文,也就是虚拟机看到的报文内容
VTEP(VXLAN Tunnel Endpoints):vxlan 网络的边缘设备,用来进行 vxlan 报文的处理(封包和解包)。vtep 可以是网络设备(比如交换机),也可以是一台机器(比如虚拟化集群中的宿主机)
VNI(VXLAN Network Identifier):VNI 是每个 vxlan 的标识,是个 24 位整数,一共有 2^24 = 16,777,216(一千多万),一般每个 VNI 对应一个租户,也就是说使用 vxlan 搭建的公有云可以理论上可以支撑千万级别的租户
Tunnel:隧道是一个逻辑上的概念,在 vxlan 模型中并没有具体的物理实体想对应。隧道可以看做是一种虚拟通道,vxlan 通信双方(图中的虚拟机)认为自己是在直接通信,并不知道底层网络的存在。从整体来说,每个 vxlan 网络像是为通信的虚拟机搭建了一个单独的通信通道,也就是隧道
3.2.1 Flannel
Flannel是CoreOS团队针对Kubernetes设计的一个网络规划实现,简单来说,它的功能有以下几点:
建立一个覆盖网络(overlay network),通过这个覆盖网络,将数据包原封不动的传递到目标容器。覆盖网络是建立在另一个网络之上并由其基础设施支持的虚拟网络。覆盖网络通过将一个分组封装在另一个分组内来将网络服务与底层基础设施分离。在将封装的数据包转发到端点后,将其解封装。
创建一个新的虚拟网卡flannel0接收docker网桥的数据,通过维护路由表,对接收到的数据进行封包和转发(vxlan)。
路由信息一般存放到etcd:多个node上的Flanneld依赖一个etcd cluster来做集中配置服务,etcd保证了所有node上flanned所看到的配置是一致的。同时每个node上的flanned监听etcd上的数据变化,实时感知集群中node的变化
Flannel致力于给k8s集群中的nodes提供一个3层网络,他并不控制node中的容器是如何进行组网的,仅仅关心流量如何在node之间流转。
如上图ip为10.1.15.2的pod1与另外一个Node上的10.1.20.3的pod2进行通信。
首先pod1通过veth对把数据包发送到docker0虚拟网桥,网桥通过查找转发表发现10.1.20.3不在自己管理的网段,就会把数据包 转发给默认路由(这里为flannel0网桥)
由于是Vlanx设备,flannel0还会对上面的包进行二次封装,封装新的以太网mac帧:
node上2的eth0接收到上述vxlan包,kernel将识别出这是一个vxlan包,于是拆包后将packet转给node上2的flannel.0。flannel.0再将这个数据包转到docker0,继而由docker0传输到Pod2的某个容器里。
如上图,总的来说就是建立VXLAN 隧道,通过UDP把IP封装一层直接送到对应的节点,实现了一个大的 VLAN。
4.Pod与Service之间的网络
k8s中 Service管理了一系列的Pods,每个Service有一个虚拟的ip,要访问service管理的Pod上的服务只需要访问你这个虚拟ip就可以了,这个虚拟ip是固定的,当service下的pod规模改变、故障重启、node重启时候,对使用service的用户来说是无感知的,因为他们使用的service的ip没有变。
当数据包到达Service虚拟ip后,数据包会被通过k8s给该servcie自动创建的负载均衡器路由到背后的pod容器。下面我们看看具体是如何做到的
4.1 netfilter
|
| Incoming
↓
+-------------------+
| NF_IP_PRE_ROUTING |
+-------------------+
|
|
↓
+------------------+
| | +----------------+
| routing decision |-------->| NF_IP_LOCAL_IN |
| | +----------------+
+------------------+ |
| |
| ↓
| +-----------------+
| | local processes |
| +-----------------+
| |
| |
↓ ↓
+---------------+ +-----------------+
| NF_IP_FORWARD | | NF_IP_LOCAL_OUT |
+---------------+ +-----------------+
| |
| |
↓ |
+------------------+ |
| | |
| routing decision |<----------------+
| |
+------------------+
|
|
↓
+--------------------+
| NF_IP_POST_ROUTING |
+--------------------+
|
| Outgoing
↓
NFIPPRE_ROUTING: 接收的数据包刚进来,还没有经过路由选择,即还不知道数据包是要发给本机还是其它机器。
NFIPLOCAL_IN: 已经经过路由选择,并且该数据包的目的IP是本机,进入本地数据包处理流程。
NFIPFORWARD: 已经经过路由选择,但该数据包的目的IP不是本机,而是其它机器,进入forward流程。
NFIPLOCAL_OUT: 本地程序要发出去的数据包刚到IP层,还没进行路由选择。
NFIPPOST_ROUTING: 本地程序发出去的数据包,或者转发(forward)的数据包已经经过了路由选择,即将交由下层发送出去。
netfilter是工作在那一层?
4.2 iptables
Filter:从名字就可以看出,这个表里面的rule主要用来过滤数据,用来控制让哪些数据可以通过,哪些数据不能通过,它是最常用的表。
Mangle:里面的rule主要用来修改IP数据包头,比如修改TTL值,同时也用于给数据包添加一些标记,从而便于后续其它模块对数据包进行处理(这里的添加标记是指往内核skb结构中添加标记,而不是往真正的IP数据包上加东西)。
Raw:在netfilter里面有一个叫做connection tracking的功能,主要用来追踪所有的连接,而raw表里的rule的功能是给数据包打标记,从而控制哪些数据包不被connection tracking所追踪。
Security:里面的rule跟SELinux有关,主要是在数据包上设置一些SELinux的标记,便于跟SELinux相关的模块来处理该数据包。
4.3 IPVS
4.4 Pod到Service的一个包的流转
iptables利用Linux内核的conntrack来记住所做的Pod选择,以便将来的流量路由到同一个Pod(禁止任何扩展事件)。 从本质上讲,iptables直接在Node上进行了集群内负载均衡,然后流量使用我们已经检查过的Pod-to-Pod路由流到Pod。
4.5 Service到Pod的一个包的流转
收到此数据包的Pod将会回发包到源Pod,回包的源IP识别为自己的IP(比如这里为Pod4的ip),将目标IP设置为最初发送数据包的Pod(这里为pod1的ip)。
数据包进入目标Pod(这里为Pod1)所在节点后,数据包流经iptables,它使用conntrack记住它之前做出的选择,并将数据包的源IP重写为Service的IP。 从这里开始,数据包通过网桥流向与Pod1的命名空间配对的虚拟以太网设备,并流向我们之前看到的Pod1的以太网设备。
5.Internet与Service之间的网络
到目前为止,我们已经了解了如何在Kubernetes集群中路由流量。下面我们希望将服务暴露给外部使用(互联网)。 这需要强调两个相关的问题:(1)从k8s的service访问Internet,以及(2)从Internet访问k8s的service.
5.1 k8s流量到Internet
从Node到公共Internet的路由流量是特定于网络的,实际上取决于网络配置。为了使本节更具体,下面使用AWS VPC讨论具体细节。
本质都是使用NAT来做
5.1.1 Node到Internet
如上图中,数据包源自Pod1的网络命名空间,并通过veth对连接到root命名空间。
一旦root命名空间,数据包就会从网桥cbr0流传到到默认设备eth0,因为数据包上的目的IP与连接到网桥的任何网段都不匹配,在到达root命名空间的以太网设备eth0之前,iptables会修改数据包。
5.2 Internet到k8s
让Internet流量进入k8s集群,这特定于配置的网络,可以在网络堆栈的不同层来实现:
(1) NodePort
(2)Service LoadBalancer
(3)Ingress控制器。
5.2.1 第四层流量入口:NodePort
让外网访问k8s内部的服务的第一个方法是创建一个NodePort类型的Service, 对于NodePort类型的Service,k8s集群中的每个Node都会打开一个端口(所有Node上的端口相同),并将该端口上收到的流量重定向到具体的Service。
对于NodePort类型的Service,我们可以通过任何Node的ip和端口号来访问NodePort服务。
创建NodePort类型的服务:
如下图,服务暴露在两个节点的端口30123上,到达任何一个端口的链接会被重定向到一个随机选择的Pod。
如何做到的?
NodePort是靠kube-proxy服务通过iptables的nat转换功能实现的,kube-proxy会在运行过程中动态创建与Service相关的iptables规则,这些规则实现了NodePort的请求流量重定向到kube-proxy进程上对应的Service的代理端口上。
kube-proxy接受到Service的请求访问后,会从service对应的后端Pod中选择一个进行访问(RR)
5.2.2 第四层流量入口:LoadBalancer
该方式是NodePort方式的扩展,这使得Service可以通过一个专用的负载均衡器来访问,这个是由具体云服务提供商来提供的,负载均衡器将流量重定向到所有节点的端口上,如果云提供商不支持负载均衡,则退化为NodePort类型
创建一个负载均衡服务:
借助AWS,负载均衡器可以识别其目标组中的Node,并将平衡群集中所有节点的流量。 一旦流量到达Node,之前在整个群集中为Service安装的iptables规则将确保流量到达感兴趣的Service的Pod上。
下面看下LoadBalancer到Service的一个数据包的流转过程:
我们来看看LoadBalancer在实践中是如何运作的。部署Service后,正在使用的云提供商将会创建一个新的负载均衡器(1)。
由于负载均衡器不能识别容器,因此一旦流量到达负载均衡器,它就会把数据包发送到在构成群集的某个Node中(2)。每个Node上的iptables规则将来自负载均衡器的传入流量重定向到正确的Pod(3)
上图显示了承载Pod的三个Node前面的网络负载平衡器。首先流量被传到的Service的负载均衡器(1)。一旦负载均衡器收到数据包(2),它就会随机选择一个VM。这里我们故意选择了没有Pod运行的node:node 2。在这里,node上运行的iptables规则将使用kube-proxy安装在集群中的内部负载平衡规则将数据包定向到正确的Node 中的Pod。 iptables执行正确的NAT并将数据包转发到正确的Pod(4)。
需要注意的是每个服务需要创建自己独有的负载均衡器,下面要讲解的一种方式所有服务只需要一个公开服务。
5.2.3 第七层流量入口:Ingress Controller
创建Ingress资源:如上定义了一个单一规则的Ingress,确保Ingress控制器接受的所有请求主机kubia.example.com的http请求被发送到端口80上的kubia-nodeport服务上。
工作原理: 如下图,客户端首先对kubia.example.com执行DNS查找,DNS服务器返回Ingress控制器的IP,客户端拿到IP后,向Ingress控制器发送Http请求,并在Host投中指定kubia.example.com。控制器接受到请求后从Host头部就知道该访问那个服务,通过与该Service关联的endpoint对象查询Pod ip,并将请求转发到某一个Pod。
这里Ingress并没把请求转发给Service,而是自己选择一个一个Pod来访问。
本文参考了大量资料,并结合作者的理解,如有不对,欢迎讨论^^
参考
http://www.linuxtcpipstack.com/685.html
https://www.ibm.com/developerworks/cn/linux/l-ntflt/index.html
https://www.ibm.com/developerworks/cn/linux/network/s-netip/index.html
https://yuque.antfin-inc.com/jialei.zjl/ylprqa/bsti0g
https://www.atatech.org/articles/134314
https://www.atatech.org/articles/113438
https://linux.cn/article-5595-1.html
https://tools.ietf.org/html/rfc7348
https://cizixs.com/2017/09/25/vxlan-protocol-introduction/
https://tonybai.com/2017/01/17/understanding-flannel-network-for-kubernetes/
https://github.com/containernetworking/plugins
https://www.kubernetes.org.cn/4105.html
https://ieevee.com/tech/2017/01/18/k8s-flannel.html
http://dockone.io/article/618
http://yizhanggou.top/kubernetes%E9%97%B4pod%E7%9A%84%E4%BA%92%E8%81%94%EF%BC%9Aflannel/
https://www.hi-linux.com/posts/30481.html
https://juejin.im/post/5bb45d63f265da0a9e532128 搭建
以上是关于K8s网络模型的主要内容,如果未能解决你的问题,请参考以下文章