Kubernetes持久化存储实战:PV与PVC从原理到生产避坑指南
1. 从一个真实困境说起Pod重启后数据去哪了刚接触Kubernetes那会儿我做过一件现在想起来还觉得蠢的事。当时在测试环境部署了一个MySQL的Pod跑得好好的数据也写进去了。结果有天节点维护Pod被调度到另一台机器上起来之后数据库空空如也之前写的测试数据全没了。我第一反应是是不是镜像有问题排查了半天才发现问题根本不在镜像而在于我压根没给Pod配持久化存储——容器里的文件系统是临时的Pod一重建写进去的东西跟着容器一起灰飞烟灭。这个坑其实特别典型。Kubernetes默认的存储模型是跟着Pod走的Pod的生命周期和数据的生命周期绑死在一起。但现实中的业务尤其是数据库、消息队列、文件服务这类有状态应用数据必须活得比Pod久。Pod可以随便死、随便重建、随便漂移数据得稳稳地待在那儿。这就是持久化存储要解决的核心矛盾。Kubernetes给出的答案是两个抽象对象PVPersistentVolume持久卷和PVCPersistentVolumeClaim持久卷声明。这两个东西是理解K8s存储体系的地基搞不清楚它们的关系后面StorageClass、动态供给、StatefulSet这些更高级的玩法根本没法往下走。这篇文章我打算把PV和PVC从概念到实操彻底讲透包括它们各自是什么、怎么配合、静态供给和动态供给的区别、绑定的底层逻辑、常见踩坑点以及我在实际项目里总结出来的一些经验。不管你是刚上手K8s的新手还是被存储问题折腾过的老手应该都能从里面找到有用的东西。2. PV和PVC到底各自扮演什么角色2.1 用停车位和停车申请来理解这对组合PV和PVC的关系我特别喜欢用一个类比来解释PV是停车位PVC是停车申请。集群管理员负责规划停车场划出一个个车位PV每个车位有固定的大小、位置、类型地上还是地下。这些车位是集群级别的资源独立于任何一辆车存在。而用户开发者不需要关心车位具体在哪、怎么划的他只需要提交一份申请PVC说我要一个10GB的、能读写的位置。系统会自动帮他找一个匹配的车位把两者绑定起来。之后Pod要停车直接引用这份申请PVC就行了。这个设计的好处在于职责分离。管理员管底层存储的供给和运维开发者管自己需要多少、什么访问模式。开发者不用去碰NFS的挂载路径、不用管云盘的具体ID只需要声明需求。这种解耦是K8s存储体系能规模化管理的关键。从技术定义上讲PV是集群里一块已经被供给出来的存储资源它可以是NFS共享目录、云厂商的块存储、本地磁盘、Ceph RBD等等。PVC则是对存储的请求包含容量大小、访问模式这些需求描述。PVC被Pod消费PV被PVC消费形成一条清晰的引用链。2.2 PV的关键字段capacity、accessModes、persistentVolumeReclaimPolicy要真正用好PV得把它的几个核心字段吃透。我按重要性排个序。capacity容量是最直观的声明这块PV有多大比如storage: 10Gi。注意单位是Gi不是GBGi是1024进制GB是1000进制虽然差别不大但配置时别写错。accessModes访问模式是新手最容易搞混的地方它决定了这块存储能被怎么挂载。有三种模式访问模式缩写含义典型场景ReadWriteOnceRWO只能被单个节点以读写方式挂载云盘、大多数块存储ReadOnlyManyROX可被多个节点以只读方式挂载共享配置、静态资源ReadWriteManyRWX可被多个节点以读写方式挂载NFS、CephFS、共享文件这里有个特别容易踩的坑RWO的Once指的是单个节点不是单个Pod。也就是说同一个节点上的多个Pod是可以同时挂载同一个RWO卷的。很多人以为RWO只能给一个Pod用其实不是。真正限制的是节点数量。而RWX才是真正意义上的多节点共享但它对底层存储有要求普通的云盘做不到必须用NFS、CephFS这类支持共享文件系统的后端。persistentVolumeReclaimPolicy回收策略决定了PV被释放后怎么处理这个字段直接关系到数据安全必须重视Retain保留PVC删除后PV保留数据还在需要管理员手动清理。这是最安全的策略生产环境涉及重要数据时首选。Delete删除PVC删除后PV和底层存储一起被删掉。动态供给出来的PV默认就是这个策略方便但有风险。Recycle回收已废弃别用了。我见过有人测试环境用Delete策略结果误删PVC把生产数据一起带走了。所以策略选择上宁可保守。2.3 PVC的匹配逻辑它怎么找到合适的PVPVC不是随便找个PV就绑的它有一套匹配规则。当你创建一个PVC时Kubernetes的控制循环会去找满足以下所有条件的PV容量足够PV的capacity必须大于等于PVC请求的容量。访问模式匹配PV支持的accessModes必须包含PVC请求的模式。存储类匹配如果PVC指定了storageClassNamePV的storageClassName必须一致。选择器匹配如果PVC用了selector标签选择器PV的标签得符合。PV处于Available状态已经被绑定的PV不会再被匹配。匹配上之后两者进入Bound状态一对一绑定。这里有个细节绑定是一对一的一个PV只能被一个PVC绑定一个PVC也只能绑一个PV。哪怕PV有100GBPVC只要了1GB剩下的99GB也浪费了别的PVC用不了。所以容量规划要合理别搞出大量大PV配小PVC的浪费。如果暂时没有匹配的PVPVC会一直处于Pending状态直到有合适的PV出现。这个机制在静态供给模式下很关键管理员得提前把PV准备好。3. 静态供给实操手动创建PV和PVC的完整流程3.1 先准备一个NFS后端作为存储底座静态供给的意思是管理员手动创建PVPVC再去绑定。要演示这个流程得先有个真实的存储后端。我用NFS来举例因为它搭建简单、支持RWX、适合练手。假设已经有一台NFS服务器导出了目录/data/nfs/k8s。在K8s的每个节点上需要安装NFS客户端工具否则Pod挂载时会报错。这个依赖经常被忽略我踩过好几次# 在每个worker节点上执行 apt-get install -y nfs-common # Debian/Ubuntu系 # 或 yum install -y nfs-utils # CentOS/RHEL系装完之后可以手动测试一下挂载是否正常确认网络和权限没问题再去配K8s能省很多排查时间。3.2 写一个PV的YAML每个字段都要有理由下面是一个NFS类型的PV定义我逐字段说明为什么这么写apiVersion: v1 kind: PersistentVolume metadata: name: nfs-pv-demo labels: type: nfs spec: capacity: storage: 5Gi accessModes: - ReadWriteMany persistentVolumeReclaimPolicy: Retain storageClassName: nfs-static nfs: server: 192.168.1.100 path: /data/nfs/k8s/demo几个关键点解释一下。storageClassName这里我显式指定为nfs-static是为了和后面PVC的声明对上避免匹配失败。reclaimPolicy用Retain因为这是演示重要数据的场景安全第一。accessModes用RWX因为NFS天然支持多节点读写。nfs字段里的server和path就是NFS服务器的地址和导出路径。创建之后用kubectl get pv看一下状态应该是Available说明这块PV已经准备好被认领了。3.3 创建PVC并验证绑定结果PV有了接下来写PVC去申请apiVersion: v1 kind: PersistentVolumeClaim metadata: name: nfs-pvc-demo spec: accessModes: - ReadWriteMany resources: requests: storage: 3Gi storageClassName: nfs-static注意这里请求的是3Gi而PV是5Gi。因为PV容量大于等于请求容量所以能匹配上。创建后执行kubectl get pvc状态会从Pending变成Bound。再用kubectl get pv看那个PV的状态也从Available变成了Bound并且CLAIM列显示了它绑定的PVC名字。这时候如果你去看PV的详情kubectl describe pv nfs-pv-demo能看到Claim字段指向了default/nfs-pvc-demo。绑定关系一目了然。3.4 把PVC挂进PodvolumeMounts和volumes的配合PVC绑定好了最后一步是让Pod用上它。这需要在Pod定义里同时写volumes和volumeMounts两块apiVersion: v1 kind: Pod metadata: name: nfs-pod-demo spec: containers: - name: app image: nginx:alpine volumeMounts: - name:>apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: nfs-dynamic provisioner: example.com/nfs parameters: server: 192.168.1.100 path: /data/nfs/k8s/dynamic reclaimPolicy: Delete volumeBindingMode: Immediateprovisioner指定了用哪个插件来创建存储这里用的是NFS的provisioner需要额外部署对应的provisioner组件。parameters是传给插件的参数。reclaimPolicy这里用Delete因为动态创建的PV本来就是按需生成的用完即删比较合理。volumeBindingMode有两个值Immediate表示PVC一创建就立刻绑定WaitForFirstConsumer表示等到有Pod真正用到这个PVC时才绑定。后者在本地存储、拓扑受限的场景下很重要能避免PV创建在错误的节点上。4.3 动态供给下PVC的写法差异有了StorageClassPVC的写法就简单多了不需要再关心PV长什么样apiVersion: v1 kind: PersistentVolumeClaim metadata: name: dynamic-pvc-demo spec: accessModes: - ReadWriteMany resources: requests: storage: 2Gi storageClassName: nfs-dynamic关键就是storageClassName指向了那个StorageClass。创建之后系统会自动生成一个PV名字通常是pvc-uuid这种格式PVC和PV自动绑定。整个过程不需要管理员介入。4.4 静态和动态怎么选一张表说清楚对比维度静态供给动态供给PV创建方式管理员手动创建系统按StorageClass自动创建适用规模小规模、固定需求大规模、弹性需求管理成本高需人工规划低自动化灵活性差容量固定好按需生成典型场景少量核心数据库微服务、多租户环境我的建议是能用动态就用动态。静态供给只在少数特殊场景下才用比如底层存储不支持动态供给或者需要精确控制某块存储的物理位置。绝大多数业务场景配好StorageClass让系统自动干活省心得多。5. 绑定失败的排查链路从Pending到Bound5.1 PVC一直Pending先看这三个地方PVC卡在Pending是最常见的存储问题。我总结了一套排查顺序基本能覆盖90%的情况。第一步看PVC的Events。执行kubectl describe pvc pvc-name重点看最下面的Events区域。如果提示no persistent volumes available for this claim and no storage class is set说明既没有匹配的PV也没指定StorageClass。如果提示storageclass.storage.k8s.io xxx not found说明StorageClass名字写错了或者没创建。第二步检查storageClassName是否一致。静态供给下PVC和PV的storageClassName必须完全相同。我见过有人PVC写了standardPV写了standard-sc结果一直匹配不上。这种问题看Events不一定能直接看出来得手动对比两边的YAML。第三步检查容量和访问模式。PVC请求的容量如果大于所有可用PV的容量就会Pending。访问模式不匹配也一样。比如PVC要RWX但集群里只有RWO的PV那就绑不上。5.2 一个真实的排查案例容量够但就是绑不上有次同事找我说他的PVC请求1Gi集群里明明有个5Gi的PVstorageClassName也一样但就是Pending。我让他把两个YAML都发过来一看就发现问题了PV的accessModes是ReadWriteOnce而PVC请求的是ReadWriteMany。容量虽然够但访问模式不匹配照样绑不上。这个案例说明匹配是多条件同时满足任何一个不满足都会失败。排查时不能只看一个维度得把容量、访问模式、storageClassName、selector、PV状态全部过一遍。我后来养成了一个习惯排查存储问题时直接列个清单逐项打勾比凭感觉猜快得多。5.3 Pod挂载失败PVC Bound了不代表万事大吉还有一种情况更隐蔽PVC已经Bound了但Pod起不来报FailedMount。这时候问题往往出在节点层面而不是PV/PVC的绑定逻辑。常见原因有几个。一是节点没装NFS客户端前面提过这个特别容易漏。二是NFS服务器权限问题比如导出目录的权限设置不对Pod里的进程没权限写。三是网络不通节点访问不到NFS服务器的端口。四是挂载路径冲突比如同一个Pod里两个volume挂到了同一个mountPath。排查这类问题kubectl describe pod pod-name看Events然后到出问题的节点上手动执行挂载命令测试基本能定位。我一般会先在节点上showmount -e nfs-server确认能列出导出目录再手动mount一次能挂上说明是K8s配置问题挂不上就是环境问题。6. 生产环境里那些文档不会告诉你的经验6.1 回收策略选错数据可能说没就没前面提过reclaimPolicy这里再强调一次因为它的影响太大了。动态供给默认用DeletePVC一删底层存储跟着删。这在测试环境很方便但生产环境如果没注意误删PVC就等于删数据。我的做法是生产环境的核心数据一律用Retain。哪怕麻烦一点需要手动清理PV也比数据丢了强。具体操作上可以在StorageClass里把reclaimPolicy设成Retain或者对重要的PVC单独指定。另外删除PVC之前一定要确认清楚kubectl delete pvc这个命令敲下去之前多看一眼名字。6.2 容量规划别让PV和PVC的容量差太大静态供给下PV和PVC是一对一绑定的PV剩下的容量不会给别的PVC用。所以如果创建了一堆10Gi的PV而PVC都只要1Gi那90%的空间就浪费了。反过来如果PV都是1Gi的突然来个要5Gi的PVC又绑不上。合理的做法是按业务实际需求规划容量档位比如小档1Gi、中档10Gi、大档100GiPVC按需选择。动态供给就没这个问题因为PV是按PVC的实际请求生成的不会浪费。6.3 StatefulSet场景下PVC的命名规律有状态应用通常用StatefulSet部署它和PVC的配合有个特殊之处每个Pod副本会有自己独立的PVC。StatefulSet通过volumeClaimTemplates来定义PVC模板创建的PVC名字是模板名-StatefulSet名-序号这种格式比如>

相关新闻

从设计稿到上墙:拼木工艺画的完整实操链路复盘

从设计稿到上墙:拼木工艺画的完整实操链路复盘

第一次看到 artcraft 这个词,我下意识把它拆成 art 和 craft——“艺术”加“手工”。后来我把它当成自己的拼木装饰画项目代号,越用越觉得贴切:真正的手工不应该是“照图施工”,而是先用艺术视角设计画面,再用工艺手段…

2026/10/19 11:13:22 阅读更多 →
Java Stream终止操作全面拆解:从惰性求值到短路与并行流

Java Stream终止操作全面拆解:从惰性求值到短路与并行流

很多人第一次写Java Stream的时候都踩过同一个坑:明明 filter() 、 map() 、 sorted() 都没写错, System.out.println() 一执行,控制台却什么都没有输出。不是你的逻辑不对,而是你忘了给Stream 添加流的终止操作 。在St…

2026/10/19 11:13:22 阅读更多 →
贝叶斯广义核推断:地形可通行性建图代码实战解析

贝叶斯广义核推断:地形可通行性建图代码实战解析

1. 从标题拆解这个项目到底在做什么1.1 标题里的三个关键词,藏着一条完整的技术链路“Bayesian Generalized Kernel Inference for Terrain Traversability Mapping”,这个标题如果直译过来,就是“用于地形可通行性建图的贝叶斯广义核推断”。…

2026/10/19 11:13:22 阅读更多 →

最新新闻

数据库入门核心:从表结构、索引到事务的实战解析

数据库入门核心:从表结构、索引到事务的实战解析

我在带新人入门的时候,最常听到的一句话是:“数据库到底是个啥?感觉就是个存数据的软件呗。”这话没毛病,但只对了一半。如果只是存数据,Excel也能干,为啥要搞出个专门的“王国”出来?因为数据库…

2026/10/19 21:10:50 阅读更多 →
从MLA到DeepSeekMoE:DeepSeek系列大模型架构演进与技术拆解

从MLA到DeepSeekMoE:DeepSeek系列大模型架构演进与技术拆解

很多人聊大模型架构的时候,总喜欢从Transformer开始铺开,但真正把架构演进讲透的,恰恰要看国内开源模型这几年的实操路线。我自己的感受是,2024年初那会儿大家还在卷榜单,到了年底风向就变了,变成卷单次推理…

2026/10/19 21:09:49 阅读更多 →
顶级投资人称过去只是热身:AI智能跃迁1000倍意味着什么

顶级投资人称过去只是热身:AI智能跃迁1000倍意味着什么

1. 当一位顶级投资人说出“过去只是热身”第一次看到这个访谈标题时,我的直觉反应不是“又在吹AI”,而是注意到一个细节:说这话的人,过去四十年几乎每一次都押中了时代级的技术浪潮。从个人电脑时代的操作系统与芯片,到…

2026/10/19 21:09:49 阅读更多 →
决策树在电信客户流失预测中的实战应用与规则落地

决策树在电信客户流失预测中的实战应用与规则落地

1. 先摸清业务:流失预测到底在解决什么问题接手电信客户流失预测这类项目,很多人第一反应就是找数据集、跑模型、调参出准确率。我之前做过一个运营商侧的流失预警项目,踩过不少坑之后发现,第一步根本不是建模,而是把“…

2026/10/19 21:08:47 阅读更多 →
表格合并全攻略:从Excel自带功能到代码方案一网打尽

表格合并全攻略:从Excel自带功能到代码方案一网打尽

表格合并这事儿,看起来简单,真做起来恶心过多少人,我太有数了。尤其是月底、年底那几天,群里一片哀嚎:销售数据分了十二个月、各门店表头还不一样、刘姐交上来的表编码是文本格式,张哥那边编码却是数字&…

2026/10/19 21:07:45 阅读更多 →
基于Python的UDP可靠传输实现:从停等协议到GBN滑动窗口

基于Python的UDP可靠传输实现:从停等协议到GBN滑动窗口

简介:这是一份基于Python实现的可靠数据传输协议课程设计资源包,以UDP为底层通道,完整演示了停等协议、GBN滑动窗口协议及SR协议的设计与实现过程。面向计算机网络课程学生、需要完成类似实验或对传输层协议感兴趣的学习者,可借此…

2026/10/19 21:05:42 阅读更多 →

日新闻

频率跟踪型PWM电源设计:臭氧发生器谐振点漂移与软开关实现

频率跟踪型PWM电源设计:臭氧发生器谐振点漂移与软开关实现

简介:本资源为电源技术方向的学术研究文档,聚焦介质阻挡放电型臭氧发生器电源的频率跟踪型PWM控制策略,面向电力电子、高压电源及臭氧应用领域的科研人员与工程技术人员。针对负载呈容性、等效电容随外加电压升高而增大、谐振频率随之漂移的难…

2026/10/19 0:00:47 阅读更多 →
Vibe Coding 遇上 TaoToken:把开发者思维圣殿的灵感流接入统一 Key 通道

Vibe Coding 遇上 TaoToken:把开发者思维圣殿的灵感流接入统一 Key 通道

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 0:00:47 阅读更多 →
HundredCV-Chat话题建模实战教程:利用4,871个内置话题标签构建主题分析系统

HundredCV-Chat话题建模实战教程:利用4,871个内置话题标签构建主题分析系统

【免费下载链接】HundredCV-Chat 项目地址: https://ai.gitcode.com/hf_mirrors/Jax-dan/HundredCV-Chat 点击查看 免费下载 HundredCV-Chat 是一个由 100 位青年简历数据开发而来的中文多轮对话数据集,共包含 24,750 组日常闲聊对话和 4,871 个话题标签…

2026/10/19 0:00:47 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/19 10:02:04 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/19 17:32:41 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/19 1:30:52 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 2:54:50 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 9:10:22 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 12:16:16 阅读更多 →