文章目录
-
- 卷(Volume)
-
- [常见的 `Volume` 类型:](#常见的
Volume类型:)
- [常见的 `Volume` 类型:](#常见的
- 持久卷(PV)
- PVC
- 创建pod使用pv
- 存储类(StorageClass)
卷(Volume)
Kubernetes 卷(Volume)为 Pod 中的容器提供了一种通过文件系统访问和共享数据的方式。数据共享可以发生在容器内不同本地进程之间,或在不同容器之间,或在多个 Pod 之间。
常见的 Volume 类型:
emptyDir
-
emptyDir:一个临时的目录,Pod 存在时有效。Pod 删除时目录会消失。
对于定义了 emptyDir 卷的 Pod,在 Pod 被指派到某节点时此卷会被创建。 就像其名称所表示的那样,emptyDir 卷最初是空的。尽管 Pod 中的容器挂载 emptyDir 卷的路径可能相同也可能不同,但这些容器都可以读写 emptyDir 卷中相同的文件。
当 Pod 因为某些原因被从节点上删除时,emptyDir 卷中的数据也会被永久删除。⚠️注意:由于emptyDir 生命周期关联Pod,所以当 Pod 被删除时,Pod 中的
Volume也会被删除,数据不会被持久化。✅提示:容器崩溃并不会导致 Pod 被从节点上移除,因此容器崩溃期间卷中的数据是安全的(Pod崩溃的话卷依然不会被保留)。
由于emptyDir的卷只能在本Pod中的容器使用,并且不能直接挂载外部资源,所以就不演示了。
hostPath
-
hostPath:将宿主机上的目录挂载到 Pod 中。适用于特定节点的存储。
hostPath卷能将主机节点文件系统上的文件或目录挂载到你的 Pod 中。 虽然这不是大多数 Pod 需要的,但是它为一些应用提供了强大的逃生舱。(注意,只能挂载Pod所在节点上的资源,这个卷用于本地存储)无论
hostPath卷是以只读还是读写方式挂载,使用时都需要小心,这是因为:- 访问主机文件系统可能会暴露特权系统凭证(例如 kubelet 的凭证)或特权 API(例如容器运行时套接字), 这些可以被用于容器逃逸或攻击集群的其他部分。
- 具有相同配置的 Pod(例如基于 PodTemplate 创建的 Pod)可能会由于节点上的文件不同而在不同节点上表现出不同的行为。
hostPath卷的用量不会被视为临时存储用量。 你需要自己监控磁盘使用情况,因为过多的hostPath磁盘使用量会导致节点上的磁盘压力。
hostPath实验验证:
宿主机挂载资源/data目录并写入一个index.html文件
bashmkdir -p /mnt/data echo " Welcome to NGINX! " > /mnt/data/index.html chmod 777 /mnt/data cat /mnt/data/index.html Welcome to NGINX!
创建使用hostPath卷的pod并访问
yamlapiVersion: v1 kind: Pod metadata: name: nginx-hostpath-pod spec: containers: - name: nginx-container image: nginx volumeMounts: - mountPath: /usr/share/nginx/html # nginx 的 Web 根目录 name: hostpath-volume # 卷的名称 volumes: - name: hostpath-volume hostPath: path: /mnt/data # 宿主机上挂载的目录路径 type: Directory # 类型为 Directory,指示这是一个目录可以看见,curl后显示了
Welcome to NGINX!,也就是我们在宿主机资源写入的index.html,而不是显示nginx默认主页。再次修改,验证是否成功挂载
ini写入"zxnbmk到此一游!"再次访问服务,可以看见页面已经被修改,挂载是没有问题
🎇 小知识:hostPath的挂载资源由于是在宿主机上,而不是在容器内部,所以删除Pod并不会将数据清除


可以发现,我们删除了Pod后,本地资源依然存在
NFS、iSCSI、Ceph
-
NFS、iSCSI、Ceph 等:将外部存储系统作为 Volume 挂载到 Pod 中。
这些类型的卷可以挂载外部存储系统的资源,比如nfs卷能将 NFS (网络文件系统) 挂载到你的 Pod 中。 不像
emptyDir那样会在删除 Pod 的同时也会被删除,nfs 卷的内容在删除 Pod 时会被保存,卷只是被卸载。 这意味着 nfs 卷可以被预先填充数据,并且这些数据可以在 Pod 之间共享。yamlapiVersion: v1 kind: Pod metadata: name: test-pd spec: containers: - name: test-container image: registry.k8s.io/test-webserver volumeMounts: - mountPath: /my-nfs-data # 容器内挂载的路径,指定容器中 `/my-nfs-data` 路径为挂载点 name: test-volume # 与 volumes 部分的卷名称匹配,这里是 `test-volume` volumes: - name: test-volume # 定义一个名为 `test-volume` 的卷 nfs: server: my-nfs-server.example.com # 指定 NFS 服务器的地址(实际的 NFS 服务器地址) path: /my-nfs-volume # NFS 服务器上共享的目录路径 readOnly: true # 设置为只读,表示挂载的目录不能写入,只能读取如上NFS卷的yaml示意,由于资源有限,只做yaml文件示例
持久卷(PV)
存储对于大多数的实际生产环境应用来说是非常重要的,我们常常需要统一和标准化存储资源管理,并且提供更多的功能和灵活性 ,PV 提供了 更强的存储资源管理能力 ,包括动态供应、跨节点共享、访问控制等功能,是 Kubernetes 持久化存储的标准化解决方案,而单独的卷管理复杂且不灵活(NFS,hostpath等),考虑到这个问题,我们的Kubernetes 拥有成熟且功能丰富的存储子系统,称为持久化卷子系统(Persistent Volume Subsystem)。
🎯🎯🎯说人话就是,持久卷(PV) 的核心意义就是将前面提到的普通卷(如
hostPath、NFS等支持持久性的卷)通过 PVC 进行持久化和标准化管理,或者使用存储类(Storage Class)来动态制备,这样用户和集群管理员就不需要再手动管理底层存储,Kubernetes 会根据 PVC 的需求自动绑定合适的 PV,实现 存储资源的自动化和灵活化管理。
pv结构
yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: static-pv-hostpath
labels:
type: local-pv # pv的标签,用于被绑定或者被筛选
spec:
capacity: # 定义存储能力
storage: 10Gi # 存储容量
accessModes:
- ReadWriteOnce # 访问模式:单节点读写
persistentVolumeReclaimPolicy: Retain # 回收策略:保留
hostPath: # 卷类型为hostPath
path: "/mnt/data" # 主机上的目录路径
type: DirectoryOrCreate # 目录不存在则创建
可以看到上面是一个hostPath类型的pv卷,一个pv拥有的属性如下:
-
存储容量:storage字段,拥有Mi和Gi两个单位
-
访问模式:
-
ReadWriteOnce卷可以被一个节点以读写方式挂载。 ReadWriteOnce 访问模式仍然可以在同一节点上运行的多个 Pod 访问(读取或写入)该卷。 对于单个 Pod 的访问,请参考 ReadWriteOncePod 访问模式。 -
ReadOnlyMany卷可以被多个节点以只读方式挂载。 -
ReadWriteMany卷可以被多个节点以读写方式挂载。 -
ReadWriteOncePod卷可以被单个 Pod 以读写方式挂载。 如果你想确保整个集群中只有一个 Pod 可以读取或写入该 PVC, 请使用 ReadWriteOncePod 访问模式。
-
-
回收策略:
Retain保留:删除PVC不会同步删除PV,等管理员手动去处理PV里的数据。NFS、HostPath都支持该项Delete删除:删除PVC同步删除PV。NFS、HostPath都不支持该项Recycle回收:删除PVC同步删除PV且重回可用状态。K8s V1.9后该回收策略标记为过时
-
卷类型(类型较多,暂不一一展示)
csi- 容器存储接口(CSI)fc- Fibre Channel(FC)存储hostPath- HostPath 卷 (仅供单节点测试使用;不适用于多节点集群;请尝试使用local卷作为替代)iscsi- iSCSI(IP 上的 SCSI)存储local- 节点上挂载的本地存储设备nfs- 网络文件系统(NFS)存储
以下的持久卷已被弃用但仍然可用。 如果你使用除
flexVolume、cephfs和rbd之外的卷类型,请安装相应的 CSI 驱动程序。awsElasticBlockStore- AWS Elastic 块存储(EBS) (从 v1.23 开始默认启用迁移)azureDisk- Azure 磁盘 (从 v1.23 开始默认启用迁移)azureFile- Azure 文件 (从 v1.24 开始默认启用迁移)cinder- Cinder(OpenStack 块存储) (从 v1.21 开始默认启用迁移)flexVolume- FlexVolume (从 v1.23 开始弃用,没有迁移计划,没有移除支持的计划)gcePersistentDisk- GCE 持久磁盘 (从 v1.23 开始默认启用迁移)portworxVolume- Portworx 卷 (从 v1.31 开始默认启用迁移)vsphereVolume- vSphere VMDK 卷 (从 v1.25 开始默认启用迁移)
旧版本的 Kubernetes 仍支持这些"树内(In-Tree)"持久卷类型:

工作原理
Kubernetes支持来自多种途径的多种类型的存储。例如iSCSI、SMB、NFS,以及对象存储等,都是不同类型的、部署在云上或自建数据中心的外部存储系统。不过,无论什么类型的存储,或来自哪里,在其对接到Kubernetes集群中后,都会被统称为卷(volume)。

总体架构如上:存储提供商提供存储阵列,Kubernetes 的
CSI提供统了一套接口开放标准,提供商就可以通过自己的插件接入集群,从而集群将这些存储资源抽象为PV卷在 Kubernetes 中进行使用。
持久化卷子系统
持久化卷子系统中的3个主要资源如下。
- 持久化卷(Persistent Volume,PV)。
- 持久化卷申请(Persistent Volume Claim,PVC)。
- 存储类(Storage Class,SC)。

上图某存储管理员创建了名为ebs-vol的25GB的ebs物理卷,通过厂商的插件与Kubernetes连接,Kubernetes管理员也创建了一个名为k8s-vol的PV卷通过插件与真实的ebs卷建立连接(将外部存储映射到集群),这时候Pod通过向PVC申请对PV的使用。
!!!小知识!!!
- PV相当于外部存储的映射**(在定义时可以小于等于外部存储卷大小)**,一种表示外部存储的简单方式。
- PVC 是Pod对存储资源的一个申请,主要包括存储空间申请、访问模式等。创建PV后,Pod就可以通过PVC向PV申请磁盘空间了。
- SC可以在 PVC(Persistent Volume Claim)中指定所需的存储卷的特性从而自动创建PV,而无需直接操作 PV 对象。
⚠️注意:一个外部存储卷只能被一个PV使用。例如,一个50GB的外部卷不能分成两个25GB的Kubernetes PV来使用。
关于容器存储接口(CSI)
容器存储接口(The Container Storage Interface, CSI)是一个开源项目,定义了一套基于标准的接口,从而使存储能够以一种统一的方式被不同的容器编排工具使用。容器存储接口(CSI) 为容器编排系统(如 Kubernetes)定义标准接口,以将任意存储系统暴露给它们的容器工作负载。
🔍**(所以存储厂商只需要按照接口编写驱动/插件即可让自家存储资源用于编排工具)**
PVC
前面已经解释pvc是Pod对存储资源的一个申请,主要包括存储空间申请、访问模式等。创建PV后,Pod就可以通过PVC向PV申请磁盘空间了。
pvc主要属性有:需要绑定的pv的访问模式,容量大小,被绑定的pv的标签
!!!特别注意!!!
- 这里pvc绑定的pv只要大于等于pvc需要的容量就可以匹配成功,并不一定非要一致,只要pv有足够能留提供pvc所需容量即可。
yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: static-pvc-example
labels:
type: local-pvc # pvc的标签,用于被绑定或者被筛选
spec:
accessModes: # 开始定义需要绑定的pv的规格
- ReadWriteOnce
resources:
requests:
storage: 8Gi # 实际上被绑定的pv只要满足大于等于10Gi就可以
selector:
matchLabels:
type: local-pv # 选中标签为type: local-pv的pv

可以发现pvc已经成功绑定上一节创建的pv
创建pod使用pv
yaml
apiVersion: v1
kind: Pod
metadata:
name: test-pvc-pod
labels:
app: test-app
spec:
containers:
- name: nginx-container
image: nginx:alpine
ports:
- containerPort: 80
volumeMounts:
- name: data-volume #引用的卷名称(必须与下面的 volumes 名称匹配),后面多个路径需要挂载就会通过这个名字来区分,所以这里需要你起个名字
mountPath: /usr/share/nginx/html # 容器内挂载点,原内容将被卷内容替换
volumes:
- name: data-volume # 被引用的卷名称,与上面路径引用的名称对应
persistentVolumeClaim:
claimName: static-pvc-example # 这里定义要使用的pvc的名字

- 由于pod被调度到node02上,我们用的是hostpath本地存储,所以根据我们pv的目录:
path: "/mnt/data"我们需要在node02上创建改目录并创建一个index.html替换pod的index.html
bash[root@node02-k8s ~]# echo "这是pv卷" > /mnt/data/index.html [root@node02-k8s ~]# cat /mnt/data/index.html
- 访问该pod,显示为刚刚的html内容
小知识,多路径多卷挂载
yaml
apiVersion: v1
kind: Pod
metadata:
name: multi-volume-pod
spec:
containers:
- name: app-container
image: nginx:alpine
volumeMounts:
- name: html-volume # 挂载第一个卷
mountPath: /usr/share/nginx/html
- name: config-volume # 挂载第二个卷
mountPath: /etc/nginx/conf.d
- name: logs-volume # 挂载第三个卷
mountPath: /var/log/nginx
volumes:
# 卷1:HTML 内容 (PVC)
- name: html-volume
persistentVolumeClaim:
claimName: html-pvc
# 卷2:配置文件 (ConfigMap)
- name: config-volume
configMap:
name: nginx-config
# 卷3:日志存储 (emptyDir)
- name: logs-volume
emptyDir: {}
本ymal仅仅用于拓展知识了解多路径多卷挂载,暂不实机演示。
存储类(StorageClass)
上面我详细讲解了创建pvc绑定pv从而使用pv的资源,整个过程来说,如果有多个pod想要使用我们的pv,就要一直创建pv,显得很繁琐,所以,这里出现了存储类(StorageClass),他就是用来动态管理我们的pv的我们称为动态供应,而上一节我们手动创建pv的过程就属于静态供应具体如下图:

StorageClass与PV的关系就好比类与对象得关系,StorageClass相当于PV的模板
由于下面的实验需要用到NFS服务器,这里仅解释原理,不进行实机操作,操作将在未来纳入我的《linux实战》专栏
-
创建StorageClass对象
yamlapiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: nfs-csi # StorageClass名称,在PVC中通过storageClassName引用 # 1. CSI Driver名称(要和CSI驱动部署时指定的名称保持一致) provisioner: nfs.csi.k8s.io # 这是最重要的字段,指定使用哪个CSI驱动来创建存储卷 # CSI (Container Storage Interface) 是K8s存储插件标准接口 # nfs.csi.k8s.io 是NFS的CSI驱动名称,需要先部署对应的CSI驱动 # 2. 存储参数 - 传递给CSI驱动的参数 parameters: # nfs-server的IP地址 server: 192.168.26.33 # 这是NFS服务器的地址,CSI驱动会连接这个服务器 # nfs-server上的存储目录路径 share: /root/nfs/data # NFS服务器上暴露的共享目录路径 # CSI驱动会在这个目录下为每个PVC创建子目录 # 3. 回收策略(支持Delete和Retain, 默认Delete) reclaimPolicy: Retain # 当PVC被删除时,底层存储卷的处理方式: # - Delete: 自动删除PV和底层存储(NFS通常不支持自动删除) # - Retain: 保留PV和底层存储,需要手动清理(适合NFS) # - Recycle: 已废弃,不推荐使用 # 这里设为Retain是因为NFS存储通常需要手动管理目录清理 # 4. 卷的绑定模式 volumeBindingMode: Immediate # 控制PVC何时绑定到PV: # - Immediate: PVC创建时立即绑定到PV(默认值) # - WaitForFirstConsumer: 延迟绑定,直到Pod调度时再绑定 # (适合有拓扑限制的存储,如本地存储) # 这里用Immediate,因为NFS存储没有节点限制 # 5. 挂载选项 - 传递给mount命令的参数 mountOptions: - hard # 硬挂载模式:服务器异常时客户端会持续重试,直到服务器恢复 # 软挂载(soft)会在超时后放弃,可能导致数据不一致 - nfsvers=4.1 # 指定NFS协议版本,4.1支持更多特性 - nolock # 禁用文件锁,提高性能(适合只读或单写场景) # 其他可用选项: # - rw/ro: 读写/只读模式 # - tcp/udp: 传输协议 # - timeo=<value>: 超时时间(十分之一秒) # - retrans=<value>: 重试次数 # 6. 允许动态扩容,默认false allowVolumeExpansion: true # 控制是否允许PVC创建后扩容: # - true: PVC可以编辑spec.resources.requests.storage字段来扩容 # - false: PVC创建后不能修改容量 # 注意:这里设为true,但NFS本身可能不支持动态扩容 # 实际效果取决于CSI驱动和底层存储系统的支持 # 其他重要字段(未在此示例中使用): # 可选的字段: # allowedTopologies: [] # 存储可用的拓扑区域 # mountOptions: [] # 挂载选项(已使用) # parameters: {} # 驱动特定参数(已使用) # provisioner: string # 驱动名称(已使用) # reclaimPolicy: string # 回收策略(已使用) # volumeBindingMode: string # 绑定模式(已使用) # allowVolumeExpansion: boolean # 扩容支持(已使用)这里要非常注意
volumeBindingMode的值,Immediate和WaitForFirstConsumer决定了是否要等待Pod成功调度后再自动创建PV -
创建PVC
yaml# PVC引用StorageClass apiVersion: v1 kind: PersistentVolumeClaim metadata: name: nfs-pvc spec: storageClassName: nfs-csi # 引用上面的StorageClass accessModes: - ReadWriteMany # NFS支持多节点读写 resources: requests: storage: 10Gi # Pod使用PVC apiVersion: v1 kind: Pod metadata: name: nginx-pod spec: containers: - name: nginx image: nginx volumeMounts: - name: data mountPath: /data volumes: - name: data persistentVolumeClaim: claimName: nfs-pvc由于我们
volumeBindingMode的值为Immediate,所以当我们创建PVC后,对应的PV就会在同时创建并被PVC绑定
🎀至此,实验完成,存储章节梗概版本暂时完结,K8s入门篇章已过大半,后续将会开通K8s详解,以更深层次的理解来研究。





