Kubernetes 存储完全指南(适用于 Kubernetes v1.35)

文档说明

  • 适用版本:Kubernetes v1.35.4
  • 前置知识:了解 Pod 的基本概念
  • 目标:从零掌握 Kubernetes 存储体系,包括 emptyDir、hostPath、PV、PVC、StorageClass 的原理与使用

第一章:为什么需要 Kubernetes 存储?

容器本身是无状态的——当 Pod 被删除或重新调度到其他节点时,容器内的所有数据都会丢失。这对于需要持久化存储的应用(如数据库、消息队列、文件存储)是不可接受的。

Kubernetes 提供了完善的存储解决方案,让应用数据可以独立于 Pod 生命周期而存在:

存储类型生命周期适用场景
emptyDir与 Pod 生命周期相同临时缓存、同 Pod 容器间共享数据
hostPath与节点生命周期相同访问宿主机文件、调试
PV / PVC独立于 Pod 生命周期持久化存储(数据库、消息队列等)
StorageClass集群级别动态自动创建 PV

第二章:emptyDir —— 临时存储

2.1 什么是 emptyDir?

emptyDir 是 Kubernetes 中最简单的存储卷类型。当 Pod 被调度到节点上时,会在节点上创建一个空目录,该目录在 Pod 的整个生命周期内存在

生命周期:emptyDir 的生命周期与 Pod 完全绑定——Pod 删除,数据随之删除。

2.2 核心特性

特性说明
创建时机Pod 被调度到节点时自动创建
初始状态空目录
数据持久性仅存在于 Pod 运行期间
容器共享同一个 Pod 中的多个容器可以共享同一个 emptyDir
存储介质默认使用节点磁盘,可指定 medium: Memory 使用内存(tmpfs)

2.3 适用场景

  • 同一 Pod 内多个容器之间的数据共享(如 Sidecar 容器读取主容器的日志)
  • 应用程序的临时缓存暂存空间(scratch space)
  • 计算过程中的中间文件存储

2.4 YAML 示例

apiVersion: v1
kind: Pod
metadata:
  name: emptydir-demo
spec:
  containers:
  - name: writer
    image: busybox
    command: ['sh', '-c', 'echo "hello" > /cache/data.txt && sleep 3600']
    volumeMounts:
    - name: cache-volume
      mountPath: /cache
  - name: reader
    image: busybox
    command: ['sh', '-c', 'sleep 3600']
    volumeMounts:
    - name: cache-volume
      mountPath: /shared
  volumes:
  - name: cache-volume
    emptyDir: {}              # 空配置即可

2.5 使用内存作为存储介质

volumes:
- name: memory-volume
  emptyDir:
    medium: Memory            # 使用 tmpfs(内存文件系统)
    sizeLimit: 256Mi          # 限制大小(v1.8+)

注意:使用 medium: Memory 时,数据存储在内存中,读写速度极快,但 Pod 重启或节点重启后数据会丢失,且受节点内存容量限制。

第三章:hostPath —— 宿主机存储

3.1 什么是 hostPath?

hostPath 卷能将宿主机节点文件系统上的文件或目录挂载到 Pod 中。

生命周期:hostPath 的数据独立于 Pod 生命周期——Pod 删除后,宿主机上的数据依然存在。

3.2 核心特性

特性说明
数据持久性数据存在于节点本地,Pod 删除不影响
节点绑定数据与特定节点绑定,Pod 调度到其他节点无法访问
使用场景访问宿主机文件(如 Docker socket)、节点监控、调试

3.3 ⚠️ 安全风险

hostPath 存在严重的安全风险

  • Pod 可以读写宿主机敏感目录(如 //etc/var
  • 可能导致宿主机文件系统被破坏
  • 可能泄露宿主机敏感信息

最佳实践

  1. 尽可能避免使用 hostPath
  2. 如果必须使用,限制范围到所需的文件或目录
  3. 只读方式挂载
  4. 使用 PodSecurityPolicyPod Security Standards 限制 hostPath 的使用

3.4 YAML 示例

apiVersion: v1
kind: Pod
metadata:
  name: hostpath-demo
spec:
  containers:
  - name: test-container
    image: busybox
    command: ['sh', '-c', 'sleep 3600']
    volumeMounts:
    - name: host-volume
      mountPath: /host-data
      readOnly: true           # 以只读方式挂载,降低风险
  volumes:
  - name: host-volume
    hostPath:
      path: /var/log           # 宿主机路径
      type: Directory          # 可选:检查路径类型

3.5 hostPath 的 type 字段

type说明
""(空)不做任何检查
DirectoryOrCreate如果目录不存在则创建(权限 0755)
Directory目录必须存在
FileOrCreate如果文件不存在则创建(权限 0644)
File文件必须存在
SocketUNIX socket 必须存在
CharDevice字符设备必须存在
BlockDevice块设备必须存在

第四章:PV 与 PVC —— 持久化存储的基石

4.1 为什么需要 PV 和 PVC?

emptyDirhostPath 都有明显局限:

  • emptyDir:数据随 Pod 删除而消失
  • hostPath:数据绑定到特定节点,Pod 迁移后无法访问

为了解决这些问题,Kubernetes 引入了 PV(PersistentVolume)PVC(PersistentVolumeClaim)

4.2 核心概念

PV(PersistentVolume,持久卷) 是集群中的一块存储资源,由管理员事先制备或通过 StorageClass 动态制备。PV 是集群级别的资源,独立于任何 Pod 的生命周期。

PVC(PersistentVolumeClaim,持久卷声明) 是用户对存储的请求。Pod 会消耗节点资源,而 PVC 会消耗 PV 资源。

核心设计思想:PV 和 PVC 将存储的“供应”存储的“使用” 解耦。用户只需声明需要多大的存储、什么访问模式,无需关心底层存储的具体实现。

4.3 PV 的关键字段

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-example
spec:
  capacity:
    storage: 10Gi                    # 存储容量
  accessModes:
    - ReadWriteMany                  # 访问模式
  persistentVolumeReclaimPolicy: Retain  # 回收策略
  storageClassName: standard         # 存储类名称
  # 以下是具体存储类型配置(二选一)
  nfs:                               # NFS 存储
    server: nfs-server.example.com
    path: /exports
  # 或 hostPath(仅用于测试)
  # hostPath:
  #   path: /data/pv

4.4 PVC 的关键字段

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-example
  namespace: default
spec:
  accessModes:
    - ReadWriteOnce                  # 访问模式
  resources:
    requests:
      storage: 5Gi                   # 请求的存储大小
  storageClassName: standard         # 存储类名称(用于匹配 PV)

4.5 访问模式(Access Modes)

访问模式说明适用场景
ReadWriteOnce(RWO)单个节点可读写大多数应用(数据库等)
ReadOnlyMany(ROX)多个节点可只读共享只读数据(配置文件)
ReadWriteMany(RWX)多个节点可读写共享文件系统(NFS)
ReadWriteOncePod(RWOP)单个 Pod 可读写(v1.27+)需要独占存储的 Pod

4.6 回收策略(Reclaim Policy)

策略说明
Retain保留数据,需管理员手动清理
Delete删除 PV 时同时删除底层存储(默认)
Recycle已弃用,执行 rm -rf 后重新可用

4.7 PV 的生命周期

PV 和 PVC 的交互遵循以下生命周期:

┌─────────────┐      ┌─────────────┐      ┌─────────────┐      ┌─────────────┐
│   制备      │ ──▶ │   绑定      │ ──▶ │   使用      │ ──▶ │   回收      │
│Provisioning │      │  Binding    │      │   Using     │      │  Reclaiming  │
└─────────────┘      └─────────────┘      └─────────────┘      └─────────────┘
阶段一:制备(Provisioning)

PV 的制备有两种方式:

静态制备(Static Provisioning) :集群管理员手动创建一批 PV 卷。这些 PV 对象存在于 Kubernetes API 中,可供用户使用。

动态制备(Dynamic Provisioning) :当静态 PV 无法匹配 PVC 时,集群可以自动创建新的 PV。基于 StorageClass 实现。

阶段二:绑定(Binding)

当用户创建 PVC 时,Kubernetes 的 PV Controller 会持续监听 API Server,发现处于待绑定状态的 PVC 后,尝试将其与满足条件的 PV 进行绑定。

绑定条件

  1. 存储容量 ≥ PVC 请求的大小
  2. 访问模式匹配
  3. StorageClass 名称匹配(如果指定)

如果找不到匹配的 PV,PVC 将保持 Pending 状态,直到有合适的 PV 可用。

阶段三:使用(Using)

Pod 通过引用 PVC 来使用存储:

apiVersion: v1
kind: Pod
metadata:
  name: pvc-demo
spec:
  containers:
  - name: app
    image: nginx
    volumeMounts:
    - name: data-volume
      mountPath: /data
  volumes:
  - name: data-volume
    persistentVolumeClaim:
      claimName: pvc-example    # 引用 PVC
阶段四:回收(Reclaiming)

当 PVC 被删除后,PV 根据回收策略决定如何处理:

  • Retain:PV 状态变为 Released,数据保留,需管理员手动清理
  • Delete:PV 和底层存储一起被删除

第五章:StorageClass —— 动态存储的核心

5.1 为什么需要 StorageClass?

静态制备存在明显问题:

  • 管理负担重:管理员需要提前创建各种大小、访问模式的 PV
  • 资源浪费:提前创建的 PV 可能长时间不被使用
  • 难以匹配:用户请求的 PVC 可能找不到完全匹配的 PV
  • 沟通成本高:用户需要了解可用的 PV 类型

StorageClass 解决了这些问题——它让存储可以按需自动创建

5.2 StorageClass 是什么?

StorageClass 是 Kubernetes 中用于描述存储类别的 API 对象。它为管理员提供了描述不同存储类型的方法。

一句话理解:StorageClass 是存储的“配置模板”——当用户创建 PVC 并指定 StorageClass 时,Kubernetes 会根据这个模板自动创建对应的 PV 和底层存储。

5.3 StorageClass 的核心字段

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: standard
  annotations:
    storageclass.kubernetes.io/is-default-class: "false"   # 是否为默认存储类
provisioner: kubernetes.io/aws-ebs                         # 制备器(必须)
parameters:                                                # 制备器参数
  type: gp2
  fsType: ext4
reclaimPolicy: Retain                                      # 回收策略(默认 Delete)
allowVolumeExpansion: true                                 # 是否允许卷扩容
volumeBindingMode: WaitForFirstConsumer                    # 卷绑定模式
mountOptions:                                              # 挂载选项
  - discard

5.4 核心字段详解

provisioner(制备器)—— 最重要的字段

每个 StorageClass 必须指定一个制备器(Provisioner),它决定了使用哪个卷插件来创建 PV

内置制备器(名称以 kubernetes.io 为前缀):

制备器说明
kubernetes.io/aws-ebsAWS EBS 块存储
kubernetes.io/gce-pdGCE 持久化磁盘
kubernetes.io/azure-diskAzure 磁盘
kubernetes.io/azure-fileAzure 文件存储
kubernetes.io/vsphere-volumevSphere 存储
kubernetes.io/cinderOpenStack Cinder

外部制备器:也支持使用外部制备器(如 CSI 驱动),名称由用户自定义。例如华为云的 nas.csi.everest.io

parameters(参数)

parameters 是传递给制备器的具体参数,不同制备器支持的参数不同。

示例(AWS EBS):

parameters:
  type: gp3                    # 磁盘类型(gp2/gp3/io1)
  fsType: ext4                 # 文件系统类型
  iopsPerGB: "10"              # 每 GB 的 IOPS(io1 专用)

示例(GCE PD):

# "slow" 存储类 —— 标准磁盘
parameters:
  type: pd-standard

# "fast" 存储类 —— SSD 磁盘
parameters:
  type: pd-ssd
reclaimPolicy(回收策略)

指定由该 StorageClass 动态创建的 PV 的回收策略:

  • Delete(默认):PVC 删除时,PV 和底层存储一起删除
  • Retain:PVC 删除时,PV 和数据保留,需手动处理
volumeBindingMode(卷绑定模式)

控制 PV 的创建和绑定时机

模式说明
Immediate(默认)PVC 创建后立即创建 PV 并绑定
WaitForFirstConsumer等到第一个使用该 PVC 的 Pod 被调度时才创建 PV

为什么需要 WaitForFirstConsumer 某些存储(如本地存储)与特定节点绑定,如果提前创建 PV 但 Pod 被调度到其他节点,会导致无法挂载。WaitForFirstConsumer 确保 PV 在 Pod 确定调度节点后才创建,避免这个问题。

allowVolumeExpansion(允许扩容)

设置为 true 时,允许在 PVC 创建后在线扩容存储容量。

5.5 默认 StorageClass

可以将某个 StorageClass 标记为集群的默认存储类。当 PVC 没有指定 storageClassName 时,会自动使用默认的 StorageClass

标记为默认

metadata:
  annotations:
    storageclass.kubernetes.io/is-default-class: "true"

注意:集群中只能有一个默认 StorageClass。如果存在多个默认 StorageClass,Kubernetes 会使用最近创建的那个。

第六章:StorageClass 工作原理深度解析

6.1 动态存储供应的完整流程

当用户创建了一个指定 storageClassName 的 PVC 时,背后发生了什么?

用户创建 PVC(指定 storageClassName)
        │
        ▼
┌───────────────────────────────────────────────────────────────┐
│ 1. API Server 接收 PVC 请求,存入 etcd                       │
└───────────────────────────────────────────────────────────────┘
        │
        ▼
┌───────────────────────────────────────────────────────────────┐
│ 2. PV Controller(在 Controller Manager 中)监听新 PVC      │
│    发现 PVC 指定了 StorageClass,但没有匹配的 PV             │
└───────────────────────────────────────────────────────────────┘
        │
        ▼
┌───────────────────────────────────────────────────────────────┐
│ 3. PV Controller 调用 StorageClass 中指定的 Provisioner     │
│    传入 parameters 中的配置参数                              │
└───────────────────────────────────────────────────────────────┘
        │
        ▼
┌───────────────────────────────────────────────────────────────┐
│ 4. Provisioner 与底层存储系统 API 交互                       │
│    在存储系统中创建实际的存储卷(如云盘、NFS 导出等)       │
└───────────────────────────────────────────────────────────────┘
        │
        ▼
┌───────────────────────────────────────────────────────────────┐
│ 5. Provisioner 返回存储卷信息,PV Controller 创建 PV 对象   │
│    PV 包含存储卷的访问信息(如云盘 ID、NFS 地址等)         │
└───────────────────────────────────────────────────────────────┘
        │
        ▼
┌───────────────────────────────────────────────────────────────┐
│ 6. PV Controller 将 PV 与 PVC 绑定                          │
│    PVC 状态变为 Bound                                       │
└───────────────────────────────────────────────────────────────┘
        │
        ▼
┌───────────────────────────────────────────────────────────────┐
│ 7. Pod 通过 PVC 使用存储                                    │
│    Kubelet 将存储卷挂载到 Pod 的容器中                      │
└───────────────────────────────────────────────────────────────┘

6.2 关键组件职责

组件职责
API Server接收并存储 PVC、StorageClass 等资源对象
PV Controller(在 Controller Manager 中)管理 PV/PVC 生命周期,包括静态绑定和触发动态供应
Provisioner(制备器)与底层存储系统交互,实际创建存储卷
Scheduler确保 Pod 被调度到能够访问 PV 的节点上
Kubelet将存储卷挂载到 Pod 的容器中

6.3 静态制备 vs 动态制备

对比维度静态制备动态制备(StorageClass)
PV 创建方式管理员手动创建系统自动创建
存储资源提前分配,可能浪费按需分配,无浪费
管理成本
响应速度立即可用需等待存储创建(通常几秒到几分钟)
适用场景小规模集群、测试环境生产环境、大规模集群

第七章:完整实战示例

7.1 场景:部署一个使用持久化存储的 MySQL

Step 1:创建 StorageClass(使用本地存储模拟,仅测试用)

注意:生产环境应使用云厂商或 NFS 等网络存储。

# storageclass.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: fast-storage
provisioner: kubernetes.io/no-provisioner   # 本地存储无制备器
volumeBindingMode: WaitForFirstConsumer
Step 2:创建 PV(静态制备)
# pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: mysql-pv
spec:
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: fast-storage
  hostPath:                         # 仅用于测试,生产环境使用网络存储
    path: /data/mysql
Step 3:创建 PVC
# pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysql-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi
  storageClassName: fast-storage
Step 4:创建 Deployment 使用 PVC
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysql
spec:
  replicas: 1
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
      - name: mysql
        image: mysql:8.0
        env:
        - name: MYSQL_ROOT_PASSWORD
          value: "password"
        volumeMounts:
        - name: mysql-storage
          mountPath: /var/lib/mysql
      volumes:
      - name: mysql-storage
        persistentVolumeClaim:
          claimName: mysql-pvc
Step 5:部署
kubectl apply -f storageclass.yaml
kubectl apply -f pv.yaml
kubectl apply -f pvc.yaml
kubectl apply -f deployment.yaml
Step 6:验证
# 查看 PV 状态
kubectl get pv

# 查看 PVC 状态(应为 Bound)
kubectl get pvc

# 查看 Pod
kubectl get pods

# 验证数据持久化:删除 Pod 后重新创建,数据依然存在
kubectl delete pod mysql-xxx
kubectl get pods   # 新 Pod 自动创建

第八章:故障排查

8.1 常见问题与解决方案

问题可能原因解决方案
PVC 一直 Pending没有匹配的 PV检查 StorageClass 名称、容量、访问模式是否匹配
PVC 一直 PendingStorageClass 不存在检查 StorageClass 是否存在:kubectl get sc
Pod 无法启动PVC 未绑定检查 PVC 状态,确保 Bound
Pod 无法启动节点无法访问存储检查网络存储是否可达,防火墙规则
数据丢失使用了 emptyDir 或错误的回收策略确认存储类型,检查 reclaimPolicy

8.2 调试命令

# 查看所有 PV
kubectl get pv

# 查看所有 PVC
kubectl get pvc -A

# 查看 PVC 详情(含事件)
kubectl describe pvc <pvc-name>

# 查看 PV 详情
kubectl describe pv <pv-name>

# 查看 StorageClass
kubectl get storageclass

# 查看 StorageClass 详情
kubectl describe storageclass <sc-name>

第九章:最佳实践总结

9.1 存储类型选择指南

场景推荐存储类型
临时缓存、同 Pod 容器共享数据emptyDir
调试、访问宿主机文件hostPath(谨慎使用)
生产环境持久化存储(数据库等)PV + PVC + StorageClass(动态制备)
共享文件系统(多 Pod 读写)NFS + PV + PVC
云上生产环境云厂商 CSI 驱动 + StorageClass

9.2 通用最佳实践

  1. 生产环境使用动态制备(StorageClass):减少管理成本,按需分配资源
  2. 合理设置回收策略:重要数据使用 Retain,临时数据使用 Delete
  3. 使用 WaitForFirstConsumer 绑定模式:避免存储与节点不匹配的问题
  4. 监控存储使用情况:防止存储空间耗尽
  5. 定期备份重要数据:PV 不是备份方案
  6. 避免使用 hostPath:除非确有需要,且务必限制访问范围
  7. 为 StorageClass 设置默认值:提升用户体验

附录:快速索引

需求命令/配置
查看所有 PVkubectl get pv
查看所有 PVCkubectl get pvc -A
查看 StorageClasskubectl get storageclass
查看 PVC 详情kubectl describe pvc <name>
创建 StorageClasskubectl apply -f storageclass.yaml
创建 PVkubectl apply -f pv.yaml
创建 PVCkubectl apply -f pvc.yaml
设置默认 StorageClassstorageclass.kubernetes.io/is-default-class: "true"
emptyDir 配置emptyDir: {}
emptyDir 使用内存emptyDir: { medium: Memory }
hostPath 配置hostPath: { path: /host/path }

第十章:NFS 实战 —— 在 Kubernetes 中使用 NFS 存储

10.1 环境准备:搭建 NFS 服务器

首先,我们需要一台独立的 NFS 服务器(可以是虚拟机或物理机),为 Kubernetes 集群提供存储能力。

第一步:安装 NFS 服务端

以 Ubuntu 22.04 为例:

# 更新系统并安装 NFS 服务端
sudo apt update
sudo apt install -y nfs-kernel-server
第二步:创建共享目录

创建一个目录用于存储 Kubernetes 的持久化数据:

# 创建共享目录
sudo mkdir -p /data/k8s-storage

# 修改权限,允许容器内的进程写入
sudo chown nobody:nogroup /data/k8s-storage
sudo chmod 777 /data/k8s-storage

参数说明chmod 777 是为所有用户开放读写执行权限。在生产环境中,你应根据实际的安全策略进行更精细的权限控制。

第三步:配置 NFS 共享

编辑 /etc/exports 文件,定义共享目录和访问权限:

sudo vim /etc/exports

添加以下内容(请将 192.168.1.0/24 替换为你的 Kubernetes 节点所在网段):

/data/k8s-storage 192.168.1.0/24(rw,sync,no_subtree_check,no_root_squash)

关键参数说明

  • rw:客户端对该目录拥有读写权限。
  • sync:同步写入,保证数据一致性。
  • no_subtree_check:禁用子树检查,提升性能。
  • no_root_squash:允许客户端的 root 用户保留 root 权限,这对于容器内进程以 root 身份运行至关重要。
第四步:启动并验证 NFS 服务
# 重新加载 exports 配置
sudo exportfs -ra

# 重启 NFS 服务
sudo systemctl restart nfs-kernel-server

# 设置开机自启
sudo systemctl enable nfs-kernel-server

# 验证共享目录是否已导出
sudo exportfs -v
第五步:在所有 Kubernetes 节点上安装 NFS 客户端

为了让每个节点都能挂载 NFS 存储,需要在所有节点(包括 master 和 worker)上安装 NFS 客户端工具。

# Ubuntu/Debian
sudo apt install -y nfs-common

# CentOS/RHEL
sudo yum install -y nfs-utils
第六步:验证节点与 NFS 服务器的连接

在任意一个 Kubernetes 节点上执行以下命令,测试是否能正确看到 NFS 服务端共享的目录:

# 查看 NFS 服务器共享的目录(请替换为你的 NFS 服务器 IP)
showmount -e 192.168.1.10

如果一切正常,你应该能看到 /data/k8s-storage


10.2 静态制备:手动创建 PV 和 PVC

这是最直接的方式,由管理员提前创建好 PV。

10.2.1 创建 PV (PersistentVolume)

创建一个 nfs-pv.yaml 文件:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfs-pv
spec:
  capacity:
    storage: 10Gi                     # PV 的总容量
  accessModes:
    - ReadWriteMany                   # 支持多节点同时读写
  persistentVolumeReclaimPolicy: Retain # 回收策略:保留数据
  storageClassName: ""                # 空字符串表示不使用 StorageClass
  nfs:                                # 指定后端为 NFS
    path: /data/k8s-storage           # NFS 服务器上的共享目录
    server: 192.168.1.10              # NFS 服务器的 IP 地址

应用此配置:

kubectl apply -f nfs-pv.yaml

查看 PV 状态:

kubectl get pv
10.2.2 创建 PVC (PersistentVolumeClaim)

创建一个 nfs-pvc.yaml 文件:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: nfs-pvc
spec:
  accessModes:
    - ReadWriteMany                   # 访问模式必须与 PV 匹配
  resources:
    requests:
      storage: 5Gi                    # 请求 5Gi 存储
  storageClassName: ""                # 静态制备,留空

应用此配置:

kubectl apply -f nfs-pvc.yaml

查看 PVC 状态,确认已与 PV 绑定(状态为 Bound):

kubectl get pvc
10.2.3 在 Pod 中使用 PVC

创建一个 nginx-pod.yaml 文件,将 NFS 存储挂载到 /usr/share/nginx/html 目录:

apiVersion: v1
kind: Pod
metadata:
  name: nginx-nfs-pod
spec:
  containers:
  - name: nginx
    image: nginx:latest
    volumeMounts:
    - name: nfs-storage
      mountPath: /usr/share/nginx/html  # 容器内的挂载路径
  volumes:
  - name: nfs-storage
    persistentVolumeClaim:
      claimName: nfs-pvc                # 引用上面创建的 PVC

应用此配置:

kubectl apply -f nginx-pod.yaml

验证 Pod 是否正常运行,并检查挂载是否成功。


10.3 动态制备:使用 NFS Provisioner(推荐方案)

静态制备需要手动管理 PV,在大规模集群中维护成本较高。动态制备通过 StorageClassProvisioner 实现存储的按需自动创建,是生产环境更推荐的方案。

10.3.1 核心原理

动态制备的核心是 NFS Subdir External Provisioner。它的工作流程如下:

  1. 用户创建 PVC 时,指定一个关联了 NFS Provisioner 的 StorageClass。
  2. Provisioner 监听到 PVC 的创建请求。
  3. Provisioner 在 NFS 服务器的共享目录下,自动创建一个以 命名空间-PVC名称-随机串 命名的子目录
  4. Provisioner 自动创建一个 PV,指向这个新的 NFS 子目录。
  5. Kubernetes 将这个新 PV 与 PVC 绑定。

这样,用户只需要创建 PVC,无需关心 PV 的细节,实现了存储的“按需分配”。

10.3.2 部署 NFS Provisioner

官方推荐使用 Helm 进行部署。

第一步:添加 Helm 仓库并更新

helm repo add nfs-subdir-external-provisioner https://kubernetes-sigs.github.io/nfs-subdir-external-provisioner/
helm repo update

第二步:安装 Provisioner

执行以下命令进行安装(请将 nfs.servernfs.path 替换为你的实际信息):

helm install nfs-provisioner nfs-subdir-external-provisioner/nfs-subdir-external-provisioner \
  --set nfs.server=192.168.1.10 \
  --set nfs.path=/data/k8s-storage \
  --set storageClass.defaultClass=true \
  --set storageClass.name=nfs-storage

参数说明

  • nfs.server:你的 NFS 服务器 IP 地址。
  • nfs.path:NFS 服务器上的共享目录。
  • storageClass.defaultClass=true:将新创建的 StorageClass 设置为集群默认存储类。
  • storageClass.name=nfs-storage:指定 StorageClass 的名称为 nfs-storage

可选参数

  • 可以通过 --set storageClass.archiveOnDelete=true 来控制 PVC 删除时,其对应的 NFS 子目录是归档(重命名)还是直接删除。
10.3.3 使用动态 PVC

部署完成后,你只需创建 PVC 并指定 storageClassName: nfs-storage 即可。

创建一个 dynamic-pvc.yaml 文件:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: dynamic-nfs-pvc
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 1Gi
  storageClassName: nfs-storage        # 指定我们刚创建的 StorageClass

应用此配置:

kubectl apply -f dynamic-pvc.yaml

查看 PVC,你会发现它自动进入了 Bound 状态,并且自动创建了一个对应的 PV。

kubectl get pvc
kubectl get pv

你可以在 NFS 服务器的 /data/k8s-storage 目录下看到 Provisioner 自动创建的子目录。

文档版本:v1.0
适用环境:Kubernetes v1.35+
前置条件:已部署 Kubernetes 集群并配置 kubectl

更多推荐