Kubernetes 存储体系与 PV/PVC 调度:从本地卷到分布式存储
Kubernetes 存储体系与 PV/PVC 调度从本地卷到分布式存储一、K8s 存储的持久化困境Pod 重建后的数据丢失风险Kubernetes 中 Pod 是 ephemeral短暂的——任何时刻都可能被重建节点故障、滚动更新、手动删除。如果应用数据存储在容器文件系统中Pod 重建后数据随之丢失。Persistent VolumePV和 Persistent Volume ClaimPVC是 K8s 解决数据持久化的核心机制但存储选型和调度策略的错误配置可能导致数据丢失、性能瓶颈或资源浪费。一个典型的生产事故数据库 Pod 使用了默认的 StorageClass基于本地存储节点故障后 Pod 被调度到其他节点但本地存储的数据无法跨节点访问导致数据库启动失败。存储选型不是简单的选最快的而是需要根据数据的重要性、访问模式和成本约束综合决策。二、K8s 存储体系与调度机制K8s 存储体系从底层到上层分为存储后端CSI Driver→ 存储类StorageClass→ 持久卷PV→ 持久卷声明PVC→ Pod 挂载。调度器根据 PVC 的需求容量、访问模式、StorageClass匹配可用的 PV或通过动态供给自动创建 PV。flowchart TB A[PVC 声明] -- B{绑定模式} B --|静态供给| C[匹配已有 PV] B --|动态供给| D[StorageClass 创建 PV] D -- E[CSI Driver 调用] E -- F{存储后端类型} F --|本地存储| G[Local PV: 高性能不可迁移] F --|网络存储| H[NFS/CIFS: 共享访问性能一般] F --|块存储| I[Cloud Disk: 高性能可迁移] F --|分布式存储| J[Ceph/MinIO: 高可用成本高] C -- K[Pod 绑定 PVC] D -- K K -- L{Pod 调度决策} L --|Local PV| M[必须调度到 PV 所在节点] L --|Network PV| N[可调度到任意节点] M -- O[节点亲和性约束] N -- P[无节点约束]三、生产级实现存储配置与调度策略# # StorageClass 配置不同存储后端的差异化供给 # # 高性能本地存储适合数据库、消息队列 # 设计意图本地 SSD 提供最低延迟但 Pod 必须调度到 # 数据所在的节点节点故障时数据不可用 apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: local-ssd provisioner: kubernetes.io/no-provisioner # 本地存储不支持动态供给 reclaimPolicy: Retain # 删除 PVC 后保留数据 volumeBindingMode: WaitForFirstConsumer # 延迟绑定等 Pod 调度后再绑定 --- # 云盘存储适合需要跨节点迁移的工作负载 # 设计意图云盘可以 detach/attach 到不同节点 # Pod 重建后可以迁移到其他节点 apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: cloud-ssd provisioner: disk.csi.aliyun.com # 阿里云 CSI parameters: type: cloud_essd # ESSD 云盘 performanceLevel: PL1 # 性能级别 reclaimPolicy: Retain volumeBindingMode: WaitForFirstConsumer allowVolumeExpansion: true # 允许在线扩容 --- # NFS 共享存储适合多 Pod 共享读写的场景 # 设计意图多 Pod 可以同时挂载同一 NFS 卷 # 适合文件上传、配置共享等场景 apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: nfs-shared provisioner: nfs.csi.k8s.io parameters: server: nfs-server.default.svc.cluster.local share: /data reclaimPolicy: Retain volumeBindingMode: Immediate# # Local PV 定义手动预分配本地存储 # # 设计意图Local PV 必须手动创建不支持动态供给 # 需要提前在目标节点上准备存储路径 apiVersion: v1 kind: PersistentVolume metadata: name: local-pv-node1 spec: capacity: storage: 100Gi volumeMode: Filesystem accessModes: - ReadWriteOnce # 本地存储只支持 RWO persistentVolumeReclaimPolicy: Retain storageClassName: local-ssd local: path: /mnt/ssd/data # 节点上的存储路径 nodeAffinity: # 节点亲和性Pod 必须调度到 node1 required: nodeSelectorTerms: - matchExpressions: - key: kubernetes.io/hostname operator: In values: - node1 --- # # 数据库 StatefulSet使用 Local PV # # 设计意图StatefulSet 保证 Pod 的稳定标识和有序部署 # 每个 Pod 绑定独立的 PVC数据不会因 Pod 重建丢失 apiVersion: apps/v1 kind: StatefulSet metadata: name: mysql spec: serviceName: mysql replicas: 3 selector: matchLabels: app: mysql template: metadata: labels: app: mysql spec: # 反亲和性每个节点最多一个 MySQL Pod # 设计意图避免单节点故障导致多个副本不可用 affinity: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: app operator: In values: - mysql topologyKey: kubernetes.io/hostname containers: - name: mysql image: mysql:8.0 volumeMounts: - name: data mountPath: /var/lib/mysql env: - name: MYSQL_ROOT_PASSWORD valueFrom: secretKeyRef: name: mysql-secret key: password volumeClaimTemplates: - metadata: name: data spec: accessModes: - ReadWriteOnce storageClassName: local-ssd resources: requests: storage: 100Gi# # 存储运维脚本 # # 检查 PVC 绑定状态 # 设计意图PVC 处于 Pending 状态可能是因为 # 没有匹配的 PV 或 StorageClass 配置错误 kubectl get pvc -A -o custom-columns\ NAMESPACE:.metadata.namespace,\ NAME:.metadata.name,\ STATUS:.status.phase,\ VOLUME:.spec.volumeName,\ CAPACITY:.status.capacity.storage,\ STORAGECLASS:.spec.storageClassName # 检查 PV 的节点亲和性 # 设计意图Local PV 的节点亲和性决定了 Pod 的调度范围 # 如果目标节点不可用Pod 将无法启动 kubectl get pv -o custom-columns\ NAME:.metadata.name,\ CAPACITY:.spec.capacity.storage,\ ACCESS:.spec.accessModes,\ RECLAIM:.spec.persistentVolumeReclaimPolicy,\ STATUS:.status.phase,\ NODE:.spec.nodeAffinity.required.nodeSelectorTerms[0].matchExpressions[0].values[0] # 在线扩容 PVC仅支持特定 StorageClass # 设计意图数据库数据增长后需要扩容存储 # 云盘支持在线扩容无需卸载本地存储不支持 kubectl patch pvc>