第8章:持久存储和卷(Persistent Storage and Volumes)
一、导读
1.1 本章学习目标
容器是临时的,Pod 重启后数据就会丢失。本章讲解 Kubernetes 的持久化存储方案:从最简单的 emptyDir 到网络存储,再到动态供给。你将理解 PV/PVC 的设计哲学、StorageClass 的自动供给机制、以及不同存储方案的适用场景。
本章的核心学习目标包括:
- 理解容器存储的临时性:为什么容器删除后数据丢失。
- 掌握 Volume 的基本类型:emptyDir、hostPath、NFS。
- 理解 PV/PVC 的设计:为什么需要分离存储的提供和使用。
- 使用 StorageClass 实现动态供给:按需自动创建存储。
- 理解访问模式和回收策略:RWO/ROX/RWX、Retain/Delete。
- 为 StatefulSet 配置持久存储:有状态应用的存储管理。
1.2 为什么本章如此重要
数据库、消息队列、文件存储等应用需要持久化数据。如果 Pod 重启后数据丢失,这些应用就无法在 Kubernetes 中运行。理解 K8s 的存储模型,是运行有状态应用的前提。
PV/PVC 的设计体现了 Kubernetes 的解耦思想:集群管理员提供存储资源(PV),用户按需申请(PVC),两者通过 StorageClass 自动匹配。这种模式使得存储管理可以独立于应用部署。
1.3 核心问题引导
为什么需要 PV 和 PVC 两个资源?为什么不直接在 Pod 中定义存储?
PersistentVolume 和 PersistentVolumeClaim 的关系是什么?
StorageClass 的动态供给是如何工作的?
访问模式 ReadWriteOnce 和 ReadOnlyMany 的区别是什么?
Pod 删除后,PV 中的数据会丢失吗?回收策略如何控制?
二、核心概念详解
2.1 容器存储的问题
容器的可写层是临时的:
- Pod 重启 → 可写层保留,数据不丢失
- Pod 删除重建 → 可写层丢失,数据丢失
- 多容器共享数据 → 需要共享存储
2.2 Volume 基础类型
2.2.1 emptyDir
volumes:
- name: cache
emptyDir:
sizeLimit: 1Gi
# medium: Memory # 使用 tmpfs- Pod 生命周期内存在
- Pod 删除时数据丢失
- 适合临时缓存、容器间共享数据
2.2.2 hostPath
volumes:
- name: data
hostPath:
path: /data
type: DirectoryOrCreate- 使用节点文件系统
- 节点数据丢失则 Pod 数据丢失
- 安全风险:可以访问节点文件
- 适合 DaemonSet 访问节点日志
2.2.3 NFS
volumes:
- name: nfs-data
nfs:
server: nfs.example.com
path: /exports/data- 网络存储,多 Pod 共享
- 不依赖节点文件系统
- 需要 NFS 服务器
2.3 PersistentVolume 和 PersistentVolumeClaim
PV/PVC 的设计将存储的提供和使用分离:
- PersistentVolume(PV):集群级存储资源,由管理员创建或动态供给
- PersistentVolumeClaim(PVC):用户的存储请求,由系统匹配 PV
# PersistentVolume
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-nfs-01
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
storageClassName: slow
nfs:
server: nfs.example.com
path: /exports/data-01
---
# PersistentVolumeClaim
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: data-pvc
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 5Gi
storageClassName: slow2.3.1 访问模式
| 模式 | 缩写 | 说明 |
|---|---|---|
| ReadWriteOnce | RWO | 单节点读写 |
| ReadOnlyMany | ROX | 多节点只读 |
| ReadWriteMany | RWX | 多节点读写 |
| ReadWriteOncePod | RWOP | 单 Pod 读写 |
2.3.2 回收策略
- Retain:PVC 删除后,PV 保留数据(手动清理)
- Delete:PVC 删除后,PV 和数据一起删除
- Recycle:已废弃
2.3.3 PV 的状态
- Available:可用,未被 PVC 绑定
- Bound:已绑定到 PVC
- Released:PVC 已删除,等待回收
- Failed:回收失败
2.4 StorageClass 动态供给
StorageClass 定义存储的"类别",实现按需自动创建 PV。
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast-ssd
provisioner: kubernetes.io/gce-pd
parameters:
type: pd-ssd
fsType: ext4
reclaimPolicy: Delete
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true# 使用 StorageClass 的 PVC
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: dynamic-pvc
spec:
accessModes:
- ReadWriteOnce
storageClassName: fast-ssd
resources:
requests:
storage: 20Gi当 PVC 创建时:
系统查找匹配的 StorageClass
StorageClass 的 provisioner 自动创建 PV
PV 自动绑定到 PVC
常见的 provisioner:
- kubernetes.io/gce-pd:Google Cloud Persistent Disk
- kubernetes.io/aws-ebs:AWS EBS
- kubernetes.io/azure-disk:Azure Disk
- nfs-subdir-external-provisioner:NFS 动态供给
2.5 在 Pod 中使用 PVC
apiVersion: v1
kind: Pod
metadata:
name: app-with-storage
spec:
containers:
- name: app
image: myapp:1.0
volumeMounts:
- name: data
mountPath: /data
volumes:
- name: data
persistentVolumeClaim:
claimName: data-pvc2.6 StatefulSet 的存储
StatefulSet 为每个 Pod 创建独立的 PVC,Pod 删除后 PVC 保留。
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: database
spec:
serviceName: db-headless
replicas: 3
selector:
matchLabels:
app: database
template:
metadata:
labels:
app: database
spec:
containers:
- name: postgres
image: postgres:15
volumeMounts:
- name: data
mountPath: /var/lib/postgresql/data
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: fast-ssd
resources:
requests:
storage: 50GiStatefulSet 创建的 PVC 命名规则:<volumeClaimTemplate-name>-<statefulset-name>-<ordinal>
data-database-0data-database-1data-database-2
三、实战案例
3.1 使用 NFS 存储
# PV
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-nfs
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
nfs:
server: 192.168.1.100
path: /exports/shared
---
# PVC
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: shared-data
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 5Gi
---
# Pod 使用 PVC
apiVersion: v1
kind: Pod
metadata:
name: app
spec:
containers:
- name: app
image: myapp:1.0
volumeMounts:
- name: data
mountPath: /data
volumes:
- name: data
persistentVolumeClaim:
claimName: shared-data3.2 数据库持久化
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: postgres
spec:
serviceName: postgres-headless
replicas: 3
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres:15
env:
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: postgres-secret
key: password
volumeMounts:
- name: data
mountPath: /var/lib/postgresql/data
resources:
requests:
cpu: 500m
memory: 1Gi
limits:
cpu: 2000m
memory: 4Gi
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: fast-ssd
resources:
requests:
storage: 100Gi四、本章小结
容器存储是临时的,Pod 删除后数据丢失。
PV/PVC 分离了存储的提供和使用,实现解耦管理。
StorageClass 实现动态供给,按需自动创建存储。
访问模式决定了存储的共享方式:RWO、ROX、RWX。
StatefulSet 为每个 Pod 创建独立的 PVC,适合有状态应用。
五、思考与练习
思考题
如果 PVC 找不到匹配的 PV,会发生什么?
StorageClass 的 volumeBindingMode: WaitForFirstConsumer 有什么作用?
为什么 StatefulSet 需要 headless Service?
如何备份 PVC 中的数据?
动态供给的 PV 被删除后,数据会丢失吗?如何防止误删?
练习题
使用 NFS 创建 PV/PVC,挂载到 Pod 中验证数据持久化。
配置 StorageClass 实现动态供给,创建 PVC 观察自动创建的 PV。
创建 StatefulSet 运行数据库,验证 Pod 重建后数据保留。
练习 PVC 的扩容:修改 PVC 的 storage 大小,观察是否生效。