在 Kubernetes(简称 K8s)集群中,容器的临时性特点使得数据持久化存储成为关键需求。如果没有持久化存储,容器重启后数据会丢失,这对于数据库、日志存储等场景来说是无法接受的。本文将从 K8s 存储核心概念入手,结合实际操作案例,详细讲解持久卷(PV)、持久卷申领(PVC)的使用,以及如何通过 NFS 实现跨节点的数据共享,帮助您快速掌握 K8s 存储配置技巧。

一、K8s 存储核心概念解析

在开始实践前,我们需要先理清 PV、PVC、StorageClass 这三个核心组件的定义与关系,它们共同构成了 K8s 的持久化存储体系。

1. 持久卷(PersistentVolume,PV)

PV 是集群中的一块 “独立存储资源”,由管理员预先创建或通过 StorageClass 动态生成,不受 Pod 生命周期影响。它可以对应物理机目录、NFS 共享目录、云存储(如 AWS EBS、阿里云 OSS)等多种存储介质,相当于 “存储池” 中的具体存储单元。

核心属性

  • 容量(Capacity):指定 PV 的存储大小,如 10Gi、50Gi。
  • 访问模式(Access Modes):定义 PV 的读写权限,共 4 种(实际常用前 3 种):
    • RWO(ReadWriteOnce):仅允许单个节点挂载读写。
    • ROX(ReadOnlyMany):允许多个节点挂载只读。
    • RWX(ReadWriteMany):允许多个节点挂载读写(NFS、GlusterFS 等分布式存储支持)。
    • RWOP(ReadWriteOncePod):仅允许单个 Pod 挂载读写(K8s 1.23 + 支持)。
  • 回收策略(Reclaim Policy):PVC 删除后 PV 的处理方式:
    • Retain(保留):PV 保留数据,需管理员手动清理(推荐生产环境使用)。
    • Delete(删除):PV 随 PVC 删除,同时删除底层存储(如云存储卷)。
    • Recycle(回收):清空 PV 数据后重新变为 “可用” 状态(已废弃,不推荐)。
  • 存储类(StorageClass):PV 的 “分类标签”,PVC 通过指定 StorageClass 名称与 PV 绑定,实现存储资源的按需匹配。

2. 持久卷申领(PersistentVolumeClaim,PVC)

PVC 是用户对存储资源的 “请求单”,类似 Pod 对 CPU、内存的资源申请。用户无需关心底层存储细节(如存储介质、服务器地址),只需通过 PVC 声明所需的存储容量、访问模式和存储类,K8s 会自动匹配符合条件的 PV 并完成绑定。

核心逻辑:Pod 不直接使用 PV,而是通过 PVC 间接引用存储资源,层级关系为:Pod → Volume(引用PVC) → PVC → PV → 底层存储(如NFS、本地目录)

3. 存储类(StorageClass)

StorageClass 的核心作用是 “分类管理存储资源”,解决 PV 手动创建的繁琐问题。管理员可以为不同类型的存储(如高性能 SSD、普通机械硬盘、NFS)定义不同的 StorageClass,用户通过 PVC 指定 StorageClass,即可自动获取对应的存储资源(需搭配 Provisioner 实现动态 PV 创建)。

二、基础实践:本地目录 + PV+PVC 配置

首先从最简单的 “本地目录存储” 开始,演示 PV、PVC 的创建与使用,理解存储资源的绑定流程。

1. 环境准备

假设已拥有一个 K8s 集群(1 个 Master 节点 + 2 个 Node 节点),需在所有节点执行以下操作,创建存储目录并准备测试文件:

# 创建本地存储目录
mkdir -p /web/data
# 新建测试首页文件(用于后续验证数据挂载)
sh -c "echo 'Hello from Kubernetes storage' > /web/data/index.html"
# 验证文件是否创建成功
cat /web/data/index.html

执行结果应显示:Hello from Kubernetes storage,表示本地目录和测试文件已准备完成。

2. 创建 PV(持久卷)

PV 的配置通过 YAML 文件定义,需指定存储类型、容量、访问模式和本地目录路径。在 Master 节点创建pv-volume.yaml文件,内容如下:

vim pv-volume.yaml

apiVersion: v1
kind: PersistentVolume
metadata:
  name: task-pv-volume  # PV名称,需唯一
  labels:
    type: local  # 自定义标签,用于筛选
spec:
  storageClassName: sanchuang  # 存储类名称,PVC需匹配此值
  capacity:
    storage: 10Gi  # PV容量
  accessModes:
    - ReadWriteOnce  # 访问模式:单节点读写
  hostPath:
    path: "/web/data"  # 关联本地存储目录

执行以下命令创建 PV:

bash

# 应用YAML文件创建PV
kubectl apply -f pv-volume.yaml
# 查看PV状态(STATUS为Available表示可用)
kubectl get pv
# 查看PV详细信息(验证配置是否正确)
kubectl describe pv task-pv-volume

预期输出中,PV 的STATUS应为AvailableStorageClasssanchuangSource字段显示HostPath: /web/data,表示 PV 创建成功且可。

3. 创建 PVC(持久卷申领)

PVC 需与 PV 的storageClassNameaccessModes保持一致,否则会绑定失败(状态为 Pending)。在 Master 节点创建pvc-claim.yaml文件:

vim pvc-claim.yaml

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: task-pv-claim  # PVC名称,需唯一
spec:
  storageClassName: sanchuang  # 匹配PV的存储类名称
  accessModes:
    - ReadWriteOnce  # 匹配PV的访问模式
  resources:
    requests:
      storage: 3Gi  # 申领3Gi存储(需小于PV容量10Gi)

执行命令创建 PVC 并验证绑定状态:

# 应用YAML文件创建PVC
kubectl apply -f pvc-claim.yaml
# 查看PVC状态(STATUS为Bound表示绑定成功)
kubectl get pvc

若输出中 PVC 的STATUSBoundVOLUME字段显示task-pv-volume(对应 PV 名称),说明 PVC 与 PV 已成功绑定。

4. 创建 Pod 使用 PVC 存储

创建一个 Nginx Pod,将 PVC 挂载到容器的 Nginx 网页目录(/usr/share/nginx/html),实现数据持久化。创建pod-pv-pvc.yaml文件:

vim pod-pv-pvc.yaml

apiVersion: v1
kind: Pod
metadata:
  name: task-pv-pod  # Pod名称
spec:
  nodeName: node1  # 定向调度到node1节点(可选)
  volumes:
    - name: task-pv-storage  # 卷名称,需与volumeMounts对应
      persistentVolumeClaim:
        claimName: task-pv-claim  # 引用PVC名称
  containers:
    - name: task-pv-container
      image: nginx  # 使用Nginx镜像
      imagePullPolicy: IfNotPresent  # 本地有镜像则不重新拉取
      ports:
        - containerPort: 80  # Nginx默认端口
          name: "http-server"
      volumeMounts:
        - mountPath: "/usr/share/nginx/html"  # 容器内挂载路径(Nginx网页目录)
          name: task-pv-storage  # 关联卷名称

执行命令创建 Pod 并验证:

bash

# 应用YAML文件创建Pod
kubectl apply -f pod-pv-pod.yaml
# 查看Pod状态(STATUS为Running表示正常运行)
kubectl get pod -o wide

待 Pod 运行后,通过 Pod 的 IP 地址访问 Nginx 服务,验证存储是否生效:

bash

# 替换为实际Pod的IP(从kubectl get pod -o wide输出中获取)
curl 10.224.166.180

预期输出Hello from Kubernetes storage,表示本地目录的测试文件已通过 PV/PVC 挂载到 Pod 中。

5. 验证数据持久化

修改 Node1 节点本地目录的index.html文件,模拟数据更新:

bash

# 在Node1节点执行,修改测试文件
vim /web/data/index.html
# 添加内容:welcome to sanchuang
# 保存后再次访问Pod
curl 10.224.166.180

预期输出包含welcome to sanchuang,说明容器内的数据随本地目录更新而同步,验证了数据持久化效果。

三、进阶实践:NFS+PV+PVC 实现跨节点共享

本地目录存储的局限性在于 “仅能在单个节点使用”,若 Pod 调度到其他节点,会无法访问存储。而 NFS(网络文件系统)支持跨节点共享目录,是 K8s 集群中常用的分布式存储方案。以下演示如何通过 NFS 实现多节点存储共享。

1. 搭建 NFS 服务器

步骤 1:选择 NFS 服务器节点

可在 K8s 集群内选择一个节点(如 Master 节点,IP:192.168.203.128)作为 NFS 服务器,也可使用独立服务器。

步骤 2:安装 NFS 服务

NFS 服务器节点所有 K8s 节点安装 NFS 工具(否则节点无法挂载 NFS 目录):

bash

# 安装NFS工具(CentOS/RHEL系统)
yum install nfs-utils -y
步骤 3:配置 NFS 共享目录

在 NFS 服务器节点执行以下操作,创建共享目录并配置权限:

bash

# 创建NFS共享目录
mkdir -p /web/data
# 准备测试文件(与本地存储一致,便于后续验证)
echo "welcome to sanchuang !!" > /web/data/index.html
# 配置NFS共享(编辑/etc/exports文件)
vim /etc/exports
# 添加以下内容(允许192.168.203.0/24网段访问,权限为读写)
/web/data 192.168.203.0/24(rw,sync,all_squash)
# 刷新NFS配置(使共享生效)
exportfs -rv
# 启动NFS服务并设置开机自启
systemctl start nfs-server
systemctl enable nfs-server
# 关闭防火墙(避免端口拦截,生产环境可配置白名单)
systemctl stop firewalld
systemctl disable firewalld
步骤 4:验证 NFS 共享

在任意 K8s 节点(如 Node1)测试 NFS 挂载,确保跨节点访问正常:

bash

# 创建临时挂载目录
mkdir -p /pv_pvc_nfs
# 挂载NFS共享目录
mount 192.168.203.128:/web/data /pv_pvc_nfs/
# 查看挂载结果(若显示NFS目录,则挂载成功)
df -Th | grep nfs
# 验证文件访问(应显示NFS服务器上的测试内容)
cat /pv_pvc_nfs/index.html

若输出welcome to sanchuang !!,说明 NFS 服务器搭建成功,跨节点共享正常。

2. 创建 NFS 类型的 PV

与本地目录 PV 不同,NFS 类型的 PV 需指定 NFS 服务器地址和共享目录。在 Master 节点创建nfs-pv.yaml文件:

vim nfs-pv.yaml

apiVersion: v1
kind: PersistentVolume
metadata:
  name: sc-nginx-pv  # PV名称
  labels:
    type: sc-nginx-pv  # 自定义标签
spec:
  capacity:
    storage: 50Gi  # PV容量(需大于PVC申领容量)
  accessModes:
    - ReadWriteMany  # 支持多节点读写(NFS核心优势)
  storageClassName: nfs  # 存储类名称(PVC需匹配)
  nfs:
    path: "/web/data"  # NFS共享目录
    server: 192.168.203.128  # NFS服务器IP
    readOnly: false  # 非只读模式

执行命令创建 PV 并验证:

# 创建PV
kubectl apply -f nfs-pv.yaml
# 查看PV状态(STATUS为Available表示可用)
kubectl get pv

3. 创建 NFS 类型的 PVC

创建pvc-nfs.yaml文件,申领 NFS 类型的存储资源:

vim pvc-nfs.yaml

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: sc-nginx-pvc  # PVC名称
spec:
  accessModes:
    - ReadWriteMany  # 匹配PV的多节点读写模式
  resources:
    requests:
      storage: 40Gi  # 申领40Gi存储(小于PV的50Gi)
  storageClassName: nfs  # 匹配NFS类型PV的存储类

执行命令创建 PVC 并验证绑定:

# 创建PVC
kubectl apply -f pvc-nfs.yaml
# 查看PVC状态(STATUS为Bound表示绑定成功)
kubectl get pvc

4. 部署多副本 Pod 使用 NFS 存储

为了体现 NFS “多节点共享” 的优势,使用 Deployment 创建 4 个 Nginx 副本,所有 Pod 共享 NFS 存储。创建pod-nfs.yaml文件:

vim pod-nfs.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment-nfs  # Deployment名称
  labels:
    app: nginx-nfs
spec:
  replicas: 4  # 4个Pod副本
  selector:
    matchLabels:
      app: nginx-nfs
  template:
    metadata:
      labels:
        app: nginx-nfs
    spec:
      volumes:
        - name: sc-pv-storage-nfs  # 卷名称
          persistentVolumeClaim:
            claimName: sc-nginx-pvc  # 引用NFS类型的PVC
      containers:
        - name: nginx
          image: nginx:latest
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 80
          volumeMounts:
            - mountPath: "/usr/share/nginx/html"  # 容器内挂载路径
              name: sc-pv-storage-nfs  # 关联卷名称

执行命令部署 Pod 并验证:

# 部署Deployment
kubectl apply -f pod-nfs.yaml
# 查看Pod状态(4个Pod均为Running表示正常)
kubectl get pod -o wide

5. 验证多节点共享效果

通过 Pod 的 IP 地址访问 Nginx 服务,验证所有 Pod 均能读取 NFS 存储的文件:

# 替换为实际Pod的IP(从kubectl get pod -o wide输出中获取)
curl 10.224.166.136
curl 10.224.104.2

所有请求均应输出welcome to sanchuang !!,说明多节点 Pod 成功共享 NFS 存储。

6. 暴露 Service 供外部访问

为了让外部机器(如 Windows)能访问 NFS 存储的 Nginx 服务,创建 NodePort 类型的 Service。创建service-pod-nfs.yaml文件:

apiVersion: v1
kind: Service
metadata:
  name: my-service-pod-nfs  # Service名称
spec:
  type: NodePort  # NodePort类型(外部可通过节点IP+端口访问)
  selector:
    app: nginx-nfs  # 匹配Deployment的Pod标签
  ports:
    - port: 80  # Service内部端口
      targetPort: 80  # Pod内部端口
      nodePort: 30007  # 外部访问端口(范围:30000-32767)

执行命令创建 Service 并验证:

# 创建Service
kubectl apply -f service-pod-nfs.yaml
# 查看Service信息(确认NodePort端口)
kubectl get svc

在 Windows 浏览器中输入http://K8s节点IP:30007(如http://192.168.203.130:30007),即可访问 Nginx 服务,显示 NFS 存储的网页内容。

7. 验证 NFS 数据同步更新

在 NFS 服务器节点修改index.html文件,模拟数据更新:

bash

# 在NFS服务器执行
vim /web/data/index.html
# 添加内容:welcome to sanchuang teacherfeng nfs pv pvc

再次通过浏览器或curl访问 Pod/Service,所有请求均会显示更新后的内容,验证了 NFS 数据的实时同步效果。

四、高级技巧:StorageClass 与动态 PV

在前面的实践中,PV 需要管理员手动创建,当集群规模较大时,手动管理 PV 效率低下。StorageClass 结合 Provisioner 可以实现 PV 的 “动态创建”,用户只需创建 PVC,K8s 会自动生成对应的 PV。

1. StorageClass 核心作用

  • 简化存储管理:管理员无需手动创建 PV,只需定义 StorageClass 模板。
  • 按需分配资源:用户通过 PVC 指定 StorageClass,自动获取匹配的存储资源。
  • 支持多存储类型:可为不同存储介质(如 SSD、NFS、云存储)定义不同的 StorageClass。

2. NFS 动态 PV 实现思路

要实现 NFS 动态 PV,需部署nfs-client-provisioner(NFS 客户端自动配置程序),它会监听 PVC 请求,自动在 NFS 服务器上创建目录并生成 PV。核心流程如下:

  1. 部署 NFS 服务器(已完成)。
  2. 部署nfs-client-provisioner(通过 Pod 运行)。
  3. 创建 StorageClass,关联nfs-client-provisioner
  4. 用户创建 PVC,Provisioner 自动创建 PV 并绑定。

3. 动态 PV 优势

  • 无需手动创建 PV,减少管理员工作量。
  • PVC 删除后,PV 和 NFS 目录可自动清理(根据回收策略配置)。
  • 支持按 PVC 需求动态调整存储容量(需 NFS 服务器支持)。

五、常见问题与解决方案

在 K8s 存储配置过程中,容易遇到各种问题,以下是常见问题及解决方法:

1. PVC 状态一直为 Pending

  • 原因 1:PVC 的storageClassName与 PV 不匹配。
    • 解决:确保 PVC 的storageClassName与 PV 一致。
  • 原因 2:PVC 的accessModes与 PV 不匹配(如 PVC 请求 RWX,PV 仅支持 RWO)。
    • 解决:调整 PVC 或 PV 的accessModes,确保一致。
  • 原因 3:PVC 申领容量超过 PV 容量。
    • 解决:减小 PVC 的storage请求,或增大 PV 容量。

2. Pod 挂载 NFS 失败(状态为 CrashLoopBackOff)

  • 原因 1:K8s 节点未安装nfs-utils工具。
    • 解决:在所有节点执行yum install nfs-utils -y
  • 原因 2:NFS 服务器防火墙未关闭,端口被拦截。
    • 解决:在 NFS 服务器执行systemctl stop firewalld,或配置 NFS 端口白名单。
  • 原因 3:NFS 共享目录权限不足(如未设置 777 权限)。
    • 解决:在 NFS 服务器执行chmod 777 /web/data,确保节点可读写。

3. 数据更新后 Pod 未同步

  • 原因:NFS 客户端缓存导致数据延迟。
    • 解决:在 PV 配置中添加 NFS 挂载参数(如mountOptions: ["noatime", "nodiratime"]),或重启 Pod 刷新缓存。

六、总结

本文从 K8s 存储核心概念出发,通过 “本地目录 + PV+PVC” 和 “NFS+PV+PVC” 两个实践案例,详细讲解了 K8s 持久化存储的配置流程。重点介绍了 NFS 的跨节点共享优势,以及 StorageClass 动态 PV 的实现思路,帮助您理解 “Pod→PVC→PV→底层存储” 的层级关系。

在实际生产环境中,需根据业务需求选择合适的存储方案:

  • 单节点存储需求:使用本地目录或 HostPath(适合测试环境)。
  • 多节点共享需求:使用 NFS、GlusterFS 等分布式存储(适合生产环境)。
  • 大规模集群:结合 StorageClass 与 Provisioner,实现 PV 动态创建,提升管理效率。

通过本文的实践,相信您已能熟练配置 K8s 存储,为容器应用提供稳定、可靠的持久化支持。

Logo

为武汉地区的开发者提供学习、交流和合作的平台。社区聚集了众多技术爱好者和专业人士,涵盖了多个领域,包括人工智能、大数据、云计算、区块链等。社区定期举办技术分享、培训和活动,为开发者提供更多的学习和交流机会。

更多推荐