在 Kubernetes 中,Volume 是一种用于持久化存储和共享数据的机制。Volume 提供了一种在容器中使用存储的方式,确保数据在容器重启或重建时不会丢失。
一. 基本概念
- Volume 的生命周期:Volume 的生命周期与 Pod 的生命周期相关联,而不是与容器的生命周期相关联。这意味着即使容器崩溃或重新启动,Volume 中的数据仍然保留。
- 数据持久性:Volume 提供了一种将数据存储在容器外部的方式,这样数据可以在容器重建后继续使用。
二.volume的类型
在Kubernetes中,volume支持Kubernetes特有的存储卷类型,也支持传统的存储卷类型。
Kubernetes独有的类型:
- ConfigMap:用于存储配置文件
- secret:用于存储敏感数据
- EmptyDir:用于一个Pod内多个容器的数据共享
- PersistentVolumeClaim:对persistentVolume的申请
常见的卷:
- CephFS
- GlusterFS
- ISCSI
- Cinder
- NFS
- RBD
- HostPath
三.通过emptyDir共享数据
emptyDir是一个特殊的Volume类型,与上述Volume不同的是,如果删除Pod,EmptyDir卷中的数据也将被删除,所以一般emptyDir用于Pod中不同容器共享数据,比如一个Pod存在两个容器A和容器B,容器A需要使用容器B产生的数据,此时可以采用emptyDir共享数据,类似的使用如Filebeat收集容器内程序产生的日志。
使用emptyDir卷时, 直接指定emptyDir:{}即可
1.编写emptyDir的Deployment文件
vim nginx-empty.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx
name: nginx
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- image: nginx:1.7.9
imagePullPolicy: IfNotPresent
name: nginx01
volumeMounts:
- mountPath: /opt
name: share-volume
- image: nginx:1.7.9
imagePullPolicy: IfNotPresent
name: nginx02
command:
- sh
- -c
- sleep 3600
volumeMounts:
- mountPath: /mnt
name: share-volume
volumes:
- name: share-volume
emptyDir: {}
#medium: Memory
备注:
volumeMounts: #定义了容器内的挂载点
- mountPath: /mnt #指定容器内的路径 /mnt
,Volume 将挂载到这里
name: share-volume #指明要挂载的 Volume 的名称,必须与 volumes
部分的名称匹配。
volumes: #定义了 Volume 的来源
- name: share-volume #指定 Volume 的名称,容器中的挂载名称必须与此匹配。
emptyDir: {} #表示一个临时目录 Volume,数据存储在 Pod 的本地磁盘上,当 Pod 被删除时数据也会丢失。
此部署文件创建一个Deployment,采用spec.voluem字段配置一个名字为share-volume,类型为emptyDir的volume,同时里面包含两个容器nginx01和nginx02,并将volume挂载到了/opt和/mnt目录下,此时/opt和/mnt目录的数据就实现了共享。
默认情况下,emptyDir支持节点上的任何介质,可以是SSD,磁盘或是网络存储,具体取决于自身环境。可以将emptyDir.medium字段设置为Memory,让Kubernetes使用tmpfs(内存支持的文件系统),虽然tmpfs非常快,但是在节点重启时,数据同样会被清除,并且设置的大小被记为Container(容器)的内存限制。
2.部署gaiDeployment
ku create -f nginx-empty.yaml
3.查看部署结果
[root@k8s-master ~]# ku get pod
NAME READY STATUS RESTARTS AGE
nginx-8f8dcfd8c-4dj6h 2/2 Running 0 8s
4.登录Pod中的容器,并查看测试结果
[root@k8s-master ~]# ku get pod
NAME READY STATUS RESTARTS AGE
nginx-8f8dcfd8c-4dj6h 2/2 Running 0 8s
[root@k8s-master ~]# ku exec -it nginx-8f8dcfd8c-4dj6h -c nginx01 -- bash
root@nginx-8f8dcfd8c-4dj6h:/# cd opt/
root@nginx-8f8dcfd8c-4dj6h:/opt# touch aaa
root@nginx-8f8dcfd8c-4dj6h:/opt# exit
exit
[root@k8s-master ~]# ku exec -it nginx-8f8dcfd8c-4dj6h -c nginx02 -- bash
root@nginx-8f8dcfd8c-4dj6h:/# cd /mnt
root@nginx-8f8dcfd8c-4dj6h:/mnt# ls
aaa
四.使用HostyoPath挂载宿主机文件
HostPath卷可以将节点上的文件或目录挂载到pod上,用于实现Pod和宿主机之间的数据共享,常用的示例有挂载宿主机的时区至Pod,或者将Pod的日志文件挂载到宿主机。
1.编写Deployment文件,实现HostPath挂载
以下为使用HostPath卷的示例,实现将主机的/etc/localtime文件挂载到pod的/etc/loacltime
[root@k8s-master ~]# vim nginx-hostPath.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx
name: nginx
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- image: nginx:1.7.9
imagePullPolicy: IfNotPresent
name: nginx
volumeMounts:
- mountPath: /etc/localtime
name: timezone-time
volumes:
- name: timezone-time
hostPath:
path: /etc/localtime
type: File
备注:关于时区的设置,通过配置/etc/localtime文件设置系统的时区
/etc/localtime用于配置系统时区(此文件是一个链接文件,链接自/usr/share/zoneinfo/Asia/shanghai)
2.创建并查看此pod
[root@k8s-master ~]# ku create -f nginx-hostPath.yaml
deployment.apps/nginx created
[root@k8s-master ~]# ku get pod
NAME READY STATUS RESTARTS AGE
nginx-59f95c99b5-ld58v 1/1 Running 0 10s
3.登录测试
[root@k8s-master ~]# ku exec -it nginx-59f95c99b5-ld58v -- bash
root@nginx-59f95c99b5-ld58v:/# date
Mon Aug 26 12:31:25 CST 2024
在配置Hostpath时,有一个type参数,用于表达不同的挂载类型,Hostpath卷常用的类型有:
- type为空字符串:默认选项,在挂载HostPath卷之前不会有任何检查
- Directoryorcreate:如果给定的path不存在任何东西,那么将根据需要创建一个权限为0755的空目录,和kubelet具有相同的组和权限
- Directory:目录必须存在于给定的路径下
- Fileorcreate:如果给定的路径不存在任何内容,则会根据需要创建一个空文件,权限设置为6644和kubelet具有相同的组和所有权
- File:文件,必须存在于给定的路径中
- Socket:UNIX套接字,必须存在于给定的路径中
- CharDevice:字符设备,必须存在于给定的路径中
- BlockDevice:块设备,必须存在于给定的路径中
五.挂载NFS共享目录至容器
1.安装nfs(所有节点)
[root@k8s-master ~]# yum -y install nfs-utils
2.设置共享目录(nfs服务器)
(1)创建共享目录
mkdir /henan/xiaoman
(2)设置共享的网段
[root@k8s-master ~]# vim /etc/exports
/henan/xiaoman 192.168.10.0/24(rw,sync,no_root_squash)
配置中的各部分
-
/henan/xiaoman
:这是 NFS 服务器上要共享的目录路径。客户端将能够访问这个目录及其内容。 -
192.168.10.0/24
:这是允许访问这个共享目录的客户端 IP 地址范围。192.168.10.0/24
表示子网掩码为255.255.255.0
的网络,允许这个子网中的所有主机访问共享目录。 -
(rw,sync,no_root_squash)
:-
这是一组 NFS 导出选项,用于控制访问权限和行为:
-
rw
:表示客户端可以读写这个目录。客户端不仅可以读取目录中的文件,还可以向其中写入数据。 -
sync
:表示所有写入操作都将同步到磁盘,这样可以确保数据的一致性和可靠性,但可能会稍微降低性能。 -
no_root_squash
:表示 NFS 服务器不将客户端的 root 用户映射为匿名用户(nobody)。如果使用root_squash
,root 用户的权限会被映射成匿名用户的权限,增加了安全性。
-
3.开启nfs
[root@k8s-master ~]# systemctl start nfs
[root@k8s-master ~]# systemctl start rpcbind
4.编写Doployment文件,挂载NFS
[root@k8s-master ~]# vim nginx-nfsVolume.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx
name: nginx
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- image: nginx:1.7.9
imagePullPolicy: IfNotPresent
name: nginx
volumeMounts:
- mountPath: /usr/share/nginx/html
name: nfs-volume
volumes:
- name: nfs-volume
nfs:
server: 192.168.10.101
path: /henan/xiaoman
5.部署此pod
[root@k8s-master ~]# ku create -f nginx-nfsVolume.yaml
6.在共享目录下放东西
[root@k8s-master ~]# echo "my name is henanxiaoman">/henan/xiaoman/index.html
7.访问或登录查看
[root@k8s-master ~]# ku get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-5c94b6fc6b-bsmns 1/1 Running 0 115s 10.244.140.72 node02 <none> <none>
[root@k8s-master ~]# curl 10.244.140.72
my name is henanxiaoman
或
[root@k8s-master ~]# ku exec -it nginx-5c94b6fc6b-bsmns -c nginx -- bash
root@nginx-5c94b6fc6b-bsmns:/# cd /usr/share/nginx/html/
root@nginx-5c94b6fc6b-bsmns:/usr/share/nginx/html# ls
index.html
[root@nginx-5c94b6fc6b-bsmns:/usr/share/nginx/html# cat index.html
my name is henanxiaoman
六.peristentVolume(PV,持久化)
为此,kubernetes引入了两个新的API资源:Persistentvolume(持久卷,简称PV)PersistentVolumeclaim(持久卷声明,简称PVC)
PV是kubernetes管理员设置的存储,PVC是对PV的请求,标识需要什么类型的PV。他们同样是集群中的一类资源,但其生命周期比较独立,管理员可以单独对PV进行增删改查,不受Pod的影响,生命周期可能比挂载它的其他资源还要长。如果一个kubernetes集群的使用者并非只有kubernetes管理员,那么可以通过提前创建PV,用以解决对存储概念不是很了解的技术人员对存储的需求。和单独配置vo1ume类似,PV也可以使用NFS、GFS、CEPH等常见的存储后端,并且可以提供更为高级的配置,比如访问模式、空间大小以及回收策略等。目前PV的提供方式有两种:静态或动态。静态PV由管理员提前创建,动态PV无需提前创建。
1.PV回收策略
当用户使用完卷时,可以从API中删除PVC对象,从而允许回收资源。回收策略会告诉PV如何处理改卷。目前回收策略可以设置为Retain、Recycle和Delete。默认的为delete
- Retain:保留,该策略允许手动回收资源,当删除PVC时,PV仍然存在,PV中的数据也存在。volume被视为已释放,管理员可以手动回收卷
- Recycle:回收,如果volume插件支持,Recycle策略会对卷执行rm rf 清理该PV,卷中的数据已经没了,但卷还在,使其可用于下一个新的PVC,但是本策略将会被用,目前只有NFS和HostPath支持该策略
- Delete:删除,如果volume插件支持,删除PVC时会同时删除PV,PV中的数据自然也就没了。动态卷默认为Delete,目前支持Delete的存储后端包括ASEBS、GCEPD、AzureDisk、OpenStackCinder等。
2.PV访问策略
在实际使用PV时,可能针对不同的应用会有不同的访问策略,比如某类Pod可以读写,某类Pod只能读,或者需要配置是否可以被多个不同的Pod同时读写等,此时可以使用PV的访问策略进行简单控制,目前支持的访问策略如下:
- ReadWriteonce:单路可读可写,可以被单节点以读写模式挂载,命令行中可以被写橙RWO
- ReadonlyMany多路只读,可以被多节点以只读模式挂载,命令行中可以被缩写为ROX
- ReadWriteMany:多路可读可写,可以被多个节点以读写模式挂载,命令行中可以被缩写为RNX
- ReadWriteOncePod:单节点只读(1.22+),只能被一个Pod以读写的模式挂载,命令行中可以被缩写为RWOP。
使用场景
- ReadWriteonce: 表示具有读写权限,但是只能被一个node挂载一次
- ReadOnlyMany:表示具有只读权限,可以被多个node多次挂载
- ReadWriterany:表示具有读写权限,可以被多个node多次挂载
- ReadWriteOncePod:表示具有读写权限,只能被一个Pod挂载
虽然PV在创建时可以指定不同的访问策略,但是也要后端的存储支持才行。比如一般情况下,大部分块存储是不支持ReadWriteMany的。
在企业内,可能存储很多不同类型的存储,比如NFS、Ceph、GlusterFS等,针对不同类型的后端存储具有不同的配置方式,这也是对集群管理员的一种挑战,因为集群管理员需要对每种存储都要有所了解。
3.PV的配置方式
(1)静态配置
静态配置是手动创建PV并定义其属性,例如容量、访问模式、存储后端等。在这种情况下,Kubernetes管理员负责管理和配置PV,然后应用程序可以使用这些PV。静态配置通常用于一些固定的存储后端,如NFS
(2)动态配置
动态配置允许Kubernetes集群根据PVC的需求自动创建PV,在这种情况下,管理员只需为存储后端配置Storageclass,然后应用程序就可以通过PVC请求存储。Kubernetes将自动创建与PVC匹配的PV,并将其绑定到PVC上。这种方法使得存储管理更加灵活和可扩展,允许管理员在集群中动态添加、删除、和管理存储资源