Kubernetes 存储系统介绍及机制实现
技术
作者:徐迪;Docker
译者:
2018-01-22 10:35

 本文分为三大部分。第一部分主要介绍 Kubernetes 中常用的几种存储,及其使用场景和生命周期等等。第二部分试图介绍一些设计原则和基本架构,并简要介绍各种存储 plugin 的实现机制及持久卷的一些特性,例如访问模式、回收策略等等。动态卷供给是一个 Kubernetes 独有的功能,这一功能允许按需创建存储卷,使管理员不必预先创建存储卷,而是随用户需求进行创建。第三部分会介绍一下 v1.9 中存储的一些新特性。


一、Kubernetes 中存储的应用场景


在 Kubernetes 中部署和运行的服务大致分为:


1. 无状态服务


Kubernetes 使用 ReplicaSet 来保证一个服务的实例数量,如果说某个 Pod 实例由于某种原因挂掉或崩溃, ReplicaSet 会立刻用这个 Pod 的模版新启一个 Pod 来替代它。由于是无状态的服务,新 Pod 与旧      Pod 一模一样。此外 Kubernetes 通过 Service (一个 Service 后面可以挂多个 Pod )对外提供一个稳定的访问接口,实现服务的高可用。


2. 普通有状态服务


和无状态服务相比,它多了状态保存的需求。 Kubernetes 提供了以 Volume 和 Persistent Volume 为基础的存储系统,可以实现服务的状态保存。


3. 有状态集群服务


和普通有状态服务相比,它多了集群管理的需求。要运行有状态集群服务要解决的问题有两个,一个是状态保存,另一个是集群管理。Kubernetes 为此开发了 StatefulSet (以前叫做 PetSet ),方便有状态集群服务在Kubernetes上部署和管理。


简单来说是通过 Init Container 来做集群的初始化工作,用 Headless Service 来维持集群成员的稳定关系,用动态存储供给来方便集群扩容,最后用 StatefulSet 来综合管理整个集群。 




分析以上的服务类型,Kubernetes 中对于存储的使用主要集中在以下几个方面:

  • 服务的基本配置文件读取、密码密钥管理等;
  • 服务的存储状态、数据存取等;
  • 不同服务或应用程序间共享数据。

二、Kubernetes 中几种常见的存储系统


目前 Kubernetes 所支持的 Volume Plugins 如下表所示。




Kubernetes 已经提供非常丰富的 Volume 和 Persistent Volume 插件,大家可以根据自己业务的需要,使用这些插件给容器提供存储服务。每一种 Plugin 的使用方法和注意事项在此不做赘述,请参考   Kubernetes Volume 的官方文档 [1]。


容器存储接口( Container Storage Interface,CSI[2] )是一项跨行业标准倡议,旨在降低云原生存储开发工作的门槛,从而进一步确保兼容性水平。 Kubernetes v1.9 已经引入了 CSI 的一套 alpha 实现版本,将新分卷插件的安装流程简化至与安装 pod 相当,并允许第三方存储供应商在无需接触核心  Kubernetes 代码库的前提下开发自己的解决方案。


如果上述的这些 Plugin 不满足业务要求, 你可以通过以下两种途径进行二次开发。

  1. 可以使用 FlexVolume 实现自己的 Volume 插件。此 Plugin 仍是 alpha 版本,后向兼容性需要考虑。具体方法在此不做赘述,参考 FlexVolume 的社区文档 [3]。
  2. 推荐使用 CSI。目前还只是alpha版本,使用时需要在 feature-gate 中 enable,不推荐在 production 环境中使用。v1.9 已经把 CSI 作为 in-tree plugin ,把 out-off-tree volume 插件的开发从 Kubernetes 中脱离出来,极大地方便了插件的开发、维护和集成。如何使用 CSI,可参考 How to Use Kubernetes 1.9.0 with CSI[4] 。


三、Kubernetes 存储的设计与基本架构


Kubernete 存储在设计的时候遵循着 Kubernetes  的一贯哲学,即声明式( Declarative )架构。同时为了尽可能多地兼容各种存储平台,Kubernetes 以 in-tree plugin 的形式来对接不同的存储系统,满足用户可以根据自己业务的需要使用这些插件给容器提供存储服务。同时兼容用户使用 FlexVolume 和 CSI定制化插件。相比较于 Docker Volume,支持的存储功能更加丰富和多样。




Kubernetes 中 mount  一个 PV 的基本过程包括:

  1. 用户通过 API 创建一个包含 PVC 的 Pod ;
  2. Scheduler 把这个 Pod 分配到某个节点,比如 Node1;
  3. Node1 上的 Kubelet 开始等待 Volume Manager 准备 device;
  4. PV controller 调用相应 Volume Plugin( in-tree 或者 out-of-tree ),创建 PV,并在系统中与对应的 PVC 绑定;
  5. Attach/Detach controller 或者 Volume Manager 通过 Volume Plugin 实现 device 挂载( Attach );
  6. Volume Manager 等待 device 挂载完成后,将卷挂载到节点指定目录( mount ), 比如 /var/lib/kubelet/pods/xxxxxxxxxxx/volumes/aws-ebs/vol-xxxxxxxxxxxxxxxxx;
  7. Node1 上的 Kubelet 此时被告知 volume 已经准备好后,开始启动 Pod,通过 volume mapping 将 PV 已经挂载到相应的容器中去。



其实对于 Kubernetes 中大部分的 Volume Plugin 来说,mount 的过程遵循着如下的规则:

/some/global/mount/path -> /var/lib/kubelet/pods/<pod uid>/volumes/<volume plugin>/<volume name>/ -> container volume

这种方式的好处相当于热插拔,一旦 Pod 挂掉,kubelet 可以马上重启,并快速 mount volume,不会出现类似于 device busy 的情形。

但是对于 hostpath 这个 Plugin 而言,直接就是  /some/global/mount/path -> container volume。


四、Persistent Volume 与 Persistent Volume Claim


一个运行中的容器,缺省情况下,对文件系统的写入,都是发生在其分层文件系统的可写层的(Copy-on-Write)。当迁移的应用程序从开发到生产环境时候,开发人员面临着巨大的挑战。当容器挂掉、崩溃或运行结束时,任何与之相关的数据都会丢失。为了解决这个问题引发的数据丢失,我们需要将数据存储持久化,也可以称为 Persistent Volume。


Kubernetes使用两种资源管理存储:

  • PersistentVolume(简称 PV ):由管理员添加的的一个存储的描述,是一个全局资源,包含存储的类型,存储的大小和访问模式等。它的生命周期独立于 Pod,例如当使用它的 Pod 销毁时对 PV 没有影响。
  • PersistentVolumeClaim(简称 PVC ):是 Namespace 里的资源,描述对 PV 的一个请求。请求信息包含存储大小,访问模式等。


Kubernetes 中的 Volume 则是基于 Docker 进行扩展,使用 Docker Volume 挂载宿主机上的文件目录到容器中。

一般来说,Kubernetes 中 Pod 通过如下三种方式来访问存储资源。

直接访问




该种方式移植性较差,可扩展能力差,把 Volume 的基本信息完全暴露给用户,有严重的安全隐患,同时需要协调不同 users 对 Volume 的访问。

静态 provision




动态 provision




StorageClass 将说明Volume将由哪种 Volume Plugin 创建、创建时参数以及从其他功能性/非功能性角度描述的后台 volume 的各种参数。一般为 storage cluster 的一些配置信息,以及 label 注释信息。




一般来说,PV 和 PVC 的生命周期分为 5 个阶段:

  1. Provisioning,即 PV 的创建,可以直接创建 PV(静态方式),也可以使用 StorageClass 动态创建
  2. Binding,将 PV 分配给 PVC
  3. Using,Pod 通过 PVC 使用该 Volume
  4. Releasing,Pod 释放 Volume 并删除 PVC
  5. Reclaiming,回收 PV,可以保留 PV 以便下次使用,也可以直接从云存储中删除



根据这 5 个阶段,Volume 的状态有以下 4 种:

  1. Available:可用
  2. Bound:已经分配给 PVC
  3. Released:PVC 解绑但还未执行回收策略
  4. Failed:发生错误


五、v1.9 中对存储做了哪些更改

  1. 引入了 CSI alpha 版本的实现,可见第二部分关于 CSI 的介绍。
  2. 修复 Bug:删除运行状态 container 的 PVC 这个 bug 会导致数据丢失。社区的解决办法是引入一个Finalizer 来保护 PVC。详细的步骤请参考相关的 Proposal[5] 及其代码实现 [6]。简单来说,这个Fianlizer 类似于垃圾回收(GC)里面的指针计数,当这个使用这个PVC的POD都被删除(deleted)或处于完成状态( completed )时,才可以删除这个 PVC。从而避免了删除正在运行中的 container的 PVC,从而引发数据丢失。


Q&A


Q:Kubernetes 和 Cloud Foundry 有什么区别,优势在什么地方?

A:Cloud Foundry 更像是 Application PaaS,Kubernetes 主要是 Container PaaS。CF 不会直接把container 层暴露给用户,Kubernetes 则不然,你可以直接访问 container。个人觉得 kubernetes 的部署和使用更简单,更直接,操作起来也更方便。

Q:请问对于 Galera Cluster 的集群存储如何设计存储方案,CSI 有考虑有状态储存的解决方案吗?

A:对于有状态服务,请使用 StatefulSet 来部署你的应用。Volume 只是一个存储的地方。StatefulSet 会负责给 Pod 设置顺序等等,保证是有序的,优雅的删除停止和扩展。

Q:有没有对象存储,比如 Ceph RGW,在 Kubernetes 集群中的使用案例,比如用户通过客户端上传、下载 PVC 中的数据之类的?

A:有试过 RGW 来存储数据,但是性能不是很好,速度要慢很多。在 Kubernetes 集群中可以使用 RGW 来存储一些静态的文件,比如配置文件,Nginx 静态 html 文件之类,用户也可以下载 PV 中的数据。不建议对 RGW 中的数据进行频繁的更改。

Q:能否详细说下 CSI ?

A:CSI 在 kubernetes v1.9 才引入。这部分内容比较多,可以去看看 CSI 文档 [2],以及 Kubernetes 官方的介绍 [7] ,以及这个 feature 实现代码 [8]。

Q:RBD 用什么插件,有没有什么坑?

A:需要在 kubelet 节点上安装 ceph-common 这个包,在 volume mount 的时候,会调用相关的命令。基本上没什么坑,RBD 提供的 volume 还是很好使的。

Q:存储这块 IO 性能下降大概多少呢,有实测过吗?

A:存储的性能与 Cluster的能力、存储的类型有很大关系。实测过 RBD 的 IO 性能,当然 case by case ,当时测出来的结果还是很不错的,相比较与 CephFS、GlusterFS 。

Q:多个 Pod 共享一个 Nas,是否可行,需要注意什么?

A:可行,但是需要注意 Volume 的读写权限,这个可以通过 mount 时候的 PV 的 access mode 进行设置,比如 ReadWrite、ReadWriteOnce 等等。

Q:CSI 和社区孵化的 volume provisioner 有什么区别?

A:CSI 的主要目的还是为了给容器存储定义一个统一的接口,方便进行定制化,以及新功能添加。社区孵化的 volume provisioner,你指的应该是 external-storage 这个项目吧,这个项目的主要目的是为了方便对 in-tree 的那些 Plugin 进行修改和定制,这样可以独立地进行更新。

Q:请问你现在有把 MySQL 可以放进 PV 里面么?求介绍这方面的经验。

A:如果只是单节点的 MySQL,直接放进 PV 就好了,跟其它正常服务一样。对于多节点的 MySQL Cluster,在部署的时候就需要注意了,建议部署成 StatefulSet ,有状态的服务。之前有试过用 Galera Cluster For MySQL 来部署集群,发现效果不是很好,尤其是在随意启停 Pod 的时候,Cluster 的没办法自组成新的集群,新节点也无法加入集群。你可以再次试验下,确认一下。 后来使用 MySQL Cluster CGE,效果很好,Cluster 能够及时回复并重新组织起来。
相关链接:

  1. https://kubernetes.io/docs/concepts/storage/volumes/
  2. https://github.com/container-storage-interface
  3. https://github.com/kubernetes/community/blob/master/contributors/devel/flexvolume.md
  4. https://blog.thecodeteam.com/2017/12/19/use-kubernetes-1-9-0-csi/
  5. https://github.com/kubernetes/community/blob/master/contributors/design-proposals/storage/postpone-pvc-deletion-if-used-in-a-pod.md
  6. https://github.com/kubernetes/kubernetes/pull/55824
  7. http://blog.kubernetes.io/2018/01/introducing-container-storage-interface.html
  8. https://github.com/kubernetes/kubernetes/pull/54529


END

289 comCount 0