首页 » K8s » 第6章 存储

第6章 存储

 

本章目录(点击跳转):

本章目标:理解 K8s 存储三件套 PV / PVC / StorageClass,会挂本地存储、NFS,理解读写模式,知道数据备份的基本思路。

6.1 为什么需要存储

Pod 是"一次性的":重启、迁移、重建都会发生。容器文件系统里写的东西,Pod 一没就全没了。

但现实里很多应用有数据要保存:

  • 数据库文件(MySQL、PostgreSQL)
  • 上传的用户文件、图片
  • 应用生成的报表、日志归档

K8s 的解决方案:存储与 Pod 解耦——数据放在"卷(Volume)"上,卷的生命周期独立于 Pod。

Pod ──(挂载)──> Volume ──> 后端存储(本地盘 / NFS / 云盘 / S3...)
                     ▲
        PVC(申请单)──┘    PV(实际资源)    StorageClass(自动造 PV 的模板)

6.2 三件套:PV、PVC、StorageClass

用"租房"类比秒懂:

概念 类比 说明
PV(PersistentVolume,持久卷) 一套具体的房子 集群级存储资源,由管理员或 StorageClass 动态创建
PVC(PersistentVolumeClaim,声明) 租房申请单 用户(应用)声明"我要 10G 读写存储",K8s 给它绑定一个 PV
StorageClass 房源中介规则 定义"什么样的 PV 可以被自动创建"(存储后端、默认大小等)

绑定规则:PVC 申请 10G,就绑定一个容量 ≥10G、读写模式兼容的 PV。PVC 被 Pod 引用后,即使 Pod 删除重建,数据还在(删除 PVC 才释放数据,且要看 reclaimPolicy)。

6.2.1 读写模式(AccessModes)

模式 缩写 含义
ReadWriteOnce RWO 只能被一个节点上的 Pod 读写(最常用;多副本 Pod 必须在同一节点)
ReadOnlyMany ROX 多个节点可读
ReadWriteMany RWX 多个节点可读写(需要 NFS 等支持)
ReadWriteOncePod RWOP 只允许一个 Pod(1.22+)

6.2.2 回收策略(reclaimPolicy)

  • Retain:PVC 删除后 PV 数据保留,需人工清理(数据重要选这个)。
  • Delete:PVC 删除后 PV 及其数据一起删。
  • Recycle:已废弃,别用。

6.3 动手实验 1:用 StorageClass 动态创建存储(推荐方式)

minikube 自带 standard StorageClass(底层是本地路径 local-path 插件)。

6.3.1 先看现有的 StorageClass

kubectl get storageclass
# 输出示例:
# NAME       PROVISIONER            RECLAIMPOLICY   ALLOWVOLUMEEXPANSION   AGE
# standard   rancher.io/local-path  Delete          true                   1d

6.3.2 创建 PVC + 使用它的 Pod

# pvc-demo.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: app-data
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi          # StorageClass 会自动创建 PV 并绑定
  # storageClassName: standard   # 不写则用默认 SC
---
apiVersion: v1
kind: Pod
metadata:
  name: data-demo
spec:
  containers:
    - name: writer
      image: busybox:1.36
      command: ["sh", "-c", "while true; do echo \"$(date) hello\" >> /data/test.log; sleep 2; done"]
      volumeMounts:
        - name: data
          mountPath: /data
  volumes:
    - name: data
      persistentVolumeClaim:
        claimName: app-data
kubectl apply -f pvc-demo.yaml
kubectl get pvc                 # STATUS 应为 Bound
kubectl get pod data-demo -o wide

# 看数据真的写进去了
kubectl exec data-demo -- cat /data/test.log

# 关键实验:删掉 Pod,数据还在吗?
kubectl delete pod data-demo
# 重建(apply 整个文件即可)
kubectl apply -f pvc-demo.yaml
kubectl exec data-demo -- cat /data/test.log
# 之前的内容还在!这就是持久化

6.3.3 扩容 PVC

kubectl patch pvc app-data -p '{"spec":{"resources":{"requests":{"storage":"2Gi"}}}}'
kubectl get pvc
# 很多存储支持在线扩容;local-path 支持

6.4 动手实验 2:手动创建 PV + PVC(理解绑定过程)

# manual-pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-manual
spec:
  capacity:
    storage: 2Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  local:
    path: /tmp/pv-data        # 节点上的本地目录
  nodeAffinity:
    required:
      nodeSelectorTerms:
        - matchExpressions:
            - key: kubernetes.io/hostname
              operator: In
              values:
                - minikube     # 本地 PV 只能被指定节点使用
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-manual
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  volumeName: pv-manual       # 指定绑定这个 PV
# 先在 minikube 节点上准备目录
minikube ssh -- mkdir -p /tmp/pv-data

kubectl apply -f manual-pv.yaml
kubectl get pv pvc
# PV 状态:Bound;PVC 状态:Bound

kubectl delete -f manual-pv.yaml
# PV 变 Released(Retain 策略,数据还在),PVC 删除
kubectl get pv

6.5 动手实验 3:NFS 存储(多节点共享,RWX)

生产中小规模集群常用 NFS。这里演示 YAML 结构(需要一台 NFS 服务器,可用 minikube 自身模拟):

# nfs-pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-nfs
spec:
  capacity:
    storage: 5Gi
  accessModes:
    - ReadWriteMany           # NFS 支持多节点读写
  persistentVolumeReclaimPolicy: Retain
  nfs:
    server: 192.168.1.100     # 你的 NFS 服务器 IP
    path: /exports/k8s
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-nfs
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 5Gi

云环境则用各家的"云盘 StorageClass"(阿里云盘、EBS、PD 等),原理与 local 一样:SC 动态创建云盘 PV。注意云盘一般是 RWO,多副本写要用 NFS 类 RWX 存储或数据库自身的副本方案。

6.6 在 Deployment / StatefulSet 中使用 PVC

6.6.1 Deployment 挂载

# 与 6.3 的 Pod 一样,把 spec 放进 Deployment 的 template 即可
apiVersion: apps/v1
kind: Deployment
metadata:
  name: app-with-data
spec:
  replicas: 1          # RWO 存储多副本要注意:Pod 必须在同一节点
  selector:
    matchLabels:
      app: app-with-data
  template:
    metadata:
      labels:
        app: app-with-data
    spec:
      containers:
        - name: app
          image: busybox:1.36
          command: ["sh", "-c", "sleep 3600"]
          volumeMounts:
            - name: data
              mountPath: /data
      volumes:
        - name: data
          persistentVolumeClaim:
            claimName: app-data

6.6.2 StatefulSet 自动创建 PVC

第 4 章讲过 volumeClaimTemplates:StatefulSet 会为每个副本自动创建独立 PVC(data-xxx-0/1/2),这是数据库集群的标准做法。

6.6.3 emptyDir(临时存储)

Pod 内多容器共享、Pod 删除即清空——第 3 章边车示例用过:

  volumes:
    - name: cache
      emptyDir: {}

6.7 备份与恢复的基本思路

K8s 存储负责"挂载",不负责"备份"。生产备份思路:

# 1) 数据库类:用应用自身工具做逻辑备份,结果写到 RWX 存储或对象存储
kubectl exec -it mysql-0 -- sh -c "mysqldump -uroot -pPASS mydb > /backup/dump.sql"

# 2) 通用方案:起一个一次性 Pod 挂载同一个 PVC 查看/拷贝
kubectl run backup-tmp --rm -it \
  --image=busybox:1.36 \
  -- sh -c "ls /data"

更完整的方案是 Velero(集群级备份工具,含 PV 快照),本书不展开,生产搜索"Velero 教程"即可。

6.8 排障速查

症状 原因/处理
PVC 一直 Pending 没有满足条件的 PV;kubectl describe pvc xxx 看 Events(容量/访问模式/SC 不对)
Pod Pending 且事件有 volume ... failed to bind 同上看 PVC
Pod 里挂载点是空的 检查 mountPath、PV 的 nodeAffinity 是否匹配所在节点
删了 PVC 数据消失 reclaimPolicy: Delete 或 SC 默认 Delete;重要数据用 Retain
多副本起不来 + RWO 存储 RWO 只允许单节点挂载,检查 Pod 分布或改用 RWX

6.9 本章小结

  • 三件套心智模型:PVC 是申请单,PV 是房源,StorageClass 是中介规则。
  • 优先用 StorageClass 动态供给,少手写 PV。
  • RWO 最常用;多节点读写要 RWX(NFS/云 NAS)。
  • 数据在 Pod 之外:删 Pod 数据在,删 PVC 按策略走。
  • 备份是应用/工具的事(mysqldump、Velero),K8s 只负责挂载。

下一章:配置与密钥——让代码和配置分离。

原文链接:第6章 存储,转载请注明来源!

赞 0