【Kubernetes系列八】存储卷与数据持久化
1. 存储卷概述
Pod本身具有生命周期,这就带了一系列的问题
第一,当一个容器损坏之后,kubelet会重启这个容器,但是文件会丢失-这个容器会是一个全新的状态;
第二,当很多容器在同一Pod中运行的时候,很多时候需要数据文件的共享。Docker支持配置容器使用存储卷将数据持久存储于容器自身文件系统之外的存储空间之中,它们可以是节点文件系统或网络文件系统之上的存储空间。相应的,kubernetes也支持类似的存储卷功能,不过,其存储卷是与Pod资源绑定而非容器。
2. 存储卷使用方式
在Pod中定义使用存储卷的配置由两部分组成:
第一部分:通过.spec.volumes字段定义在Pod之上的存储卷列表,其支持使用多种不同类型的存储卷且配置参数差别很大;
第二部分:通过.spce.containers.volumeMounts字段在容器上定义的存储卷挂载列表,它只能挂载当前Pod资源中定义的具体存储卷,当然,也可以不挂载任何存储卷。
pod级别定义存储卷,一个为emptyDir类型,一个是gitRepo类型
......
volumes:
- name: data
emptyDir: {}
- name: example
gitRepo:
repository: https://github.com/ikubernetes/k8s_book.git
revision: master
directory:
定义好的存储卷可由pod资源内的各容器进行挂载
通过命令# kubectl explain pod.spec.containers.volumeMounts 可以进行查看
......
volumeMounts:
- name <string> -required- #指定要挂载的存储卷的名称,必选字段
mountPath <string> -required- #挂载点路径,容器文件系统的路径,必选字段
readOnly <boolean> #是否挂载为只读卷
subPath <string> #挂载存储卷时使用的子路径,及mountPath指定的路径下使用一个子路径作为其挂载点。
示列:容器myapp将上面定义的data存储卷挂载于/var/log/myapp,将examply挂载到/webdata/example目录
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
volumeMounts:
- name: data
mountPath: /var/log/myapp/
- name: example
mountPath: /webdata/example/
3. 节点存储卷hostpath
hostPath类型的存储卷是指将工作节点上的某文件系统的目录或文件挂载于Pod中的一种存储卷,独立于Pod资源的生命周期,具有持久性。在Pod删除时,数据不会丢失。
~]# kubectl explain pod.spec.volumes.hostPath
path <string> -required- #指定工作节点上的目录路径
type <string> #指定存储卷类型
DirectoryOrCreate 指定的路径不存在时自动创建其权限为0755的空目录,属主和属组为kubelet
Directory 必须存在的目录路径
FileOrCreate 指定的路径不存在时自动创建其权限为0644的空文件,属主和属组为kubelet
File 必须存在的文件路径
Socket 必须存在的Socket文件路径
CharDevice 必须存在的字符设备文件路径
BlockDevice 必须存在的块设备文件路径
下面是定义在vol-hostpath.yaml配置文件中的pod资源,运行这日志收集代理应用filebeat,负责收集工作节点及容器相关的日志信息发往Redis服务器,它使用三个hostpath类型的存储卷
~]# cat vol-hostpath.yaml
apiVersion: v1
kind: Pod
metadata:
name: vol-hostpath-pod
spec:
containers:
- name: filebeat
image: ikubenetes/filebeat:5.6.7-alpine
env:
- name: REDIS_HOST
value: redis.ilinux.io:6379
- name: LOG_LEVEL
value: info
volumeMounts:
- name: varlog
mountPath: /var/log
- name: socket
mountPath: /var/run/docker.sock
- name: varlibdockercontainers
mountPath: /var/lib/docker/containers
readOnly: true
terminationGracePeriodSeconds: 30
volumes:
- name: varlog
hostPath:
path: /var/log
- name: varlibdockercontainers
hostPath:
path: /var/lib/docker/containers
- name: socket
hostPath:
path: /var/run/docker.sock
这类pod资源通常受控于daemonset类型的pod控制器,它运行于集群中的每个工作节点上,负责收集工作节点上系统级的相关数据,因此使用hostPath存储卷也是理所应当的。
4. 网络存储卷
nfs存储卷用于将事先存在的NFS服务器上导出的存储空间挂载到Pod中供容器使用。与emptyDir不同的是,当pod资源删除时emptyDir也会被删除,而NFS在Pod对象删除时仅是被卸载而非删除。这就意味NFS能够允许我们提前对数据进行处理,而且这些数据可以在Pod之间相互传递,并且NFS可以同时被多个Pod挂载并进行读写。
~]# kubectl explain pod.spec.volumes.nfs
server <string> -required- #NFS服务器的IP地址或主机名,必选字段
path <string> -required- #NFS服务器导出(共享)的文件系统路径,必选字段
readOnly <boolean> #是否以只读方式挂载,默认为false
示列:
~]# yum -y install nfs-utils
~]# showmount -e 10.157.27.114
Export list for 10.157.27.114:
/data/k8s/v1 10.157.27.0/24
~]# vim vol-nfs.yaml
apiVersion: v1
kind: Pod
metadata:
name: vol-nfs-pod
labels:
app: redis
spec:
containers:
- name: redis
image: redis:4-alpine
ports:
- containerPort: 6379
name: redisport
volumeMounts:
- mountPath: /data
name: redisdata
volumes:
- name: redisdata
nfs:
server: 10.157.27.114
path: /data/k8s/v1
readOnly: false
~]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE
vol-nfs-pod 1/1 Running 0 13m 10.244.3.40 node03.ilinux.io <none>
5.PV与PVC
PV:PersistentVolume(PV)是集群中已由管理员配置的一段网络存储。集群中的资源就像一个节点是一个集群资源。PV是诸如卷之类的卷插件,但是具有独立于使用PV的任何单个Pod的生命周期。该API对象捕获存储的实现细节,即NFS,ISCSI或云提供商特定的存储系统
PVC:PersistentVolumeClaim(PVC)是用户存储的请求。它类似于Pod。Pod消耗节点资源,PVC消耗存储资源。Pod可以请求特定级别的资源(CPU和内存)。权限要求可以请求特定的大小和访问模式。
StorageClass:虽然PersistentVolumeClaims允许用户使用抽象存储资源,但是常见的是,用户需要具有不同属性(如性能)的PersistentVolumes,用于不同的问题。集群管理员需要能够提供多种不同于PersistentVolumes的PersistentVolumes,而不仅仅是大小和访问模式,而不会使用户了解这些卷的实现细节。对于这些需求,存在StorageClass资源。
(1) 创建PV
PersistentVolume Spec主要支持以下几个通用字段,用于定义PV的容量、访问模式、和回收策略
~]# kubectl explain pv.spec
capacity <map[string]string> #当前PV的容量;目前,capacity仅支持空间设定,将来应该还可以指定IOPS和throughput。
accessModes <[]string> #访问模式;尽管在PV层看起来并无差异,但存储设备支持及启用的功能特性却可能不尽相同。例如NFS存储支持多客户端同时挂载及读写操作,但也可能是在共享时仅启用了只读操作,其他存储系统也存在类似的可配置特性。因此,PV底层的设备或许存在其特有的访问模式,用户使用时必须在其特性范围内设定其功能。参考:https://kubernetes.io/docs/concepts/storage/persistent-volumes/#access-modes
- ReadWribeOnce:仅可被单个节点读写挂载;命令行中简写为RWO。
- ReadOnlyMany:可被多个节点同时只读挂载;命令行中简写为ROX。
- ReadWriteMany:可被多个节点同时读写挂载;命令行中简写为RWX。
persistentVolumeReclaimPolicy <string> #PV空间被释放时的处理机制;可用类型仅为Retain(默认)、Recycle或Delete,具体说明如下。
- Retain:保持不动,由管理员随后手动回收。
- Recycle:空间回收,即删除存储卷目录下的所有文件(包括子目录和隐藏文件),目前仅NFS和hostPath支持此操作。
- Delete:删除存储卷,仅部分云端存储系统支持,如AWS EBS、GCE PD、Azure Disk和Cinder
volumeMode <string> #卷模型,用于指定此卷可被用作文件系统还是裸格式的块设备;默认为Filesystem。
storageClassName <string> #当前PV所属的StorageClass的名称;默认为空值,即不属于任何StorageClass。
mountOptions <[]string> #挂载选项组成的列表,如ro、soft和hard等。
(2) 创建PVC
PersistentVolumeClaim是存储卷类型的资源,它通过申请占用某个PersistentVolume而创建,它与PV是一对一的关系,用户无须关系其底层实现细节。申请时,用户只需要指定目标空间的大小、访问模式、PV标签选择器和StorageClass等相关信息即可。PVC的Spec字段的可嵌套字段具体如下:
~]# kubectl explain pvc.spec
accessModes <[]string> #当前PVC的访问模式,其可用模式与PV相同
resources <Object> #当前PVC存储卷需要占用的资源量最小值;目前,PVC的资源限定仅指其空间大小
selector <Object> #绑定时对PV应用的标签选择器(matchLabels)或匹配条件表达式(matchEx-pressions),用于挑选要绑定的PV;如果同时指定了两种挑选机制,则必须同时满足两种选择机制的PV才能被选出
storageClassName <string> #所依赖的存储卷的名称
volumeMode <string> #卷模型,用于指定此卷可被用作于文件系统还是裸格式的块设备;默认为“Filesystem”
volumeName <string> #用于直接指定要绑定的PV的卷名
(3) 在pod中使用PVC
在Pod资源中调用PVC资源,只需要在定义volumes时使用persistentVolumeClaims字段嵌套指定两个字段即可。具体如下:
~]# kubectl explain pod.spec.volumes.persistentVolumeClaim
claimName <string> -required- #要调用的PVC存储卷的名称,PVC卷要与Pod在同一名称空间中
readOnly <boolean> #是否将存储卷挂载为只读模式,默认为false。
示列:
说明:下面示例中,准备了一台NFS Server创建了几个共享目录提供给Kubernetes作为PV使用。在创建PV的同时指定了不同的大小和不同的访问权限,然后在创建PVC时候指定了大小为6Gi,故满足条件的PV只有pv003~pv005,这里通过标签选择器选择了pv003。Pod中的容器使用了MySQL,并将MySQL的数据目录挂载到PV上
- 准备NFS服务
~]# mkdir /data/volumes/v{1..5} -p
~]# vim /etc/exports
/data/volumes/v1 10.157.27.0/24(rw,no_root_squash)
/data/volumes/v2 10.157.27.0/24(rw,no_root_squash)
/data/volumes/v3 10.157.27.0/24(rw,no_root_squash)
/data/volumes/v4 10.157.27.0/24(rw,no_root_squash)
/data/volumes/v5 10.157.27.0/24(rw,no_root_squash)
~]# exportfs -arv
~]# showmount -e #配置生效
- 创建PV,这里创建5个PV,存储大小各不相等,是否可读也不相同
~]# vim pv-nfs-demo.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-nfs-001
labels:
name: pv001
spec:
nfs:
path: /data/volumes/v1
server: 10.157.27.114
readOnly: false
accessModes: ["ReadWriteOnce","ReadWriteMany"]
capacity:
storage: 2Gi
persistentVolumeReclaimPolicy: Retain
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-nfs-002
labels:
name: pv002
spec:
nfs:
path: /data/volumes/v2
server: 10.157.27.114
readOnly: false
accessModes: ["ReadWriteOnce"]
capacity:
storage: 5Gi
persistentVolumeReclaimPolicy: Retain
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-nfs-003
labels:
name: pv003
spec:
nfs:
path: /data/volumes/v3
server: 10.157.27.114
readOnly: false
accessModes: ["ReadWriteOnce","ReadWriteMany"]
capacity:
storage: 6Gi
persistentVolumeReclaimPolicy: Retain
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-nfs-004
labels:
name: pv004
spec:
nfs:
path: /data/volumes/v4
server: 10.157.27.114
readOnly: false
accessModes: ["ReadWriteOnce","ReadWriteMany"]
capacity:
storage: 7Gi
persistentVolumeReclaimPolicy: Retain
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-nfs-005
labels:
name: pv005
spec:
nfs:
path: /data/volumes/v5
server: 10.157.27.114
readOnly: false
accessModes: ["ReadWriteOnce","ReadWriteMany"]
capacity:
storage: 8Gi
persistentVolumeReclaimPolicy: Retain
~]# kubectl apply -f pv-nfs-demo.yaml
~]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pv-nfs-001 2Gi RWO,RWX Retain Available 5s
pv-nfs-002 5Gi RWO Retain Available 5s
pv-nfs-003 6Gi RWO,RWX Retain Available 5s
pv-nfs-004 7Gi RWO,RWX Retain Available 4s
pv-nfs-005 8Gi RWO,RWX Retain Available 4s
- 创建PVC,绑定PV
pvc需要和pod在同一名称空间
~]# vim vol-nfs-pvc.yaml
#创建PVC
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nfs-pvc
spec:
accessModes: ["ReadWriteMany"]
resources:
requests:
storage: 6Gi #指定PVC大小为6Gi
selector: #这里通过标签选择器指定了所使用的pv卷为key为name,value为pv003的pv资源
matchLabels:
name: pv003
---
#创建Pod
apiVersion: v1
kind: Pod
metadata:
name: pvc-mysql
labels:
app: mysql
spec:
containers:
- name: pvc-mysql-pod
image: mysql:latest
imagePullPolicy: IfNotPresent
ports:
- name: mysqlport
containerPort: 3306
volumeMounts:
- name: mysqldata
mountPath: /var/lib/mysql
env:
- name: MYSQL_ROOT_PASSWORD
value: "mysql"
volumes:
- name: mysqldata
persistentVolumeClaim: #通过该字段定义使用pvc
claimName: nfs-pvc #指定pvc的名称
readOnly: false #关闭只读
~]# kubectl apply -f vol-nfs-pvc.yaml
~]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
nfs-pvc Bound pv-nfs-003 6Gi RWO,RWX 5s
~]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pv-nfs-001 2Gi RWO,RWX Retain Available 15m
pv-nfs-002 5Gi RWO Retain Available 15m
pv-nfs-003 6Gi RWO,RWX Retain Bound default/nfs-pvc 15m
pv-nfs-004 7Gi RWO,RWX Retain Available 15m
pv-nfs-005 8Gi RWO,RWX Retain Available 15m
- 测试验证
~]# ls /data/volumes/v3/ #查看服务端文件生成
~]# kubectl delete -f vol-nfs-pvc.yaml #删除pvc
~]# kubectl delete -f pv-nfs-demo.yaml #删除pv
~]# ls /data/volumes/v3/ #上面删除pv和pvc,看文件还在
~]# kubectl apply -f pv-nfs-demo.yaml
~]# kubectl apply -f vol-nfs-pvc.yaml #重新创建pvc
发现数据还是可以正常使用
6.生命周期
PV是集群中的资源。PVC是对这些资源的请求,也是对资源的索赔检查。PV和PVC之间的相互作用遵循这个生命周期:
Provisioning—>Binding—>Using—>Releasing—>Recycling
(1) 供应准备Provisioning
PV有两种提供方式:静态或者动态
Static:集群管理员创建多个PV。它们携带可供集群用户使用的真实存储的详细信息。它们存在于Kubernetes API中,可用于消费。
Dynamic:当管理员创建的静态PV都不匹配用户的PersistentVolumesClaim时,集群可能会尝试为PVC动态配置卷。此配置基于StorageClasses:PVC必须请求一个类,并且管理员必须已经创建并配置该类才能进行动态配置。要求该类的声明有效地位自己禁用动态配置。
(2) 绑定Binding
用户创建PVC并指定需要的资源和访问模式。在找到可用PV之前,PVC会保持未绑定状态。
(3) 使用Using
用户可在Pod中像volume一样使用PVC。
(4) 释放Releasing
用户删除PVC来回收存储资源,PV将变成“released”状态。由于还保留着之前的数据,这些数据要根据不同的策略来处理,否则这些存储资源无法被其它PVC使用
(5) 回收Recycling
PV可以设置三种回收策略:保留(Retain)、回收(Recycle)和删除(Delete)。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南