资讯动态

Kubernetes 存储实战:PVC 动态供给与 Pod 重启后数据丢失排查

发布时间:2026/8/3 18:05:14 来源:尧图企业网站定制
Kubernetes 存储实战:PVC 动态供给与 Pod 重启后数据丢失排查写数据库、写上传文件、写缓存,只要 Pod 里的进程往磁盘写东西,你迟早会撞上同一个问题:Pod 一重启,数据全没了。很多人第一反应是「加个 volume 不就行了」,结果加了emptyDir发现重启照样丢,或者用了 PVC 却发现多副本时数据对不上。这篇把动态供给讲清楚,再把「重启后数据丢失」这个最高频的坑逐层拆开。先搞清楚:emptyDir 为什么救不了你新手最容易写出这样的 volume:apiVersion:v1kind:Podmetadata:name:demospec:containers:-name:appimage:busyboxcommand:[sh,-c,echo hi /data/log.txt; sleep 3600]volumeMounts:-name:cachemountPath:/datavolumes:-name:cacheemptyDir:{}# 生命周期绑定 Pod,Pod 没了目录就没了emptyDir的生命周期和 Pod 绑定。容器崩溃重启(同一个 Pod 内)数据还在,但 Pod 被删除重建(滚动更新、节点驱逐、kubectl delete pod)时,emptyDir会被清空。它的定位是「临时暂存」,不是持久化。要跨 Pod 生命周期保留数据,必须用PersistentVolume(PV)。动态供给:别再手写 PV早期用法是管理员先手动创建一堆 PV,用户再用 PVC 去认领。规模一大就崩溃。现在的标准做法是动态供给:你只声明「我要多大、什么类型」,StorageClass背后的 provisioner 自动帮你开一块盘。先看集群有哪些 StorageClass:kubectl get storageclass# NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE# standard (default) k8s.io/minikube-hostpath Delete Immediate# gp3 ebs.csi.aws.com Delete WaitForFirstConsumer带(default)的是默认类。写 PVC 时不指定storageClassName就用它:apiVersion:v1kind:PersistentVolumeClaimmetadata:name:data-pvcspec:accessModes:-ReadWriteOnce# 单节点读写,最常用resources:requests:storage:5GistorageClassName:gp3# 显式指定,别依赖默认类应用到 Deployment:apiVersion:apps/v1kind:Deploymentmetadata:name:webspec:replicas:1selector:matchLabels:{app:web}template:metadata:labels:{app:web}spec:containers:-name:webimage:nginxvolumeMounts:-name:datamountPath:/usr/share/nginx/htmlvolumes:-name:datapersistentVolumeClaim:claimName:data-pvc现在 Pod 重启、重建,/usr/share/nginx/html里的数据都还在——只要 PVC 没被删。坑一:PVC 一直 Pending,Pod 也起不来kubectl get pvc看到Pending,先看事件:kubectl describe pvc># Events:# Warning ProvisioningFailed ... no volume plugin matched常见三种原因:storageClassName写错或集群根本没这个类。kubectl get sc核对名字。VolumeBindingMode 是WaitForFirstConsumer。这不是错误——它故意等到有 Pod 调度了才创建卷(为了把卷开在 Pod 所在的可用区)。此时 PVC Pending 是正常的,等 Pod 调度上去就会 Bound。如果 Pod 也 Pending,去查 Pod 的调度事件。底层配额不足,比如云账号 EBS 卷数量到上限。看 provisioner 的日志。坑二:多副本时数据「对不上」——ReadWriteOnce 的真相把上面的replicas改成 3,你可能会发现 3 个 Pod 看到的数据不一致,甚至有 Pod 卡在ContainerCreating。原因在accessModes。ReadWriteOnce(RWO)的含义是同一时刻只能被一个节点挂载读写。多个副本如果被调度到不同节点,后面的 Pod 挂不上卷:kubectl describe pod web-xxx# Warning FailedAttachVolume Multi-Attach error for volume pvc-...# Volume is already exclusively attached to one node三种访问模式记牢:模式缩写语义ReadWriteOnceRWO单节点读写(块存储,如 EBS)ReadOnlyManyROX多节点只读ReadWriteManyRWX多节点读写(需 NFS、CephFS 等文件存储)结论:多副本共享写数据,要么用支持 RWX 的存储(NFS/CephFS),要么改架构——用StatefulSet给每个副本一块独立的 PVC。坑三:重启后数据还是丢了——三个隐藏杀手你明明用了 PVC,数据却还是没了。逐个排查:1. ReclaimPolicy 是 Delete,PVC 被删了kubectl getpv# RECLAIM POLICY: DeleteDelete策略下,删除 PVC 会连带删掉 PV 和底层云盘。如果你的 CI 脚本或helm uninstall删了 PVC,数据就真没了。生产环境重要数据把 StorageClass 的reclaimPolicy设成Retain,删 PVC 后 PV 保留、数据还在,可手动恢复。2. subPath 挂错,写进了容器层volumeMounts:-name:datamountPath:/app/datasubPath:prod# 卷内的子目录如果应用实际写的是/app/logs而不是/app/data,那/app/logs还在容器可写层里,重启就没。确认应用的写入路径和mountPath完全一致,进容器df -h看目标目录是不是挂载点:kubectlexec-itweb-xxx --df-h/app/data# Filesystem ... Mounted on# /dev/nvme1n1 ... /app/data ← 是独立设备才说明挂上了如果Mounted on显示的是overlay(容器根文件系统),说明根本没挂上卷,写的都是临时数据。3. StatefulSet 缩容删了 PVCStatefulSet缩容默认不会删 PVC(这是有意保护数据)。但如果你手动kubectl delete pvc,或者设了persistentVolumeClaimRetentionPolicy: Delete,缩容时 PVC 就被回收了。检查:kubectl get statefulset db-oyaml|grep-A3RetentionPolicy一个能落地的排查顺序数据丢失时,按这个顺序查,基本 5 分钟定位:# 1. Pod 到底挂没挂上卷?kubectlexec-itpod--df-hmountPath# 看是不是 overlay# 2. PVC 是不是 Bound?kubectl get pvc# 3. PV 的回收策略?会不会被连带删除?kubectl getpv-ocustom-columnsNAME:.metadata.name,POLICY:.spec.persistentVolumeReclaimPolicy# 4. 应用写入路径 vs mountPath/subPath 对得上吗?kubectl get podpod-oyaml|grep-A5volumeMounts小结emptyDir是临时暂存,Pod 重建即清空,别拿它做持久化。动态供给靠StorageClass,PVC 只声明「多大 什么类」,provisioner 自动开盘。PVC Pending 先看describe:多半是storageClassName错,或WaitForFirstConsumer在等 Pod 调度。ReadWriteOnce是单节点读写,多副本共享写要用 RWX 存储或 StatefulSet。重要数据把reclaimPolicy设成Retain,删 PVC 也不丢盘。一句话记忆点:数据丢了先df -h看目标目录是不是 overlay——是 overlay 就压根没挂上卷,别的都白查。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价