前言
在Kubernetes集群中,容器的存储默认是临时的,当Pod重启或删除时,容器内的数据会丢失。对于数据库、日志系统、文件存储等需要持久化数据的应用场景,我们需要使用Kubernetes提供的持久化存储机制。PersistentVolumeClaim(PVC)是Kubernetes存储架构中的核心抽象,它将存储的使用者(Pod)和存储的提供者(PersistentVolume)解耦,使得应用开发者无需关心底层存储的具体实现细节。
PVC是什么?
PersistentVolumeClaim(PVC)是用户对存储资源的请求声明。它类似于Pod对计算资源(CPU、内存)的请求,但PVC请求的是存储资源。通过PVC,用户可以声明所需的存储大小、访问模式等需求,而无需关心存储的具体实现。
Kubernetes存储架构
Kubernetes的存储系统由三个核心概念组成:
| 概念 | 说明 |
|---|---|
PV (PersistentVolume) | 集群中的一块存储资源,由管理员手动创建或通过StorageClass动态创建。PV是集群级别的资源,独立于Pod的生命周期。 |
PVC (PersistentVolumeClaim) | 用户对存储资源的申请。PVC会自动绑定到满足条件的PV上。 |
StorageClass | 定义了存储的类别和动态供应的参数,允许用户在创建PVC时自动创建对应的PV。 |
PVC解决了什么问题?
在传统容器部署中,直接使用volume挂载存储存在以下问题:
痛点案例1:存储配置与应用耦合
场景:某团队开发了一个微服务应用,需要在开发、测试、生产环境分别部署。
问题:
- 不同环境使用不同的存储后端(开发用本地存储,测试用
NFS,生产用云存储) - 每个环境都需要修改
Pod配置中的volume定义 - 应用开发者需要了解每个环境的存储细节
- 迁移环境时需要大量修改配置文件
解决方案:
- 使用
PVC抽象存储需求,应用只声明需要10Gi存储和读写模式 - 不同环境的集群管理员配置各自的
StorageClass - 应用配置在各环境保持一致,存储后端由
PVC自动适配
痛点案例2:存储资源的复用和管理
场景:运维团队需要管理大量的存储资源,多个应用共享存储系统。
问题:
- 手动为每个应用分配存储空间,管理复杂
- 存储空间难以复用,删除应用后存储资源闲置
- 缺乏统一的存储配额和权限管理
- 存储扩容需要修改每个使用存储的
Pod配置
解决方案:
- 使用
PV池管理存储资源,支持动态分配和回收 - 通过
PVC的生命周期管理实现存储的自动回收或保留 - 使用
StorageClass实现存储配额、性能等级的分类管理 - 支持
PVC扩容,无需修改Pod配置
痛点案例3:有状态应用的部署
场景:部署MySQL数据库集群,每个实例需要独立的持久化存储。
问题:
- 每个数据库实例需要独立且持久的数据目录
Pod重启后必须绑定到原来的存储,避免数据丢失- 数据库扩容时需要自动创建新的存储卷
- 删除数据库实例时需要保留数据以便恢复
解决方案:
- 使用
StatefulSet配合volumeClaimTemplates自动为每个副本创建PVC PVC与Pod名称绑定,保证重启后存储的一致性StorageClass的reclaimPolicy设置为Retain保留数据- 支持数据库集群的弹性伸缩
PVC基础使用
访问模式
PVC支持以下访问模式(AccessModes):
| 访问模式 | 缩写 | 说明 |
|---|---|---|
ReadWriteOnce | RWO | 卷可以被单个节点以读写方式挂载。最常见,适用于大多数块存储。 |
ReadOnlyMany | ROX | 卷可以被多个节点以只读方式挂载。适用于共享配置文件、静态资源等场景。 |
ReadWriteMany | RWX | 卷可以被多个节点以读写方式挂载。需要存储系统支持,如NFS、CephFS等。 |
ReadWriteOncePod | RWOP | 卷只能被单个Pod以读写方式挂载(Kubernetes 1.22+特性)。 |
简单的Pod使用PVC示例
以下是创建PVC并在Pod中使用的基本示例:
1. 创建PersistentVolume
首先创建一个PV(在使用动态供应前,通常需要管理员预先创建):
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-local-storage
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: local-storage # 可选
hostPath:
path: /data/k8s-storage
type: DirectoryOrCreate
2. 创建PersistentVolumeClaim
在被Pod/Deployment使用前,用户需要创建对应的PVC申请存储:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-app-data
namespace: default
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
storageClassName: local-storage
说明:
accessModes:必须与可用PV的访问模式匹配resources.requests.storage:请求的存储空间大小storageClassName:指定存储类,如果不指定则使用默认StorageClass
3. 在Pod中使用PVC
创建Pod并挂载PVC:
apiVersion: v1
kind: Pod
metadata:
name: app-with-storage
namespace: default
spec:
containers:
- name: app
image: nginx:latest
volumeMounts:
- name: data-volume
mountPath: /usr/share/nginx/html
volumes:
- name: data-volume
persistentVolumeClaim:
claimName: pvc-app-data
4. 验证存储挂载
# 查看PVC状态
kubectl get pvc pvc-app-data
# 输出示例
# NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
# pvc-app-data Bound pv-local-storage 10Gi RWO local-storage 2m
# 进入Pod验证挂载
kubectl exec -it app-with-storage -- df -h /usr/share/nginx/html
网络存储协议与使用
Kubernetes支持多种网络存储协议,可以将远程存储挂载到Pod中。以下介绍常见的网络存储协议及其使用方式。
NFS协议
NFS(Network File System)是最常用的网络文件系统协议,支持ReadWriteMany访问模式,允许多个Pod同时读写。
NFS服务端配置
假设你已经有一个NFS服务器nfs.team.local,导出目录为/hpc。如果需要搭建NFS服务器,可以参考以下步骤:
# 在NFS服务器上安装nfs-server(Ubuntu/Debian)
apt-get update
apt-get install -y nfs-kernel-server
# 创建共享目录
mkdir -p /data/nfs-share
chmod 777 /data/nfs-share
# 配置NFS导出
cat >> /etc/exports <<EOF
/data/nfs-share *(rw,sync,no_subtree_check,no_root_squash)
EOF
# 重启NFS服务
exportfs -ra
systemctl restart nfs-kernel-server
直接使用NFS卷
最简单的方式是直接在Pod中定义NFS卷:
apiVersion: v1
kind: Pod
metadata:
name: nfs-pod-direct
spec:
containers:
- name: app
image: nginx:latest
volumeMounts:
- name: nfs-volume
mountPath: /data/hpc
volumes:
- name: nfs-volume
nfs:
server: nfs.team.local
path: /hpc
readOnly: false
通过PV/PVC使用NFS
推荐的方式是通过PV和PVC使用NFS存储:
1. 创建NFS PersistentVolume:
类似的mount命令为:
sudo mount -t nfs -o rw,relatime,vers=3,rsize=1048576,wsize=1048576,hard,nolock,proto=tcp nfs.team.local:/hpc /data/hpc
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-nfs-hpc
spec:
capacity:
storage: 100Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
storageClassName: nfs-storage
mountOptions:
- vers=3
- rsize=1048576
- wsize=1048576
- hard
- nolock
- proto=tcp
nfs:
server: nfs.team.local
path: /hpc
说明:
mountOptions:对应命令mount -o的参数vers=3:使用NFSv3协议rsize=1048576:读缓冲区大小为1MBwsize=1048576:写缓冲区大小为1MBhard:硬挂载,网络中断时会重试nolock:不使用NFS文件锁proto=tcp:使用TCP协议
2. 创建PVC:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-nfs-hpc
namespace: default
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 50Gi
storageClassName: nfs-storage
3. 在Deployment中使用:
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app-with-nfs
namespace: default
spec:
replicas: 3
selector:
matchLabels:
app: web-app
template:
metadata:
labels:
app: web-app
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
volumeMounts:
- name: shared-data
mountPath: /usr/share/nginx/html
volumes:
- name: shared-data
persistentVolumeClaim:
claimName: pvc-nfs-hpc
由于NFS支持ReadWriteMany,这个Deployment的3个副本可以同时读写同一个存储卷。
CIFS/SMB协议
CIFS(Common Internet File System)也称为SMB(Server Message Block),主要用于Windows文件共享,也可以在Linux系统中使用。
使用CIFS卷示例
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-cifs-share
spec:
capacity:
storage: 50Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
storageClassName: cifs-storage
mountOptions:
- vers=3.0
- dir_mode=0777
- file_mode=0777
csi:
driver: smb.csi.k8s.io
volumeHandle: smb-server.example.com/share
volumeAttributes:
source: "//smb-server.example.com/share"
nodeStageSecretRef:
name: cifs-secret
namespace: default
---
apiVersion: v1
kind: Secret
metadata:
name: cifs-secret
namespace: default
type: Opaque
stringData:
username: "myuser"
password: "mypassword"
注意:从Kubernetes 1.26开始,内置的CIFS卷插件已被废弃,推荐使用CSI驱动(如csi-driver-smb)。
iSCSI协议
iSCSI(Internet Small Computer System Interface)是基于TCP/IP的块存储协议,将SCSI命令封装在IP包中传输。
iSCSI存储示例
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-iscsi-storage
spec:
capacity:
storage: 20Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: iscsi-storage
iscsi:
targetPortal: 192.168.1.100:3260
iqn: iqn.2021-01.com.example:storage.target01
lun: 0
fsType: ext4
readOnly: false
说明:
targetPortal:iSCSI Target的IP地址和端口iqn:iSCSI限定名称,标识存储目标lun:逻辑单元号fsType:文件系统类型
注意:使用iSCSI前,需要在Kubernetes节点上安装iscsi-initiator-utils:
# CentOS/RHEL
yum install -y iscsi-initiator-utils
# Ubuntu/Debian
apt-get install -y open-iscsi
网络存储协议对比
| 协议 | 类型 | 访问模式 | 性能 | 适用场景 |
|---|---|---|---|---|
NFS | 文件系统 | RWX | 中等 | 共享文件存储、日志收集、静态资源 |
CIFS/SMB | 文件系统 | RWX | 中等 | Windows环境、办公文件共享 |
iSCSI | 块存储 | RWO | 高 | 数据库、高性能应用 |
Ceph RBD | 块存储 | RWO | 高 | 云原生存储、大规模集群 |
CephFS | 文件系统 | RWX | 中高 | 分布式文件存储 |
StorageClass动态存储供应
StorageClass提供了一种描述存储类别的方式,允许管理员定义不同类型的存储服务。使用StorageClass可以实现存储的动态供应,当用户创建PVC时自动创建对应的PV。
StorageClass工作原理
NFS动态存储供应
NFS本身不支持动态供应,需要使用外部供应器(如nfs-subdir-external-provisioner)。
1. 部署NFS Provisioner
apiVersion: v1
kind: ServiceAccount
metadata:
name: nfs-client-provisioner
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: nfs-client-provisioner-runner
rules:
- apiGroups: [""]
resources: ["persistentvolumes"]
verbs: ["get", "list", "watch", "create", "delete"]
- apiGroups: [""]
resources: ["persistentvolumeclaims"]
verbs: ["get", "list", "watch", "update"]
- apiGroups: ["storage.k8s.io"]
resources: ["storageclasses"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["events"]
verbs: ["create", "update", "patch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: run-nfs-client-provisioner
subjects:
- kind: ServiceAccount
name: nfs-client-provisioner
namespace: kube-system
roleRef:
kind: ClusterRole
name: nfs-client-provisioner-runner
apiGroup: rbac.authorization.k8s.io
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nfs-client-provisioner
namespace: kube-system
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: nfs-client-provisioner
template:
metadata:
labels:
app: nfs-client-provisioner
spec:
serviceAccountName: nfs-client-provisioner
containers:
- name: nfs-client-provisioner
image: registry.k8s.io/sig-storage/nfs-subdir-external-provisioner:v4.0.2
volumeMounts:
- name: nfs-client-root
mountPath: /persistentvolumes
env:
- name: PROVISIONER_NAME
value: k8s-sigs.io/nfs-subdir-external-provisioner
- name: NFS_SERVER
value: nfs.team.local
- name: NFS_PATH
value: /hpc
volumes:
- name: nfs-client-root
nfs:
server: nfs.team.local
path: /hpc
2. 创建StorageClass
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: nfs-client
annotations:
storageclass.kubernetes.io/is-default-class: "true"
provisioner: k8s-sigs.io/nfs-subdir-external-provisioner
parameters:
archiveOnDelete: "true"
pathPattern: "${.PVC.namespace}-${.PVC.name}"
mountOptions:
- vers=3
- rsize=1048576
- wsize=1048576
- hard
- nolock
- proto=tcp
reclaimPolicy: Delete
volumeBindingMode: Immediate
说明:
provisioner:供应器名称,必须与Deployment中的PROVISIONER_NAME一致parameters.archiveOnDelete:删除PVC时是否归档数据parameters.pathPattern:子目录命名模式reclaimPolicy:回收策略,Delete或RetainvolumeBindingMode:绑定模式,Immediate(立即绑定)或WaitForFirstConsumer(等待首次使用)
3. 使用动态供应创建PVC
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-nfs-dynamic
namespace: default
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 10Gi
storageClassName: nfs-client
此时无需手动创建PV,nfs-client-provisioner会自动创建。
云存储动态供应
云服务商通常提供了CSI驱动支持动态供应,以阿里云为例:
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: alicloud-disk-ssd
provisioner: diskplugin.csi.alibabacloud.com
parameters:
type: cloud_ssd
regionId: cn-hangzhou
zoneId: cn-hangzhou-b
fsType: ext4
reclaimPolicy: Delete
allowVolumeExpansion: true
volumeBindingMode: WaitForFirstConsumer
StorageClass参数详解
| 参数 | 说明 | 可选值 |
|---|---|---|
| provisioner | 存储供应器 | 内置或外部供应器名称 |
| parameters | 供应器特定参数 | 依供应器而定 |
| reclaimPolicy | 回收策略 | Delete(删除)、Retain(保留) |
| allowVolumeExpansion | 是否允许扩容 | true、false |
| volumeBindingMode | 绑定模式 | Immediate、WaitForFirstConsumer |
| mountOptions | 挂载选项 | 文件系统挂载参数 |
Deployment使用PVC
Deployment适用于无状态应用,所有副本共享同一个PVC(需要存储支持ReadWriteMany)或每个副本使用独立的PVC(ReadWriteOnce)。
场景1:共享存储(ReadWriteMany)
多个副本共享同一份数据,适合静态资源服务器、日志收集等场景:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: shared-pvc
namespace: default
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 20Gi
storageClassName: nfs-client
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-server
namespace: default
spec:
replicas: 3
selector:
matchLabels:
app: web-server
template:
metadata:
labels:
app: web-server
spec:
containers:
- name: nginx
image: nginx:1.25
ports:
- containerPort: 80
volumeMounts:
- name: html-data
mountPath: /usr/share/nginx/html
volumes:
- name: html-data
persistentVolumeClaim:
claimName: shared-pvc
场景2:独立存储(ReadWriteOnce)
每个副本需要独立存储,但使用Deployment管理。需要为每个副本创建独立的PVC:
# 需要手动创建多个PVC
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: app-pvc-0
namespace: default
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
storageClassName: local-storage
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: app-pvc-1
namespace: default
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
storageClassName: local-storage
---
# 使用不同的Deployment分别挂载
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-instance-0
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: myapp
instance: "0"
template:
metadata:
labels:
app: myapp
instance: "0"
spec:
containers:
- name: app
image: myapp:latest
volumeMounts:
- name: data
mountPath: /data
volumes:
- name: data
persistentVolumeClaim:
claimName: app-pvc-0
注意:对于需要独立存储的有状态应用,推荐使用StatefulSet而不是Deployment。
StatefulSet使用PVC
StatefulSet专为有状态应用设计,提供了volumeClaimTemplates特性,可以为每个副本自动创建独立的PVC。
StatefulSet的存储特性
- 稳定的存储标识:每个
Pod的PVC名称固定为<volumeClaimTemplate名称>-<StatefulSet名称>-<序号> - 顺序创建和删除:
Pod和PVC按序号顺序创建 - 持久化保留:删除
StatefulSet时默认不会删除PVC,数据得以保留 - 扩缩容支持:扩容时自动创建新的
PVC,缩容时保留PVC供后续恢复使用
MySQL集群示例
以下是一个使用StatefulSet部署MySQL集群的完整示例:
apiVersion: v1
kind: Service
metadata:
name: mysql
namespace: default
labels:
app: mysql
spec:
ports:
- port: 3306
name: mysql
clusterIP: None
selector:
app: mysql
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
namespace: default
spec:
serviceName: mysql
replicas: 3
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:8.0
ports:
- containerPort: 3306
name: mysql
env:
- name: MYSQL_ROOT_PASSWORD
value: "MySecurePassword123"
- name: MYSQL_DATABASE
value: "myapp"
volumeMounts:
- name: mysql-data
mountPath: /var/lib/mysql
subPath: mysql
- name: mysql-conf
mountPath: /etc/mysql/conf.d
volumes:
- name: mysql-conf
configMap:
name: mysql-config
volumeClaimTemplates:
- metadata:
name: mysql-data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 50Gi
storageClassName: nfs-client
---
apiVersion: v1
kind: ConfigMap
metadata:
name: mysql-config
namespace: default
data:
my.cnf: |
[mysqld]
default-storage-engine=INNODB
character-set-server=utf8mb4
max_connections=1000
innodb_buffer_pool_size=1G
部署后会创建3个Pod和3个PVC:
Pod:mysql-0、mysql-1、mysql-2PVC:mysql-data-mysql-0、mysql-data-mysql-1、mysql-data-mysql-2
每个MySQL实例都有独立的持久化存储,即使Pod重启也能保留数据。
Redis集群示例
apiVersion: v1
kind: ConfigMap
metadata:
name: redis-config
namespace: default
data:
redis.conf: |
port 6379
cluster-enabled yes
cluster-config-file /data/nodes.conf
cluster-node-timeout 5000
appendonly yes
dir /data
---
apiVersion: v1
kind: Service
metadata:
name: redis-cluster
namespace: default
spec:
ports:
- port: 6379
targetPort: 6379
name: client
- port: 16379
targetPort: 16379
name: gossip
clusterIP: None
selector:
app: redis-cluster
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: redis-cluster
namespace: default
spec:
serviceName: redis-cluster
replicas: 6
selector:
matchLabels:
app: redis-cluster
template:
metadata:
labels:
app: redis-cluster
spec:
containers:
- name: redis
image: redis:7.0-alpine
ports:
- containerPort: 6379
name: client
- containerPort: 16379
name: gossip
command:
- redis-server
args:
- /conf/redis.conf
volumeMounts:
- name: redis-data
mountPath: /data
- name: redis-conf
mountPath: /conf
volumes:
- name: redis-conf
configMap:
name: redis-config
volumeClaimTemplates:
- metadata:
name: redis-data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 20Gi
storageClassName: nfs-client
PostgreSQL集群示例
apiVersion: v1
kind: Service
metadata:
name: postgres
namespace: default
spec:
ports:
- port: 5432
name: postgres
clusterIP: None
selector:
app: postgres
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: postgres
namespace: default
spec:
serviceName: postgres
replicas: 3
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres:15-alpine
ports:
- containerPort: 5432
name: postgres
env:
- name: POSTGRES_PASSWORD
value: "SecurePassword456"
- name: POSTGRES_DB
value: "myapp"
- name: PGDATA
value: /var/lib/postgresql/data/pgdata
volumeMounts:
- name: postgres-data
mountPath: /var/lib/postgresql/data
resources:
requests:
cpu: 500m
memory: 1Gi
limits:
cpu: 1000m
memory: 2Gi
volumeClaimTemplates:
- metadata:
name: postgres-data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 100Gi
storageClassName: nfs-client
存储最佳实践
1. 选择合适的访问模式
- RWO(ReadWriteOnce):适用于数据库、块存储,单节点读写
- ROX(ReadOnlyMany):适用于配置文件、静态资源的分发
- RWX(ReadWriteMany):适用于共享文件系统、多节点协作
2. 合理设置回收策略
- Retain(保留):用于重要数据,删除
PVC后PV和数据保留,需要手动清理 - Delete(删除):用于临时数据或可重建的数据,删除
PVC时自动清理PV和底层存储
3. 使用StorageClass分类管理
为不同性能要求的应用创建不同的StorageClass:
# 高性能SSD存储
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast-ssd
provisioner: kubernetes.io/aws-ebs
parameters:
type: io2
iopsPerGB: "50"
reclaimPolicy: Delete
---
# 标准HDD存储
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: standard-hdd
provisioner: kubernetes.io/aws-ebs
parameters:
type: sc1
reclaimPolicy: Delete
4. 启用存储扩容
在StorageClass中启用卷扩容:
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: expandable-storage
provisioner: kubernetes.io/aws-ebs
allowVolumeExpansion: true
扩容PVC:
# 编辑PVC增加storage大小
kubectl edit pvc my-pvc
# 或使用patch命令
kubectl patch pvc my-pvc -p '{"spec":{"resources":{"requests":{"storage":"100Gi"}}}}'
5. 使用volumeBindingMode优化调度
对于拓扑感知的存储(如本地存储、云盘),使用WaitForFirstConsumer模式:
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: local-storage
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer
这样可以确保PV在Pod调度到节点后再绑定,避免Pod因存储位置不匹配而无法调度。
6. 监控存储使用情况
# 查看PVC使用情况
kubectl get pvc -A
# 查看PV列表
kubectl get pv
# 查看StorageClass
kubectl get storageclass
# 查看Pod的存储挂载
kubectl describe pod <pod-name>
7. 数据备份策略
对于重要数据,建议:
- 使用
VolumeSnapshot进行定期快照 - 将数据同步到对象存储(如
S3) - 使用专业备份工具(如
Velero)
常见问题排查
1. PVC一直处于Pending状态
原因:
- 没有匹配的
PV可用 StorageClass不存在或配置错误- 存储配额已满
排查方法:
# 查看PVC详情
kubectl describe pvc <pvc-name>
# 查看事件
kubectl get events --sort-by='.lastTimestamp'
# 检查StorageClass
kubectl get storageclass
2. Pod无法挂载PVC
原因:
- 节点缺少必要的存储客户端(如
nfs-common、open-iscsi) - 存储服务器网络不通
- 权限配置错误
排查方法:
# 查看Pod事件
kubectl describe pod <pod-name>
# 在节点上测试挂载
mount -t nfs nfs.team.local:/hpc /mnt/test
# 查看kubelet日志
journalctl -u kubelet -f
3. 存储性能问题
优化建议:
- 调整
NFS的rsize、wsize参数 - 使用
SSD替代HDD - 启用缓存机制
- 考虑使用分布式存储系统
4. 删除Namespace时PVC无法删除
原因:PVC可能被保护或存在Finalizer
解决方法:
# 查看PVC的finalizers
kubectl get pvc <pvc-name> -o yaml | grep finalizers -A 5
# 移除finalizers
kubectl patch pvc <pvc-name> -p '{"metadata":{"finalizers":null}}'
总结
PersistentVolumeClaim(PVC)是Kubernetes存储架构的核心抽象,通过将存储的使用和供应解耦,极大地简化了应用的存储管理。本文详细介绍了:
- PVC基础概念:
PV、PVC、StorageClass三者的关系和作用 - 网络存储协议:
NFS、CIFS、iSCSI等协议的使用方法和配置示例 - 动态存储供应:通过
StorageClass实现存储的自动创建和管理 - 实际应用场景:
Deployment和StatefulSet中使用PVC的完整示例 - 最佳实践:存储选型、配置优化、监控和故障排查
通过合理使用PVC和StorageClass,可以实现灵活、可靠、高效的持久化存储方案,满足各类有状态应用的需求。无论是数据库、缓存、文件系统还是日志存储,Kubernetes的存储系统都能提供良好的支持。