存储卷与持久化存储:数据持久化方案

深入理解 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 重建后仍绑定同一存储                        │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

思考题

  1. 为什么 Local Persistent Volume 推荐使用 WaitForFirstConsumer 而不是 Immediate 绑定?
  2. 如何设计一个跨集群的存储迁移方案?
  3. 在无状态应用中,如何决定哪些数据需要持久化存储?

引用与参考

  1. Volumes
  2. Persistent Volumes
  3. Storage Classes
  4. CSI Documentation

下篇预告

下一篇文章我们将进入 第二阶段:进阶实践,首先探讨 Helm:包管理利器,包括:

  • Chart 结构与模板
  • values 配置管理
  • 仓库操作与发布
  • 最佳实践与 CI/CD 集成

敬请期待!