存储卷与持久化存储:数据持久化方案
深入理解 Kubernetes 存储体系,包括 EmptyDir、HostPath、PV/PVC 持久化存储,以及 StorageClass 动态供给。
概述
容器原生是无状态的设计,但实际应用中需要持久化数据。Kubernetes 通过存储卷(Volume)提供了灵活的数据管理方案。本文将深入探讨:
学习目标:
- 理解 Kubernetes 存储模型与卷类型
- 掌握 PV/PVC 持久化存储机制
- 学会使用 StorageClass 实现动态供给
- 了解 CSI(容器存储接口)
- 掌握存储数据安全与备份策略
存储卷概述
为什么需要存储卷?
┌─────────────────────────────────────────────────────────────────┐
│ Kubernetes 存储模型 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Pod 是临时性的 │
│ │
│ ┌───────────────┐ │
│ │ Pod │ │
│ │ │ │
│ │ Container │ ← 容器删除,数据丢失 │
│ │ (数据) │ │
│ └───────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────┐ │
│ │ Volume │ ← 数据持久化 │
│ │ │ │
│ │ /data │ ← Pod 重建后仍存在 │
│ └───────────────┘ │
│ │
│ Volume 生命周期: │
│ - 与 Pod 绑定 │
│ - Pod 删除时释放 │
│ - 支持多种存储后端 │
│ │
└─────────────────────────────────────────────────────────────────┘
存储卷类型
┌─────────────────────────────────────────────────────────────────┐
│ Volume 类型一览 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ 临时存储 │ │ 节点存储 │ │ 网络存储 │ │
│ ├─────────────────┤ ├─────────────────┤ ├─────────────────┤ │
│ │ emptyDir │ │ hostPath │ │ NFS │ │
│ │ │ │ │ │ iSCSI │ │
│ │ Pod 生命周期 │ │ 节点文件系统 │ │ Ceph │ │
│ │ 内存/磁盘 │ │ 节点访问 │ │ 云盘 │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
│ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ 持久化存储 │ │ 配置存储 │ │ 特殊存储 │ │
│ ├─────────────────┤ ├─────────────────┤ ├─────────────────┤ │
│ │ persistentVol │ │ configMap │ │ downwardAPI │ │
│ │ (PV/PVC) │ │ secret │ │ projected │ │
│ │ │ │ │ │ │ │
│ │ 独立生命周期 │ │ 配置文件 │ │ 元数据注入 │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
临时存储卷
EmptyDir
# emptydir-example.yaml
apiVersion: v1
kind: Pod
metadata:
name: test-emptydir
spec:
containers:
- name: writer
image: busybox:1.36
command: ["sh", "-c", "echo 'data' > /data/file.txt; sleep 3600"]
volumeMounts:
- name: data
mountPath: /data
- name: reader
image: busybox:1.36
command: ["sh", "-c", "cat /data/file.txt && sleep 3600"]
volumeMounts:
- name: data
mountPath: /data
volumes:
- name: data
emptyDir: {}
# EmptyDir 存储介质配置
volumes:
- name: cache
emptyDir:
medium: Memory # 使用内存存储(tmpfs)
sizeLimit: 1Gi # 限制大小
# medium 选项:
# - 空/默认:节点磁盘
# - Memory:tmpfs(内存存储,性能高,但有限制)
EmptyDir 使用场景
┌─────────────────────────────────────────────────────────────────┐
│ EmptyDir 适用场景 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ✓ 临时缓存 │
│ ✓ 同一 Pod 多容器共享数据 │
│ ✓ 合并写入(如日志处理:应用写 + Agent 读) │
│ ✓ 短期计算任务的工作目录 │
│ │
│ ✗ 长期存储(Pod 删除后数据丢失) │
│ ✗ 跨 Pod 共享(独立于 Pod) │
│ ✗ 大数据存储(消耗节点资源) │
│ │
└─────────────────────────────────────────────────────────────────┘
节点存储卷
HostPath
# hostpath-example.yaml
apiVersion: v1
kind: Pod
metadata:
name: test-hostpath
spec:
containers:
- name: app
image: myapp:1.0
volumeMounts:
- name: host-data
mountPath: /mnt/host
volumes:
- name: host-data
hostPath:
path: /data/logs
type: DirectoryOrCreate # 不存在则创建
# type 选项:
# - DirectoryOrCreate:目录不存在则创建
# - Directory:目录必须存在
# - FileOrCreate:文件不存在则创建
# - File:文件必须存在
# - Socket:Unix Socket
# - CharDevice:字符设备
# - BlockDevice:块设备
HostPath 使用场景
┌─────────────────────────────────────────────────────────────────┐
│ HostPath 注意事项 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 限制: │
│ ✗ 只能调度到特定节点(需要 NodeSelector) │
│ ✗ 存储在节点文件系统,非分布式 │
│ ✗ 权限问题(需要考虑 Pod Security Policy) │
│ │
│ 适用场景(谨慎使用): │
│ ✓ 系统日志收集(读取 /var/log) │
│ ✓ 监控数据收集(读取 /proc, /sys) │
│ ✓ Kubernetes 组件数据(如 etcd 数据目录) │
│ ✓ 节点级别的缓存 │
│ │
│ 生产环境建议: │
│ - 使用 NodeSelector 限制 Pod 调度 │
│ - 结合 Pod Security Policy 限制权限 │
│ - 避免存储敏感数据 │
│ │
└─────────────────────────────────────────────────────────────────┘
持久化存储(PV/PVC)
PV/PVC 架构
┌─────────────────────────────────────────────────────────────────┐
│ PV/PVC 存储模型 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Pod │ │ Pod │ │ Pod │ │
│ │ PVC ──────┼──────┼───── PVC │ │ PVC ──────┼───│
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │ │ │ │
│ └────────────────────┼────────────────────┘ │
│ ▼ │
│ ┌─────────────┐ │
│ │ PVC │ │
│ │ Persistent │ │
│ │VolumeClaim │ │
│ └──────┬──────┘ │
│ │ │
│ ▼ │
│ ┌─────────────┐ │
│ │ PV │ │
│ │Persistent │ │
│ │ Volume │ │
│ └──────┬──────┘ │
│ │ │
│ ▼ │
│ ┌─────────────┐ │
│ │ Storage │ │
│ │ Backend │ │
│ │ (NFS/Ceph) │ │
│ └─────────────┘ │
│ │
│ 用户操作: │
│ 1. 管理员创建 PV(静态供给)或 StorageClass(动态) │
│ 2. 用户创建 PVC │
│ 3. 系统自动绑定 PV 到 PVC │
│ 4. Pod 使用 PVC │
│ │
└─────────────────────────────────────────────────────────────────┘
PersistentVolume(PV)
# pv-example.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-demo
labels:
type: fast-storage
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce # 单节点读写
# - ReadOnlyMany # 多节点只读
# - ReadWriteMany # 多节点读写(NFS/CephFS)
persistentVolumeReclaimPolicy: Retain # Retain/Delete/Recycle
storageClassName: fast-storage
mountOptions:
- hard
- nfsvers=4.1
nfs:
path: /exports/data
server: nfs-server.internal
# accessModes 说明:
# - ReadWriteOnce (RWO):单节点读写
# - ReadOnlyMany (ROX):多节点只读
# - ReadWriteMany (RWX):多节点读写
PersistentVolumeClaim(PVC)
# pvc-example.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-demo
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
storageClassName: fast-storage
# 可选:指定 PV
selector:
matchLabels:
type: fast-storage
---
# Pod 使用 PVC
apiVersion: v1
kind: Pod
metadata:
name: app-with-pvc
spec:
containers:
- name: app
image: myapp:1.0
volumeMounts:
- name: data
mountPath: /var/lib/app/data
volumes:
- name: data
persistentVolumeClaim:
claimName: pvc-demo
readOnly: false
PV/PVC 绑定
# 查看 PV 和 PVC 状态
kubectl get pv
kubectl get pvc
# 绑定状态:
# - Bound:已绑定
# - Available:可用但未绑定
# - Pending:等待绑定
# - Failed:绑定失败
# 手动绑定(通过 selector)
kubectl patch pvc pvc-demo -p '{"spec":{"selector":{"matchLabels":{"type":"fast-storage"}}}}'
Reclaim Policy
# PV 回收策略
# Retain(默认):保留数据,需要手动清理
# - Pod 删除后 PV 和数据保留
# - 可以重新绑定到新的 PVC
# - 需要管理员手动清理
# Delete:删除存储卷
# - Pod 删除后自动删除 PV 和底层存储
# - 数据丢失,不可恢复
# - 使用云盘等动态存储时常见
# Recycle(已废弃):
# - 删除数据,保留 PV
# - 效果等同于 Retain 后手动清理
# - 推荐使用动态存储替代
StorageClass 动态供给
为什么需要 StorageClass?
┌─────────────────────────────────────────────────────────────────┐
│ 动态供给 vs 静态供给 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 静态供给(Static) │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 管理员预先创建 PV │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ 用户手动创建 PVC │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ 手动绑定 │ │
│ └─────────────────────────────────────────────────────────┘ │
│ 问题:管理员需要预先创建大量 PV │
│ │
│ 动态供给(Dynamic) │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 用户创建 PVC │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ StorageClass 根据 PVC 请求 │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ 自动创建 PV(按需) │ │
│ └─────────────────────────────────────────────────────────┘ │
│ 优点:用户无需预先知道存储细节 │
│ │
└─────────────────────────────────────────────────────────────────┘
StorageClass 定义
# storageclass-aws.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast-storage
provisioner: ebs.csi.aws.com # CSI 驱动
parameters:
type: gp3 # AWS EBS 类型
iops: "3000"
throughput: "125"
encrypted: "true"
kmsKeyId: alias/aws/ebs
reclaimPolicy: Delete # Delete/Retain
allowVolumeExpansion: true # 允许扩容
mountOptions:
- debug
volumeBindingMode: WaitForFirstConsumer # 延迟绑定
allowedTopologies:
- matchLabelExpressions:
- key: topology.ebs.csi.aws.com/zone
values:
- us-east-1a
常见云厂商 StorageClass
# AWS EBS
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: ebs-sc
provisioner: ebs.csi.aws.com
parameters:
type: gp3
encrypted: "true"
---
# GCE Persistent Disk
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: pd-standard
provisioner: pd.csi.storage.gke.io
parameters:
type: pd-standard
replication-type: regional-pd
---
# Azure Disk
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: azure-disk
provisioner: disk.csi.azure.com
parameters:
skuName: StandardSSD_LRS
---
# NFS(需要 nfs-subdir-external-provisioner)
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: nfs-storage
provisioner: k8s-sigs.io/nfs-subdir-external-provisioner
parameters:
archiveOnDelete: "false"
pathPattern: "${.PVC.namespace}/${.PVC.name}"
volumeBindingMode 详解
# 立即绑定(Immediate)
volumeBindingMode: Immediate
# - PVC 创建时立即创建 PV
# - 可能在 Pod 调度前创建(可能导致调度失败)
# - 适用于有拓扑限制的存储(如云盘)
---
# 延迟绑定(WaitForFirstConsumer)
volumeBindingMode: WaitForFirstConsumer
# - 直到 Pod 调度后才绑定
# - 考虑 Pod 的拓扑限制,选择合适的 PV
# - 推荐用于大多数场景
# - 允许设置 allowedTopologies 限制可用区
延迟绑定工作流程
┌─────────────────────────────────────────────────────────────────┐
│ WaitForFirstConsumer 工作流程 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 1. 用户创建 PVC │
│ kubectl apply -f pvc.yaml │
│ → PVC 状态:Pending(等待消费) │
│ │
│ 2. 创建 Pod 引用该 PVC │
│ kubectl apply -f pod.yaml │
│ │
│ 3. Scheduler 调度 Pod │
│ - 考虑节点亲和性、污点容忍等 │
│ - 确定 Pod 调度的节点/可用区 │
│ │
│ 4. PVC 与 PV 绑定 │
│ - 根据 Pod 拓扑选择合适的 PV │
│ - 创建云盘/NFS 等存储 │
│ → PVC 状态:Bound │
│ │
│ 5. Pod 启动 │
│ │
└─────────────────────────────────────────────────────────────────┘
PVC 扩容
启用扩容
# StorageClass 启用扩容
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: ebs-sc
allowVolumeExpansion: true # 关键配置
# 需要存储驱动支持(大多数云驱动支持)
在线扩容 PVC
# 修改 PVC 大小
kubectl patch pvc data-pvc -p '{"spec":{"resources":{"requests":{"storage":"20Gi"}}}}'
# 检查扩容状态
kubectl get pvc data-pvc
# 扩容流程:
# 1. PVC 大小更新
# 2. StorageClass 调用云盘/存储 API 扩容
# 3. Pod 中的文件系统自动扩展(或需要手动)
# 注意:某些文件系统需要手动扩展
# ext4/XFS: 自动扩展
# 其他:可能需要重启 Pod 或手动执行 resize2fs
文件系统扩容触发
# 如果文件系统不支持自动扩展,需要重启 Pod
# 策略1:删除并重新创建 Pod
kubectl delete pod app-0
kubectl apply -f statefulset.yaml
# 策略2:更新 PVC 后删除 Pod(StatefulSet 会自动重建)
kubectl patch pvc data-pvc -p '{"spec":{"resources":{"requests":{"storage":"20Gi"}}}}'
kubectl delete pod app-0 -n default
CSI(容器存储接口)
CSI 架构
┌─────────────────────────────────────────────────────────────────┐
│ CSI 组件架构 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Kubernetes │ │
│ │ │ │
│ │ ┌─────────────────────────────────────────────────┐ │ │
│ │ │ CSI Plugin (K8s 内置) │ │ │
│ │ │ │ │ │
│ │ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │ │
│ │ │ │ Node │ │Controller│ │ Identity │ │ │ │
│ │ │ │Plugin │ │ Plugin │ │ Service │ │ │ │
│ │ │ └─────────┘ └─────────┘ └─────────┘ │ │ │
│ │ └─────────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ │ gRPC │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ CSI Driver(存储厂商提供) │ │
│ │ │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │ CSI │ │ CSI │ │ CSI │ │ CSI │ │ │
│ │ │ Node │ │Controller│ │ CO │ │ Identity│ │ │
│ │ │ Driver │ │ Driver │ │ Plugin │ │ Plugin │ │ │
│ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 实际存储系统 │ │
│ │ AWS EBS / GCE PD / Azure Disk / Ceph / NFS ... │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
CSI 操作流程
┌─────────────────────────────────────────────────────────────────┐
│ 创建 PVC 完整流程 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 1. Provisioning(创建存储) │
│ PVC 创建 → StorageClass → Controller Plugin │
│ → 调用云盘 API 创建磁盘 │
│ │
│ 2. Attachment(挂载到节点) │
│ Pod 调度到 Node → Node Plugin │
│ → 将磁盘挂载到节点 │
│ │
│ 3. Mount(挂载到容器) │
│ kubelet 调用 Node Plugin │
│ → 格式化/挂载到 /var/lib/kubelet/pods/... │
│ │
│ 4. 容器使用 │
│ 容器内访问挂载路径 │
│ │
│ 5. Cleanup(删除时) │
│ Pod 删除 → Node Plugin 卸载 │
│ → Controller Plugin 删除云盘 │
│ │
└─────────────────────────────────────────────────────────────────┘
常用 CSI 驱动
# AWS EBS CSI
helm repo add aws-ebs-csi https://kubernetes-sigs.github.io/aws-ebs-csi-driver
helm install aws-ebs-csi aws-ebs-csi/aws-ebs-csi-driver
# GCE PD CSI
gcloud container clusters get-credentials cluster-name
kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gcp-compute-persistent-disk-csi-driver/master/setup/kubernetes/deployment.yaml
# Ceph CSI
kubectl apply -f https://raw.githubusercontent.com/ceph/ceph-csi/master/deploy/rbd/kubernetes
kubectl apply -f https://raw.githubusercontent.com/ceph/ceph-csi/master/deploy/cephfs/kubernetes
# NFS CSI
kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/nfs-subdir-external-provisioner/master/deploy/object.yaml
StatefulSet 与存储
StatefulSet 存储特点
# statefulset-storage.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
spec:
selector:
matchLabels:
app: mysql
serviceName: mysql
replicas: 3
template:
spec:
containers:
- name: mysql
image: mysql:8.0
volumeMounts:
- name: data
mountPath: /var/lib/mysql
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: "fast-storage"
resources:
requests:
storage: 10Gi
# 特点:
# - 每个 Pod 有稳定的 PVC 名称(mysql-data-0, mysql-data-1...)
# - Pod 重建后仍绑定到同一 PVC
# - 扩容时自动创建新的 PVC
# - 缩容时保留 PVC(取决于 reclaimPolicy)
StatefulSet 存储工作流程
┌─────────────────────────────────────────────────────────────────┐
│ StatefulSet Volume 管理 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 创建顺序: │
│ │
│ mysql-0 │
│ └── 创建 PVC: data-mysql-0 → 绑定 PV │
│ └── 启动 │
│ │
│ mysql-1 │
│ └── 创建 PVC: data-mysql-1 → 绑定 PV │
│ └── 启动 │
│ │
│ mysql-2 │
│ └── 创建 PVC: data-mysql-2 → 绑定 PV │
│ └── 启动 │
│ │
│ 删除顺序: │
│ │
│ mysql-2 → 删除 Pod,保留 PVC(默认) │
│ mysql-1 → 删除 Pod,保留 PVC │
│ mysql-0 → 删除 Pod,保留 PVC │
│ │
│ 再次扩容时: │
│ mysql-0 → 复用原有 PVC │
│ │
└─────────────────────────────────────────────────────────────────┘
存储安全与备份
存储权限
# Pod 使用特定 ServiceAccount
spec:
serviceAccountName: app-sa
---
# RBAC 限制 PVC 创建权限
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: pvc-creator
rules:
- apiGroups: [""]
resources: ["persistentvolumeclaims"]
verbs: ["create", "get", "list"]
- apiGroups: ["storage.k8s.io"]
resources: ["storageclasses"]
verbs: ["get", "list"]
---
# PodSecurityPolicy 限制存储访问
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
name: restricted-psp
spec:
volumes:
- persistentVolumeClaim
- secret
# 限制 HostPath
allowedHostPaths:
- pathPrefix: /mnt/data
readOnly: true
数据备份策略
┌─────────────────────────────────────────────────────────────────┐
│ 存储备份策略 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 应用层备份: │
│ ✓ 数据库主从复制 │
│ ✓ 应用层定期备份到对象存储 │
│ ✓ 使用 Velero 备份 PV(快照) │
│ │
│ 存储层备份: │
│ ✓ 云盘快照(AWS/GCE/Azure) │
│ ✓ 存储系统自带备份(Ceph/GlusterFS) │
│ ✓ 卷复制到另一个存储 │
│ │
│ 推荐方案: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Velero(云原生备份工具) │ │
│ │ - 备份 PV + 应用配置 │ │
│ │ - 支持快照和文件级别恢复 │ │
│ │ - 支持多云厂商 │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
Velero 备份示例
# 安装 Velero
velero install \
--provider aws \
--plugins velero/velero-plugin-for-aws:v1.0.0 \
--bucket backup-bucket \
--secret-file ./credentials-velero \
--backup-location-config region=us-east-1
# 创建备份
velero backup create mysql-backup \
--include-namespaces production \
--selector app=mysql
# 恢复
velero restore create mysql-restore \
--from-backup mysql-backup
常见问题与避坑指南
Q1:PVC 一直 Pending?
# 排查步骤
# 1. 查看 PVC 事件
kubectl describe pvc data-pvc
# 常见原因:
# - StorageClass 不存在
kubectl get storageclass
# - 存储配额不足
kubectl describe namespace
# - 拓扑限制不满足(可用区)
# - StorageClass 参数错误
# 2. 查看 StorageClass 配置
kubectl get storageclass fast-storage -o yaml
# 3. 检查 CSI 驱动状态
kubectl get pods -n kube-system -l app=csi-driver
Q2:Pod 无法挂载 PVC?
# 排查步骤
# 1. 检查 PVC 状态
kubectl get pvc
# 2. 检查 Pod 事件
kubectl describe pod app
# 常见原因:
# - 权限不足(SecurityContext)
# - 节点无存储资源(云盘配额)
# - CSI 驱动问题
# 3. 检查存储插件
kubectl get csidriver
Q3:如何迁移数据?
# 方法1:使用 rsync(临时 Pod)
kubectl run data-migration \
--image=busybox \
--rm -it -- sh
# rsync -avz /source/ <svc-ip>:/dest/
# 方法2:使用 Velero 迁移
# 1. 备份源集群
velero backup create backup-001 --include-namespaces db
# 2. 恢复目标集群
velero restore create restore-001 --from-backup backup-001
Q4:如何清理废弃 PV?
# 1. 查找 Released 状态的 PV
kubectl get pv | grep Released
# 2. 删除 PV(数据会丢失)
kubectl delete pv pv-name
# 3. 如果想保留数据
# - 修改 reclaimPolicy 为 Retain
# - 手动清理存储系统
# - 重新创建 PV 绑定到 PVC
# 4. 清理残留的 PVC
kubectl get pvc --all-namespaces | grep Terminated
kubectl delete pvc pvc-name -n namespace
总结
┌─────────────────────────────────────────────────────────────────┐
│ 核心要点回顾 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 临时存储 │
│ ├── emptyDir:Pod 生命周期,内存/磁盘 │
│ └── hostPath:节点文件系统,有限制 │
│ │
│ 持久化存储 │
│ ├── PV:集群级存储资源 │
│ ├── PVC:Pod 请求存储 │
│ └── 绑定:PVC → PV │
│ │
│ StorageClass │
│ ├── 动态供给:按需创建 PV │
│ ├── volumeBindingMode:Immediate/WaitForFirstConsumer │
│ └── 扩容支持:allowVolumeExpansion │
│ │
│ CSI │
│ ├── 标准接口:统一存储管理 │
│ └── 云厂商驱动:AWS/GCE/Azure/NFS... │
│ │
│ StatefulSet │
│ ├── volumeClaimTemplates:自动管理 PVC │
│ └── 稳定标识:Pod 重建后仍绑定同一存储 │
│ │
└─────────────────────────────────────────────────────────────────┘
思考题
- 为什么 Local Persistent Volume 推荐使用 WaitForFirstConsumer 而不是 Immediate 绑定?
- 如何设计一个跨集群的存储迁移方案?
- 在无状态应用中,如何决定哪些数据需要持久化存储?
引用与参考
下篇预告
下一篇文章我们将进入 第二阶段:进阶实践,首先探讨 Helm:包管理利器,包括:
- Chart 结构与模板
- values 配置管理
- 仓库操作与发布
- 最佳实践与 CI/CD 集成
敬请期待!