目录
[pv / pvc.yaml](#pv / pvc.yaml)
一、云原生储存及存储进阶
现在的集群里,Pod 的数据默认存在节点本地磁盘:
- Pod 删了、漂移到其他节点,数据就丢了
- 多个 Pod 想共享数据做持久化,原生很难搞定
所以 K8s 引入了:
- PersistentVolume(PV):集群里的 "持久化存储卷",相当于硬盘
- PersistentVolumeClaim(PVC):Pod 申请使用 PV 的 "申请单"
但问题来了:PV 谁来创建?手动创建 PV 太麻烦,还得写死节点路径,无法动态分配。
这时候就需要 StorageClass + CSI 来解决。
1、什么是StorageClass和CSI
StorageClass 是 K8s 里的 "存储类型模板",用来自动创建 PV,实现存储动态供给。
关键作用:
1、动态创建PV(不用管理员手动创建 PV,用户只要在 PVC 里指定 storageClassName,集群会自动创建对应的 PV 并绑定。)
2、区分不用存储类型(fast:高性能 SSD 存储 slow:大容量 HDD 存储 nfs:共享存储)
3、定义回收策略,绑定模式(比如 PV 用完删除时,数据是直接删除还是保留,由 StorageClass 统一控制。)
CSI 是一套标准接口,让外部存储(NFS / 云盘 / 本地盘)能接入 K8s,提供持久化存储能力。
CSI的工作原理
- 你在集群里部署 CSI 驱动(比如 Cephfs CSI 驱动)
- CSI 驱动会监听集群里的 PVC 申请
- 当 PVC 指定了对应 StorageClass,驱动就会:自动在存储服务器上创建卷,在 K8s 里创建对应的 PV,把 PV 绑定给 PVC,Pod 启动时自动挂载卷到容器里
| 组件 | 角色 | 一句话定位 |
|---|---|---|
| CSI 驱动 | 干活的底层驱动 | 连接外部存储,执行创建 / 挂载 / 删除卷的操作 |
| StorageClass | 配置模板 | 告诉 CSI 驱动:用哪种存储、什么参数、怎么创建 PV |
2、什么是Rook
Rook 允许 Ceph 存储使用 Kubernetes 原语在 Kubernetes 上运行。通过在 Kubernetes 集群中运行 Ceph,Kubernetes 应用程序可以挂载由 Rook 管理的块设备和文件系统,或者可以使用 S3/Swift API 进行对象存储。Rook 操作员会自动配置存储组件并监控集群,以确保存储保持可用和健康。
Rook 操作员是一个简单的容器,它具有引导和监视存储集群所需的一切。操作员将启动和监视Ceph 监视 pod、Ceph OSD 守护进程以提供 RADOS 存储,以及启动和管理其他 Ceph 守护进程。操作员通过初始化 pod 和运行服务所需的其他资源来管理池、对象存储 (S3/Swift) 和文件系统的 CRD。
操作员将监控存储守护进程,以确保集群健康。必要时将启动 Ceph mons 或进行故障转移,并随着集群的增长或收缩进行其他调整。操作员还将监视 Ceph 自定义资源 (CR) 中指定的所需状态更改并应用更改。
Rook 会自动配置 Ceph-CSI 驱动程序以将存储安装到您的 pod。该rook/ceph镜像包含管理集群所需的所有工具。Rook 不在 Ceph 数据路径中。许多 Ceph 概念(如放置组和 Crush 地图)都是隐藏的,因此您不必担心它们。相反,Rook 为管理员创建了一种简化的用户体验,包括物理资源、池、卷、文件系统和存储桶。可以使用 Ceph 工具在需要时应用高级配置。
3、Rook核心架构
- Rook Operator
Rook 核心控制组件,以容器方式运行:
- 自动初始化、拉起 Ceph 存储集群
- 持续监控 Ceph 各类守护进程健康状态
- 故障自愈、配置变更、版本升级全自动化
- Rook Agent
- 以 DaemonSet 运行在每个 K8s 存储节点
- 集成 FlexVolume/CSI 存储插件,对接 K8s 存储卷框架
- 负责底层主机存储操作:磁盘格式化、卷挂载 / 卸载、网络存储设备接入
- Rook Discover
- 自动探测每个节点上新增 / 已有裸磁盘、存储设备
- 自动上报硬件信息到 Rook 对应 CRD
- 节点新增磁盘后:Discover 感知 → 通知 Operator → Operator 调度 Agent 完成磁盘格式化、初始化并纳入 Ceph 集群作为 OSD 存储盘
- Rook Cluster
- 通过 CRD 提供存储集群整体配置能力
- 对外提供三类存储服务:块存储、对象存储、共享文件存储
- 集群内部可划分多个 Pool 做资源隔离与配额管理
4、安装Rook
1)集群初始化
前置准备
- 每个准备做存储的节点,必须要有空闲裸硬盘(不分区,不格式化,不挂载,空盘)
- 所有节点时间必须同步
- 集群网络互通,无防火墙拦截
这里使用的是TKE的kubernetes服务
| 主机名 | 节点角色 | 配置 | 数据盘 | 是否作为 OSD |
|---|---|---|---|---|
| k8s-master | 平台托管控制节点 | 托管节点 | 无 | 否 |
| 172.17.16.167 | 存储工作节点 | 4C8G | /dev/vdb | 是 |
| 172.17.17.21 | 存储工作节点 | 4C8G | /dev/vdb | 是 |
| 172.17.16.243 | 存储工作节点 | 4C8G | /dev/vdb | 是 |
版本选择
查看版本匹配k8s版本:https://rook.io/docs/rook/latest-release/Getting-Started/quickstart/
2)安装rook
# 创建存放目录下载安装包
[root@VM-16-167-tencentos ~]# mkdir rook && cd rook
[root@VM-16-167-tencentos ~]# wget https://github.com/rook/rook/archive/refs/tags/v1.19.5.tar.gz
# 解压安装包
[root@VM-16-167-tencentos rook]# tar -zxvf rook-1.19.5.tar.gz
[root@VM-16-167-tencentos rook]# cd rook-1.19.5/deploy/examples/
# 创建CRD(给K8s 安装 Rook 的 专属 API)不装这个,后面所有资源都创建不了。
[root@VM-16-167-tencentos examples]# kubectl apply -f crds.yaml
# 查看创建出来的crd
[root@VM-16-167-tencentos examples]# kubectl get crd | grep rook
cephblockpoolradosnamespaces.ceph.rook.io 2026-05-03T08:05:49Z
cephblockpools.ceph.rook.io 2026-05-03T08:05:49Z
cephbucketnotifications.ceph.rook.io 2026-05-03T08:05:49Z
cephbuckettopics.ceph.rook.io 2026-05-03T08:05:49Z
cephclients.ceph.rook.io 2026-05-03T08:05:49Z
cephclusters.ceph.rook.io 2026-05-03T08:05:49Z
cephcosidrivers.ceph.rook.io 2026-05-03T08:05:50Z
cephfilesystemmirrors.ceph.rook.io 2026-05-03T08:05:51Z
cephfilesystems.ceph.rook.io 2026-05-03T08:05:51Z
cephfilesystemsubvolumegroups.ceph.rook.io 2026-05-03T08:05:51Z
cephnfses.ceph.rook.io 2026-05-03T08:05:51Z
cephnvmeofgateways.ceph.rook.io 2026-05-03T08:05:52Z
cephobjectrealms.ceph.rook.io 2026-05-03T08:05:53Z
cephobjectstores.ceph.rook.io 2026-05-03T08:05:53Z
cephobjectstoreusers.ceph.rook.io 2026-05-03T08:05:53Z
cephobjectzonegroups.ceph.rook.io 2026-05-03T08:05:54Z
cephobjectzones.ceph.rook.io 2026-05-03T08:05:54Z
cephrbdmirrors.ceph.rook.io 2026-05-03T08:05:54Z
# 创建rook的权限(ServiceAccount + RBAC + NameSpace)
[root@VM-16-167-tencentos examples]# kubectl apply -f common.yaml
# 创建operator (是 Rook 的「总管 + 安装工」)
# 创建之前需要更改yaml文件中的一些配置
[root@VM-16-167-tencentos examples]# vim operator.yaml
添加默认使用的镜像
ROOK_CSI_CEPH_IMAGE: "registry.cn-hangzhou.aliyuncs.com/rookcloud/cephcsi:v3.12.2"
ROOK_CSI_REGISTRAR_IMAGE: "registry.cn-hangzhou.aliyuncs.com/rookcloud/csi-node-driver-registrar:v2.11.1"
ROOK_CSI_RESIZER_IMAGE: "registry.cn-hangzhou.aliyuncs.com/rookcloud/csi-resizer:v1.11.1"
ROOK_CSI_PROVISIONER_IMAGE: "registry.cn-hangzhou.aliyuncs.com/rookcloud/csi-provisioner:v5.0.1"
ROOK_CSI_SNAPSHOTTER_IMAGE: "registry.cn-hangzhou.aliyuncs.com/rookcloud/csi-snapshotter:v8.0.1"
ROOK_CSI_ATTACHER_IMAGE: "registry.cn-hangzhou.aliyuncs.com/rookcloud/csi-attacher:v4.6.1"
# 修改使用的主镜像
622 containers:
623 - name: rook-ceph-operator
624 image: registry.cn-hangzhou.aliyuncs.com/rookcloud/ceph:v1.15.6
625 imagePullPolicy: IfNotPresent
# 修改ROOK_ENABLE_DISCOVERY_DAEMON为"true"
= true:开启 Rook 自动磁盘发现守护进程
创建初期集群不需要(因为我们后面在 cluster.yaml 里手动指定了磁盘 / 节点)
后期扩容时:新节点插入新磁盘 → 自动发现、自动加入 Ceph 集群,不用再改配置
# 关闭新版本的独立csi模式
ROOK_USE_CSI_OPERATOR: "false"
# 创建资源
[root@VM-16-167-tencentos examples]# kubectl apply -f operator.yaml
# 查看资源,必须所有pod都running及READY1/1才可以执行下一步
[root@VM-16-167-tencentos examples]# kubectl get pod -n rook-ceph
NAME READY STATUS RESTARTS AGE
rook-ceph-operator-9f659b74f-fpn4n 1/1 Running 0 3m2s
rook-discover-52lzr 1/1 Running 0 101s
rook-discover-hkmgq 1/1 Running 0 101s
rook-discover-vxcvs 1/1 Running 0 101s
说明:
rook-ceph-operator :Ceph 集群大管家 / 总指挥
rook-discover(每台节点都一个):全集群磁盘扫描探头
3)安装ceph
cluster.yaml是用于部署Rook集群的YAML文件,它定义了Rook集群的配置和部署方式。
包含的资源:Cluster、ClusterRole、ClusterRoleBinding、Service、PodDisruptionBudget、DaemonSet、Secret(可选)
功能和特点:
- Cluster:该部分定义了Rook集群的配置,包括Ceph存储的版本、Monitors的数量、池的配置、网络设置等。
- ClusterRole:该部分定义了Rook集群所需要的权限,以便能够执行与Ceph存储相关的操作,如创建OSD等。
- ClusterRoleBinding:该部分将ClusterRole和ServiceAccount绑定在一起,以授予Rook集群所需的权限。
- Service:该部分定义了用于访问Rook集群的Service,包括Ceph Monitors和MGR的服务。
- PodDisruptionBudget:该部分定义了Pod的干扰预算,确保在维护和升级期间仍然能够保持足够的可用性。
- DaemonSet:该部分定义了在每个节点上运行的Rook组件,如Ceph Monitors、OSDs和MGRs。
- Secret(可选):该部分可以包含用于访问Ceph集群的认证密钥。
cluster.yaml文件提供了一个可用的示例配置,您可以根据需要进行修改和自定义。
[root@VM-16-167-tencentos examples]# vim cluster.yaml
# 修改镜像
image: registry.cn-hangzhou.aliyuncs.com/rookcloud/ceph:v18.2.4
imagePullPolicy: IfNotPresent
# 修改skipUpgradeChecks: false为true
说明:升级时是否跳过前置健康检查
当你升级 Ceph 版本时(比如换个镜像版本),Rook 会先做这些检查:
集群状态是否健康(ceph status 是 HEALTH_OK)
PG 状态是否正常(没有 unclean/peering 状态的 PG)
OSD、MON 等组件是否全在线
如果 skipUpgradeChecks: false(默认):
升级前必须所有检查都通过
集群不健康会直接阻止升级,避免数据风险
如果改成 true:
跳过这些健康检查,直接开始升级
风险很高,不建议在生产环境开启,仅用于特殊紧急场景

# 关闭 SSL/TLS 加密访问 Dashboard
ssl: fals

# 关闭节点自动发现开关
# 含义:是否让 Rook 自动在集群里的所有节点上部署 Ceph 组件(OSD、MON 等)
# 当前值 true 表示:只要是 K8s 节点,都会被纳入候选,Rook 会尝试在上面部署服务
useAllNodes: false
# 关闭磁盘自动发现开关
# 含义:是否让 Rook 自动使用节点上的所有裸盘作为 Ceph OSD 存储
# 当前值 true 表示:节点上所有未分区、未挂载的裸盘,都会被 Rook 扫描并尝试创建 OSD
useAllDevices: false
# allowMultiplePerNode: false修改true
说明:
我们现在处于TKE上得K8S,标签名中得hostname都是IP地址,K8S调度里非常容易识别失败!,所有要改成True,这样反亲和就会失效
只要hostname不是IP地址,能分清就不需要更改

# 添加ceph安装节点和磁盘名字
nodes:
- name: "172.17.16.167"
devices:
- name: "vdb"
- name: "172.17.17.21"
devices:
- name: "vdb"
- name: "172.17.16.243"
devices:
- name: "vdb"
# 注意这里的name是node节点名字
[root@VM-16-167-tencentos examples]# kubectl get nodes
NAME STATUS ROLES AGE VERSION
172.17.16.167 Ready <none> 29m v1.34.1-tke.4
172.17.16.243 Ready <none> 29m v1.34.1-tke.4
172.17.17.21 Ready <none> 29m v1.34.1-tke.4
就是这里的名字
# 创建资源
[root@VM-16-167-tencentos examples]# kubectl apply -f cluster.yaml
# 查看资源
[root@VM-16-167-tencentos examples]# kubectl get pod -n rook-ceph
NAME READY STATUS RESTARTS AGE
csi-cephfsplugin-bmg7t 3/3 Running 0 2m17s
csi-cephfsplugin-cpwk2 3/3 Running 0 2m17s
csi-cephfsplugin-h4flp 3/3 Running 0 2m16s
csi-cephfsplugin-provisioner-85bc477bd7-djtxm 6/6 Running 0 2m17s
csi-cephfsplugin-provisioner-85bc477bd7-gnccx 6/6 Running 0 2m16s
csi-rbdplugin-ckd59 3/3 Running 0 2m17s
csi-rbdplugin-hq6s8 3/3 Running 0 2m17s
csi-rbdplugin-provisioner-7cf84d4f9f-kbqfx 6/6 Running 0 2m17s
csi-rbdplugin-provisioner-7cf84d4f9f-nmch9 6/6 Running 0 2m17s
csi-rbdplugin-rcqxv 3/3 Running 0 2m17s
rook-ceph-crashcollector-172.17.16.167-64d9756974-6hh56 1/1 Running 0 53s
rook-ceph-crashcollector-172.17.16.243-65957f598c-5tnb9 1/1 Running 0 62s
rook-ceph-crashcollector-172.17.17.21-6f95c85cd4-h5w64 1/1 Running 0 51s
rook-ceph-exporter-172.17.16.167-657c4db5fb-xqfx7 1/1 Running 0 50s
rook-ceph-exporter-172.17.16.243-5679c594d9-xknpg 1/1 Running 0 62s
rook-ceph-exporter-172.17.17.21-7ffbbfbf78-jfjfx 1/1 Running 0 47s
rook-ceph-mgr-a-7fd648b999-scckb 3/3 Running 0 82s
rook-ceph-mgr-b-668cf49488-kcthp 3/3 Running 0 81s
rook-ceph-mon-a-5cdd448c8d-hdwml 2/2 Running 0 2m5s
rook-ceph-mon-b-67fc67fd75-mwb5w 2/2 Running 0 102s
rook-ceph-mon-c-7f6ccccdd5-g826b 2/2 Running 0 92s
rook-ceph-operator-9f659b74f-fpn4n 1/1 Running 0 21m
rook-ceph-osd-0-55799b548d-8rnnc 2/2 Running 0 54s
rook-ceph-osd-1-856974c8c5-s4b8g 2/2 Running 0 53s
rook-ceph-osd-2-6c75bb476c-hkmmg 2/2 Running 0 53s
rook-ceph-osd-prepare-172.17.16.167-lcggw 0/1 Completed 0 60s
rook-ceph-osd-prepare-172.17.16.243-9sng2 0/1 Completed 0 59s
rook-ceph-osd-prepare-172.17.17.21-782bl 0/1 Completed 0 59s
rook-discover-52lzr 1/1 Running 0 19m
rook-discover-hkmgq 1/1 Running 0 19m
rook-discover-vxcvs 1/1 Running 0 19m
4)安装ceph客户端工具
安装此工具会可以使用ceph相关命令
# 修改toolbox.yaml文件
[root@VM-16-167-tencentos examples]# vim toolbox.yaml
# 修改镜像地址
registry.cn-hangzhou.aliyuncs.com/rookcloud/ceph:v18.2.4
# apply
[root@VM-16-167-tencentos examples]# kubectl apply -f toolbox.yaml
# 查看工具pod
[root@VM-16-167-tencentos examples]# kubectl get po -n rook-ceph -l app=rook-ceph-tools
NAME READY STATUS RESTARTS AGE
rook-ceph-tools-69997987f6-5kjq4 1/1 Running 0 14s
# 进入工具pod检查
[root@VM-16-167-tencentos examples]# kubectl exec -it rook-ceph-tools-69997987f6-5kjq4 -n rook-ceph -- bash
# 查看ceph集群状态
bash-5.1$ ceph status
cluster:
id: 8e60842b-2ea4-4e20-ba5a-35340d7e4a90
health: HEALTH_OK
services:
mon: 3 daemons, quorum a,b,c (age 20m)
mgr: a(active, since 19m), standbys: b
osd: 3 osds: 3 up (since 19m), 3 in (since 20m)
data:
pools: 1 pools, 1 pgs
objects: 2 objects, 449 KiB
usage: 80 MiB used, 150 GiB / 150 GiB avail
pgs: 1 active+clean
# 查看osd状态
bash-5.1$ ceph osd status
ID HOST USED AVAIL WR OPS WR DATA RD OPS RD DATA STATE
0 172.17.16.167 26.7M 49.9G 0 0 0 0 exists,up
1 172.17.17.21 26.7M 49.9G 0 0 0 0 exists,up
2 172.17.16.243 26.7M 49.9G 0 0 0 0 exists,up
# 查看ceph集群空间
bash-5.1$ ceph df
--- RAW STORAGE ---
CLASS SIZE AVAIL USED RAW USED %RAW USED
hdd 150 GiB 150 GiB 80 MiB 80 MiB 0.05
TOTAL 150 GiB 150 GiB 80 MiB 80 MiB 0.05
--- POOLS ---
POOL ID PGS STORED OBJECTS USED %USED MAX AVAIL
.mgr 1 1 449 KiB 2 1.3 MiB 0 47 GiB
# 查看svc
# 由于新版的rook-ceph-mgr-dashboard不支持修改svc的type,就算修改成nodeport,系统一会自动修改为clusterip类型,所以要重新创建一个svc的yaml文件
[root@VM-16-167-tencentos examples]# kubectl get svc -n rook-ceph
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
rook-ceph-exporter ClusterIP 192.168.1.45 <none> 9926/TCP 22m
rook-ceph-mgr ClusterIP 192.168.122.32 <none> 9283/TCP 22m
rook-ceph-mgr-dashboard ClusterIP 192.168.124.151 <none> 7000/TCP 22m
rook-ceph-mon-a ClusterIP 192.168.26.127 <none> 6789/TCP,3300/TCP 23m
rook-ceph-mon-b ClusterIP 192.168.30.23 <none> 6789/TCP,3300/TCP 22m
rook-ceph-mon-c ClusterIP 192.168.1.37 <none> 6789/TCP,3300/TCP 22m
# 创建nodeport的svc
[root@VM-16-167-tencentos examples]# kubectl apply -f dashboard-external-http.yaml
# 查看
[root@VM-16-167-tencentos examples]# kubectl get svc -n rook-ceph
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
rook-ceph-exporter ClusterIP 192.168.1.45 <none> 9926/TCP 25m
rook-ceph-mgr ClusterIP 192.168.122.32 <none> 9283/TCP 25m
rook-ceph-mgr-dashboard ClusterIP 192.168.124.151 <none> 7000/TCP 25m
rook-ceph-mgr-dashboard-external-http NodePort 192.168.85.140 <none> 7000:32015/TCP 3s
rook-ceph-mon-a ClusterIP 192.168.26.127 <none> 6789/TCP,3300/TCP 25m
rook-ceph-mon-b ClusterIP 192.168.30.23 <none> 6789/TCP,3300/TCP 25m
rook-ceph-mon-c ClusterIP 192.168.1.37 <none> 6789/TCP,3300/TCP 25m
访问我们的公网ip + nodeport的端口

账号:admin
密码:kubectl -n rook-ceph get secret rook-ceph-dashboard-password -o jsonpath="{.data.password}" | base64 --decode && echo

5)块存储
块存储只针对有状态的服务,且容器需要单独的存储,数据不需要共享,例如redis集群,mysql集群等等,集群中的所有容器都需要挂载一款独有的块存储。
创建块存储的Storageclass
# 查看ceph提供的存储驱动
[root@VM-16-167-tencentos ~]# kubectl get csidriver
NAME ATTACHREQUIRED PODINFOONMOUNT STORAGECAPACITY TOKENREQUESTS REQUIRESREPUBLISH MODES AGE
rook-ceph.cephfs.csi.ceph.com true false false <unset> false Persistent 34m
rook-ceph.rbd.csi.ceph.com true false false <unset> false Persistent 34m
# 创建storageclass动态存储
[root@VM-16-167-tencentos rbd]# kubectl apply -f storageclass.yaml
StorageClass文件说明
# ==============================================
# Ceph RBD 块存储池(3副本,生产级高可用配置)
# ==============================================
apiVersion: ceph.rook.io/v1
kind: CephBlockPool
metadata:
name: replicapool # 存储池名称,SC 会引用它
namespace: rook-ceph # 必须与 Rook 集群命名空间一致
spec:
failureDomain: host # 副本打散级别:host = 跨节点分散,保证高可用
replicated:
size: 3 # 三副本,数据安全,丢失1个节点不影响业务
requireSafeReplicaSize: true # 强制安全副本数,禁止单副本导致数据丢失
---
# ==============================================
# Ceph RBD 块存储 StorageClass
# 用于 K8s 动态供给 PV(块存储最常用)
# ==============================================
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: rook-ceph-block # SC 名称,PVC 必须填写这个名字
provisioner: rook-ceph.rbd.csi.ceph.com # RBD CSI 驱动名称(固定)
parameters:
clusterID: rook-ceph # Ceph 集群所在命名空间(固定)
pool: replicapool # 绑定上面创建的 RBD 存储池
imageFormat: "2" # RBD 镜像格式,v2 是标准格式
imageFeatures: layering # RBD 特性,兼容所有内核,最稳定
# ========================
# CSI 密钥配置(自动生成,无需修改)
# ========================
csi.storage.k8s.io/provisioner-secret-name: rook-csi-rbd-provisioner
csi.storage.k8s.io/provisioner-secret-namespace: rook-ceph
csi.storage.k8s.io/controller-expand-secret-name: rook-csi-rbd-provisioner
csi.storage.k8s.io/controller-expand-secret-namespace: rook-ceph
csi.storage.k8s.io/controller-publish-secret-name: rook-csi-rbd-provisioner
csi.storage.k8s.io/controller-publish-secret-namespace: rook-ceph
csi.storage.k8s.io/node-stage-secret-name: rook-csi-rbd-node
csi.storage.k8s.io/node-stage-secret-namespace: rook-ceph
csi.storage.k8s.io/fstype: ext4 # 磁盘格式:ext4 最稳定,推荐生产使用
allowVolumeExpansion: true # 允许存储在线扩容(生产必备)
reclaimPolicy: Delete # PVC 删除后,Ceph 中的块设备自动清理
volumeBindingMode: Immediate # 立即绑定 PV,无需等待 Pod 启动
# 查看StorageClass(没有命名空间隔离性)
[root@VM-16-167-tencentos rbd]# kubectl get sc
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
cbs (default) com.tencent.cloud.csi.cbs Delete Immediate false 86m
rook-ceph-block rook-ceph.rbd.csi.ceph.com Delete Immediate true 46s
cbs (default)腾讯云自带的云硬盘 SC

说明:
application_metadata: rbd代表这个池已经被 Ceph 标记为 RBD(块存储)专用,这是 CSI 驱动能识别并创建卷的前提条件。
crush_rule: replicapool绑定了我们在 CephBlockPool 里配置的 CRUSH 规则,副本会按 failureDomain: host 策略跨节点分布,保证高可用。
create_time: 2026-05-03T09:12:50.056316+0000存储池创建时间,证明它是刚被我们的 CephBlockPool 资源创建的。
其他缓存相关参数(cache_*)当前都是默认值,说明我们没有配置缓存层,使用的是基础三副本策略,稳定可靠,适合生产环境使用。
测试动态申请PV
# 创建工作目录
[root@VM-16-167-tencentos ~]# mkdir storageClass && cd storageClass
# 创建pvc文件
[root@VM-16-167-tencentos storageClass]# vim block-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mysql-pv-claim
labels:
app: wordpress
spec:
storageClassName: rook-ceph-block # 块存储CSI名称
accessModes:
- ReadWriteOnce # 访问模式
resources:
requests:
storage: 10Gi # 请求容量
说明:
块存储只能使用ReadWriteOnce,Ceph RBD 是块设备,就像服务器里的一块物理硬盘,有一个核心限制:
同一块硬盘,不能同时被多台服务器挂载并读写,否则会出现数据错乱、文件系统损坏。
所以 RBD 块存储天生只支持 ReadWriteOnce,这也是数据库等单实例业务最常用的模式
# 创建pvc 并查看是否自动申请pv
[root@VM-16-167-tencentos storageClass]# kubectl apply -f block-pvc.yaml
# 查看pv和pvc
[root@VM-16-167-tencentos storageClass]# kubectl get pv,pvc
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
persistentvolume/pvc-d0f7176c-ed44-40b2-95c3-3508b2295a5e 10Gi RWO Delete Bound default/mysql-pv-claim rook-ceph-block <unset> 10s
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
persistentvolumeclaim/mysql-pv-claim Bound pvc-d0f7176c-ed44-40b2-95c3-3508b2295a5e 10Gi RWO rook-ceph-block <unset> 10s
# 查看pvc的绑定情况
[root@VM-16-167-tencentos storageClass]# kubectl describe pvc mysql-pv-claim
Name: mysql-pv-claim
Namespace: default
StorageClass: rook-ceph-block
Status: Bound
Volume: pvc-d0f7176c-ed44-40b2-95c3-3508b2295a5e # PV
Labels: app=wordpress
Annotations: pv.kubernetes.io/bind-completed: yes
pv.kubernetes.io/bound-by-controller: yes
volume.beta.kubernetes.io/storage-provisioner: rook-ceph.rbd.csi.ceph.com
volume.kubernetes.io/storage-provisioner: rook-ceph.rbd.csi.ceph.com
Finalizers: [kubernetes.io/pvc-protection]
Capacity: 10Gi
Access Modes: RWO
VolumeMode: Filesystem
Used By: <none>
# 查看ceph和pv的ID对应关系
[root@VM-16-167-tencentos storageClass]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
pvc-d0f7176c-ed44-40b2-95c3-3508b2295a5e 10Gi RWO Delete Bound default/mysql-pv-claim rook-ceph-block <unset> 2m40s
[root@VM-16-167-tencentos storageClass]# kubectl get pv pvc-d0f7176c-ed44-40b2-95c3-3508b2295a5e -o yaml
.....
volumeHandle: 0001-0009-rook-ceph-0000000000000002-7f13570f-016c-4ea5-88b6-77294ca932b3

这是 Ceph 块存储(RBD)的 唯一卷 ID(UUID)K8s 和 Ceph 之间靠它识别你这块 10GiB 存储。
创建有状态服务挂载pvc
StatefulSet是部署有状态的服务,一般情况是以集群的形式出现,故无法给其中的每一个副本单独绑定一个pvc,所以k8s则出现volumeClaimTemplates的概念。
[root@VM-16-167-tencentos storageClass]# vim mysql-sts.yaml
# ----------------------------------------------
# MySQL StatefulSet 部署文件
# 命名空间:tcloud
# 存储类型:Ceph RBD 块存储(rook-ceph-block)
# ----------------------------------------------
# Headless Service(有状态应用必须使用)
# 作用:为 MySQL Pod 提供稳定的 DNS 域名访问
apiVersion: v1
kind: Service
metadata:
name: mysql
namespace: tcloud # 资源所属命名空间
labels:
app: mysql
spec:
ports:
- port: 3306 # MySQL 默认服务端口
name: mysql
clusterIP: None # Headless Service 固定配置
selector:
app: mysql # 关联标签匹配的 Pod
---
# StatefulSet:用于部署 MySQL 这类有状态应用
# 特点:Pod 有序创建、固定名称、独立存储、稳定网络
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
namespace: tcloud # 资源所属命名空间
spec:
serviceName: "mysql" # 绑定 Headless Service
replicas: 3 # 3 副本实例
selector:
matchLabels:
app: mysql
# Pod 模板定义
template:
metadata:
labels:
app: mysql
spec:
terminationGracePeriodSeconds: 10 # 优雅停止等待时间
containers:
- name: mysql
image: mysql:8.0
ports:
- containerPort: 3306
name: mysql
# MySQL root 密码(环境变量配置)
env:
- name: MYSQL_ROOT_PASSWORD
value: "123456"
# 数据盘挂载
volumeMounts:
- name: mysql-data
mountPath: /var/lib/mysql
# ------------------------------
# 存储申请模板(自动创建 PVC)
# 每个 MySQL Pod 都会独立创建一块存储
# 使用 Ceph 块存储:rook-ceph-block
# ------------------------------
volumeClaimTemplates:
- metadata:
name: mysql-data
spec:
accessModes:
- ReadWriteOnce # 块存储仅支持单节点读写
storageClassName: rook-ceph-block # Ceph 块存储类(全局可用)
resources:
requests:
storage: 5Gi # 每个实例分配 5GB 存储空间
# apply
[root@VM-16-167-tencentos storageClass]# kubectl create ns tcloud
[root@VM-16-167-tencentos storageClass]# kubectl apply -f mysql-sts.yaml
# 查看pvc
[root@VM-16-167-tencentos storageClass]# kubectl get pvc -n tcloud
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
mysql-data-mysql-0 Bound pvc-7e9e80c8-440f-47b5-bec1-9aa6023a2fe0 5Gi RWO rook-ceph-block <unset> 11s
mysql-data-mysql-1 Bound pvc-70d3b8a1-fb60-4c7b-a104-b29d74314b1d 5Gi RWO rook-ceph-block <unset> 9s
mysql-data-mysql-2 Bound pvc-159cfe3a-8aca-4039-99e6-ddca664d5226 5Gi RWO rook-ceph-block <unset> 1s
# 查看pv
[root@VM-16-167-tencentos storageClass]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
pvc-159cfe3a-8aca-4039-99e6-ddca664d5226 5Gi RWO Delete Bound tcloud/mysql-data-mysql-2 rook-ceph-block <unset> 11s
pvc-70d3b8a1-fb60-4c7b-a104-b29d74314b1d 5Gi RWO Delete Bound tcloud/mysql-data-mysql-1 rook-ceph-block <unset> 18s
pvc-7e9e80c8-440f-47b5-bec1-9aa6023a2fe0 5Gi RWO Delete Bound tcloud/mysql-data-mysql-0 rook-ceph-block <unset> 21s
pvc-d0f7176c-ed44-40b2-95c3-3508b2295a5e 10Gi RWO Delete Bound default/mysql-pv-claim rook-ceph-block <unset> 20m
测试动态申请pv
# 查看pod
[root@VM-16-167-tencentos storageClass]# kubectl get pod -n tcloud
NAME READY STATUS RESTARTS AGE
mysql-0 1/1 Running 0 64s
mysql-1 1/1 Running 0 61s
mysql-2 1/1 Running 0 54s
# 进入pod 添加测试数据
[root@VM-16-167-tencentos storageClass]# kubectl exec -it mysql-0 -n tcloud -- mysql -uroot -p123456
mysql> create database test_ceph;
Query OK, 1 row affected (0.02 sec)
mysql> use test_ceph;
Database changed
mysql> create table t_user(id int);
Query OK, 0 rows affected (0.07 sec)
mysql> insert into t_user values(100);
Query OK, 1 row affected (0.02 sec)
mysql> select * from t_user;
+------+
| id |
+------+
| 100 |
+------+
1 row in set (0.00 sec)
# 删除pod
[root@VM-16-167-tencentos storageClass]# kubectl delete pod mysql-0 -n tcloud
# 等待pod重新running
[root@VM-16-167-tencentos storageClass]# kubectl exec -it mysql-0 -n tcloud -- mysql -uroot -p123456
# 查询数据
mysql> use test_ceph;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
mysql> select * from t_user;
+------+
| id |
+------+
| 100 |
+------+
1 row in set (0.00 sec)
6)文件存储
用于多个pod共享一个文件,例如nginx,tomcat等等
[root@VM-16-167-tencentos storageClass]# cd /root/rook/rook-1.19.5/deploy/examples/
[root@VM-16-167-tencentos examples]# kubectl apply -f filesystem.yaml
# 查看pod
[root@VM-16-167-tencentos examples]# kubectl -n rook-ceph get pod -l app=rook-ceph-mds
NAME READY STATUS RESTARTS AGE
rook-ceph-mds-myfs-a-fc895576c-p77mg 2/2 Running 0 12s
rook-ceph-mds-myfs-b-55b79dcb5c-8dj8c 2/2 Running 0 11s
创建文件存储的Stroageclass
[root@VM-16-167-tencentos examples]# cd csi/cephfs/
[root@VM-16-167-tencentos cephfs]# kubectl apply -f storageclass.yaml
文件说明
# ==============================================
# Rook CephFS StorageClass 配置文件
# 作用:提供 Kubernetes 共享文件存储(ReadWriteMany 多节点读写)
# ==============================================
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: rook-cephfs # StorageClass 名称,PVC 中直接引用
# 存储供应者:CephFS CSI 驱动
provisioner: rook-ceph.cephfs.csi.ceph.com
parameters:
# Rook Ceph 集群所在命名空间
clusterID: rook-ceph
# CephFS 文件系统名称(必须与已创建的 fsName 一致)
fsName: myfs
# CephFS 数据存储池
pool: myfs-replicated
# CSI 插件使用的认证密钥(Rook 自动生成)
csi.storage.k8s.io/provisioner-secret-name: rook-csi-cephfs-provisioner
csi.storage.k8s.io/provisioner-secret-namespace: rook-ceph
csi.storage.k8s.io/controller-expand-secret-name: rook-csi-cephfs-provisioner
csi.storage.k8s.io/controller-expand-secret-namespace: rook-ceph
csi.storage.k8s.io/controller-publish-secret-name: rook-csi-cephfs-provisioner
csi.storage.k8s.io/controller-publish-secret-namespace: rook-ceph
csi.storage.k8s.io/node-stage-secret-name: rook-csi-cephfs-node
csi.storage.k8s.io/node-stage-secret-namespace: rook-ceph
# 回收策略:删除 PVC 时自动删除存储数据
reclaimPolicy: Delete
# 允许存储在线扩容
allowVolumeExpansion: true
# 查看sc
[root@VM-16-167-tencentos cephfs]# kubectl get sc
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
cbs (default) com.tencent.cloud.csi.cbs Delete Immediate false 128m
rook-ceph-block rook-ceph.rbd.csi.ceph.com Delete Immediate true 43m
rook-cephfs rook-ceph.cephfs.csi.ceph.com Delete Immediate true 45s
测试动态申请pv
[root@VM-16-167-tencentos storageClass]# vim nginx-ceph.yaml
# Nginx + CephFS 共享存储测试
# 作用:3 副本 Nginx 共享同一个存储(ReadWriteMany)
---
apiVersion: v1
kind: Service
metadata:
name: nginx
namespace: tcloud
labels:
app: nginx
spec:
type: ClusterIP
ports:
- port: 80
name: web
selector:
app: nginx
---
# CephFS 共享存储 PVC
# 支持多节点同时读写(RWX)
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: nginx-share-pvc
namespace: tcloud
spec:
storageClassName: rook-cephfs # 使用 CephFS 文件存储
accessModes:
- ReadWriteMany # 多节点共享读写
resources:
requests:
storage: 1Gi # 申请 1GB 共享空间
---
# Nginx 多副本 Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
namespace: tcloud
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
name: web
# 挂载共享存储到 Nginx 网页目录
volumeMounts:
- name: web-root
mountPath: /usr/share/nginx/html
# 引用 CephFS 共享 PVC
volumes:
- name: web-root
persistentVolumeClaim:
claimName: nginx-share-pvc
# apply
[root@VM-16-167-tencentos storageClass]# kubectl apply -f nginx-ceph.yaml
# 查看pvc和pv
[root@VM-16-167-tencentos storageClass]# kubectl get pvc -n tcloud
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
mysql-data-mysql-0 Bound pvc-7e9e80c8-440f-47b5-bec1-9aa6023a2fe0 5Gi RWO rook-ceph-block <unset> 16m
mysql-data-mysql-1 Bound pvc-70d3b8a1-fb60-4c7b-a104-b29d74314b1d 5Gi RWO rook-ceph-block <unset> 16m
mysql-data-mysql-2 Bound pvc-159cfe3a-8aca-4039-99e6-ddca664d5226 5Gi RWO rook-ceph-block <unset> 16m
nginx-share-pvc Bound pvc-a83b1ee0-15ae-4168-95a0-4378396bbf87 1Gi RWX rook-cephfs <unset> 16s
[root@VM-16-167-tencentos storageClass]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
pvc-159cfe3a-8aca-4039-99e6-ddca664d5226 5Gi RWO Delete Bound tcloud/mysql-data-mysql-2 rook-ceph-block <unset> 17m
pvc-70d3b8a1-fb60-4c7b-a104-b29d74314b1d 5Gi RWO Delete Bound tcloud/mysql-data-mysql-1 rook-ceph-block <unset> 17m
pvc-7e9e80c8-440f-47b5-bec1-9aa6023a2fe0 5Gi RWO Delete Bound tcloud/mysql-data-mysql-0 rook-ceph-block <unset> 17m
pvc-a83b1ee0-15ae-4168-95a0-4378396bbf87 1Gi RWX Delete Bound tcloud/nginx-share-pvc rook-cephfs <unset> 31s
pvc-d0f7176c-ed44-40b2-95c3-3508b2295a5e 10Gi RWO Delete Bound default/mysql-pv-claim rook-ceph-block <unset> 37m
# 查看pod
[root@VM-16-167-tencentos storageClass]# kubectl get pod -n tcloud
NAME READY STATUS RESTARTS AGE
mysql-0 1/1 Running 0 15m
mysql-1 1/1 Running 0 17m
mysql-2 1/1 Running 0 17m
nginx-8b6bcb6c6-4kdp9 1/1 Running 0 69s
nginx-8b6bcb6c6-h8sdq 1/1 Running 0 69s
nginx-8b6bcb6c6-hpwrf 1/1 Running 0 69s
# 进入pod添加测试页面
[root@VM-16-167-tencentos storageClass]# kubectl exec -it nginx-8b6bcb6c6-4kdp9 -n tcloud -- bash
root@nginx-8b6bcb6c6-4kdp9:/# echo 'Hello CephFS' > /usr/share/nginx/html/index.html
# 进入别的pod看index.html文件内容
[root@VM-16-167-tencentos storageClass]# kubectl exec -it nginx-8b6bcb6c6-h8sdq -n tcloud -- bash
root@nginx-8b6bcb6c6-h8sdq:/# cat /usr/share/nginx/html/index.html
Hello CephFS
# 删除pod等重建running看数据是否还存在
[root@VM-16-167-tencentos storageClass]# kubectl delete pod nginx-8b6bcb6c6-4kdp9 -n tcloud
[root@VM-16-167-tencentos storageClass]# kubectl exec -it nginx-8b6bcb6c6-c26b8 -n tcloud -- cat /usr/share/nginx/html/index.html
Hello CephFS
7)PVC扩容
文件存储扩容
支持 CEPHFS 在线热扩容
# 先查看需要扩容的pvc
[root@VM-16-167-tencentos ~]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
mysql-pv-claim Bound pvc-d0f7176c-ed44-40b2-95c3-3508b2295a5e 10Gi RWO rook-ceph-block <unset> 89m
[root@VM-16-167-tencentos ~]# kubectl get pvc -A
NAMESPACE NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
default mysql-pv-claim Bound pvc-d0f7176c-ed44-40b2-95c3-3508b2295a5e 10Gi RWO rook-ceph-block <unset> 89m
tcloud mysql-data-mysql-0 Bound pvc-7e9e80c8-440f-47b5-bec1-9aa6023a2fe0 5Gi RWO rook-ceph-block <unset> 69m
tcloud mysql-data-mysql-1 Bound pvc-70d3b8a1-fb60-4c7b-a104-b29d74314b1d 5Gi RWO rook-ceph-block <unset> 69m
tcloud mysql-data-mysql-2 Bound pvc-159cfe3a-8aca-4039-99e6-ddca664d5226 5Gi RWO rook-ceph-block <unset> 69m
tcloud nginx-share-pvc Bound pvc-a83b1ee0-15ae-4168-95a0-4378396bbf87 1Gi RWX rook-cephfs <unset> 52m
# 扩容tcloud命名空间下的nginx-share-pvc
[root@VM-16-167-tencentos ~]# kubectl edit pvc -n tcloud nginx-share-pvc
accessModes:
- ReadWriteMany
resources:
requests:
storage: 5Gi # 修改空间为5G
storageClassName: rook-cephfs
# 查看pvc是否容量是否为5G
[root@VM-16-167-tencentos ~]# kubectl get pvc -n tcloud
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
mysql-data-mysql-0 Bound pvc-7e9e80c8-440f-47b5-bec1-9aa6023a2fe0 5Gi RWO rook-ceph-block <unset> 73m
mysql-data-mysql-1 Bound pvc-70d3b8a1-fb60-4c7b-a104-b29d74314b1d 5Gi RWO rook-ceph-block <unset> 73m
mysql-data-mysql-2 Bound pvc-159cfe3a-8aca-4039-99e6-ddca664d5226 5Gi RWO rook-ceph-block <unset> 73m
nginx-share-pvc Bound pvc-a83b1ee0-15ae-4168-95a0-4378396bbf87 5Gi RWX rook-cephfs <unset> 56m
# 查看pv是否也是5G
[root@VM-16-167-tencentos ~]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
pvc-159cfe3a-8aca-4039-99e6-ddca664d5226 5Gi RWO Delete Bound tcloud/mysql-data-mysql-2 rook-ceph-block <unset> 73m
pvc-70d3b8a1-fb60-4c7b-a104-b29d74314b1d 5Gi RWO Delete Bound tcloud/mysql-data-mysql-1 rook-ceph-block <unset> 74m
pvc-7e9e80c8-440f-47b5-bec1-9aa6023a2fe0 5Gi RWO Delete Bound tcloud/mysql-data-mysql-0 rook-ceph-block <unset> 74m
pvc-a83b1ee0-15ae-4168-95a0-4378396bbf87 5Gi RWX Delete Bound tcloud/nginx-share-pvc rook-cephfs <unset> 57m
pvc-d0f7176c-ed44-40b2-95c3-3508b2295a5e 10Gi RWO Delete Bound default/mysql-pv-claim rook-ceph-block <unset> 94m
# 验证pod的挂载空间是否生效
[root@VM-16-167-tencentos ~]# kubectl exec -n tcloud $(kubectl get pod -n tcloud | grep nginx | awk '{print $1}') -- df -h /usr/share/nginx/html
Filesystem Size Used Avail Use% Mounted on
192.168.26.127:6789,192.168.30.23:6789,192.168.1.37:6789:/volumes/csi/csi-vol-6f3c0e76-c37c-4c3a-9042-b4520d186024/4eab5b2b-6501-427a-b4b7-3f45c4583ab7 5.0G 0 5.0G 0% /usr/share/nginx/html
块存储扩容
块存储支持热扩容
# 验证mysqlpod的存储空间
[root@VM-16-167-tencentos ~]# kubectl exec -it mysql-0 -n tcloud -- df -h /var/lib/mysql
Filesystem Size Used Avail Use% Mounted on
/dev/rbd0 4.9G 201M 4.7G 5% /var/lib/mysql
是5G
# 找到mysql-0的pvc
[root@VM-16-167-tencentos ~]# kubectl get pvc -n tcloud
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
mysql-data-mysql-0 Bound pvc-7e9e80c8-440f-47b5-bec1-9aa6023a2fe0 5Gi RWO rook-ceph-block <unset> 78m
mysql-data-mysql-1 Bound pvc-70d3b8a1-fb60-4c7b-a104-b29d74314b1d 5Gi RWO rook-ceph-block <unset> 78m
mysql-data-mysql-2 Bound pvc-159cfe3a-8aca-4039-99e6-ddca664d5226 5Gi RWO rook-ceph-block <unset> 78m
nginx-share-pvc Bound pvc-a83b1ee0-15ae-4168-95a0-4378396bbf87 5Gi RWX rook-cephfs <unset> 62m
# 扩容
[root@VM-16-167-tencentos ~]# kubectl edit pvc -n tcloud mysql-data-mysql-0
- ReadWriteOnce
resources:
requests:
storage: 20Gi # 扩容
storageClassName: rook-ceph-block
等一会,扩容需要时间
# 查看pvc是否也是20
[root@VM-16-167-tencentos ~]# kubectl get pvc -n tcloud
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
mysql-data-mysql-0 Bound pvc-7e9e80c8-440f-47b5-bec1-9aa6023a2fe0 20Gi RWO rook-ceph-block <unset> 80m
mysql-data-mysql-1 Bound pvc-70d3b8a1-fb60-4c7b-a104-b29d74314b1d 5Gi RWO rook-ceph-block <unset> 80m
mysql-data-mysql-2 Bound pvc-159cfe3a-8aca-4039-99e6-ddca664d5226 5Gi RWO rook-ceph-block <unset> 80m
nginx-share-pvc Bound pvc-a83b1ee0-15ae-4168-95a0-4378396bbf87 5Gi RWX rook-cephfs <unset> 63m
# pv
[root@VM-16-167-tencentos ~]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
pvc-159cfe3a-8aca-4039-99e6-ddca664d5226 5Gi RWO Delete Bound tcloud/mysql-data-mysql-2 rook-ceph-block <unset> 80m
pvc-70d3b8a1-fb60-4c7b-a104-b29d74314b1d 5Gi RWO Delete Bound tcloud/mysql-data-mysql-1 rook-ceph-block <unset> 81m
pvc-7e9e80c8-440f-47b5-bec1-9aa6023a2fe0 20Gi RWO Delete Bound tcloud/mysql-data-mysql-0 rook-ceph-block <unset> 81m
pvc-a83b1ee0-15ae-4168-95a0-4378396bbf87 5Gi RWX Delete Bound tcloud/nginx-share-pvc rook-cephfs <unset> 64m
pvc-d0f7176c-ed44-40b2-95c3-3508b2295a5e 10Gi RWO Delete Bound default/mysql-pv-claim rook-ceph-block <unset> 101m
可以看到对应的PV也成功扩容
# 查看mysql-0的挂载空间
[root@VM-16-167-tencentos ~]# kubectl exec -it mysql-0 -n tcloud -- df -h /var/lib/mysql
Filesystem Size Used Avail Use% Mounted on
/dev/rbd0 20G 201M 20G 1% /var/lib/mysql
8)PVC克隆
文件存储克隆
基于已有的 CephFS PVC,直接完整复制一份全新 PVC,数据一模一样,不用备份、不用传文件、不用停机。
# 先看源 PVC 信息
[root@VM-16-167-tencentos ~]# kubectl get pvc nginx-share-pvc -n tcloud
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
nginx-share-pvc Bound pvc-a83b1ee0-15ae-4168-95a0-4378396bbf87 5Gi RWX rook-cephfs <unset> 76m
# 编写克隆 PVC yaml
[root@VM-16-167-tencentos ~]# vim cephfs-clone.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nginx-share-pvc-clone
namespace: tcloud
spec:
storageClassName: rook-cephfs
accessModes:
- ReadWriteMany
resources:
requests:
storage: 3Gi #克隆的大小 必须 ≥ (源PVC)5Gi
# 核心克隆配置:指定源PVC
dataSource:
name: nginx-share-pvc
kind: PersistentVolumeClaim
# apply
[root@VM-16-167-tencentos ~]# kubectl apply -f cephfs-clone.yaml
# 查看
[root@VM-16-167-tencentos ~]# kubectl get pvc -n tcloud nginx-share-pvc-clone
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
nginx-share-pvc-clone Bound pvc-31900ef3-2e2f-4434-831a-240d3105230e 5Gi RWX rook-cephfs <unset> 46s
# 起一个新 Nginx,挂载克隆 PVC
[root@VM-16-167-tencentos ~]# vim nginx-clone-test.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-clone-test
namespace: tcloud
spec:
replicas: 1
selector:
matchLabels:
app: nginx-clone
template:
metadata:
labels:
app: nginx-clone
spec:
containers:
- name: nginx
image: nginx:alpine
volumeMounts:
- name: web-data
mountPath: /usr/share/nginx/html
volumes:
- name: web-data
persistentVolumeClaim:
claimName: nginx-share-pvc-clone
# apply
[root@VM-16-167-tencentos ~]# kubectl apply -f nginx-clone-test.yaml
# 查看index.html和之前写入的测试文件是否一致
[root@VM-16-167-tencentos ~]# kubectl exec -it $(kubectl get pod -n tcloud | grep nginx-clone | awk '{print $1}') -n tcloud -- cat /usr/share/nginx/html/index.html
Hello CephFS
块存储克隆
基于pod的mysql-0的pvc进行克隆,因为我们之前写入过测试数据
# 查看pvc
[root@VM-16-167-tencentos ~]# kubectl get pvc -n tcloud mysql-data-mysql-0
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
mysql-data-mysql-0 Bound pvc-7e9e80c8-440f-47b5-bec1-9aa6023a2fe0 20Gi RWO rook-ceph-block <unset> 103m
# 编写克隆pvc的yaml
[root@VM-16-167-tencentos ~]# vim mysql-rbd-clone.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mysql-data-mysql-0-clone
namespace: tcloud
spec:
storageClassName: rook-ceph-block
accessModes:
- ReadWriteOnce
# 容量和源盘保持一致
resources:
requests:
storage: 20Gi
# 克隆数据源:原mysql-0的PVC
dataSource:
kind: PersistentVolumeClaim
name: mysql-data-mysql-0
# apply
[root@VM-16-167-tencentos ~]# kubectl apply -f mysql-rbd-clone.yaml
# 查看克隆的pvc
nginx-share-pvc-clone Bound pvc-31900ef3-2e2f-4434-831a-240d3105230e 5Gi RWX rook-cephfs <unset> 9m21s
[root@VM-16-167-tencentos ~]# kubectl get pvc -n tcloud mysql-data-mysql-0-clone
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
mysql-data-mysql-0-clone Bound pvc-4bb4d9e1-647a-4121-842b-08974ee4b765 20Gi RWO rook-ceph-block <unset> 22s
# 用 MySQL 镜像启动克隆盘(直接带数据)
[root@VM-16-167-tencentos ~]# vim mysql-clone.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: mysql-clone
namespace: tcloud
spec:
replicas: 1
selector:
matchLabels:
app: mysql-clone
template:
metadata:
labels:
app: mysql-clone
spec:
containers:
- name: mysql
image: mysql:8.0
volumeMounts:
- name: mysql-data
mountPath: /var/lib/mysql
env:
- name: MYSQL_ROOT_PASSWORD
value: "123456"
volumes:
- name: mysql-data
persistentVolumeClaim:
claimName: mysql-data-mysql-0-clone
# apply
[root@VM-16-167-tencentos ~]# kubectl apply -f mysql-clone.yaml
# 进入挂载克隆pvc的mysql查看数据
[root@VM-16-167-tencentos ~]# kubectl exec -it $(kubectl get pod -n tcloud | grep mysql-clone | head -n1 | awk '{print $1}') -n tcloud -- bash
bash-5.1# mysql -uroot -p123456
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sys |
| test_ceph |
+--------------------+
5 rows in set (0.00 sec)
mysql> use test_ceph;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
mysql> show tables;
+---------------------+
| Tables_in_test_ceph |
+---------------------+
| t_user |
+---------------------+
1 row in set (0.00 sec)
mysql> select * from t_user;
+------+
| id |
+------+
| 100 |
+------+
1 row in set (0.00 sec)
之前写的测试数据,完整克隆过来了!
二、Helm
1、什么是helm
Helm 是 Kubernetes 官方提供的包管理器,类比 Linux 系统的 YUM/Apt,核心作用是简化 K8s 应用的部署、版本管理和生命周期维护。在无 Helm 时,需手动依次部署 Deployment、Service 等资源,步骤繁琐且难以规模化管理;Helm 通过打包机制将 K8s 资源配置动态化,支持版本控制,大幅降低复杂应用(尤其是微服务)的部署难度。
Helm 4 是 Kubernetes 包管理工具 Helm 时隔 6 年发布的全新主版本(2025 年 11 月正式发布 v4.0.0),在完全兼容 Helm 3 主流 Chart 格式的基础上,完成了核心架构重构、扩展能力全面升级、安全性与现代化改造,解决了 Helm 3 的架构技术债务,为未来的功能迭代奠定了基础。
2、helm基础认知
- Chart:Helm 的 "安装包",包含运行一个 Kubernetes 应用所需的所有资源定义(如 Deployment、Service 等)。
- Repository:存储和共享 Chart 的仓库,类似代码仓库(如官方的 Bitnami 仓库、私有仓库)。
- Release:Chart 在 Kubernetes 集群中的 "运行实例",一个 Chart 可部署为多个不同 Release。
- Values:配置文件,用于在部署时自定义 Chart 参数(如镜像版本、服务端口),实现 "一次打包,多环境复用"。
- Templates:Kubernetes 资源的模板文件,结合 Values 动态生成最终部署配置。
3、安装helm
# 安装Helm(这里是使用的是最新版)
[root@k8s-master01 helm]# wget https://get.helm.sh/helm-v4.1.4-linux-amd64.tar.gz
# 解压
[root@k8s-master01 helm]# tar -zxvf helm-v4.1.4-linux-amd64.tar.gz
[root@k8s-master01 helm]# mv linux-amd64/helm /usr/local/bin/
[root@k8s-master01 helm]# helm version
version.BuildInfo{Version:"v4.1.4", GitCommit:"05fa37973dc9e42b76e1d2883494c87174b6074f", GitTreeState:"clean", GoVersion:"go1.25.9", KubeClientVersion:"v1.35"}
# 配置仓库
# 添加阿里仓库和Bitnami仓库(Bitnami 包含大量主流应用 Chart)
[root@k8s-master01 ~]# helm repo add aliyun https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts
[root@k8s-master01 ~]# helm repo add bitnami https://charts.bitnami.com/bitnami
# 更新仓库
[root@k8s-master01 ~]# helm repo update
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "aliyun" chart repository
...Successfully got an update from the "bitnami" chart repository
...Successfully got an update from the "ingress-nginx" chart repository
Update Complete. ⎈Happy Helming!⎈
# 如果ingress-nginx报错就是网络问题
# 查看仓库
[root@k8s-master01 ~]# helm repo list
NAME URL
ingress-nginx https://kubernetes.github.io/ingress-nginx
aliyun https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts
bitnami https://charts.bitnami.com/bitnami
4、helm常用命令
仓库管理
# 添加仓库
helm repo add 仓库名 地址
# 查看已添加仓库
helm repo list
# 删除仓库
helm repo remove 仓库名
# 更新所有仓库索引
helm repo update
chart搜索下载
# 搜索仓库应用
helm search repo 关键词
# 下载 Chart(压缩包)
helm pull 仓库/应用名
# 下载并解压
helm pull 仓库/应用名 --untar
# Helm4 推荐:OCI 源直接拉取(无需添加仓库)
helm pull oci://registry.k8s.io/ingress-nginx/ingress-nginx --untar
安装应用
# 基础安装
helm install 发布名 仓库/应用名
# 指定命名空间 + 自动创建命名空间
helm install 发布名 仓库/应用名 -n 命名空间 --create-namespace
# 自定义配置文件
helm install 发布名 仓库/应用名 -f values.yaml
# Helm4 新参数:部署失败自动回滚(替代旧 --atomic)
helm install 发布名 仓库/应用名 --rollback-on-failure
查看已部署的应用
# 查看所有命名空间的 Release
helm list -A
# 查看指定命名空间
helm list -n 命名空间
# 查看应用详情
helm status 发布名 -n 命名空间
# 查看版本更新历史
helm history 发布名 -n 命名空间
升级和回滚
# 升级应用
helm upgrade 发布名 仓库/应用名 -n 命名空间
# 升级 + 自定义配置
helm upgrade 发布名 仓库/应用名 -f values.yaml -n 命名空间
# Helm4 新参数:升级失败自动回滚
helm upgrade 发布名 仓库/应用名 --rollback-on-failure
# 回滚到上一版本
helm rollback 发布名 -n 命名空间
# 回滚到指定版本
helm rollback 发布名 版本号 -n 命名空间
卸载应用
# 卸载 Release
helm uninstall 发布名 -n 命名空间
本地chart开发
# 创建新 Chart
helm create 目录名
# 检查 Chart 语法
helm lint 目录名
# 打包 Chart
helm package 目录名
测试渲染
# 本地渲染 YAML(不部署)
helm template 发布名 Chart路径
# 模拟安装(不真实创建资源)
helm install 发布名 仓库/应用名 --dry-run
5、自定义chart实例
Helm Chart标准目录结构
mychart/ # Chart 根目录(自定义名称)
├── Chart.yaml # Chart 元数据(名称、版本、描述)
├── values.yaml # 默认配置参数(用户可覆盖)
├── templates/ # K8s 资源模板目录
│ ├── deployment.yaml # Deployment 模板
│ ├── service.yaml # Service 模板
│ ├── ingress.yaml # Ingress 模板
│ ├── _helpers.tpl # 辅助函数、标签定义(复用代码)
│ ├── NOTES.txt # 安装完成后的提示信息
│ └── hpa.yaml # 可选:弹性伸缩模板
├── charts/ # 可选:存放依赖的子 Chart
├── Chart.lock # 可选:依赖版本锁定文件
├── .helmignore # 可选:打包时忽略的文件(类似 .gitignore)
└── README.md # 可选:Chart 使用说明
1、创建前置环境配置文件
# 创建实例标准数据目录
[root@k8s-master01 ~]# mkdir -p redis-ms/templates
chart.yaml
Chart.yaml 是 Helm 安装包(Chart)的身份证 / 说明书,没有这个文件,Helm 就不认识你的部署模板,无法安装、升级、管理应用。
# 1. Chart.yaml
cat > redis-ms/Chart.yaml << EOF
apiVersion: v2
name: redis-ms
version: 1.0.0
appVersion: latest
EOF
values.yaml
所有可变参数(密码、镜像、节点、副本数)都写在这里,只改这一个文件,就能全局生效,不用去修改 Deployment / PVC 等底层模板。
# 2. values.yaml
cat > redis-ms/values.yaml << EOF
password: "123456"
image:
repository: swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/redis
tag: latest
master:
replicas: 1
slave:
replicas: 1
nodeName: k8s-node01
EOF
pv / pvc.yaml
# 3. PV
cat > redis-ms/templates/pv.yaml << EOF
apiVersion: v1
kind: PersistentVolume
metadata:
name: redis-data-pv
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce
hostPath:
path: /opt/redis-data
type: Directory
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- k8s-node01
persistentVolumeReclaimPolicy: Retain
EOF
# 4. PVC
cat > redis-ms/templates/pvc.yaml << EOF
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: redis-data-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
volumeName: redis-data-pv
EOF
主从节点deployment
# 5. Redis 主节点 Deployment
cat > redis-ms/templates/master-deploy.yaml << EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis-master
spec:
replicas: {{ .Values.master.replicas }}
selector:
matchLabels:
app: redis
role: master
template:
metadata:
labels:
app: redis
role: master
spec:
# 固定调度到 k8s-node01,防止Pod漂移
nodeName: {{ .Values.nodeName }}
containers:
- name: redis-master
image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
command: ["redis-server"]
args:
- --requirepass {{ .Values.password }} # Redis访问密码
- --save 60 1 # RDB持久化
- --appendonly yes # AOF持久化
volumeMounts:
- name: data
mountPath: /data # 数据持久化挂载目录
ports:
- containerPort: 6379
volumes:
- name: data
persistentVolumeClaim:
claimName: redis-data-pvc # 绑定PVC实现持久化
EOF
# 6. Redis 从节点 Deployment
cat > redis-ms/templates/slave-deploy.yaml << EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis-slave
spec:
replicas: {{ .Values.slave.replicas }}
selector:
matchLabels:
app: redis
role: slave
template:
metadata:
labels:
app: redis
role: slave
spec:
# 与主节点同节点部署,保证网络连通
nodeName: {{ .Values.nodeName }}
containers:
- name: redis-slave
image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
command: ["redis-server"]
args:
- --slaveof redis-master 6379 # 【核心】连接主节点(Service名称)
- --masterauth {{ .Values.password }} # 主节点密码
- --requirepass {{ .Values.password }} # 从节点密码
ports:
- containerPort: 6379
EOF
service
因为主节点用来做通信,从节点不需要和外部服务通信,所以不需要service
# 7. 主节点SVC
cat > redis-ms/templates/master-svc.yaml << EOF
apiVersion: v1
kind: Service
metadata:
name: redis-master
spec:
type: ClusterIP
ports:
- port: 6379
selector:
app: redis
role: master
EOF
2、安装Release(redis)
# 在node1上创建redis数据存放目录
[root@k8s-node01 ~]# mkdir -p /opt/redis-data
[root@k8s-node01 ~]# chmod 777 /opt/redis-data
# 创建命令空间
[root@k8s-master01 ~]# kubectl create ns redis-ms
# 创建实例
[root@k8s-master01 ~]# helm install redis-ms ./redis-ms -n redis-ms
# 查看安装的资源
[root@k8s-master01 ~]# kubectl get pod -n redis-ms
NAME READY STATUS RESTARTS AGE
redis-master-567bcd8577-qb9q8 1/1 Running 0 117s
redis-slave-5bf8bb8b86-rjslq 1/1 Running 0 117s
[root@k8s-master01 ~]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
redis-data-pv 1Gi RWO Retain Bound redis-ms/redis-data-pvc <unset> 2m5s
[root@k8s-master01 ~]# kubectl get pvc -n redis-ms
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
redis-data-pvc Bound redis-data-pv 1Gi RWO <unset> 2m11s
[root@k8s-master01 ~]# kubectl get svc -n redis-ms
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
redis-master ClusterIP 10.96.113.113 <none> 6379/TCP 2m17s
3、测试实例
# 进入master pod写入测试数据
[root@k8s-master01 ~]# kubectl exec -it $(kubectl get pods -n redis-ms -l role=master -o name) -n redis-ms -- redis-cli -a 123456 set test hello
# 在node01节点查询数据情况
[root@k8s-node01 ~]# ls /opt/redis-data
appendonlydir dump.rdb
# 进入slave pod查询数据
[root@k8s-master01 ~]# kubectl exec -it $(kubectl get pods -n redis-ms -l role=slave -o name) -n redis-ms -- redis-cli -a 123456 get test
"hello"
# 删除pod重建
[root@k8s-master01 ~]# kubectl delete $(kubectl get pods -n redis-ms -l role=master -o name) -n redis-ms
# 查询数据持久化
[root@k8s-master01 ~]# kubectl exec -it $(kubectl get pods -n redis-ms -l role=master -o name) -n redis-ms -- redis-cli -a 123456 get test
"hello"
4、升级Release
# 1. 修改节点副本数(1→2)
[root@k8s-master01 ~]# sed -i 's/replicas: 1/replicas: 2/g' redis-ms/values.yaml
# 2. 执行升级命令
[root@k8s-master01 ~]# helm upgrade redis-ms ./redis-ms -n redis-ms
# 3. 验证升级结果
[root@k8s-master01 ~]# kubectl get pods -n redis-ms
NAME READY STATUS RESTARTS AGE
redis-master-567bcd8577-ftgt5 1/1 Running 0 3m20s
redis-master-567bcd8577-pjg64 1/1 Running 0 10s
redis-slave-5bf8bb8b86-mv8s8 1/1 Running 0 10s
redis-slave-5bf8bb8b86-rjslq 1/1 Running 0 8m10s
5、回滚Release
# 查看release的历史版本
[root@k8s-master01 ~]# helm history redis-ms -n redis-ms
REVISION UPDATED STATUS CHART APP VERSION DESCRIPTION
1 Wed May 6 13:09:20 2026 superseded redis-ms-1.0.0 latest Install complete
2 Wed May 6 13:17:19 2026 deployed redis-ms-1.0.0 latest Upgrade complete
# 回滚初始版本1
[root@k8s-master01 ~]# helm rollback redis-ms 1 -n redis-ms
Rollback was a success! Happy Helming!
# 验证pod
[root@k8s-master01 ~]# kubectl get pods -n redis-ms
NAME READY STATUS RESTARTS AGE
redis-master-567bcd8577-ftgt5 1/1 Running 0 5m5s
redis-slave-5bf8bb8b86-rjslq 1/1 Running 0 9m55s
6、打包Release
打包是为了规范、安全、分发、版本管理,适合团队 / 生产使用。
[root@k8s-master01 ~]# helm package redis-ms/
Successfully packaged chart and saved it to: /root/redis-ms-1.0.0.tgz
# 执行完成后,会生成文件:redis-ms-1.0.0.tgz
# 把这个 tgz 文件传输给别人
# 同事使用
# 1. 创建命名空间
kubectl create ns redis-ms
# 2. 在node01上安装数据存储目录
mkdir -p /opt/redis-data
chmod 777 /opt/redis-data
# 3. 直接安装压缩包(无需解压!无需改配置)
helm install redis-ms ./redis-ms-1.0.0.tgz -n redis-ms
如果同事需要改密码、节点、镜像,不用改模板,直接两种方式:
方式 1:解压修改配置(推荐)
# 解压
tar -zxvf redis-ms-1.0.0.tgz
# 修改配置文件
vim redis-ms/values.yaml
# 安装
helm install redis-ms ./redis-ms -n redis-ms
方式 2:安装时直接覆盖参数
helm install redis-ms ./redis-ms-1.0.0.tgz -n redis-ms \
--set password=654321 \
--set nodeName=k8s-node02
7、卸载Release
# 1. 卸载 Redis 实例(Release)
[root@k8s-master01 ~]# helm uninstall redis-ms -n redis-ms
# 2. 删除命名空间(连带删除 PVC/Pod/SVC)
[root@k8s-master01 ~]# kubectl delete namespace redis-ms