在微服务环境中,由于服务的动态性和分布性,存储问题变得比较棘手。一方面,我们需要确保数据的一致性和可靠性;另一方面,我们也需要提供足够的性能和可扩展性来满足服务的需求。此外,由于存储系统通常是底层基础设施的一部分,它的问题往往很难排查,而且也难以为上层服务带来直接的收益(难以说服客户为解决这块的工单花钱买专门对应的维保)。
Kubernetes 作为目前最被广泛使用的容器编排平台,提供了容器存储接口 (CSI Container Storage Interface) 来帮助使用者解决存储相关的问题,是一种将存储系统集成到 Kubernetes 的标准化方法。在这篇文章中,我们以 Ceph CSI 来举例,对集群中存储系统从底层磁盘到pod使用做一个分析。
存储系统概述
首先介绍下 Kubernetes 和容器存储接口(CSI)的概念,以及它们如何从底层磁盘存储到 PV、PVC 等 Kubernetes 存储相关的组件。
Kubernetes 存储概述
K8s 本身提供了一套灵活且可扩展的存储解决方案,包括以下几个主要组件:
- Persistent Volume(PV):PV 是集群中的一块物理存储,可以是网络存储、本地磁盘等。它提供了一种让用户不用关心底层存储设备的具体实现。
- Persistent Volume Claim(PVC):PVC ,从名字里加了个Claim可以看出来是对 PV 的一种申请,用户可以通过 PVC 请求存储资源。K8s 则会根据 PVC 的申请自动分配合适的 PV。
- Storage Class:Storage Class 是一种描述不同类型存储的模板,用于动态创建 PV。它可以包含存储类型、分配策略、回收策略等信息。
容器存储接口(CSI)
容器存储接口(CSI)是一种标准化的接口,提供她的目的是将第三方存储系统集成到K8s和其他容器编排平台。通过 CSI,存储提供商就可以直接通过开发插件来支持K8s,而无需修改其对应的核心代码。当然,CSI也提供了一组通用的 API,使得K8s可以与各种存储系统进行交互,包括创建、删除、扩展、挂载和卸载存储卷等操作。
从存储提供者的服务到 PV、PVC 的过程
存储提供者通过实现 CSI 插件来支持 Kubernetes。这个插件通常包括了控制器组件(负责处理创建、删除、扩展等操作)和节点组件(负责处理挂载和卸载操作)。
集群管理员根据存储提供商的文档创建一个或多个 Storage Class,描述不同类型的存储。这些 Storage Class 可以包含预配置的参数,如性能、分配策略、回收策略等。
用户根据需要创建 PVC,请求存储资源。在 PVC 中,用户可以指定所需的存储大小、访问模式(如只读、读写等)和 Storage Class。
Kubernetes 根据 PVC 的请求和 Storage Class 的配置,自动创建一个匹配的 PV。如果使用动态存储分配,Kubernetes 的 External Provisioner(是 Kubernetes 中的一个组件,用于动态创建 PV) 会通过 CSI 插件调用存储系统的 API,创建一个新的存储卷。
用户在 Pod 的配置文件中引用 PVC,将存储卷挂载到容器中。当 Pod 被调度到节点上时,Kubernetes 会通过 CSI 插件将存储卷挂载到容器的指定目录。
当 Pod 和 PVC 不再需要时,用户可以删除它们。Kubernetes 会根据 Storage Class 的reclaimPolicy字段定义的回收策略来处理 PV,例如释放存储资源或保留数据。
通过这种方式,Kubernetes 和 CSI 共同实现了从存储服务商提供的存储到集群中 PV、PVC 等存储相关组件的管理。给用户提供了一种灵活且可扩展的存储解决方案,使得用户可以更加专注于应用程序侧的开发和运维。

三种不同的存储技术
而在了解ceph之前,先看下三种不同的ceph会用到的存储技术,分别是块存储,文件存储和对象存储
块存储(Block Storage)
数据组织:块存储将数据分割成固定大小的块,每个块都有一个唯一的地址。这些块在逻辑上是连续的,但在物理存储设备上可能是分散的。块存储本身提供了一种底层的、灵活的数据存储方式,它允许用户自定义数据的组织和访问方式。甚至你可以理解块存储就类似于给了你一块磁盘,你自己去定义文件的存储访问方式。
访问方式:块存储使用块地址进行数据访问,一般通过 SCSI、iSCSI、Fibre Channel 等常见的数据存储和传输协议实现。操作系统可以将块设备抽象为一个虚拟磁盘,并在其上创建文件系统。
使用场景:块存储一般用于需要低延迟、高吞吐量和高 IOPS 的场景,如数据库、虚拟机等。它可以提供高性能的随机读写能力,但需要操作系统来管理文件系统和数据的组织。
文件存储(File Storage)
数据组织:文件存储以文件和目录的形式组织数据,提供了一种层次化的命名空间。文件系统负责管理文件的元数据(如文件名、大小、权限等)和数据块的映射关系,目前QAPM依赖TCS的存储主要也是用的文件存储。
访问方式:文件存储使用文件路径进行数据访问,支持标准的文件操作(如打开、关闭、读取、写入等)。文件存储可以通过网络文件系统协议(如 NFS、SMB/CIFS 等)在多个客户端之间共享。
使用场景:文件存储适用于需要共享数据、简化数据管理的场景,如文件服务器、备份、归档等。它提供了一种直观的数据访问方式,但相较于块存储,性能可能较低,因为构造文件系统会带来额外的文件系统开销(需要维护文件的元数据、目录结构以及数据块的映射关系)及文件锁还有文件存储系统多端同步时造成的网络延迟问题。
对象存储(Object Storage)
数据组织:对象存储将数据存储为一个个对象,每个对象包含了数据(Data)、元数据(MetaData)和唯一标识符(Unique Identifier),元数据也就是些文件名、文件大小、创建时间、修改时间、权限等信息。元数据使得存储系统能够组织、管理和检索数据。对象存储提供了一个扁平的地址空间,对象之间没有层次关系(或者说是没有文件层级关系)。
访问方式:对象存储使用唯一标识符(如 URL)进行数据访问,支持简单的操作(如创建、删除、读取、写入等)。对象存储通常通过 RESTful API、HTTP/HTTPS 协议实现,可以方便地进行互联网访问。
使用场景:对象存储适用于需要大规模、高可扩展、高可用性的场景,如云存储、大数据、备份、归档等。它可以轻松地存储和管理海量数据,但不支持随机读写和文件系统语义,即对象存储的 API 通常只支持对整个对象的读取和写入,而不支持对对象内部的特定位置进行随机读写,且没有提供类似于文件系统的层次化命名空间和文件操作语义。

当然Ceph的三种存储的实现方式和此理念相同,但是作为一个分布式的系统,增加了一些特性,这里不多去赘述,文章主要目的也是从上到下捋一下集群存储的过程,而不是详述每个细节。
Ceph CSI 概述
作为我们例子重的存储提供者Ceph和K8s交互的桥梁,Ceph CSI(Container Storage Interface for Ceph)顾名思义,是一个用于将 Ceph 存储系统集成到 Kubernetes 和其他容器编排平台的插件。是一个目前被广泛应用的高性能、可扩展的分布式存储系统,适用于各种场景,如对象存储、块存储和文件系统。遵循K8s的CSI规范,提供了一组标准化的 API,使得K8s能和Ceph 存储系统进行交互,完成诸如创建、删除、扩展、挂载和卸载存储卷等操作。
Ceph CSI 的特点
支持多种 Ceph 存储类型:Ceph CSI 插件支持 Ceph 的多种存储类型,包括了我们介绍过的 Ceph RBD(块存储)、CephFS(文件系统)和 Ceph Object Storage(对象存储)。让用户能根据应用程序的需求灵活选择合适的存储类型。
动态存储分配:Ceph CSI 插件支持 K8s 的动态存储分配功能。用户可以通过创建 PVC 来请求存储资源,然后 K8s 会自动创建和分配一个匹配的 PV。这个过程是通过 Ceph CSI 插件与 Ceph 存储系统的 API 进行交互来完成的,用户不需要做什么操作。
存储卷快照和克隆:插件支持存储卷快照和克隆功能。用户可以创建存储卷的快照,用于数据备份或者创建新的存储卷。此外,用户还可以通过克隆功能创建一个与现有存储卷相同的副本,用于测试或者数据分析等场景。
数据加密:插件支持存储卷的数据加密功能。用户可以选择在 Ceph 存储系统中对数据进行加密,来增强数据的安全性和隐私性。
Ceph CSI 的工作原理
插件主要由两部分组成:控制器组件和节点组件。
控制器组件:负责处理存储卷的生命周期管理操作,如创建、删除、扩展、快照和克隆等。控制器组件通常部署在 K8s 控制平面上,通过与 Ceph 存储系统的 API 进行交互。
节点组件:负责在 K8s 工作节点上处理存储卷的挂载和卸载操作。节点组件部署在每个工作节点上,通过与 Ceph 存储系统的 API 或者直接访问存储设备来实现存储卷的挂载和卸载。
如果你选择Ceph来作为存储提供者,那么当用户创建一个 PVC 时,流程就是K8s 的 External Provisioner 会根据 PVC 的请求和 Storage Class 的配置,通过 Ceph CSI 插件调用 Ceph 存储系统的 API,在底层磁盘中创建一个新的存储卷。然后,当 Pod 被调度到节点上时,Kubernetes 会通过 Ceph CSI 插件将存储卷挂载到容器的指定目录。
至于Ceph本身,已经有大量文档去做详细的剖析和介绍,这里也不多赘述了。

总结
总的来说,在设计分布式集群的存储系统时,为了确保高可用性和性能,通常会选择使用类似于Ceph这样的分布式存储服务,而不是直接依赖本地磁盘作为持久化存储卷(PV)。K8s通过容器存储接口(CSI)为开发者提供了一种更加灵活和简化的方式来管理存储资源,让开发者可以更加专注于应用程序的开发,而不需要过多地关注底层基础设施的细节。