K8s概述(二):k8s组件选型

存储

使用

Volume是将宿主机目录与容器目录绑定挂载在一起。k8s把存储抽象为PV、PVC,把存储提供者与使用者分离。使用者只需要声明希望持有的持久化存储的属性(PVC),提供者提供可用的各种类型存储、节点、用户名密码等,提供PV。这样子的好处是使用者不需要关心底层是使用什么存储设备来实现的。

存储的选择

存储的选择:排除NFS,有单点问题,在GlusterFS和Ceph中做选择。根据网上找到的一篇文章以及本人自己dd后的测试结果,GlusterFS比Ceph略快一些:
cephvsglusterfs-1

维度 GlusterFS Ceph
适用于 大文件,顺序访问 对象和块存储
文件存储速度 略快 略慢
K8s限制 不能跨Node挂载多个ReadWrite

什么是对象存储

【块存储】磁盘阵列,硬盘,用户需要先格式化磁盘,相当于主机的磁盘,多机无法共享
【文件存储】FTP、NFS服务器,用于多主机共享数据
【对象存储】内置大容量硬盘的分布式服务器。
简单来说块存储读写快,不利于共享,文件存储读写慢,利于共享。能否弄一个读写快,利 于共享的出来呢。于是就有了对象存储。
因为文件存储需要顺序读写,文件与metadata是放在一起的,存储过程先将文件按照文件系统的最小快大小来打算再写入硬盘,过程中没有区分数据/metadata,每个块最后会告知你下一个要读取的块的地址,顺序读写。
对象存储将metadata独立出来,控制单元叫做元数据服务器,负责存储数据的分布式服务器叫做OSD。
用户访问对象,会先访问元数据服务器,得到数据存储在哪些OSD,用户可以并行访问多台OSD。
缺点:对象存储需要大容量硬盘,如果数据不是海量,建议使用文件存储。

网络

kube-dns

流程

  • kube-dns配置本身的pod的ip到每个容器的/etc/resolv.conf。每次查询其他服务都会导向kube-dns,下面是kube-dns的地址:
    resolv-1
  • kubedns监控Kubernetes中Service和Endpoint的变化,把DNS信息保存在内存结构中,po中每次通过service访问其他pod都在这里获取域名相对应的svc vip。
    ss-1
  • 通过svc vip,就可以访问pod内的服务,具体是kube-proxy通过iptables实现的:
    • 通过iptables把请求这个VIP的request转发到不同的pod
      vip-1
      dnat-1

优化

当你的宿主机上有大量 Pod 的时候,成百上千条 iptables 规则不断地被刷新,会大量占用该宿主机的 CPU 资源,甚至会让宿主机“卡”在这个过程中。所以说,一直以来,基于 iptables 的 Service 实现,都是制约 Kubernetes 项目承载更多量级的 Pod 的主要障碍。

网络组件的选择

找到了容器的ip接下来我们怎么做容器间互联呢,大体上有三种方案。首先我们来看一下UDP封装。

Flannel UDP

通过容器网关进入主机上的docker0网桥出现在宿主机上,按照宿主机上的路由规则,发往100.96.0.0/16的包需要走到flannel0设备,这是一个TUN设备,用于在操作系统内核和用户应用进程之间传递IP包。

操作系统把这个包交给flannel0后,flannel0就会交给创建这个设备的应用程序,即flanneld。Flanneld看到这个设备是发往100.96.2.3的,就会发往Node2。Flanneld怎么知道这个包是发往Node2呢,因为起k8s和节点加入的时候就已经给每个node分配了子网,这些子网与宿主机之间的关系都保存在etcd里面。然后会封装为一个UDP包发送到Node2。

这种模式有什么问题呢,我们可以看到每发一次包都需要经过三次内核态和用户态的转换,先是从container1到dokcer0,然后从flannel0到TUN设备,最后是从TUN设备到eth0。

udp-1

Flannel VXLAN

VXLAN在内核态完成上述封装和解封装的工作。VTEP:用于对VXLAN报文进行封装/解封装,在一段封装报文后通过隧道向另一端VTEP发送封装报文,另一端VTEP接收到封装的报文解封装后根据封装的MAC地址进行转发。

  • 发往Node2的包发往flannel.1(VTEP设备),隧道入口
  • 通过路由规则,发往Node2容器的包要发往Node2的flannel.1设备(flanneld维护)
  • 通过ARP表查到node2的flannel.1的MAC地址(flanneld维护)
  • 封装VXLAN头,以表示要发给对端的哪个VTEP设备(用VNI指明)
  • 封装UDP包
  • 通过FDB表查找到发往flannel.1的MAC地址的包需要发往的IP(flanneld维护)

vxlan-1

Flannel.1是宿主机的VTEP设备。VTEP设备用于对VXLAN报文进行封装/解封装。

Container1发送的包会通过dokcer0出现在宿主机,然后经过路由规则发给flannel.1设备,即隧道的入口。VXLAN找到这条隧道的出口,即Node2的VTEP设备,这个设备的信息是每台宿主机上的flanneld进程维护的,它会在宿主机上加上路由信息,发往右边那个宿主机的包,都需要经过flannel.1设备发出,并发往右边设备的flannel.1。所以需要把这个包封装在目的MAC

  • flannel.1作为网桥设备,flanneld在FDB表中加上网桥设备的转发依据
    • 发往目标VTEP设备mac地址的帧应该通过flannel.1设备发往的目标宿主机的ip地址
  • VXLAN 模式组建的覆盖网络,其实就是一个由不同宿主机上的 VTEP 设备,也就是 flannel.1 设备组成的虚拟二层网络。对于 VTEP 设备来说,它发出的“内部数据帧”就仿佛是一直在这个虚拟的二层网络上流动。

Flannel host-gw

host-gw将每个 Flannel 子网(Flannel Subnet,比如:10.244.1.0/24)的“下一跳”,设置成为该子网对应的宿主机的IP 地址。这台主机充当了网关。
<目的容器IP地址段> via <网关的IP地址> dev eth0

  • 通过 Etcd 和宿主机上的 flanneld 来维护路由信息
  • 无需额外的封包解封
  • 要求集群宿主机之间二层连通

二层连通:同个子网的意思。需要使用路由表的下一跳来设置MAC地址。

hostgw-1

Calico

Calico跟Flannel host-gw模式一致,只是不是使用flannel维护路由信息,而是使用BGP来维护:

  • 使用BGP维护路由信息。
  • 不会在宿主机上创建网桥。

calico-1

Calico还可以适用不同子网,使用IPIP把从容器到目标宿主机的IP包,伪装为一个从原宿主机到目标宿主机的IP包。缺点就是增加了封包解包,增加了消耗
calico-ipip-1

发往10.233.2.3的ip包需要进入cali5863f3设备出现在宿主机,然后宿主机的网络栈会根据路由规则把它发给正确的网关。

比如在左边的route的表里面有一条规则,目的地址是10.233.2.3的包,需要经过route上面的接口,发给右边宿主机的网关route。

这些规则都是Calico维护的。Calico 使用的这个 tunl0 设备,是一个 IP 隧道(IP tunnel)设备。在上面的例子中,IP 包进入 IP 隧道设备之后,就会被 Linux 内核的 IPIP 驱动接管。IPIP 驱动会将这个 IP 包直接封装在一个宿主机网络的 IP 包中。
而原 IP 包本身,则会被直接封装成新 IP 包的 Payload。这样,原先从容器到 Node 2 的 IP 包,就被伪装成了一个从 Node 1 到 Node 2 的 IP 包。由于宿主机之间已经使用路由器配置了三层转发,也就是设置了宿主机之间的“下一跳”。所以这个 IP 包在离开 Node 1 之后,就可以经过路由器,最终“跳”到 Node 2 上。这时,Node 2 的网络内核栈会使用 IPIP 驱动进行解包,从而拿到原始的 IP 包。然后,原始 IP 包就会经过路由规则和 Veth Pair 设备到达目的容器内部。

损耗

网络 损耗
Host network 0%
Flannel(UDP) 50%
Flannel(vxlan) 20% ~ 30%
Flannel(host-gw) 10%
Calico 10%
comments powered by Disqus