背景
K8s中的插件local-path-provisioner能够动态创建PV,使用local-pathPV时,无需手动建立PV,只需建立一个PVC,在其中指定storageClassName: local-path即可,插件会自动为其分配PV和存储目录
这个插件的好处是:
- 用户无需手动配置PV,只需创建PVC绑定即可;
- 在非文件共享的场景下,用户也无需关心服务Pod被调度至哪个节点,因为插件会自动根据Pod所在节点分配存储区
现在我误删了所有PV,结果是执行kubectl get pv命令时,看到了状态是Terminating的PV,这说明PV处于正在被删除的状态
但当我执行命令kubectl get pvc -n authen查看PVC时,却发现绑定了PV的PVC是正常Bound的状态
原因分析
之所以被删除时的PV表现为Terminating状态而非直接消失,是因为该PV存在**finalizer字段**,这个字段的作用就是在集群删除PV时,不立刻清除PV,而是将其标记起来,呈现Terminating状态,这是为了确保一些重要的清理动作在资源被物理删除前完成,此时,PVC仍然是可用的
我现在存在两个Terminating状态的PV,它们各自的名称是:
pvc-539fd3b7-12ed-488b-9c86-59e07de28b05
pvc-63a03d24-f078-43af-ae6c-0ce2328ba54e
查看其中一个PV的信息:
master@master:~/authen/pvc$ kubectl get pv pvc-539fd3b7-12ed-488b-9c86-59e07de28b05 -o yaml
apiVersion: v1
kind: PersistentVolume
metadata:
annotations:
local.path.provisioner/selected-node: master
pv.kubernetes.io/provisioned-by: rancher.io/local-path
creationTimestamp: "2025-10-10T07:46:40Z"
deletionGracePeriodSeconds: 0
deletionTimestamp: "2025-10-22T06:34:12Z"
finalizers:
- kubernetes.io/pv-protection
name: pvc-539fd3b7-12ed-488b-9c86-59e07de28b05
resourceVersion: "30449839"
uid: bea50dbb-7f29-4e21-bb22-d9e61ce13cec
spec:
accessModes:
- ReadWriteOnce
capacity:
storage: 10Gi
claimRef:
apiVersion: v1
kind: PersistentVolumeClaim
name: ldap-config-pvc
namespace: authen
resourceVersion: "27503744"
uid: 539fd3b7-12ed-488b-9c86-59e07de28b05
hostPath:
path: /opt/local-path-provisioner/pvc-539fd3b7-12ed-488b-9c86-59e07de28b05_authen_ldap-config-pvc
type: DirectoryOrCreate
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- master
persistentVolumeReclaimPolicy: Delete
storageClassName: local-path
volumeMode: Filesystem
status:
phase: Bound
可以看到,在metadata下确实存在finalizers字段,正是它导致了PV始终处于Terminating状态
解决
整体思路:删除指令已经下达,所以只能先使删除成功完成,接着重建PV和PVC
删除PV和失效PVC
通过删除PV的finalizer字段,完成对该PV的删除动作:
kubectl patch pv pvc-539fd3b7-12ed-488b-9c86-59e07de28b05 -p '{"metadata":{"finalizers":null}}'
# 或可手动编辑:kubectl edit pv pvc-539fd3b7-12ed-488b-9c86-59e07de28b05
执行后可使用kubectl get pv检查,确认该PV已被删除
使用相同的方法删除另一个PV:pvc-63a03d24-f078-43af-ae6c-0ce2328ba54e
完成后,查看PVC,可能会看到先前的PVC已是Lost状态:
master@master:~/authen/pvc$ kubectl get pvc -n authen
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
ldap-config-pvc Lost pvc-539fd3b7-12ed-488b-9c86-59e07de28b05 0 local-path 12d
ldap-data-pvc Lost pvc-63a03d24-f078-43af-ae6c-0ce2328ba54e 0 local-path 12d
此时再将这两个失效的PVC清除,类似于删除PV,同样需要删去finalizer字段,以删除ldap-data-pvc为例:
master@master:~/authen/pvc$ kubectl delete pvc ldap-data-pvc -n authen --force --grace-period=0
master@master:~/authen/pvc$ kubectl patch pvc ldap-data-pvc -n authen -p '{"metadata":{"finalizers":null}}'
Pending状态,则可以无需删除PVC,这是因为此时PVC处于等待PV的状态,只要重建PV,就可以立刻进行绑定,从而恢复正常重建PV
当我完成全部的修复工作后,发现似乎从理论上来说不需要重建PV这一步,这是因为只要local-path-provisioner插件仍在运行,只需要重新创建一次PVC,需要的PV就会自动生成了。但我仍然将重建PV这一步写在这里,主要有两个原因:
- 我原本的恢复步骤就是这样做的
- 似乎不能保证自动生成的新PV仍然指向原本的节点或目录,如果指向了新的节点或目录,可能导致服务数据丢失
通过手动创建指向原目录的PV重建删除的PV,以重建pvc-539fd3b7-12ed-488b-9c86-59e07de28b05为例,这个PV原本指向master节点的/opt/local-path-provisioner/pvc-539fd3b7-12ed-488b-9c86-59e07de28b05目录,绑定至名为ldap-config-pvc的PVC,命名空间为authen:
创建reclaim.yaml:
apiVersion: v1
kind: PersistentVolume
metadata:
name: pvc-539fd3b7-12ed-488b-9c86-59e07de28b05 # 酌情修改
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
storageClassName: local-path
persistentVolumeReclaimPolicy: Delete
local:
path: /opt/local-path-provisioner/pvc-539fd3b7-12ed-488b-9c86-59e07de28b05 # 酌情修改
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- master # 酌情修改
claimRef:
namespace: authen # 酌情修改
name: ldap-config-pvc # 酌情修改
然后执行kubectl apply -f reclaim.yaml即可,执行kubectl get pv查看是否已经重建,此时的PV还没有绑定PVC,所以状态应该是Available
相同的操作,重建pvc-63a03d24-f078-43af-ae6c-0ce2328ba54e这个PV
都完成后,应该能看到两个PV都是Available的状态
重建PVC
重新执行初次建立PVC时的yaml文件即可,例如:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: ldap-data-pvc
namespace: authen
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
storageClassName: local-path
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: ldap-config-pvc
namespace: authen
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
storageClassName: local-path
如此一来,查看PVC状态可以看到Bound状态的PVC:
master@master:~/authen/pvc$ kubectl get pvc -n authen
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
ldap-config-pvc Bound pvc-539fd3b7-12ed-488b-9c86-59e07de28b05 10Gi RWO local-path 2s
ldap-data-pvc Bound pvc-63a03d24-f078-43af-ae6c-0ce2328ba54e 10Gi RWO local-path 3s
此时,重建的两个PV的状态也应从Available变为Bound,修复完成