ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Kubernetes持久化存储实战:NFS搭建PV/PVC全攻略

Kubernetes持久化存储实战:NFS搭建PV/PVC全攻略 1. 为什么团队最终选了NFS做PV后端先说结论在Kubernetes里用NFS做PersistentVolumePV的存储后端是目前中小团队性价比最高的方案没有之一。我见过不少团队一上来就追Ceph、上Rook结果集群还没跑稳先被存储折腾掉半条命——OSD宕了、网络抖动、重建三副本、均衡数据运维全是眼泪。而NFS这套东西只要你会配Linux共享目录基本就能在半小时内把K8s的持久化存储跑通。我最初接触PV/PVC是在一个电商中台的容器化改造项目里业务方要求所有Pod的数据必须“不随Pod死亡而消失”。当时我们面临一个很现实的问题测试环境有二十多个应用每个应用都要挂存储但存储预算有限也不可能给每个命名空间单独搞一套Ceph。后来就是靠一台2U服务器配了8块SATA盘装了个Ubuntu 24.04开了NFS服务把目录export出去然后在K8s里创建NFS类型的PV用PVC去动态匹配——整个存储方案就落地了。适合谁来参考这篇文章我认为有两类人特别需要刚接触K8s存储没搞懂PV、PVC、StorageClass三者关系的新手。已经在生产环境用了NFS但遇到权限问题、Pod挂载失败、回收策略不对等坑的运维。这篇文章我会把NFS Server的搭建、PV/PVC的YAML编写、Pod挂载PVC的完整流程、以及我在生产环境踩过的权限坑全部摊开讲。你看完直接照着抄作业就行。2. NFS相比其他存储方案的真实优劣势2.1 为什么我不推荐一上来就用Ceph不是说Ceph不好而是它不适合所有场景。Ceph的架构决定了它的复杂度一套Ceph集群最少要三个节点起步每个节点都要管理OSD、MON、MGR进程磁盘故障时还要处理数据重新平衡。我见过很多初创公司搭了Ceph结果半年都没人能正常运维最后只能找外部专家救火。而NFS的数据链路就简单得多客户端发起挂载请求NFS Server验证权限根据exports文件建立RPC连接读写数据直接基于文件系统操作整个协议栈只有三层客户端、网络、服务器。没有副本同步没有一致性协议也没有CAP定理的甜蜜负担。这就意味着当你的Pod需要挂载存储时核心流程就变为“客户端发起挂载请求 → NFS Server验证权限并响应 → 通过RPC在网络文件系统上读写数据”链路短问题排查起来也快。2.2 NFS适合什么不适合什么NFS适合的是共享只读配置、多Pod共享写入日志、数据量不大但对持久化有要求的目录。K8s的官方文档里都说了NFS支持多个Pod同时挂载同一个PV进行读写而且支持ReadWriteMany模式这是本地盘hostPath和云盘云厂商块存储做不到的。NFS不适合的则是高并发随机IO、数据库数据文件尤其是PostgreSQL、MySQL主库、以及对延迟敏感的核心业务。NFS毕竟是网络文件系统走的是RPC协议单次读写都要经过网络往返延迟比本地盘高一个数量级。我们的压测数据是本地SSD的4K随机读延迟在0.1ms级别而NFS在同一网络环境下普遍在0.8-1.5ms写操作更高。如果业务对IOPS有硬性要求别用NFS。2.3 NFS、hostPath、云盘怎么选存储类型读写模式数据生命周期适合场景不适合场景hostPath单节点读写随节点存在临时调试、DaemonSet日志多节点共享、数据持久性要求高云盘块存储RWO单节点读写随磁盘存在数据库、单Pod状态应用多Pod共享数据NFSRWX多节点读写随NFS目录存在多Pod共享、日志收集、配置文件高并发随机IO、数据库数据文件Ceph RBDRWO / RWX需配置随存储池存在大规模生产环境中小团队人力有限时从表格可以清楚看出来NFS的核心价值在“RWX”也就是多节点并发读写。在实际的生产使用中很多应用其实需要这种能力多个API副本同时写日志目录、多个Worker同时读取模型文件、多个Pod挂同一份配置文件做热更新。云盘做不到这些而NFS天生支持。3. NFS Server端搭建与参数精讲3.1 Ubuntu 24.04下的NFS Server安装与配置我这次用的是Ubuntu 24.04内核版本6.8NFS相关包在源里都有直接用apt装就行。CentOS/RHEL系的操作也类似就是把apt换成yum。# Ubuntu 24.04安装NFS服务端 sudo apt update sudo apt install nfs-kernel-server -y安装完成后创建共享目录。这里有一个经验不要直接用根目录下的目录做export也不要把共享目录建在系统盘上除非你只是想测试。生产环境我会单独挂一块数据盘到/nfs-data然后在这个目录下按业务再切分子目录。# 创建NFS共享根目录 sudo mkdir -p /nfs-data/k8s-pv-shared sudo chown nobody:nogroup /nfs-data/k8s-pv-shared sudo chmod 777 /nfs-data/k8s-pv-shared关于权限这里多说一句。很多人第一次配NFS时会在权限上翻车因为NFS的权限校验是“客户端用户身份 Server端文件系统权限”双重叠加的。也就是说即使K8s节点上的Pod以root身份运行NFS Server端仍然会根据export配置和文件权限来决定是否允许访问。这就是为什么上面的chmod直接给了777——先打通权限通道后续再根据业务收紧。修改/etc/exports文件sudo vim /etc/exports写入如下内容/nfs-data/k8s-pv-shared *(rw,sync,no_subtree_check,no_root_squash)逐项解释一下参数含义rw允许读写。这是PV挂载的基本要求只读的话很多应用跑不起来。sync同步写入。客户端请求写入后Server端先落盘再响应。性能比async差一点但数据一致性有保障。K8s环境我建议用sync毕竟容器重启是常态不能拿数据开玩笑。no_subtree_check禁用子树检查。如果你的export目录是根目录下的子目录NFS默认会检查每个请求访问的文件是否仍在导出目录里。禁用可以提升性能但会降低一点安全性。内网环境问题不大。no_root_squash关闭root压缩。默认情况下客户端root用户会被映射为nobody这样会导致以root身份运行的容器在写文件时权限不足。K8s里很多镜像默认以root启动所以在测试环境直接上no_root_squash最简单有效。配置生效并启动服务sudo exportfs -r sudo systemctl restart nfs-kernel-server sudo systemctl status nfs-kernel-server在K8s节点上验证showmount -e 192.168.1.100输出类似Export list for 192.168.1.100: /nfs-data/k8s-pv-shared *看到这个就说明NFS服务端已经就绪了。这一步是很多教程会漏讲的——你必须在所有K8s节点上安装nfs-common客户端工具否则即使PV创建成功Pod调度到节点上也会卡在“ContainerCreating”状态挂载不了。3.2 客户端节点准备工作最容易漏的一步# 在每一个K8s节点包括master执行 sudo apt install nfs-common -yCentOS系对应的是nfs-utilssudo yum install nfs-utils -y这一步别省。K8s的kubelet在挂载NFS卷时会调用本机的mount.nfs命令。如果节点上没有nfs-common/nfs-utilskubelet会报“mount failed: exec: mount.nfs: executable file not found in $PATH”——这个报错我见过太多次了90%的“PV挂不上”其实是节点缺客户端工具而不是PV配置有问题。3.3 多NFS Server负载均衡的简单思路如果你只有一个NFS Server那它就是个单点。一旦宕机所有挂载这个PV的Pod都会变成只读状态写入直接失败。更严重的已经建立的TCP连接会挂起Pod里的应用会假死。有条件的团队可以再起一台备机用rsync或者inotify同步数据目录。但坦白说很多中小团队不会专门搭一套高可用NFS。我个人的建议是如果NFS只是用来挂日志和配置文件单点问题可以接受只要做好Server磁盘告警就行如果真有核心业务依赖NFS那就老实上Ceph或者云厂商的NAS服务别自己用NFS硬扛高可用。4. PV与PVC核心概念与YAML编写实战4.1 PV/PVC到底是什么用一个生活化的类比来解释PV是仓库里已经打包好的空房间PVC是你拿着钥匙去申请使用这个房间的凭证。管理员负责规划房间PV开发者负责申请使用PVC。两者之间通过容量、访问模式等属性进行匹配绑定。K8s的存储体系需要一个完整的层次结构我按照实际使用顺序帮你梳理一下。要理解NFS实现的PV/PVC体系你得先搞清楚K8s存储体系中的几个层级StorageClass存储类负责动态创建PV。它就像工厂里的流水线——你提交一个PVC申请StorageClass自动帮你开模生产对应的PV。PVPersistentVolume集群里的存储资源由管理员预先创建。它像是一个静态房源已经对接好了后端存储比如NFS的某个导出目录。PVCPersistentVolumeClaim用户的存储请求。Pod通过声明PVC来“申请”PV资源。PV与PVC的绑定关系当PVC声明的容量、访问模式与某个PV匹配时PV和PVC就被绑定在一起Pod挂载时直接使用PVC即可访问对应的PV存储空间。StorageClass的动态供给如果集群配置了StorageClass可以自动创建PV来满足PVC请求无需管理员预先静态创建PV。这种方式更自动化适合大规模使用。但NFS属于最基础的存储类型许多团队倾向于静态定义PV逻辑透明、便于管理。用表格总结这几个概念的分工概念层次作用类比StorageClass存储模板按需动态创建PV按户型自动造房的流水线PV集群资源对接真实存储后端已建好的房源PVC用户请求声明需要的存储规格申请房子的凭证Pod引用PVC使用者应用挂载使用存储入住并实际使用我这里想特别强调一下如果你是用NFS做PV官方推荐的方式其实是静态PV。为什么因为NFS本身没有动态创建目录的能力。虽然你可以用StorageClass配合Provisioner比如nfs-subdir-external-provisioner来实现动态创建子目录但在生产环境里静态PV的透明度和可控性更好。尤其是你要精细控制每个PV的容量、挂载路径和回收策略时静态PV的YAML写在Git仓库里谁改谁知道。4.2 NFS PV的YAML编写技巧这是一个标准的NFS类型PV定义apiVersion: v1 kind: PersistentVolume metadata: name: nfs-pv-log labels: usage: log-storage spec: capacity: storage: 100Gi volumeMode: Filesystem accessModes: - ReadWriteMany persistentVolumeReclaimPolicy: Retain nfs: server: 192.168.1.100 path: /nfs-data/k8s-pv-shared/log readOnly: false一个yaml里看似只有几行但每个字段都有讲究capacity.storage声明这个PV的总容量为100Gi。注意NFS本身不原生支持容量配额这个100Gi只是给调度器的“参考值”。如果NFS后端实际容量小于这个数写满时不会触发K8s的“磁盘已满”事件而是直接在NFS端报No space left。所以填容量时务必依据NFS服务器上该目录的真实容量来定。volumeMode固定为Filesystem。NFS不支持裸块设备。accessModes这里是最能体现NFS优势的地方——ReadWriteMany。这意味着多个Pod可以同时挂载并读写同一个PV。这是hostPath和云盘给不了的。persistentVolumeReclaimPolicy我强烈建议用Retain。这意味着PV被释放后NFS上的数据不会被自动删除需要管理员手动清理。如果用DeleteNFS类型的PV在删除时会尝试调用NFS Provisioner清理后端数据配置不当容易误删数据。一旦误删NFS端是找不回来的——没有快照没有回收站。nfs.readOnly绝大多数情况设为false否则Pod只能读不能写。4.3 PVC的声明与匹配规则PVC的YAML相对简单apiVersion: v1 kind: PersistentVolumeClaim metadata: name: log-pvc namespace: production spec: accessModes: - ReadWriteMany resources: requests: storage: 10Gi selector: matchLabels: usage: log-storagePVC去匹配PV的逻辑主要有三条访问模式必须一致。PVC申请的容量必须小于等于PV的容量。如果PVC写了selector标签选择器那么PV必须带有对应标签。我在4.2的PV定义里故意加了一个usage: log-storage的标签就是给PVC的selector用的。这样做的好处是精准匹配避免集群里其他PV被“意外绑走”。如果你的集群里有很多PV一定建议把标签选择器用上否则PVC很可能绑定到一个你不希望关联的存储上。关于PV与PVC的匹配还有一个细节当PVC声明10Gi而集群里存在一个100Gi的PV时PVC会直接绑定这个100Gi的PV。也就是说K8s不会“切一块”出来而是一整个PV被这个PVC独占。所以建立PV时尽量按业务的实际使用量来划分避免浪费。4.4 StorageClass动态供给在NFS场景下的使用前面提到静态PV的透明可控。但在多环境dev/prod/test都需要快速创建存储时动态供给也是一个合理的选择。NFS的动态供给需要第三方Provisioner通常使用nfs-subdir-external-provisioner它会根据PVC的名字在NFS服务端自动创建子目录并生成对应的PV完成绑定。apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: nfs-sc provisioner: k8s-sigs.io/nfs-subdir-external-provisioner parameters: archiveOnDelete: true使用这种StorageClass时YAML里只需要声明PVCProvisioner会自动处理PV的创建和绑定不需要管理员预先编写PV定义。我的建议是测试环境用StorageClass动态供给快速方便生产环境用静态PV精准控制。动态供给的目录命名规则、回收策略、权限都需要提前验证好否则后期数据清理时会很麻烦。5. Pod挂载PVC的完整实操流程5.1 Deployment挂载PVC的YAML示例理论全部到位后我们直接看一个完整的Deployment定义这次以一个Nginx日志收集服务为例——用NFS来存储Nginx产生的访问日志apiVersion: apps/v1 kind: Deployment metadata: name: nginx-log-shipper namespace: production spec: replicas: 3 selector: matchLabels: app: nginx-log-shipper template: metadata: labels: app: nginx-log-shipper spec: containers: - name: nginx image: nginx:1.25 ports: - containerPort: 80 volumeMounts: - name: log-volume mountPath: /var/log/nginx volumes: - name: log-volume persistentVolumeClaim: claimName: log-pvc这个Dployment里三个副本共享同一份日志目录。如果是用云盘这是完全做不到的但NFS天然支持RWX三个Nginx Pod的日志都会写入NFS服务端同一个目录你只需要在NFS服务器上tail日志文件就能同时看到所有实例的访问记录。执行命令创建并验证kubectl apply -f deployment.yaml kubectl get pods -n production -w当Pod状态变成Running后在Pod里验证挂载kubectl exec -it nginx-log-shipper-xxxxx -n production -- df -h输出里应该有类似192.168.1.100:/nfs-data/k8s-pv-shared/log 100G 2.1G 98G 3% /var/log/nginx看到这个就说明NFS挂载成功了。注意这里的容量显示是PV里声明的100Gi不是NFS服务端实际磁盘容量。5.2 多Pod同时挂载同一个PVC时的注意事项RWX模式允许多Pod并发读写但“允许并发”不代表“任意并发都没问题”。有几个问题你要提前想清楚多个Pod同时写同一个文件时会产生锁竞争。如果业务是日志Append模式还好如果是随机覆盖写需要你在应用层做文件锁。NFSv4支持文件锁但要确保服务端和客户端都配置正确。遇到过锁失效的情况查到最后是Kubernetes节点上NFS客户端挂载参数里缺少了lock选项。多Pod同时写同一个文件的场景建议日志按Pod名或节点名切分文件避免共享文件写入冲突。5.3 StatefulSet挂载PVC的特殊处理方式有状态应用如Kafka、Elasticsearch、Zookeeper用NFS PV/PVC时有个陷阱StatefulSet定义的volumeClaimTemplates会自动创建PVC但绑定的PV数量是跟副本数对应的。比如3个副本volumeClaimTemplates会创建3个独立的PVC每个PVC绑定一个独立的NFS子目录。volumeClaimTemplates: - metadata: name: data spec: accessModes: - ReadWriteMany resources: requests: storage: 50Gi如果用StatefulSet NFS PV每个Pod都会获得一个独立的挂载目录。此时你在NFS服务端要规划好至少3个独立的导出子目录或者用StorageClass动态创建。切忌把多个副本指向同一个Path否则不同副本间的数据会互相踩踏。6. 权限问题与安全配置深挖6.1 为什么Pod经常报Permission denied权限是NFS挂载水土不服的头号原因。K8s Pod里的进程通常以非root用户运行比如UID是1000GID是1000有些镜像用的UID是101比如Nginx官方镜像。而NFS服务端目录的文件权限如果默认是root所有Pod里的非root用户根本没有写权限。常见的排查方法是检查Pod的securityContext。一个可测试的配置是在Deployment里指定fsGroupspec: securityContext: fsGroup: 1000K8s会让NFS挂载目录的Group自动变为fsGroup指定的值并赋予该Group读写权限。但要注意fsGroup对NFS卷的效果取决于NFS服务端是否开启了root_squash。如果服务端把root压缩成nobodyNFS传回来的文件所有者和组可能全是nobodyfsGroup就失效了。这背后的逻辑是K8s的fsGroup机制要靠客户端setgid来改变目录属组但NFS是网络文件系统服务端收到setgid请求时会根据导出配置决定是否允许。如果no_root_squash没开启客户端的root权限被压缩为普通用户setgid自然失败。在NFS场景下最简单粗暴但也最有效的做法是服务端共享目录chmod 777Pod里securityContext里设置fsGroup为实际运行用户的GID。这两者配合起来基本没有权限问题。6.2 root_squash vs no_root_squash 到底怎么选这里想多说几句因为这是我被问过最多的问题。NFS服务端的root_squash机制默认是开启的。它会把客户端的root用户UID 0映射为匿名用户nobody从而防止客户端以root身份随意读写服务端文件。这是很合理的安全机制因为NFS客户端本质上就是网络上的一个用户它的root不应该等同于服务端的root。但是在K8s环境里很多容器以root启动如果服务端开启了root_squash容器里root用户写的文件在服务端会变成nobody所有。此时如果Pod的fsGroup设置不当其他非root用户想读这些文件就会Failed。反过来如果你在exports里加上no_root_squash容器里的root就等价于服务端的root文件权限控制重新回到应用层K8s的fsGroup也能正常工作了。所以我的实际建议分成两种情况场景建议理由纯测试环境/内部研发环境no_root_squash避免权限排查消耗大量时间快速跑通业务生产环境/多租户环境保留root_squash安全第一配合正确的UID/GID规划生产环境用root_squash的话每个应用需要规划固定的UID/GID并在Deployment里通过securityContext指定。同时确保NFS服务端的共享目录属主属组与之一致。这个规划流程前期投入大但后期非常稳。6.3 安全加固的几个基础点NFS本身在网络安全方面不算太强内网使用注意几个基础点就够了exports文件里的访问地址不要用*而是要限定为K8s节点的网段或IP比如192.168.1.0/24。这样只有集群网段内的机器可以挂载。不要让NFS服务暴露到公网。168.1.x这个私有网段一旦被外部访问到NFS目录就会裸奔。可以在/etc/hosts.deny和/etc/hosts.allow里做TCP层面的访问限制。导出的目录尽量避免与NFS服务器的系统目录重叠。防火墙层面只放行K8s节点IP对2049端口NFS端口的访问。7. 常见问题与排查技巧实录7.1 PVC一直Pending怎么办PVC卡在Pending状态99%的原因是集群里没有匹配的PV。用下面的命令先看清楚状态kubectl describe pvc log-pvc常见报错信息是“no persistent volumes available for this claim and no storage class is set”。这种情况排查顺序查看PV列表kubectl get pv确认有没有相同accessModes和容量的PV。查看PV的Status是否是Available。如果PV被之前的PVC绑定了Status是Bound那对新的PVC来说不可用。确认PVC和PV的accessModes、容量是否匹配。确认PV有没有被打了污点taint或者标签隔离。确认PV的StorageClassName字段与PVC一致。如果PV里设置了storageClassName但不写具体的类名PVC没写同样的类名就无法匹配。NFS静态PV建议直接在PV定义中省略storageClassName字段或设为PVC同样留空这样匹配最简单。7.2 Pod一直ContainerCreating事件报mount failedkubectl describe pod nginx-log-shipper-xxxxxEvent里如果出现MountVolume.SetUp failed for volume pvc-xxx : mount failed: exit status 32 Mounting arguments: ... 192.168.1.100:/nfs-data/k8s-pv-shared ... Output: mount.nfs: Connection timed out先确定NFS Server的IP和端口是否通telnet 192.168.1.100 2049不通的情况下需要检查NFS服务端是否挂掉了systemctl status nfs-kernel-server防火墙是否放行iptables -L -n | grep 2049K8s节点到NFS Server的rpcbind111端口是否可达rpcinfo -p 192.168.1.100K8s节点是否安装nfs-commonwhich mount.nfs如果报错是mount.nfs: Protocol not supported说明客户端和服务端的NFS版本协商失败。检查服务端是否启用了NFSv4cat /proc/fs/nfsd/versions输出里应该有4。如果没有在/etc/default/nfs-kernel-server里开启v4重启服务。7.3 文件写入权限报错症状是应用启动后无法写入挂载目录日志报Permission denied。排查# 先看挂载是否正常 df -h | grep nfs # 再看实际目录权限 ls -ln /nfs-data/k8s-pv-shared/log我踩过一个印象深刻的坑明明NFS目录是777权限Pod里是root用户运行但写入还是失败。后来查到原因——K8s节点上的NFS客户端挂载参数里带了root_squash导致客户端root被压成了nobody服务端看到nobody的动作全部拒绝。最后在exports里加no_root_squash并重新exportfs -r才解决。7.4 NFS PV被释放后如何重新利用当PVC被删除后由于我们用的是Retain回收策略PV会变成Released状态。此时这个PV不能被新的PVC绑定看起来就像“卡住”了一样。处理方式kubectl get pv kubectl describe pv nfs-pv-log确认PV状态为Released后需要手动清理并让PV重新变回Available。操作步骤确认后端数据是否需要保留。Retain策略保证数据还在NFS目录里。删除PV对象不会删除NFS后端数据。用相同参数重新创建PV。kubectl delete pv nfs-pv-log kubectl apply -f pv.yaml新创建的PV由于没有绑定记录状态会回到Available。注意在K8s版本较新的环境中 Released或Failed状态的PV无法直接用kubectl apply更新状态delete recreate是最省心的办法。7.5 性能问题排查如果挂载NFS后应用变慢先要看是不是网络问题。NFS走的是TCP协议如果网络出现丢包性能会断崖式下降。排查ping -c 100 192.168.1.100 | grep loss sar -n TCP 1 5另外一个容易被忽略的是NFS块大小。默认挂载参数是rsize/wsize1048576在NFSv4下但某些客户端可能协商成较小的值导致大文件读写慢。建议在PV里加上挂载参数spec: nfs: server: 192.168.1.100 path: /nfs-data/k8s-pv-shared/log readOnly: false mountOptions: - rsize1048576 - wsize1048576 - hard - nfsvers4.2mountOptions会直接传给NFS客户端。hard表示写失败时无限重试nfsvers4.2强制使用NFSv4.2协议Ubuntu 24.04上的NFS Server默认支持。这个配置能明显提升大文件读写的吞吐量。7.6 速查表PV/PVC/NFS常见问题问题症状可能原因排查命令解决建议PVC Pending无可用PVkubectl describe pvc检查PV状态、accessMode、容量挂载超时网络不通telnet 192.168.1.100 2049检查防火墙与服务状态Permission deniedroot_squash或fsGroup配置不当ls -ln /nfs-data/xxx调整exports或securityContextPod创建后一直等待节点无nfs-commonwhich mount.nfs安装nfs-common/nfs-utils写入卡死NFS服务端无响应ping sar检查服务端IO和网络文件不是最新NFS缓存导致mount参数加noac修改mountOptions8. 生产级配置模板从PV到Pod一步到位综合以上全部经验我提供一套可以直接套用的生产级配置模板。这套模板经过了测试环境验证也经过线上日志收集环境的考验。NFS服务端Ubuntu 24.04# /etc/exports配置 /nfs-data/k8s-pv-shared 192.168.1.0/24(rw,sync,no_subtree_check,no_root_squash)创建PV# nfs-pv.yaml apiVersion: v1 kind: PersistentVolume metadata: name: nfs-pv-log labels: usage: log-storage spec: capacity: storage: 100Gi volumeMode: Filesystem accessModes: - ReadWriteMany persistentVolumeReclaimPolicy: Retain mountOptions: - rsize1048576 - wsize1048576 - hard - nfsvers4.2 nfs: server: 192.168.1.100 path: /nfs-data/k8s-pv-shared/log readOnly: falsekubectl apply -f nfs-pv.yaml kubectl get pv创建PVC# log-pvc.yaml apiVersion: v1 kind: PersistentVolumeClaim metadata: name: log-pvc namespace: production spec: accessModes: - ReadWriteMany resources: requests: storage: 10Gi selector: matchLabels: usage: log-storagekubectl apply -f log-pvc.yaml kubectl get pvc -n production看到Bound状态说明绑定成功。部署应用# deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: nginx-log-shipper namespace: production spec: replicas: 3 selector: matchLabels: app: nginx-log-shipper template: metadata: labels: app: nginx-log-shipper spec: securityContext: fsGroup: 1000 containers: - name: nginx image: nginx:1.25 ports: - containerPort: 80 volumeMounts: - name: log-volume mountPath: /var/log/nginx volumes: - name: log-volume persistentVolumeClaim: claimName: log-pvc这个配置全部跑起来后三个Nginx副本的日志都会落到NFS服务端的同一个目录里。你可以直接用tail -f /nfs-data/k8s-pv-shared/log/access.log实时观察所有实例的访问记录。9. 从我自己的踩坑经历里总结的几点心得说几个只有真正落地跑过才会懂的经验第一NFS PV的storage容量声明如果写的太大而底层磁盘实际容量不足等到写满了你会收到一堆“No space left”的告警但K8s侧没有任何提示。所以PV容量最好与实际NFS后端磁盘容量或目录配额保持一致同时在NFS服务端做好磁盘空间监控。我现在的做法是每个PV对应NFS服务端的一个子目录在子目录里用quota做限额这样PV声明多少容量就真实限制多少容量。第二不要在生产环境直接使用NFS共享目录和研发共用。研发的调试脚本往往以root执行一旦脚本误操作把共享目录清了生产数据就没了。我见过一次事故开发在NFS根目录执行rm -rf命令刚好把生产环境日志目录删了。后来我把生产PV和开发PV在NFS Server端用独立的用户、独立目录严格隔离权限也只给到各自对应的节点网段。第三NFS挂载参数里的hard选项字面意思是“坚强的”但它有个副作用——如果NFS Server长时间无响应客户端会一直挂着Pod里的进程卡在IO上Kubelet检测不到重启信号最终的结果就是Pod假死。遇到NFS Server故障时最快的恢复方式是重启NFS服务或者直接重启K8s节点上的kubelet。如果你更追求业务的快速失败可以把mountOptions改成soft,timeo50,retrans2让客户端在超时后快速失败配合Pod的重启策略让业务自动恢复。第四不要迷信ReadWriteMany多Pod同时写同一个NFS目录如果应用层没有做日志轮转和文件名管理日志文件会被多个Pod同时打开句柄冲突和写入错乱会让你排查到怀疑人生。我们的规范是每个Pod写独立的子目录目录名用Pod名或节点名区分最后在日志采集层统一汇聚。从初次接触K8s存储到彻底搞懂NFS PV/PVC这套链路我中间经历过PVC Pending时的抓狂也经历过权限问题反复修不好的烦躁。但现在再看NFS作为Kubernetes的存储后端确实是中小团队最务实的选择。它能用最简单的架构解决掉“Pod数据不丢、多Pod能共享”这两个最核心的问题而且排查思路清晰、运维成本低。如果你也正在K8s存储方案上纠结不妨先把NFS这套跑熟等业务规模真的大到NFS撑不住时你自然就知道要不要上Ceph、怎么上Ceph了。
返回列表