Kubernetes Volume

学习参考:

一、先搞懂:为什么要有 Volume?它到底是啥?

1. 容器天生的 “缺陷”

你可以把容器想象成网吧的还原电脑

  • 开机能用,能存东西、改文件;

  • 一旦重启 / 关机,你存的所有东西全没了,系统自动还原。

对应到 K8s 里:容器崩溃、Pod 删除重建后,容器里写入的日志、上传的文件、数据库数据会全部丢失。 同时,同一个 Pod 里如果有两个容器(比如业务程序 + 日志采集),两个容器之间默认没法互相读写文件。

2. Volume 就是解决这两个问题的

Volume 通俗说就是给容器外接的 “存储文件夹 / 硬盘”

  • 可以是临时的,也可以是永久保存的;

  • 可以是本机的目录,也可以是网络上的共享文件夹;

  • 同一个 Pod 里的多个容器可以共用同一个 Volume,互相传文件。

3. 最核心的使用逻辑(两步走,所有 Volume 通用)

第一步:在 Pod 的 spec.volumes声明你要用什么存储(相当于你先拿出一个 U 盘) 第二步:在容器的 volumeMounts把这个存储挂到容器的某个目录(相当于把 U 盘插到电脑的 USB 口上)

  • 两者靠 name 字段配对,名字必须完全一样。

对应你之前做过的 Nginx 实验,直观对应关系:

spec:
  # 第一步:定义卷(声明“我有一个叫 html 的U盘”)
  volumes:
  - name: html
    configMap:
      name: webapp-1

  containers:
  - name: nginx
    image: nginx
    # 第二步:挂载到容器内(把U盘插到容器的 /usr/share/nginx/html 目录)
    volumeMounts:
    - name: html      # 和上面 volumes 的 name 对应,必须完全一致
      mountPath: /usr/share/nginx/html

###


二、高频重要命令(按场景分类,附用途说明)

1. 日常查看类

# 查看某个 Pod 的卷挂载详情,能看到挂了哪些卷、路径是什么
kubectl describe pod <Pod名字>

# 以 YAML 格式导出 Pod 的完整卷配置,核对配置是否正确
kubectl get pod <Pod名字> -o yaml

# 查看 ConfigMap 里有什么内容
kubectl get configmap <cm名字> -o yaml
# 查看 Secret 里的内容(base64 编码)
kubectl get secret <secret名字> -o yaml

2 . 排障排查类(挂载失败必用)

# 看 Pod 事件,找 FailedMount 报错,是挂载失败第一排查命令
kubectl describe pod <Pod名字> | grep -A 20 Events

# 登录节点,看 kubelet 日志,找底层挂载失败的具体原因
journalctl -u kubelet -f

# 进入容器,验证文件有没有挂进去、内容对不对
kubectl exec -it <Pod名字> -- ls <挂载目录路径>
kubectl exec -it <Pod名字> -- cat <挂载目录路径>/文件名

# 节点手动测试挂载(NFS 排障专用,先确认节点本身能挂上)
mount -t nfs 10.1.8.30:/nfsshares /tmp/test

三、最容易踩的坑(重点避坑)

  1. 挂载覆盖原目录 把卷挂到容器里已经存在的目录,目录里原来的文件会全部被覆盖消失。 如果只想加一个文件,用 subPath 单文件挂载,别整目录挂。

  2. 权限不足 NFS、hostPath 挂载后,容器里写文件报 Permission denied

  • NFS:服务端加 no_root_squash 参数;

  • 其他卷:可以在 Pod 里指定运行用户,或者给目录开放权限。

  1. 生命周期搞混

  • emptyDir:Pod 删,数据没;

  • ConfigMap/Secret:Pod 删,配置还在集群里;

  • NFS/PV:Pod 删,数据保留在存储服务器上。

  1. 节点没装客户端 网络存储(NFS、Ceph)必须所有节点都装客户端工具,不然 Pod 调度到未安装的节点,直接挂载失败。

  2. PVC 找不到 PV PVC 一直 Pending,大概率是没有符合容量、访问模式的 PV,或者 PV 已经被其他 PVC 绑定了。 一个 PV 只能绑定一个 PVC,是一对一的关系。


四、一句话总结重点

  • Volume 就是给容器接外部存储,解决容器数据丢失、多容器共享文件两大问题;

  • 临时共享用 emptyDir,注入配置用 ConfigMap/Secret,节点本地用 hostPath,共享持久化用 NFS;

  • 生产统一用 PV+PVC 解耦存储管理和使用,配合 StorageClass 实现自动供给;

  • 排障先看 Pod 事件,再看节点 kubelet 日志,最后手动测试存储连通性。

环境准备

[root@master30 ~]# kubectl create ns storage
[root@master30 ~]# kubectl config set-context --current --namespace storage

Volume 类型

Kubernetes支持Volume类型有:

  • emptyDir

  • hostPath

  • gcePersistentDisk

  • awsElasticBlockStore

  • nfs

  • iscsi

  • fc (fibre channel)

  • flocker

  • glusterfs

  • rbd

  • cephfs

  • gitRepo

  • secret

  • persistentVolumeClaim

  • downwardAPI

  • projected

  • azureFileVolume

  • azureDisk

  • vsphereVolume

  • Quobyte

  • PortworxVolume

  • ScaleIO

  • StorageOS

  • local

emptyDir

默认情况下,当Pod分配到Node上时,将会创建emptyDir,只要Node上的Pod一直运行,Volume就会一直存。当Pod(不管任何原因)从Node上被删除时,emptyDir也同时会删除,存储的数据也将永久删除。

emptyDir:Pod 内部的「临时共享抽屉」

大白话解释

Pod 创建的时候,自动在节点硬盘上开一个空文件夹,共享给 Pod 里所有容器用; Pod 一删除,这个文件夹连带里面的东西一起删掉,永久消失。

核心特点
  • 生命周期 = Pod 的生命周期,Pod 在数据在,Pod 删数据没;

  • 同一个 Pod 里的所有容器,都能读写这个文件夹,互相传文件。

什么时候用?

最典型场景:同一个 Pod 里,业务容器写日志,sidecar 采集容器读日志 比如:业务程序把日志写到这个共享目录,日志采集容器从这个目录读日志发走,两个容器不用互相进入对方的系统。 还有就是临时计算缓存、中转文件,不需要持久保存的数据。

重点注意

❌ 绝对不能用来存需要长期保存的数据,Pod 一删就彻底没了。

实验:准备一个包含2个容器的pod,使用emptyDir。

实验核心目的: 这个实验是为了直观验证 Kubernetes 中 emptyDir 卷的两个核心特性: 1.同 Pod 多容器共享存储:同一个 Pod 内的多个容器,可以通过 emptyDir 卷互相读写文件,实现容器间数据交换; 2.生命周期与 Pod 完全绑定:emptyDir 是临时存储,Pod 创建时自动生成,Pod 删除时目录和数据会被彻底销毁,数据不会持久保存。

[root@master30 ~]# vim pod-with-emptyDir.yaml
apiVersion: v1
kind: Pod
metadata:
  name: busybox
  labels:
    app: busybox
spec:
  # 第一步:在Pod层面定义卷
  volumes:
  - name: datavolume    # 卷的标识名,用于和容器挂载绑定
    emptyDir: {}        # 卷类型:空目录,使用默认配置(节点磁盘存储)

  # 第二步:两个容器分别挂载同一个卷
  containers:
  - name: busybox1
    image: docker.io/library/busybox
    imagePullPolicy: IfNotPresent
    command: ['sh', '-c', 'echo Hello Kubernetes! && sleep 3600']
    volumeMounts:
    - mountPath: /data    # 容器内的挂载目录
      name: datavolume    # 和上面volumes的name一一对应

  - name: busybox2
    image: docker.io/library/busybox
    imagePullPolicy: IfNotPresent
    command: ['sh', '-c', 'echo Hello Kubernetes! && sleep 3600']
    volumeMounts:
    - mountPath: /data    # 第二个容器也挂载到自己的 /data 目录
      name: datavolume    # 绑定同一个卷,实现共享

配置说明:

  • spec.volumes:定义卷,是默认卷类型。

  • spec.containers.volumeMounts:引用卷

# 创建pod
[root@master30 ~ 17:00:02]# kubectl apply -f  pod-with-emptyDir.yaml 
pod/busybox created
[root@master30 ~ 17:00:13]# kubectl  get pods
NAME      READY   STATUS    RESTARTS   AGE
busybox   2/2     Running   0          5s

# 获取容器ID
[root@master30 ~ 17:01:11]# kubectl  describe  pod busybox | egrep -o "Container ID.*//.{12}"
Container ID:  containerd://81f980630140
Container ID:  containerd://64543c7c77ff

# 获取容器所在节点
[root@master30 ~ 17:01:30]# kubectl describe  pod busybox | egrep Node:
Node:             worker32.zy.cloud/10.1.8.32
       worker32.laoma.cloud/10.1.8.32

# 登录到node查看容器挂载情况
[root@worker32 ~ 12:09:48]# crictl  inspect 81f980630140 | egrep datavolume
        "hostPath": "/var/lib/kubelet/pods/749f07a8-c232-46b9-80ea-98313382f9a3/volumes/kubernetes.io~empty-dir/datavolume",
          "host_path": "/var/lib/kubelet/pods/749f07a8-c232-46b9-80ea-98313382f9a3/volumes/kubernetes.io~empty-dir/datavolume"
          "source": "/var/lib/kubelet/pods/749f07a8-c232-46b9-80ea-98313382f9a3/volumes/kubernetes.io~empty-dir/datavolume",

# 创建数据
[root@master30 ~ 17:01:52]# kubectl  exec  busybox -c busybox1 -- touch /data/b1-f1
[root@master30 ~ 17:04:51]# kubectl  exec  busybox -c busybox2 -- ls /data/
b1-f1

# node上查看数据
[root@worker32 ~ 17:02:49]# ls /var/lib/kubelet/pods/749f07a8-c232-46b9-80ea-98313382f9a3/volumes/kubernetes.io~empty-dir/datavolume/
b1-f1

# 删除pod,验证emptyDir
[root@master30 ~ 17:05:04]# kubectl  delete pod busybox --force 
Warning: Immediate deletion does not wait for confirmation that the running resource has been terminated. The resource may continue to run on the cluster indefinitely.
pod "busybox" force deleted
[root@master30 ~ 17:07:40]# kubectl get pods
No resources found in storage namespace.
[root@worker32 ~ 17:07:05]# ls /var/lib/kubelet/pods/749f07a8-c232-46b9-80ea-98313382f9a3/volumes/kubernetes.io~empty-dir/datavolume/
ls: cannot access '/var/lib/kubelet/pods/749f07a8-c232-46b9-80ea-98313382f9a3/volumes/kubernetes.io~empty-dir/datavolume/': No such file or directory


# 容器删除后,需要等待一些时间,临时卷数据等待才会删除。

实验核心目的:
1.同 Pod 多容器共享存储:同一个 Pod 内的多个容器,可以通过 emptyDir 卷互相读写文件,实现容器间数据交换;
2.生命周期与 Pod 完全绑定:emptyDir 是临时存储,Pod 创建时自动生成,Pod 删除时目录和数据会被彻底销毁,数据不会持久保存。

hostPath

hostPath允许Pod将Node的文件系统中某个目录挂载到Pod内部。pod删除后,hostPath卷数据保留。

hostPath:直接用节点本机的文件夹

大白话解释

把 Worker 节点本机硬盘上的某个目录,直接塞到容器里给容器用。 容器删了,节点上的文件还在;但 Pod 如果漂移到别的节点,就会挂载新节点的目录,数据就对不上了。

核心特点
  • 数据存在节点本地,Pod 删除不影响数据;

  • Pod 换节点运行,数据就不连续了(A 节点存的东西,B 节点看不到);

  • 安全风险高:容器能读写宿主机的文件,恶意容器可以修改宿主机系统。

什么时候用?

基本只有两类场景用:

  1. DaemonSet 组件:比如采集节点系统日志、访问 Docker 套接字,必须读取节点本机的文件;

  2. 临时测试用,生产环境强烈不推荐。

示例

把节点的 /var/log 目录挂进容器,让容器能查看节点的系统日志:

volumes:
- name: 节点日志
  hostPath:
    path: /var/log   # 宿主机上的目录
    type: Directory  # 类型是目录
volumeMounts:
- name: 节点日志
  mountPath: /host-log
重点坑

不要用来存数据库、业务数据,节点一挂数据就没了,而且 Pod 调度到别的节点就读不到数据了。

实验:准备一个pod,使用hostPath。

实验核心目标 本实验是 emptyDir 实验的对比延伸,核心验证 hostPath 卷的三大特性: 1.直接映射节点本地目录:将 Worker 节点上指定的本地目录,直接挂载到容器内部,容器读写的就是宿主机真实文件; 2.数据独立于 Pod 持久化:Pod 删除后,节点本地目录的数据不会被清理,永久保留,和 emptyDir「随 Pod 销毁」形成核心区别; 3.挂载权限精细控制:可以单独给某个容器设置只读挂载,防止容器修改宿主机文件,提升安全性。

[root@master30 ~]# vim pod-with-hostPath.yaml
apiVersion: v1          # API版本:Pod 属于 Kubernetes 核心 v1 资源组,固定写法
kind: Pod               # 资源类型:声明当前要创建的是 Pod 资源
metadata:               # 元数据块:定义 Pod 的身份标识与标签
  name: busybox         # Pod 名称:同一命名空间内唯一,是操作 Pod 的唯一标识
  labels:               # 标签集合:用于筛选、匹配、关联 Service/Deployment 等
    app: busybox        # 自定义标签键值对,可通过 kubectl get pods -l app=busybox 筛选该Pod

spec:                   # Pod 规格核心块:描述Pod内部所有运行配置
  # ============== 存储卷定义区 ==============
  volumes:              # 卷列表:Pod 级别的存储声明,所有容器都可以通过名称引用
  - name: datavolume    # 卷的标识名:容器挂载时通过该名称绑定,必须和下方 volumeMounts.name 完全一致
    hostPath:           # 卷类型:主机路径卷,直接将 Worker 节点的本地目录映射进容器
      path: /busyboxdir # 【核心字段】Worker 节点上的真实本地目录路径
                         # 注意:路径是 Pod 调度到的 Worker 节点的本地路径,不是 Master 节点,也不是容器内路径
                         # 未显式指定 type 时,默认目录不存在会自动创建

  # ============== 容器定义区 ==============
  containers:
  # ---------- 第一个容器:读写模式挂载 ----------
  - name: busybox1                    # 容器名称:Pod 内唯一,exec、查看日志时用 -c 指定容器
    image: docker.io/library/busybox  # 容器镜像:私有仓库中的 busybox 基础镜像
    imagePullPolicy: IfNotPresent     # 镜像拉取策略:节点本地已有该镜像则不拉取,缺失才从仓库下载
    command: ['sh', '-c', 'echo Hello Kubernetes! && sleep 3600']
    # 容器启动命令:打印启动日志后休眠 3600 秒,保证容器持续运行不自动退出
    volumeMounts:                     # 容器挂载配置:将上方定义的卷挂载到容器内部
    - mountPath: /data                # 容器内挂载点:卷会被挂载到容器的 /data 目录
      name: datavolume                # 绑定卷名:和上方 volumes[].name 必须完全一致才能挂载成功
      # 未显式写 readOnly,默认值为 false,即【读写模式】
      # 效果:容器内对 /data 目录的所有增删改操作,会直接同步到 Worker 节点的 /busyboxdir 目录

  # ---------- 第二个容器:只读模式挂载 ----------
  - name: busybox2                    # 第二个容器名称,与 busybox1 区分
    image: docker.io/library/busybox
    imagePullPolicy: IfNotPresent
    command: ['sh', '-c', 'echo Hello Kubernetes! && sleep 3600']
    volumeMounts:
    - mountPath: /data                # 同样挂载到容器内的 /data 路径
      name: datavolume                # 绑定同一个卷,实现两个容器共享节点同一份目录数据
      readOnly: true                  # 【核心权限控制】设置为只读挂载
                                        # 生效层级:文件系统挂载层面强制限制,容器无法绕过
                                        # 实验现象:busybox2 执行 touch 写入会直接报 Read-only file system
                                        # 作用:防止容器意外或恶意修改宿主机文件,降低安全风险
# 创建pod
[root@master30 ~]# kubectl apply -f pod-with-hostPath.yaml
pod/busybox created

# 获取容器ID
[root@master30 ~]# kubectl describe pod busybox |egrep -o 'Container ID.*//.{12}'
Container ID:  containerd://318922d0c666
Container ID:  containerd://0688146babe5

# 获取容器所在节点
[root@master30 ~]# kubectl describe pod busybox |grep Node:
Node:             worker32.laoma.cloud/10.1.8.32

# 登录到worker31查看容器挂载情况
root@worker32:~# crictl inspect 318922d0c666|grep busyboxdir
        "hostPath": "/busyboxdir",
          "host_path": "/busyboxdir"
          "source": "/busyboxdir",
          
原理验证
直接证明:容器内的 /data 目录,100% 对应节点本地的 /busyboxdir,没有中间层、没有专属 Pod 目录,就是宿主机目录的直接映射。
和 emptyDir 的直观区别:
emptyDir 的宿主机路径是 kubelet 自动生成的 Pod 专属目录(/var/lib/kubelet/pods/<UUID>/...);
hostPath 的宿主机路径是用户自定义的任意路径,和 Pod 生命周期无关。          
          
          
                  
# 创建数据
[root@master30 ~]# kubectl exec busybox -c busybox1 -- touch /data/b1-f1
[root@master30 ~]# kubectl exec busybox -c busybox2 -- ls /data
b1-f1
[root@master30 ~ 17:28:28]# kubectl exec busybox -c busybox2 -- touch /data/b2-f3
touch: /data/b2-f3: Read-only file system
command terminated with exit code 1


# node上查看数据
root@worker32:~# ls /busyboxdir/
b1-f1

# 删除pod,验证hostPath
[root@master30 ~]# kubectl delete pod busybox --force
root@worker32:~# ls /busyboxdir/
b1-f1
# pod删除后,hostPath卷数据保留。


核心验证 hostPath 卷的三大特性:
1.直接映射节点本地目录:将 Worker 节点上指定的本地目录,直接挂载到容器内部,容器读写的就是宿主机真实文件;
2.数据独立于 Pod 持久化:Pod 删除后,节点本地目录的数据不会被清理,永久保留,和 emptyDir「随 Pod 销毁」形成核心区别;
3.挂载权限精细控制:可以单独给某个容器设置只读挂载,防止容器修改宿主机文件,提升安全性。

NFS 存储

NFS卷,将数据存储在NFS共享中。

NFS 网络存储卷:「公司公共文件服务器」

大白话解释

有一台独立的 NFS 服务器,专门存文件;所有节点都能连上它,把上面的共享文件夹挂进容器。 不管 Pod 跑在哪个节点、删了重建多少次,挂的都是同一份文件,数据永远不会丢。

核心特点
  • 数据持久化:Pod 删、节点坏,数据都在 NFS 服务器上;

  • 多 Pod 共享:几十个 Pod 同时挂同一个 NFS 目录,都能读写同一份文件;

  • 属于网络存储,NFS 服务器挂了,所有挂它的 Pod 都会出问题。

前置必须条件(你之前踩过的坑)

所有 Worker 节点,都必须安装 NFS 客户端工具

  • CentOS:yum install nfs-utils

  • Ubuntu:apt install nfs-common 没装的话,Pod 就会报 FailedMount 挂载失败,也是你之前遇到 access denied 类报错的常见原因之一。

示例(就是你实验里的写法)
volumes:
- name: nfs
  nfs:
    server: 10.1.8.30    # NFS 服务器的 IP
    path: /nfsshares     # NFS 上共享的目录
volumeMounts:
- name: nfs
  mountPath: /usr/share/nginx/html
重点注意
  1. NFS 服务端要配置好权限,放行节点 IP,不然会拒绝挂载;

  2. 适合存静态网页、共享文件、日志,不适合存高频读写的数据库,性能不够。

准备NFS共享

# 安装 NFS server
[root@master30 ~]# apt install -y nfs-kernel-server

# 安创建NFS目录 修改创建文件夹的权限
#mkdir -m 777:创建共享目录并开放最高读写权限
#为什么要 777:NFS 默认开启 root_squash(root 用户压缩),容器内 root 用户会被映射为服务端的匿名用户,权限不足会导致容器内写入失败,实验环境用 777 规避权限问题
#写入测试页:作为后续验证挂载生效的参照物,Nginx 挂载后会直接读取该文件作为首页
[root@master30 ~]# mkdir -m 777 /nfsshares
[root@master30 ~]# echo hello laoma > /nfsshares/index.html

# 配置共享,允许所有客户端访问
#/etc/exports 是 NFS 服务端的核心配置文件,定义「哪个目录、允许谁访问、有什么权限」。
#/nfsshares:要对外共享的本地目录
#*:允许所有 IP 客户端访问,实验环境简化配置,生产建议限定网段如 10.1.8.0/24
#(rw):授予读写权限
[root@master30 ~]# cat << EOF > /etc/exports
/nfsshares *(rw)
EOF

# 重启 nfs server
[root@master30 ~]# systemctl restart nfs-server.service

# 客户端安装
root@worker31:~# apt install -y nfs-common
root@worker32:~# apt install -y nfs-common

准备 pod

[root@master30 ~]# vim pod-with-nfs.yaml 
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: nginx
  name: nginx
spec:
  volumes:
  - name: nfs
    nfs:
      server: 10.1.8.30   # ★核心:NFS服务端IP,独立于任何Worker节点
      path: "/nfsshares"  # ★核心:NFS服务端的共享目录路径
  containers:
  - image: docker.io/library/nginx
    name: nginx
    volumeMounts:
    - name: nfs
      mountPath: "/usr/share/nginx/html" # 容器内挂载点:Nginx网页根目录
# 创建pod
[root@master30 ~]# kubectl apply -f pod-with-nfs.yaml 
pod/nginx created

# 获取容器ID
[root@master30 ~ 19:14:19]# kubectl describe pod nginx |egrep -o 'Container ID.*//.{12}'
Container ID:   containerd://b31995547b70


# 获取容器所在节点
[root@master30 ~ 19:14:59]# kubectl describe pod nginx |grep Node:
Node:             worker32.zy.cloud/10.1.8.32


# 登录到worker31查看容器挂载情况
[root@worker32 ~ 19:16:27]# crictl  inspect b31995547b70  | grep -e nfs -e html
        "containerPath": "/usr/share/nginx/html",
        "hostPath": "/var/lib/kubelet/pods/bc01d674-9419-4e7e-acf4-e28ac9fdf84f/volumes/kubernetes.io~nfs/nfs",
          "container_path": "/usr/share/nginx/html",
          "host_path": "/var/lib/kubelet/pods/bc01d674-9419-4e7e-acf4-e28ac9fdf84f/volumes/kubernetes.io~nfs/nfs"
          "destination": "/usr/share/nginx/html",
          "source": "/var/lib/kubelet/pods/bc01d674-9419-4e7e-acf4-e28ac9fdf84f/volumes/kubernetes.io~nfs/nfs",
[root@worker32 ~ 19:16:33]# df|grep nfsshare
10.1.8.30:/nfsshares              101590016  6529536  89853952   7% /var/lib/kubelet/pods/bc01d674-9419-4e7e-acf4-e28ac9fdf84f/volumes/kubernetes.io~nfs/nfs

# 访问容器
[root@master30 ~ 19:15:04]# kubectl  get pod -o wide 
NAME    READY   STATUS    RESTARTS   AGE     IP              NODE                NOMINATED NODE   READINESS GATES
nginx   1/1     Running   0          7m21s   10.224.170.29   worker32.zy.cloud   <none>           <none>

[root@master30 ~ 19:18:49]# curl  10.224.170.29
hello zy


# 创建数据
[root@master30 ~]# kubectl exec nginx -- ls /usr/share/nginx/html
index.html
[root@master30 ~]# kubectl exec nginx -- touch /usr/share/nginx/html/test.html
[root@master30 ~]# ls /nfsshares
index.html  test.html

#删除本地的/nfsshares目录下的文件,容器中的/usr/share/ngin/html也会删除,
[root@master30 ~ 19:21:20]# rm -f /nfsshares/test.html 
[root@master30 ~ 19:21:31]# ls /nfsshares/
index.html
[root@master30 ~ 19:21:36]# kubectl exec nginx -- ls /usr/share/nginx/html
index.html

#删除容器中的/usr/share/ngin/html,本地的/nfsshares目录下的文件也会删除,
[root@master30 ~ 19:21:48]# kubectl exec nginx -- touch /usr/share/nginx/html/zhangyi
[root@master30 ~ 19:23:15]# kubectl exec nginx -- ls /usr/share/nginx/html
index.html
zhangyi
[root@master30 ~ 19:23:19]# ls /nfsshares/
index.html  zhangyi



# 删除pod,验证nfs卷数据
[root@master30 ~ 19:23:28]# kubectl  delete  pod nginx  --force 
Warning: Immediate deletion does not wait for confirmation that the running resource has been terminated. The resource may continue to run on the cluster indefinitely.
pod "nginx" force deleted
[root@master30 ~ 19:24:32]# ls /nfsshares
index.html  zhangyi
# pod删除后,nfs卷数据保留。



验证 Kubernetes NFS 网络持久化卷的三大核心特性:
数据持久化:Pod 删除重建后,数据完整保留,不随 Pod 销毁(和 emptyDir 核心区别)
跨节点共享:存储独立于 Worker 节点,Pod 漂移到任意节点都能读到同一份数据(和 hostPath 核心区别)
挂载工作机制:K8s 通过节点本地挂载 NFS 共享,再映射进容器的两层挂载逻辑

持久性存储

学习参考:持久卷

Kubernetes 使用 persistent volume(PV)架构为集群提供永久存储。

PV + PVC 持久化体系(生产核心,重点中的重点)

先讲痛点:为什么不直接用 NFS?

如果每个 Pod 的 YAML 里,都直接写死 NFS 的 IP 和路径,会有两个大问题:

  1. 开发人员必须知道存储服务器的地址、配置细节,职责混乱;

  2. 以后换存储(比如从 NFS 换成 Ceph),所有业务的 YAML 都要改一遍,工作量巨大。

所以 K8s 设计了 PV 和 PVC,把「存储的管理」和「存储的使用」彻底分开。

大白话类比(非常好懂)
  • PV(PersistentVolume,持久卷):管理员提前准备好的「仓库」 管理员提前把 NFS、Ceph、云硬盘这些存储,一个个创建成 PV,标好大小、读写规则、位置,放在集群里等着被用。

  • PVC(PersistentVolumeClaim,持久卷申请):开发提交的「仓库申请单」 开发不用管存储在哪、是什么类型,只需要写清楚:我要多大的空间、能不能多人读写,提交 PVC 后,集群自动找一个符合要求的 PV 绑定给它用。

  • 绑定规则:一对一,一个 PV 只能绑定一个 PVC,绑定了就不能给别人用了。

两个核心属性
① 访问模式(AccessModes)

就是这个仓库允许多个人一起用,还是只能一个人用:

缩写 全称 大白话 适合什么存储
RWO ReadWriteOnce 只能一个节点挂载读写 本地硬盘、云硬盘
ROX ReadOnlyMany 多个节点都能挂,但只能读不能改 共享文件、静态资源
RWX ReadWriteMany 多个节点同时挂载读写 NFS、CephFS 这类共享存储
② 回收策略(ReclaimPolicy)

用户退租(删除 PVC)后,仓库怎么处理:

  • Retain(保留):PVC 删了,PV 和数据都留着,管理员手动处理,生产推荐,防止误删数据;

  • Delete(删除):PVC 删了,PV 和数据一起删掉,适合临时测试。

PV/PVC 专属命令

# 查看集群所有 PV,看状态、容量、绑定的 PVC
kubectl get pv
kubectl get pv -o wide

# 查看所有命名空间的 PVC,看有没有绑定成功(Bound=成功,Pending=没匹配到)
kubectl get pvc -A

# 查看 PV 详细信息、事件、报错
kubectl describe pv <PV名字>
# 查看 PVC 详细信息,排查为什么 Pending、绑定失败
kubectl describe pvc <PVC名字> -n <命名空间>

# 删除 PVC
kubectl delete pvc <PVC名字> -n <命名空间>

# 查看存储类(动态供给用)
kubectl get sc

PV 和 PVC 架构

开发人员不知道特定云环境的细节的情况下,只需要使用persistentVolumeClaim(PVC)请求PV资源,实现持久化存储。

  • Persistent Volume,由PersistentVolume API对象定义,代表集群中现有存储。PV的生命周期与使用其的pod无关。Persistent Volume 是集群级别资源。

  • Persistent Volume Claim,由PersistentVolumeClaim API对象定义,代表开发人员请求PV。Persistent Volume Claim 是 namespace 级别资源。

创建 PV 和 PVC

创建 PV

集群管理员可以创建任意数量PV,取决于后端存储。

为了方便演示,我们这里使用NFS后端存储。

pv文件示例:

[root@master30 ~]# vim pv.yaml
apiVersion: v1          # PV 属于 K8s 核心 v1 API 组,固定写法
kind: PersistentVolume  # 资源类型:持久卷,集群级资源,不属于任何命名空间
metadata:
  name: web             # PV 名称,集群内唯一,是管理、绑定的核心标识
spec:
  capacity:             # 容量声明:定义该存储卷的总大小
    storage: 5Gi        # 容量 5GB,后续 PVC 申请的容量不能超过该值
  volumeMode: Filesystem # 卷模式:Filesystem 文件系统模式(默认值),挂载后是目录形式
                         # 另一种为 Block 块设备模式,以裸磁盘形式挂载,适合数据库高性能场景
  accessModes:          # 访问模式:PV 绑定匹配的核心条件之一,定义挂载规则
    - ReadWriteOnce     # ReadWriteOnce(RWO):仅允许被单个节点以读写模式挂载
                         # ⚠️ 常见配置误区:NFS 本身支持多节点同时读写,配成 RWO 会直接浪费 NFS 的共享能力
  nfs:                  # 后端存储类型:NFS 网络存储
    path: /nfsshares    # NFS 服务端的共享目录路径
    server: 10.1.8.30   # NFS 服务端 IP 地址
    
    
  这是一个静态手动创建的 NFS 类型 PersistentVolume(持久卷,简称 PV),是 K8s 存储体系的核心抽象:由管理员提前创建,把底层 NFS 存储封装成集群级资源,后续业务通过 PVC(持久卷申请)来申请使用,实现「存储底层细节」和「业务使用」的解耦。
[root@master30 ~ 19:33:18]# kubectl apply -f pv.yaml 
persistentvolume/web created
[root@master30 ~ 19:33:27]# kubectl  get pv
NAME   CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   VOLUMEATTRIBUTESCLASS   REASON   AGE
web    5Gi        RWO            Retain           Available                          <unset>                          3m6s


列名	含义	当前值解释
NAME	PV 的名称,集群范围内唯一,是管理、绑定 PV 的核心标识	web:你创建的 PV 名字叫 web
CAPACITY	PV 的存储容量,代表该卷能提供的最大存储空间	5Gi:容量为 5GB
ACCESS MODES	访问模式,定义该 PV 支持的挂载规则,用缩写展示	RWO:即 ReadWriteOnce,仅支持单个节点以读写模式挂载
RECLAIM POLICY	回收策略,定义绑定的 PVC 删除后,PV 和数据的处理方式	Retain:保留策略。PVC 删除后,PV 和底层数据都会保留,需管理员手动清理复用(生产推荐,防止误删数据)
STATUS	PV 的当前生命周期状态	Available:可用状态。表示该 PV 空闲,还没有被任何 PVC 绑定,可以被匹配的 PVC 申请绑定
CLAIM	已绑定的 PVC 信息,格式为 命名空间/PVC名称	空:当前还没有 PVC 和它绑定,所以显示空白;绑定成功后会显示对应的 PVC
STORAGECLASS	关联的存储类名称	空:这是静态手动创建的 PV,没有关联 StorageClass,属于 “无类” PV;动态供给的 PV 会显示对应的存储类名
VOLUMEATTRIBUTESCLASS	卷属性类,K8s 较新版本新增的字段,用于高级卷属性管理	<unset>:未设置。普通静态 NFS 类 PV 一般不需要配置,默认未设置即可
REASON	异常原因	空:状态正常,无异常;如果 PV 创建、绑定失败,这里会显示对应的报错原因
AGE	PV 从创建到现在的存活时间	3m6s:已经创建了 3 分 6 秒

创建 PVC

用户创建PVC,pod使用PVC申请特定容量、特定modes和特定存储类别的存储。master监控PVCs,查找匹配的PV或者等待后端存储创建相应PV,然后绑定PV和PVC。

pvc示例:

[root@master30 ~]# vim pvc.yaml
apiVersion: v1                # PVC 属于 K8s 核心 v1 API 组,固定写法
kind: PersistentVolumeClaim   # 资源类型:持久卷声明,【命名空间级资源】,只能在当前命名空间使用
metadata:
  name: webclaim              # PVC 名称,同一命名空间内唯一
spec:
  accessModes:                # 要求的访问模式:PV 必须满足该模式才能绑定
    - ReadWriteOnce           # ReadWriteOnce(RWO):要求单节点读写权限                       
  resources:
    requests:                 # 申请的资源规格
      storage: 5Gi            # 申请 5GB 存储空间,匹配的 PV 容量必须 ≥ 5Gi
           
      
      这是一个 PersistentVolumeClaim(持久卷声明,简称 PVC),相当于业务方向集群提交的「存储申请单」。
开发者不用关心底层是 NFS、Ceph 还是云硬盘,只声明需要的容量、访问模式,集群会自动匹配符合条件的 PV(持久卷)并绑定,实现存储使用与底层存储细节的解耦。
[root@master30 ~ 19:37:25]# kubectl  apply  -f  pvc.yaml 
persistentvolumeclaim/webclaim created
[root@master30 ~ 19:37:43]# kubectl  get pvc
NAME       STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
webclaim   Bound    web      5Gi        RWO                           <unset>                 7s


列名	含义	当前值解释
NAME	PVC 的名称,同一命名空间内唯一,Pod 挂载时通过这个名称引用	webclaim:你创建的 PVC 名称
STATUS	PVC 的当前生命周期状态	Bound:已绑定。表示 PVC 已经成功匹配并绑定到了符合条件的 PV,是正常可用状态
VOLUME	实际绑定到的 PV 名称	web:对应你之前创建的名为 web 的 PV,证明两者一对一绑定成功
CAPACITY	实际分配到的存储空间大小	5Gi:最终获得 5GB 容量,和对应 PV 的容量一致
ACCESS MODES	实际获得的访问模式(用缩写展示)	RWO:即 ReadWriteOnce,单节点读写模式,和对应 PV 的访问模式一致
STORAGECLASS	关联的存储类名称	空:这是静态 PV 绑定场景,没有使用 StorageClass 动态供给,因此为空
VOLUMEATTRIBUTESCLASS	卷属性类,K8s 较新版本用于高级卷属性管理的字段	<unset>:未设置,普通静态存储场景无需配置,不影响使用
AGE	PVC 从创建到现在的存活时间	7s:刚创建 7 秒

创建Pod使用PV
[root@master30 ~]# vim pod-with-pvc.yaml
apiVersion: v1
kind: Pod
metadata:
  name: web          # Pod 名称,同命名空间内唯一
  labels:
    name: web        # 标签,用于资源筛选、关联 Service
spec:
  containers:
    - image: docker.io/library/nginx
      name: web                          # 容器名称
      ports:
        - containerPort: 80              # 容器暴露的服务端口
          name: web-port
      volumeMounts:
        - name: web-persistent-storage   # 和下方 volumes.name 一一对应绑定
          mountPath: /usr/share/nginx/html  # 容器内挂载点:Nginx 网页根目录

  # 存储卷定义:直接引用 PVC,完全不写底层存储细节
  volumes:
    - name: web-persistent-storage
      persistentVolumeClaim:    # 卷类型:通过持久卷声明使用存储
        claimName: webclaim     # ★核心:引用已创建的 PVC 名称,底层是 NFS 还是其他存储对业务完全透明
        
        
        
1.核心挂载链路(四层解耦)
Pod 挂载 → PVC(webclaim)→ PV(web)→ 底层 NFS 存储(10.1.8.30:/nfsshares)

2.开发只需要知道 PVC 名称,不用关心存储地址、类型;管理员负责维护 PV 和底层存储,职责完全分离。

3.关键特性(重点)
3.1数据持久化:删除 Pod 只会解除挂载关系,PVC、PV 和 NFS 上的数据全部保留,重建 Pod 后数据自动恢复。
3.2命名空间约束:PVC 是命名空间级资源,Pod 和 PVC 必须在同一个命名空间下才能成功挂载。
3.3权限继承:Pod 获得的读写能力、访问模式,完全继承自绑定的 PV/PVC。
3.4存储透明:后续底层存储从 NFS 替换为 Ceph / 云盘,只需管理员修改 PV,业务 YAML 无需任何改动。
[root@master30 ~ 19:45:13]# kubectl apply -f pod-with-pcv.yaml
pod/web created
[root@master30 ~ 19:45:29]# kubectl  get pods
NAME   READY   STATUS    RESTARTS   AGE
web    1/1     Running   0          3s

[root@master30 ~ 19:45:52]# kubectl get pod -o wide
NAME   READY   STATUS    RESTARTS   AGE     IP              NODE                NOMINATED NODE   READINESS GATES
web    1/1     Running   0          3m31s   10.224.170.30   worker32.zy.cloud   <none>           <none>


# 测试:访问pod web页面
[root@master30 ~ 19:49:00]# curl http://10.224.170.30
hello zy
[root@master30 ~ 19:49:42]# echo test > /nfsshares/test.html
[root@master30 ~ 11:45:45]# curl http://10.224.170.30/test.html
test


# 删除pod
[root@master30 ~]# kubectl delete pod web --force

PV accessModes

PersistentVolume 卷访问模式有:

  • ReadWriteOnce,卷可以被一个节点以读写方式挂载,也允许同一节点上的多个 Pod 读写访问卷。

  • ReadOnlyMany,卷可以被多个节点以只读方式挂载。

  • ReadWriteMany,卷可以被多个节点以读写方式挂载。

  • ReadWriteOncePod,卷可以被单个 Pod 以读写方式挂载。 如果你想确保整个集群中只有一个 Pod 可以读取或写入该 PVC, 请使用 ReadWriteOncePod 访问模式。这只支持 CSI 卷以及需要 Kubernetes 1.22 以上版本。

    这篇博客文章 Introducing Single Pod Access Mode for PersistentVolumes 描述了更详细的内容。

在命令行接口(CLI)中,访问模式也使用以下缩写形式:

  • RWO - ReadWriteOnce

  • ROX - ReadOnlyMany

  • RWX - ReadWriteMany

  • RWOP - ReadWriteOncePod

不同存储后端支持不同的模式:

卷插件 ReadWriteOnce ReadOnlyMany ReadWriteMany ReadWriteOncePod
AzureFile -
CephFS -
CSI 取决于驱动 取决于驱动 取决于驱动 取决于驱动
FC - -
FlexVolume 取决于驱动 -
GCEPersistentDisk - -
Glusterfs -
HostPath - - -
iSCSI - -
NFS -
RBD - -
VsphereVolume - -(Pod 运行于同一节点上时可行) -
PortworxVolume - -

重要: 每个卷同一时刻只能以一种访问模式挂载,即使该卷能够支持多种访问模式。 例如,一个 GCEPersistentDisk 卷可以被某节点以 ReadWriteOnce 模式挂载,或者被多个节点以 ReadOnlyMany 模式挂载,但不可以同时以两种模式挂载。

常见的NAS存储都支持三种存储模式:ReadWriteOnce、ReadOnlyMany、ReadWriteMany。

PV volumeModes

特性状态: Kubernetes v1.18 [stable]

针对 PV 持久卷,Kubernetes 支持两种卷模式(volumeModes):Filesystem(文件系统)Block(块)volumeMode 是一个可选的 API 参数。 如果该参数被省略,默认的卷模式是 Filesystem

  • Filesystem 卷,会被 Pod 挂载(Mount) 到某个目录。 如果卷的存储来自某块设备而该设备目前为空,Kuberneretes 会在第一次挂载卷之前在设备上创建文件系统。

  • Block 卷,会被作为原始块设备来使用。 这类卷以块设备的方式交给 Pod 使用,其上没有任何文件系统。 这种模式对于为 Pod 提供一种使用最快可能方式来访问卷而言很有帮助, Pod 和卷之间不存在文件系统层。另外,Pod 中运行的应用必须知道如何处理原始块设备。 关于如何在 Pod 中使用 volumeMode: Block 的卷, 可参阅原始块卷支持

PVC与PV匹配规则

  • PV的mode必须高于PVC申请的最低要求:mode 优先级,可简单理解为ROX<RWO<RWX。例如,用户请求RWO模式PV,但是目前只有NFS PV(RWO+ROX+RWX),PVC将匹配NFS。

  • 容量满足最低要求:具有相同modes卷会被分组,然后根据size分类(由小到大)。

  • pv storage classes:用于对pv进行分类,pvc可以根据storageClassName参数申请特定类型pv。如果pv设置了storageClassName,那么 pvc 申请资源的时候也要指定storageClassName。例如,storageClassName指定为 ns1-storage,ns1中pvc申请也指定storageClassName为ns1-storage。

PV 回收策略

当用户不再使用其存储卷时,他们可以从 API 中将 PVC 对象删除, 从而允许该资源被回收再利用。PersistentVolume 对象的回收策略告诉集群, 当其被从申领中释放时如何处理该数据卷。

PersistentVolume 回收策略支持:Retain(保留)、Recycle(回收)、Delete(删除)。

Retain(保留)

回收策略 Retain 使得用户可以手动回收资源。当 PersistentVolumeClaim 对象被删除时,PersistentVolume 卷仍然存在,对应的数据卷被视为"已释放(released)"。 由于卷上仍然保留上一次关联的pvc信息,清理掉上一次关联的pvc信息才可分配给其他pvc。Retain(保留)策略是默认策略

示例:

 [root@master30 ~]# vim pv-pvc-Retain.yaml
 ---
 # 持久卷 PV:集群级存储资源,由管理员创建,封装底层NFS存储细节
 apiVersion: v1
 kind: PersistentVolume
 metadata:
   name: web          # PV名称,集群内唯一
 spec:
   capacity:
     storage: 5Gi     # 存储总容量
   #persistentVolumeReclaimPolicy: Retain  
   # 回收策略:静态PV默认就是Retain,不写也生效
   # Retain含义:PVC删除后,PV和底层数据都会保留,需管理员手动清理复用
   volumeMode: Filesystem   # 卷模式:文件系统模式(默认值),以目录形式挂载使用
   accessModes:
     - ReadWriteOnce    # 访问模式:单节点读写(缩写RWO)
                        # 注意:NFS天然支持多节点同时读写,配RWO会浪费共享能力,NFS场景建议用ReadWriteMany
   nfs:                # 后端存储类型:NFS网络存储
     path: /nfsshares  # NFS服务端的共享目录路径
     server: 10.1.8.30 # NFS服务端IP地址
 ---
 # 持久卷声明 PVC:命名空间级存储申请,业务方使用,只声明需求、不关心底层存储
 apiVersion: v1
 kind: PersistentVolumeClaim
 metadata:
   name: webclaim     # PVC名称,同命名空间内唯一,Pod挂载时通过该名称引用
 spec:
   accessModes:
     - ReadWriteOnce  # 申请的访问模式,必须和PV一致才能绑定成功
   resources:
     requests:
       storage: 5Gi   # 申请的存储容量,匹配的PV容量必须 ≥ 该值
[root@master30 ~ 20:09:31]# kubectl apply -f pv-pvc-Retain.yaml
persistentvolume/web unchanged
persistentvolumeclaim/webclaim unchanged
[root@master30 ~ 20:09:36]# kubectl get pv
NAME   CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM              STORAGECLASS   VOLUMEATTRIBUTESCLASS   REASON   AGE
web    5Gi        RWO            Retain           Bound    storage/webclaim                  <unset>                          74s
[root@master30 ~ 20:09:42]# kubectl get pvc
NAME       STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
webclaim   Bound    web      5Gi        RWO                           <unset>                 69s

删除 pvc 验证

[root@master30 ~ 20:12:05]# kubectl delete pvc webclaim 
persistentvolumeclaim "webclaim" deleted

[root@master30 ~ 20:12:09]# kubectl get pv
NAME   CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS     CLAIM              STORAGECLASS   VOLUMEATTRIBUTESCLASS   REASON   AGE
web    5Gi        RWO            Retain           Released   storage/webclaim                  <unset>                          3m45s


# 创建新pvc,无法绑定
[root@master30 ~ 20:12:13]# vim pvc-db.yaml
---
# 持久卷声明 PVC:面向业务的存储申请单,命名空间级资源
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: dbclaim              # PVC 名称,同命名空间内唯一,Pod 挂载时通过该名称引用
spec:
  accessModes:               # 要求的访问模式,必须和 PV 一致才能绑定成功
    - ReadWriteOnce          # ReadWriteOnce(RWO):仅允许单个节点以读写模式挂载
                             # 适配数据库场景:避免多节点同时写入同一份数据,保证数据一致性
  resources:
    requests:                # 存储资源申请
      storage: 5Gi           # 申请 5GB 存储空间,匹配的 PV 容量必须 ≥ 5Gi
[root@master30 ~ 20:12:30]# kubectl apply -f pvc-db.yaml 
persistentvolumeclaim/dbclaim created
[root@master30 ~ 20:12:41]# kubectl get pvc dbclaim 
NAME      STATUS    VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
dbclaim   Pending                                                     <unset>                 5s
                                11s

# 手动清理卷上claimRef信息,删除claimRef部分
[root@master30 ~]# kubectl edit pv web
......
spec:
  accessModes:
  - ReadWriteOnce
  capacity:
    storage: 5Gi
  # 删除claimRef部分
  # claimRef:
  #   apiVersion: v1
  #   kind: PersistentVolumeClaim
  #   name: webclaim
  #   namespace: test
  #   resourceVersion: "112157"
  #   uid: 0839cdc1-81bb-11e9-9ca8-52540000fa0a
  # 删除claimRef部分
  nfs:
    path: /web
    server: 10.1.8.30
......
# dbclaim 绑定成功
[root@master30 ~ 20:16:18]# kubectl get pvc dbclaim 
NAME      STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
dbclaim   Bound    web      5Gi        RWO                           <unset>                 3m39s
                      3m49s

清理环境

[root@master30 ~ 20:16:20]# kubectl delete pvc dbclaim
persistentvolumeclaim "dbclaim" deleted
[root@master30 ~ 20:16:34]# kubectl delete pv web
persistentvolume "web" deleted
[root@master30 ~ 20:16:38]# ls /nfsshares/
index.html  test.html  zhangyi

注意:删除pv,并不会删除后端存储中数据。

Recycle(回收)

警告:回收策略 Recycle 已被废弃。取而代之的建议方案是使用动态制备。

Recycle(回收)策略的效果与 Retaine(保留)一致。

Delete(删除)

对于支持 Delete 回收策略的卷插件,删除动作会将 PersistentVolume 对象从 Kubernetes 中移除,同时也会从外部基础设施(如 AWS EBS 或 GCE PD 卷)中移除所关联的存储资产。

示例:

[root@master30 ~]# vim pv-pvc-Delete.yaml
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: web
spec:
  capacity:
    storage: 5Gi
  persistentVolumeReclaimPolicy: Delete
  volumeMode: Filesystem
  accessModes:
    - ReadWriteOnce
  nfs:
    path: /nfsshares
    server: 10.1.8.30
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: webclaim
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi
[root@master30 ~ 20:17:14]# kubectl apply -f pv-pvc-Delete.yaml
persistentvolume/web created
persistentvolumeclaim/webclaim created
[root@master30 ~ 20:17:33]# kubectl get pv
NAME   CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM              STORAGECLASS   VOLUMEATTRIBUTESCLASS   REASON   AGE
web    5Gi        RWO            Delete           Bound    storage/webclaim                  <unset>                          6s
[root@master30 ~ 20:17:39]# kubectl get pvc
NAME       STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
webclaim   Bound    web      5Gi        RWO                           <unset>                 13s

删除 pvc 验证

[root@master30 ~ 20:17:46]# kubectl delete pvc webclaim 
persistentvolumeclaim "webclaim" deleted
[root@master30 ~ 20:18:05]# kubectl get pv
NAME   CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM              STORAGECLASS   VOLUMEATTRIBUTESCLASS   REASON   AGE
web    5Gi        RWO            Delete           Failed   storage/webclaim                  <unset>                          35s


[root@master30 ~ 20:18:55]# kubectl describe pv web
Name:            web
Labels:          <none>
Annotations:     pv.kubernetes.io/bound-by-controller: yes
Finalizers:      [kubernetes.io/pv-protection]
StorageClass:    
Status:          Failed
Claim:           storage/webclaim
Reclaim Policy:  Delete
Access Modes:    RWO
VolumeMode:      Filesystem
Capacity:        5Gi
Node Affinity:   <none>
Message:         error getting deleter volume plugin for volume "web": no deletable volume plugin matched
Source:
    Type:      NFS (an NFS mount that lasts the lifetime of a pod)
    Server:    10.1.8.30
    Path:      /nfsshares
    ReadOnly:  false
Events:
  Type     Reason              Age   From                         Message
  ----     ------              ----  ----                         -------
  Warning  VolumeFailedDelete  58s   persistentvolume-controller  error getting deleter volume plugin for volume "web": no deletable volume plugin matched

kubernetes提示信息为Error getting deleter volume plugin for volume "web": no deletable volume plugin matched,也就是为找到删除插件,所以无法删除。

此时我们可以手动删除pv,手动删除pv,并不会删除后端存储数据。

思考

问题:如何创建一个只能绑定给特定pvc的pv?

答案:在定义pv的时候,指定claimRef属性。

---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: web
spec:
  capacity:
    storage: 5Gi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteOnce
  nfs:
    path: /nfsshares
    server: 10.1.8.30
  claimRef:
    name: webclaim
    namespace: test

PV 卷的类型

PV 持久卷是用插件的形式来实现的。Kubernetes 目前支持以下插件:

  • csi - 容器存储接口 (CSI)

  • fc - Fibre Channel (FC) 存储

  • hostPath - HostPath 卷 (仅供单节点测试使用;不适用于多节点集群;请尝试使用 local 卷作为替代)

  • iscsi - iSCSI (SCSI over IP) 存储

  • local - 节点上挂载的本地存储设备

  • nfs - 网络文件系统 (NFS) 存储

以下的持久卷已被弃用。这意味着当前仍是支持的,但是 Kubernetes 将来的发行版会将其移除。

  • azureFile - Azure File (于 v1.21 弃用

  • flexVolume - FlexVolume (于 v1.23 弃用

  • gcePersistentDisk - GCE Persistent Disk (于 v1.17 弃用

  • portworxVolume - Portworx 卷 (于 v1.25 弃用

  • vsphereVolume - vSphere VMDK 卷 (于 v1.19 弃用

  • cephfs - CephFS 卷 (于 v1.28 弃用

  • rbd - Rados Block Device (RBD) 卷 (于 v1.28 弃用

旧版本的 Kubernetes 仍支持这些“树内(In-Tree)”持久卷类型:

  • awsElasticBlockStore - AWS Elastic Block Store (EBS) (v1.27 开始不可用

  • azureDisk - Azure Disk (v1.27 开始不可用

  • cinder - Cinder (OpenStack block storage) (v1.27 开始不可用

  • photonPersistentDisk - Photon 控制器持久化盘。(从 v1.15 版本开始将不可用

  • scaleIO - ScaleIO 卷(v1.21 之后不可用

  • flocker - Flocker 存储 (v1.25 之后不可用

  • quobyte - Quobyte 卷 (v1.25 之后不可用

  • storageos - StorageOS 卷 (v1.25 之后不可用

原始块卷支持

特性状态: Kubernetes v1.18 [stable]

以下卷插件支持原始块卷,包括其动态制备(如果支持的话)的卷:

  • CSI

  • FC(光纤通道)

  • GCEPersistentDisk(已弃用)

  • iSCSI

  • Local 卷

  • OpenStack Cinder

  • RBD(已弃用)

  • RBD(Ceph 块设备,已弃用)

  • VsphereVolume

pv示例

apiVersion: v1
kind: PersistentVolume
metadata:
  name: block-pv
spec:
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteOnce
  volumeMode: Block
  persistentVolumeReclaimPolicy: Retain
  fc:
    targetWWNs: ["50060e801049cfd1"]
    lun: 0
    readOnly: false

pvc示例

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: block-pvc
spec:
  accessModes:
    - ReadWriteOnce
  volumeMode: Block
  resources:
    requests:
      storage: 10Gi

pod示例

 apiVersion: v1
 kind: Pod
 metadata:
   name: pod-with-block-volume
 spec:
   containers:
     - name: fc-container
       image: fedora:26
       command: ["/bin/sh", "-c"]
       args: [ "tail -f /dev/null" ]
       volumeDevices:
         - name: data
           devicePath: /dev/xvda
   volumes:
     - name: data
       persistentVolumeClaim:
         claimName: block-pvc

说明:向 Pod 中添加原始块设备时,你要在容器内设置设备路径而不是挂载路径。

环境清理

 [root@master30 ~]# kubectl delete ns storage

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐