很多团队想上 K8s,又怕有状态应用——数据库、消息队列、文件存储,分分钟让你怀疑人生。今天我们从 StorageClass 到 PVC 到 CSI 驱动,手把手搞定 Kubernetes 有状态存储。
痛点:为什么 K8s 存储这么难?
Pod 是无状态的,随时可以漂移。但 MySQL、Elasticsearch、MinIO 这些应用需要持久化数据,一旦 Pod 被调度到其他节点,数据就丢了。
K8s 存储的核心问题:
1. 数据需要跟着 Pod 走(或共享存储)
2. 存储需要先 Provision(创建)才能用
3. 不同云厂商存储方案不同
# 传统方式:手动创建 PV,烦死
apiVersion: v1
kind: PersistentVolume
metadata:
name: mysql-pv
spec:
capacity:
storage: 20Gi
accessModes:
- ReadWriteOnce
# 手动指定云厂商的存储卷——每个环境都要改!
awsElasticBlockStore:
volumeID: vol-0a2b3c4d5e6f
fsType: ext4
核心概念:从 StorageClass 到 PVC
存储架构全览
StorageClass (存储类)
↓ 动态 Provision
PersistentVolumeClaim (PVC) → 声明需要多少存储
↓ 绑定
PersistentVolume (PV) → 实际的存储卷
↓ 挂载
Pod → 应用容器
StorageClass:存储的”抽象层”
StorageClass 是 K8s 的存储抽象,不同云厂商、不同存储类型都可以通过同一个接口访问:
# storageclass.yaml - 腾讯云 CBS 为例
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: cbs-high-performance # StorageClass 名称
provisioner: cloud.tencent.com/qcloud-cbs # CSI 驱动
parameters:
type: SSD_CLOUD_BSE # 存储类型:SSD/高性能/HDD
zone: ap-guangzhou-3 # 可用区
encrypted: "true" # 是否加密
reclaimPolicy: Retain # Retain(保留)/Delete(删除)
mountOptions:
- discard # 开启 TRIM,SSD 性能优化
动态 Provisioning:PVC 申请 → 自动创建 PV
# pvc.yaml - MySQL 的存储声明
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mysql-data-pvc
namespace: production
spec:
accessModes:
- ReadWriteOnce # 单节点读写(常用)
# - ReadOnlyMany # 多节点只读
# - ReadWriteMany # 多节点读写(需要 NFS/CEPH 等)
resources:
requests:
storage: 50Gi # 请求 50Gi
storageClassName: cbs-high-performance # 指定存储类
# 提交后,K8s 自动创建 PV
kubectl apply -f pvc.yaml
# 查看 PVC 状态
kubectl get pvc -n production
# NAME STATUS VOLUME CAPACITY ACCESS MODES
# mysql-data-pvc Bound pvc-8a2b3c4d-xxxx-xxxx-xxxx-5e6f7a8b9c0d 50Gi RWO
# 查看自动创建的 PV
kubectl get pv pvc-8a2b3c4d-xxxx-xxxx-xxxx-5e6f7a8b9c0d
# NAME CAPACITY RECLAIM POLICY STATUS CLAIM
# pvc-8a2b3c4d-xxxx-xxxx-xxxx-5e6f7a8b9c0d 50Gi Retain Bound production/mysql-data-pvc
CSI 驱动:K8s 存储的标准接口
CSI(Container Storage Interface)是 K8s 存储的标准化方案,2019 年 GA 后成为唯一官方支持的存储扩展方式。
主流云厂商 CSI 驱动对比
| 云厂商 | CSI 驱动 | 支持存储类型 | GitHub |
|---|---|---|---|
| 腾讯云 | tencentcloudyun-csi | CBS(云盘)、CFS(文件存储)、COS(对象存储) | github.com/TencentCloud/kubernetes-csi-tencentcloud |
| 阿里云 | disk-csi-plugin | ESSD/SSD/高效云盘、NAS、OSS | github.com/kubernetes-sigs/alibaba-cloud-csi-driver |
| 华为云 | hwameistor/huaweicloud-csi | SSD/超高 IO/普通云硬盘、SFS Turbo、OBS | github.com/HuaweiCloudSRE/huaweicloud-csi-plugin |
| AWS | aws-ebs-csi-driver | gp3/gp2/io2/io1 | github.com/kubernetes-sigs/aws-ebs-csi-driver |
部署腾讯云 CSI 驱动
# 方式1:Helm 一键安装(推荐)
helm repo add csi-tencent-cloud https://TencentCloud.github.io/kubernetes-csi-tencentcloud/charts
helm repo update
helm install csi-tencent-cloud/csi-tencent-cloud \
--namespace kube-system \
--name csi-tencent-cloud \
--set cloud.secretID=AKIDxxxxxxxxxx \
--set cloud.secretKey=xxxxxxxxxxxxxx
# 方式2:手动 YAML 安装
kubectl apply -f https://raw.githubusercontent.com/TencentCloud/kubernetes-csi-tencentcloud/master/deploy/kubernetes/csi-controller.yaml
kubectl apply -f https://raw.githubusercontent.com/TencentCloud/kubernetes-csi-tencentcloud/master/deploy/kubernetes/csi-node.yaml
实战:部署有状态 MySQL 集群
# mysql-statefulset.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
namespace: production
spec:
serviceName: mysql-headless # Headless Service 名称
replicas: 3
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:8.0
ports:
- containerPort: 3306
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: root-password
volumeMounts:
- name: data
mountPath: /var/lib/mysql
resources:
requests:
cpu: "500m"
memory: "1Gi"
limits:
cpu: "2000m"
memory: "4Gi"
volumes:
- name: config
configMap:
name: mysql-config
volumeClaimTemplates: # 自动创建 PVC
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: "cbs-high-performance"
resources:
requests:
storage: 100Gi
# 查看 StatefulSet 和 PVC 关联
kubectl get statefulset -n production
kubectl get pvc -n production
# NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS
# data-mysql-0 Bound pvc-xxx 100Gi RWO cbs-high-performance
# data-mysql-1 Bound pvc-yyy 100Gi RWO cbs-high-performance
# data-mysql-2 Bound pvc-zzz 100Gi RWO cbs-high-performance
# 每个 MySQL Pod 自动绑定独立的 PVC(数据目录独立)
kubectl get pods -n production -l app=mysql
# NAME READY STATUS RESTARTS AGE
# mysql-0 1/1 Running 0 2m
# mysql-1 1/1 Running 0 1m
# mysql-2 1/1 Running 0 30s
Volume Snapshots:数据备份与恢复
# volume-snapshot.yaml - 备份 MySQL 数据
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshotClass
metadata:
name: mysql-snapshot-class
driver: cloud.tencent.com/qcloud-cbs
deletionPolicy: Delete
---
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
name: mysql-backup-2026-05-19
namespace: production
spec:
volumeSnapshotClassName: mysql-snapshot-class
source:
persistentVolumeClaimName: data-mysql-0 # 备份 mysql-0 的数据
# 创建快照
kubectl apply -f volume-snapshot.yaml
# 查看快照状态
kubectl get volumesnapshot -n production
# NAME READYTOUSE SOURCEPVC RESTORESIZE
# mysql-backup-2026-05-19 true data-mysql-0 100Gi
# 从快照恢复数据到新 PVC
kubectl apply -f - <<EOF
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mysql-restore-pvc
namespace: production
spec:
dataSource:
name: mysql-backup-2026-05-19
kind: VolumeSnapshot
apiGroup: snapshot.storage.k8s.io
storageClassName: cbs-high-performance
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 100Gi
EOF
存储 QoS:保障关键应用性能
# storageclass-with-qos.yaml - 限速存储类
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: cbs-essd
provisioner: cloud.tencent.com/qcloud-cbs
parameters:
type: ESSD_CLOUD_PLUS # 顶级 SSD,极低延迟
performanceLevel: PL3 # 云盘性能等级(PL0-PL3)
encrypted: "true"
reclaimPolicy: Retain
volumeBindingMode: WaitForFirstConsumer # 等 Pod 调度后再创建 PV
| 存储类型 | 性能等级 | IOPS | 吞吐量 | 适用场景 |
|---|---|---|---|---|
| SSD 云硬盘 | PL0 | 1,000 | 50 MB/s | 开发测试 |
| SSD 云硬盘 | PL1 | 3,000 | 100 MB/s | 轻量数据库 |
| SSD 云硬盘 | PL2 | 6,000 | 200 MB/s | 中型数据库 |
| ESSD 云硬盘 | PL3 | 50,000+ | 500 MB/s+ | 核心业务 |
👤 作者简介
一枚在大中原腹地(河南)卖公有云的女/男士,主营腾讯云/阿里云/华为云,曾踩坑无数,现专注AI大模型应用落地。关注公众号「AI热点日报」,围观AI前沿动态~
博客:y1cloud.cn
