Kubernetes Volume(卷)精讲
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
三、最容易踩的坑(重点避坑)
-
挂载覆盖原目录 把卷挂到容器里已经存在的目录,目录里原来的文件会全部被覆盖消失。 如果只想加一个文件,用
subPath单文件挂载,别整目录挂。 -
权限不足 NFS、hostPath 挂载后,容器里写文件报
Permission denied。
-
NFS:服务端加
no_root_squash参数; -
其他卷:可以在 Pod 里指定运行用户,或者给目录开放权限。
-
生命周期搞混
-
emptyDir:Pod 删,数据没;
-
ConfigMap/Secret:Pod 删,配置还在集群里;
-
NFS/PV:Pod 删,数据保留在存储服务器上。
-
节点没装客户端 网络存储(NFS、Ceph)必须所有节点都装客户端工具,不然 Pod 调度到未安装的节点,直接挂载失败。
-
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 节点看不到);
-
安全风险高:容器能读写宿主机的文件,恶意容器可以修改宿主机系统。
什么时候用?
基本只有两类场景用:
-
DaemonSet 组件:比如采集节点系统日志、访问 Docker 套接字,必须读取节点本机的文件;
-
临时测试用,生产环境强烈不推荐。
示例
把节点的 /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
重点注意
-
NFS 服务端要配置好权限,放行节点 IP,不然会拒绝挂载;
-
适合存静态网页、共享文件、日志,不适合存高频读写的数据库,性能不够。
准备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 和路径,会有两个大问题:
-
开发人员必须知道存储服务器的地址、配置细节,职责混乱;
-
以后换存储(比如从 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
更多推荐

所有评论(0)