
前言
在 Kubernetes 的世界里,无状态应用因其易于管理和扩展而备受青睐。然而,现实世界中的大多数应用都是有状态的,它们需要一个可靠的地方来持久化存储数据。Kubernetes 通过一套强大的存储抽象------PersistentVolume (PV)、PersistentVolumeClaim (PVC) 和 StorageClass (SC)------优雅地解决了这个问题。
随着业务的增长,数据量自然会增加,最初分配的存储空间可能会变得捉襟见肘。此时,如何对存储卷进行扩容就成了一个关键问题。本文将作为一篇纯理论文章,深入探讨 Kubernetes 中存储卷扩容的两种核心思路:动态扩容 和静态迁移式扩容,并特别点出 NFS 存储在此过程中的特性,以及对 Ceph 这类高级存储方案进行展望。本文旨在为后续的实战文章(如 PVC 静态迁移、NFS/Ceph 对接 K8s 实现动态扩容等)打下坚实的理论基础。
一、Kubernetes 存储三大支柱:PV、PVC 与 StorageClass
要理解扩容,首先必须清晰地理解这三个核心概念的关系:
存储组件关系图
物理层 抽象层 用户层 挂载 绑定 动态创建 映射 映射 映射 NFS 存储 Ceph 存储 云存储 StorageClass 存储类 PersistentVolume 持久卷 Pod 应用 PVC 存储申请
核心概念详解
-
PersistentVolume (PV):由集群管理员创建或由 StorageClass 动态配置的、集群中的一块存储。它是一个资源对象,拥有独立于任何特定 Pod 的生命周期。PV 封装了底层存储的实现细节,无论是 NFS、Ceph 还是云厂商的块存储。
-
PersistentVolumeClaim (PVC) :由用户(开发者)创建的、对存储资源的"申请"。PVC 描述了对存储的具体需求,例如需要多大空间(
spec.resources.requests.storage
)、需要什么样的访问模式(accessModes
)等。Pod 在其定义中引用 PVC,而不是直接引用 PV。 -
StorageClass (SC) :充当 PV 的"工厂"或"模板"。当用户创建一个 PVC,并且该 PVC 指定了一个 StorageClass 时,这个 SC 会根据预定义的模板(
provisioner
)自动地创建一个匹配该 PVC 需求的 PV,并与之绑定。这个过程就是动态配置 (Dynamic Provisioning)。
核心关系:PVC "消费" PV。Pod 通过挂载 PVC 来使用存储。而 StorageClass 则是实现存储自动化(即动态配置)的关键。
二、存储扩容的核心挑战与两种路径
当一个已挂载给 Pod 的 PVC 空间不足时,我们面临扩容的需求。在 Kubernetes 中,这主要通过两条路径实现。
路径一:动态卷扩容 (Dynamic Volume Expansion)
这是 Kubernetes 官方推荐的、现代化的扩容方式。它允许用户在不中断服务或极短中断的情况下,直接在线扩大 PVC 的容量。
动态扩容流程图
用户 PVC对象 Kubernetes API StorageClass CSI插件 底层存储 文件系统 1. 编辑PVC容量 2. 检测容量变化 3. 验证allowVolumeExpansion 4. 返回允许扩容 5. 更新PV容量 6. 触发扩容操作 7. 调用存储API扩容 8. 返回扩容成功 9. 扩展文件系统 10. 扩容完成 整个过程通常在线完成,服务不中断 用户 PVC对象 Kubernetes API StorageClass CSI插件 底层存储 文件系统
实现前提:
- StorageClass 支持 :创建 PVC 时所使用的 StorageClass 必须在其定义中包含
allowVolumeExpansion: true
字段。 - 后端存储插件支持:底层的存储驱动(CSI 插件)必须实现了卷扩容的功能。
理论流程:
- 用户编辑现有的 PVC 对象,将其
spec.resources.requests.storage
的值修改为一个更大的值。 - Kubernetes 检测到 PVC 容量的变化,并验证其关联的 StorageClass 是否允许扩容。
- 如果允许,Kubernetes 会更新对应的 PV 对象的容量。
- CSI 存储插件监听到 PV 的变化,并调用底层存储系统的 API 来实际扩大物理卷的容量。
- 最后,CSI 插件会触发节点上的
kubelet
,使其扩展文件系统以利用新增的空间。
关键点 :PV 是可直接支持动态扩容的,但 PVC 的扩容能力取决于其创建时是否由一个允许扩容的 StorageClass 所管理。
路径二:静态"扩容"之迁移大法
如果你的 PVC 在创建时就没有关联 StorageClass,或者关联的 SC 不支持扩容,那么你就无法使用动态扩容。在这种情况下,唯一的办法就是通过数据迁移来"曲线救国",实现类似扩容的效果。
静态迁移流程图
完成阶段 切换阶段 迁移阶段 准备阶段 失败 异常 成功 正常 迁移完成 清理旧PVC和工具Pod 切换到新PVC 修改应用配置 启动应用验证 应用运行正常? 回滚到旧PVC 同时挂载新旧PVC 启动数据迁移工具Pod 执行数据复制 数据完整性检查 创建新的大容量PVC 旧PVC空间不足 停止应用Pod写入
ASCII 迁移流程示意
适用场景:
- PVC 是通过手动创建并与一个静态 PV 绑定的。
- PVC 关联的 StorageClass 没有设置
allowVolumeExpansion: true
。 - 底层的存储系统本身就不支持在线扩容。
理论流程:
- 创建新 PVC:创建一个新的、容量更大的 PVC。如果环境不是动态配置的,则需要管理员先创建一个更大的新 PV。
- 挂载与数据复制 :
- 停止应用 Pod 对旧 PVC 的写入。
- 启动一个临时的"工具" Pod,这个 Pod 同时挂载旧的 PVC 和新的 PVC。
- 在工具 Pod 内部,使用
rsync
、cp -a
或tar
等命令将数据从旧卷完整地复制到新卷。
bash
# 数据迁移示例命令
rsync -avP /old-volume/ /new-volume/
# 或使用 tar 保持权限
tar -C /old-volume -cf - . | tar -C /new-volume -xf -
- 切换应用挂载:修改应用的 Deployment、StatefulSet 或其他工作负载的定义,将其挂载的 PVC 从旧的 PVC 更改为新的 PVC。
- 验证与清理:启动应用,验证数据是否完整且应用运行正常。确认无误后,删除旧的 PVC 和 PV,以及临时的工具 Pod。
这种方式本质上不是"扩容",而是"搬家"。它通常需要停机,操作更繁琐,但却是许多老旧或特定场景下唯一的选择。
三、特别注意:NFS 存储的"配额幻象"
NFS (Network File System) 是 Kubernetes 中非常常用的一种存储解决方案,尤其是在私有化部署环境中。然而,它有一个非常重要的特性需要特别注意:
NFS 本身通常不强制执行由 PVC 定义的存储配额。
这意味着,当你在 PVC 中定义 storage: 1Gi
时,这个 1Gi
的限制对于 NFS 来说更像是一个"标签"或"声明",Kubernetes 会据此进行调度和展示,但当 Pod 实际写入数据时,NFS 服务器并不会阻止其写入超过 1Gi 的数据(只要 NFS 服务器的物理磁盘空间足够)。
💡 深入了解:为什么NFS无法限制存储配额?
技术原理解析:
- NFS 协议本身是一个网络文件系统协议,它关注的是文件的读写操作,而不是存储配额管理
- 传统的配额限制通常在文件系统层面实现(如 ext4 的 quota 功能),但 NFS 客户端看到的是远程文件系统的挂载点
- Kubernetes 的 PVC 容量限制主要用于调度决策,告诉调度器这个 Pod 需要多少存储空间,但不是强制性的物理限制
实际影响:
- 容量规划困难:无法准确预估实际存储使用量
- 成本控制风险:可能出现存储使用量远超预期的情况
- 多租户隔离问题:不同应用可能会争抢同一个 NFS 服务器的存储空间
解决方案:
- 使用支持配额的存储系统(如 Ceph RBD)
- 在 NFS 服务器端配置目录级别的配额限制
- 通过监控和告警及时发现存储使用异常
存储配额对比图
块存储 (如Ceph RBD) NFS存储 实际限制: 严格1GB PVC: 1GB 安全: 配额保护 实际可写入: 无限制 PVC: 1GB 风险: 存储爆满
注意:在实际测试中,我们向一个定义为 1G 大小的、由 NFS 提供支持的 PVC 中写入了 2G 的数据量,操作完全成功。这证明了使用 NFS 存储时,PVC 的容量限制是无效的。这对于容量规划和成本控制是一个巨大的风险点,必须高度警惕。
四、高级存储的未来:Ceph 简介
当 NFS 的简便性无法满足企业级应用对性能、可靠性和高级功能的需求时,像 Ceph 这样的分布式存储系统就进入了视野。
Ceph 统一存储架构
Kubernetes 集群 Ceph 集群 存储接口层 核心组件 底层存储 Pod 应用 PVC Rook Operator 磁盘1 磁盘2 磁盘N... Monitor 监控节点 OSD 存储节点 MDS 元数据服务 Manager 管理节点 块存储 RBD 文件存储 CephFS 对象存储 RGW
Ceph 是什么?
Ceph 是一个开源的、统一的、分布式的软件定义存储系统。它极具弹性,可大规模扩展,并能在一个集群中同时提供三种存储接口:
- 块存储 (RBD):类似云硬盘,性能高,适合数据库等场景。
- 文件存储 (CephFS):兼容 POSIX 的文件系统,类似 NFS,但提供了更高的可用性和扩展性。
- 对象存储 (RGW):兼容 S3 和 Swift API,适合存储海量非结构化数据。
💡 深入了解:Ceph 核心技术优势
CRUSH 算法:
- Ceph 使用 CRUSH (Controlled Replication Under Scalable Hashing) 算法来确定数据存储位置
- 无需中央元数据服务器,客户端可以直接计算数据位置,避免单点故障
- 支持故障域隔离,可以按机架、数据中心等维度分布数据副本
自愈能力:
- 当存储节点故障时,Ceph 会自动检测并重新平衡数据
- 支持多副本和ncorridge 两种数据保护方式
- 可以在不停机的情况下添加或移除存储节点
性能特性:
- 支持 SSD 缓存加速,提升读写性能
- 客户端直接与存储节点通信,避免网络瓶颈
- 支持快照、克隆、精简配置等高级功能
为什么 Ceph 是 K8s 的理想搭档?
- 云原生集成 :通过 Rook 项目,Ceph 可以作为云原生应用在 Kubernetes 内部进行部署、管理和升级,实现了存储自身的自动化运维。
- 功能强大:原生支持动态扩容、快照、克隆、多副本冗余、故障自愈等高级功能。
- 性能与扩展性:可以随着 Kubernetes 集群节点的增加而横向扩展,提供强大的 I/O 性能。
存储方案对比表
特性 | NFS | Ceph RBD | Ceph CephFS | 云存储 |
---|---|---|---|---|
配额控制 | ❌ 无效 | ✅ 严格 | ✅ 支持 | ✅ 严格 |
动态扩容 | ⚠️ 有限 | ✅ 原生支持 | ✅ 原生支持 | ✅ 原生支持 |
高可用性 | ❌ 单点故障 | ✅ 多副本 | ✅ 多副本 | ✅ 多副本 |
性能 | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
部署复杂度 | ⭐ 简单 | ⭐⭐⭐⭐ 复杂 | ⭐⭐⭐⭐ 复杂 | ⭐⭐ 中等 |
成本 | ⭐⭐⭐⭐⭐ 低 | ⭐⭐⭐ 中等 | ⭐⭐⭐ 中等 | ⭐⭐ 较高 |
Ceph 代表了 Kubernetes 存储的更高阶形态,虽然配置和维护相对复杂,但它提供的强大功能和可靠性是传统存储方案难以比拟的。
总结与展望
本文从理论角度深入探讨了 Kubernetes 存储的核心概念,重点分析了 PVC 动态扩容与静态迁移的技术原理。我们了解到:
- PV 支持动态扩容,但 PVC 需要在创建时指定支持扩容的 StorageClass
- NFS 存储存在"配额幻象",PVC 限制无法真正约束存储使用
- Ceph 等分布式存储为 Kubernetes 提供了企业级的存储解决方案
在后续的实战系列文章中,我们将深入探讨:
- PVC 静态迁移的完整操作流程
- 动态扩容的最佳实践与故障排除
- NFS 与 Kubernetes 集成的动态扩容方案
- Ceph 存储在 Kubernetes 中的部署与管理
感谢阅读!如果本文对您有帮助,请点赞收藏支持一下!
推荐阅读
- Kubernetes 官方文档:Persistent Volumes
- Kubernetes 官方文档:StorageClass
- Kubernetes 官方文档:Expand Persistent Volumes
- Kubernetes CSI 介绍
- Rook 官方文档:在 Kubernetes 中部署 Ceph