K8s 数据存储全解析:从 EmptyDir 到 PV/PVC

发布时间:2026/7/4 4:46:35

K8s 数据存储全解析:从 EmptyDir 到 PV/PVC 前言在 Kubernetes 中Pod 是调度和运行的最小单元但 Pod 本身具有“ ephemeral”短暂的特性——它们可以被频繁地创建、销毁和重新调度。当容器崩溃或 Pod 被删除时容器内部文件系统中的数据也会随之丢失。为了解决这一困境Kubernetes 引入了Volume数据卷抽象。Volume 的生命周期独立于容器即使容器被重建Volume 中的数据也会保留并可以被同一 Pod 中的其他容器共享。Kubernetes 支持多种类型的 Volume包括emptyDir、hostPath、NFS、Ceph、云存储等。本文将系统介绍几种常用的存储机制并通过实际示例演示如何在不同场景下为 Pod 配置持久化存储最后深入讲解 PVPersistentVolume和 PVCPersistentVolumeClaim的使用方法及回收策略。一、K8s 中可用的数据卷概览Kubernetes Volume 本质上就是一个目录它与 Pod 绑定Pod 中的所有容器都可以挂载并访问该目录。Volume 的生命周期与 Pod 相同但内容是否持久取决于 Volume 的类型。Kubernetes 支持以下主流 Volume 类型emptyDir临时目录Pod 删除时数据清除。hostPath宿主机节点上的目录Pod 删除后数据保留但节点故障则数据不可用。NFS网络文件系统支持多节点同时挂载数据持久化。PVC/PV通过 PersistentVolume 和 PersistentVolumeClaim 实现存储资源与存储消费的解耦。其他Ceph、GlusterFS、AWS EBS、GCE PD、Azure Disk 等。二、emptyDir 类型存储emptyDir是最基础的 Volume 类型。当 Pod 被分配到节点时会在该节点上创建一个空目录Pod 中的所有容器都可以挂载该目录。注意emptyDir的生命周期与 Pod 完全一致——Pod 从节点删除时该目录及其内容会被永久清除但如果只是容器被销毁而 Pod 还在则 Volume 不受影响。示例同一个 Pod 内两个容器共享 Volume下面的 YAML 文件描述了一个 Pod其中包含两个容器pods1和pods2它们共享一个名为share-vm的 emptyDir Volume# pods.yml apiVersion: v1 kind: Pod metadata: name: share-pods spec: containers: - name: pods1 image: busybox volumeMounts: - name: share-vm mountPath: /pods1 args: - /bin/sh - -c - echo hello k8s pods /pods1/hello ; sleep 3000 - name: pods2 image: busybox volumeMounts: - name: share-vm mountPath: /pods2 args: - /bin/sh - -c - cat /pods2/hello ; sleep 3000 volumes: - name: share-vm emptyDir: {}执行命令创建 Pod[rootmaster data]# kubectl apply -f pods.yml查看pods2容器的日志确认它能读取到pods1写入的数据[rootmaster data]# kubectl logs share-pods pods2 hello k8s pods通过docker inspect可以在宿主机上看到两个容器挂载的正是同一个临时目录路径类似/var/lib/kubelet/pods/pod-id/volumes/kubernetes.io~empty-dir/share-vm。emptyDir特别适合 Pod 内容器之间需要临时共享数据的场景无需额外配置但数据不持久化。三、hostPath 存储类型hostPathVolume 将宿主机节点文件系统中的已有目录挂载到 Pod 的容器中。一些系统级 Pod如kube-apiserver就会使用hostPath挂载节点上的证书目录[rootmaster data]# kubectl edit --namespacekube-system pod kube-apiserver-master可以看到类似下面的挂载配置volumes: - hostPath: path: /etc/ssl/certs type: DirectoryOrCreate name: ca-certs - hostPath: path: /etc/pki type: DirectoryOrCreate name: etc-pki - hostPath: path: /etc/kubernetes/pki type: DirectoryOrCreate name: k8s-certshostPath的特点是即使 Pod 被删除宿主机上的目录依然保留持久性比emptyDir强。但缺点也很明显——它增加了 Pod 与节点的耦合性Pod 必须调度到存在特定目录的节点上且节点故障时数据无法自动迁移。示例将宿主机/tmp目录挂载到 nginx 容器# hostpath.yml apiVersion: v1 kind: Pod metadata: name: myhostpath spec: containers: - name: nginx image: nginx volumeMounts: - name: hostpath mountPath: /usr/share/nginx/html volumes: - name: hostpath hostPath: path: /tmp type: Directory创建 Pod找到其运行的节点假设为 node1[rootmaster data]# kubectl apply -f hostpath.yml [rootmaster data]# kubectl get pod -o wide NAME READY STATUS RESTARTS AGE IP NODE myhostpath 1/1 Running 0 7m 10.244.1.95 node1在 node1 上创建测试文件[rootnode1 data]# echo test hostpath /tmp/index.html通过 Pod IP 访问 nginx[rootmaster data]# curl 10.244.1.95 test hostpath四、使用 NFS 作为数据存储NFSNetwork File System是一种成熟的网络文件共享协议支持多个节点同时挂载数据持久化且独立于节点。要在 Kubernetes 中使用 NFS需要先搭建一台 NFS 服务器并在所有节点上安装nfs-utils客户端。步骤 1搭建 NFS 服务器IP 示例172.16.213.230# 在 NFS 服务器上 [rootnfsserver ~]# yum install nfs-utils -y [rootnfsserver ~]# mkdir -p /nfs [rootnfsserver ~]# chown -R nfsnobody.nfsnobody /nfs [rootnfsserver ~]# echo /nfs *(rw,sync,no_root_squash) /etc/exports [rootnfsserver ~]# systemctl start nfs步骤 2在每个 K8s 节点安装客户端[rootnode1 ~]# yum install nfs-utils -y步骤 3在 Pod 中挂载 NFS# nfs-pod.yml apiVersion: v1 kind: Pod metadata: name: nfs-pod spec: containers: - name: nginx image: nginx volumeMounts: - name: nfsdata mountPath: /usr/share/nginx/html volumes: - name: nfsdata nfs: path: /nfs server: 172.16.213.230创建 Pod 并测试[rootmaster data]# kubectl apply -f nfs-pod.yml [rootnfsserver ~]# echo nfs test /nfs/index.html [rootmaster data]# curl $(kubectl get pod nfs-pod -o jsonpath{.status.podIP}) nfs test五、使用 NFS PV 存储数据5.1 PV 和 PVC 概念PersistentVolumePV由管理员预先创建的存储资源是集群中的一块存储“存货”独立于任何 Pod。PV 具有持久性生命周期与集群相当。PersistentVolumeClaimPVC由用户开发者创建的存储请求指明所需存储的大小和访问模式。Kubernetes 会根据 PVC 的要求自动匹配并绑定合适的 PV。通过 PV/PVC用户只需要关心“我需要多大的存储”而不用关心底层是 NFS、Ceph 还是云硬盘管理员则负责管理真实的存储后端。5.2 创建 PV编写nfs-pv1.yml定义一个容量为 1Gi、访问模式为ReadWriteMany的 PV存储后端为 NFSapiVersion: v1 kind: PersistentVolume metadata: name: mypv1 spec: capacity: storage: 1Gi accessModes: - ReadWriteMany persistentVolumeReclaimPolicy: Recycle storageClassName: nfs1 nfs: path: /nfs/pv1 server: 172.16.213.230关键字段说明accessModes支持ReadWriteOnce单节点读写、ReadOnlyMany多节点只读、ReadWriteMany多节点读写。persistentVolumeReclaimPolicyPV 回收策略可选Retain保留数据需管理员手动处理、Recycle自动清理数据即rm -rf、Delete删除云存储资源。storageClassName存储类名称用于 PVC 匹配 PV。nfs指定 NFS 服务器地址和共享路径。创建 PV[rootmaster data]# kubectl apply -f nfs-pv1.yml5.3 创建 PVC编写nfs-pvc1.yml申请 1Gi 存储空间并使用相同的storageClassNameapiVersion: v1 kind: PersistentVolumeClaim metadata: name: mypvc1 spec: accessModes: - ReadWriteMany storageClassName: nfs1 resources: requests: storage: 1Gi volumeName: mypv1 # 可选直接指定绑定的 PV创建 PVC 并查看绑定状态[rootmaster data]# kubectl apply -f nfs-pvc1.yml [rootmaster data]# kubectl get pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE mypvc1 Bound mypv1 1Gi RWX nfs1 10s [rootmaster data]# kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS AGE mypv1 1Gi RWX Recycle Bound default/mypvc1 nfs1 30s5.4 在 Pod 中使用 PVC创建一个 Deployment通过persistentVolumeClaim引用mypvc1# deploy-pod2-nginx.yml apiVersion: apps/v1 kind: Deployment metadata: name: nginxserver-deployment spec: replicas: 3 selector: matchLabels: app: nginxserver template: metadata: labels: app: nginxserver spec: containers: - name: nginx-pod image: nginx volumeMounts: - name: mynfsdata mountPath: /usr/share/nginx/html volumes: - name: mynfsdata persistentVolumeClaim: claimName: mypvc1为方便外部访问创建一个 NodePort Service# nfs-service-nginx.yml apiVersion: v1 kind: Service metadata: name: service-nginx spec: type: NodePort selector: app: nginxserver ports: - protocol: TCP nodePort: 31000 port: 80 targetPort: 80应用并测试[rootmaster data]# kubectl apply -f deploy-pod2-nginx.yml [rootmaster data]# kubectl apply -f nfs-service-nginx.yml [rootmaster data]# kubectl get svc NAME TYPE CLUSTER-IP PORT(S) AGE service-nginx NodePort 10.106.0.151 80:31000/TCP 55m现在可以通过http://任意节点IP:31000访问 nginx。将文件放入 NFS 服务器的/nfs/pv1/index.html即可看到内容。5.5 PV 回收策略实战Recycle 策略示例当 PVC 被删除时Kubernetes 会启动一个recyclerPod 清理 PV 中的数据然后将 PV 状态重置为Available[rootmaster data]# kubectl delete pvc mypvc1 [rootmaster data]# kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS AGE mypv1 1Gi RWX Recycle Available nfs1 4h注意Recycle策略已被标记为废弃推荐使用动态 PV 供给或Retain。Retain 策略示例如果希望删除 PVC 后保留数据可以将 PV 的回收策略设置为Retain创建 PV 时指定或通过 patch 修改persistentVolumeReclaimPolicy: Retain删除 PVC 后PV 状态变为Released但数据依然保留在 NFS 服务器上。此时该 PV 不能被新的 PVC 绑定需要管理员手动删除并重新创建 PV删除 PV 对象不会删除后端数据[rootmaster data]# kubectl delete pv mypv2 [rootmaster data]# kubectl apply -f nfs-pv2.yml # 重新创建新创建的 PV 状态为Available可供后续 PVC 使用。结尾Kubernetes 的数据存储机制为容器化应用提供了灵活、持久的存储方案。从基础的emptyDir、hostPath到网络存储 NFS再到解耦存储供给与消费的 PV/PVCK8s 覆盖了从开发测试到生产环境的多种存储需求。本文通过丰富的示例演示了每种存储类型的使用场景和配置方法并深入讲解了 PV 的回收策略Recycle 与 Retain对数据生命周期的影响。在实际生产环境中建议对于临时、非关键的共享数据使用emptyDir。仅当需要访问节点特定文件如证书、设备文件时使用hostPath。对于多节点共享的持久化数据优先使用 NFS、Ceph 或云存储并结合 PV/PVC 进行资源管理。合理设置回收策略避免误删重要数据对于生产数据推荐使用Retain策略。理解并熟练运用这些存储机制将使你在 Kubernetes 中构建有状态应用时更加得心应手。

相关新闻