Kubernetes数据存储
Kubernetes数据存储
什么是数据卷
Pod本身具有生命周期,这就带了一系列的问题,
- 当一个容器损坏之后,kubelet会重启这个容器,但是文件会丢失-这个容器会是一个全新的状态;
- 当很多容器在同一Pod中运行的时候,很多时候需要数据文件的共享。
Docker支持配置容器使用存储卷将数据持久存储于容器自身文件系统之外的存储空间之中,它们可以是节点文件系统或网络文件系统之上的存储空间。相应的,kubernetes也支持类似的存储卷功能,不过,其存储卷是与Pod资源绑定而非容器。
存储卷概述
简单来说,存储卷是定义在Pod资源之上、可被其内部的所有容器挂载的共享目录,它关联至某外部的存储设备之上的存储空间,从而独立于容器自身的文件系统,而数据是否具有持久能力取决于存储卷自身是否支持持久机制。Pod、容器与存储卷的关系图如下。
存储卷类型
Kubernetes支持非常丰富的存储卷类型,包括本地存储(节点)和网络存储系统中的诸多存储机制,还支持Secret和ConfigMap这样的特殊存储资源。
例如,关联节点本地的存储目录与关联GlusterFS存储系统所需要的配置参数差异巨大,因此指定存储卷类型时也就限定了其关联到的后端存储设备。通过命令# kubectl explain pod.spec
可以查看当前kubernetes版本支持的存储卷类型。常用类型如下:
非持久性存储
- emptyDir
- hostPath
网络连接性存储
- SAN:iscsi
- NFS:nfs、cfs
分布式存储
- glusterfs、cephfs、rbd
云端存储
- awsElasticBlockStore、azureDisk、gitRepo
emptydir
emptyDir存储卷是Pod对象生命周期中的一个临时目录,类似于Docker上的“docker 挂载卷”,在Pod对象启动时即被创建,而在Pod对象被移除时会被一并删除(永久删除)。
当pod的存储方案设定为emptydir的时候,pod启动时,就会在pod所在节点的磁盘空间开辟出一块空卷,最开始里面是什么都没有的,pod启动后容器产生的数据会存放到那个空卷中。空卷变成了一个临时卷供pod内的容器读取和写入数据,一旦pod容器消失,节点上开辟出的这个临时卷就会随着pod的销毁而销毁
注意:一个容器崩溃了不会导致数据的丢失,因为容器的崩溃并不移除Pod。
使用场景
一般来说emptydir的用途都是用来充当临时存储空间,例如一些不需要数据持久化的微服务,我们都可以用emptydir来当做微服务pod的存储方案
- 充当临时存储空间,当pod内容器产生的数据不需要做持久化存储的时候用emptydir
- 设制检查点以从崩溃事件中恢复未执行完毕的长计算
使用示例
案例说明
这里定义了一个deploy资源对象(vol-emptydir-deploy),在其内部定义了两个容器,其中一个容器是辅助容器sidecar,每隔10秒生成一行信息追加到index.html文件中;
另一个是nginx容器,将存储卷挂载到站点家目录。然后访问nginx的html页面验证两个容器之间挂载的emptyDir实现共享。
创建资源清单
vi vol-emptydir-deploy.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: vol-emptydir-deploy
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
volumes: #定义存储卷
- name: html #定义存储卷的名称
emptyDir: {} #定义存储卷的类型
containers:
- name: nginx
image: nginx:1.12
ports:
- containerPort: 80
volumeMounts: #在容器中定义挂载存储卷的名和路径
- name: html
mountPath: /usr/share/nginx/html
- name: sidecar
image: alpine
volumeMounts: #在容器中定义挂载存储卷的名和路径
- name: html
mountPath: /html
command: ["/bin/sh", "-c"]
args:
- while true; do
echo $(hostname) $(date) >> /html/index.html;
sleep 10;
done
创建deploy
kubectl apply -f vol-emptydir-deploy.yml
kubectl get pods -o wide
访问测试
curl 10.244.1.16
我们发现nginx可以共享来自sidecar的数据
测试存储卷
登录sidecar
登录sidecar,并查看目录文件
kubectl exec vol-emptydir-deploy-86cd768757-5mtzx -c sidecar -it -- /bin/sh
在改容器中写入文件
echo "hello world" > hello
登录nginx
再打开一个窗口登录nginx,查看hello文件是否存在
kubectl exec vol-emptydir-deploy-86cd768757-5mtzx -c nginx -it -- /bin/sh
删除hello文件
rm -rf hello
在sidecar中查看文件时已经删除了
测试文件持久性
登录sidecar
登录sidecar并在html中写入一段文件
kubectl exec vol-emptydir-deploy-86cd768757-5mtzx -c sidecar -it -- /bin/sh
echo "This is a text to test whether the pod is persistent" > /html/index.html
访问测试
curl 10.244.1.16
删除Pod
手动删除容器模拟容器销毁,用于是pod是被控制器管理的,删除后会被重建新的pod
kubectl delete pod vol-emptydir-deploy-86cd768757-5mtzx
我们发现销毁pod后新建的pod转移到node2节点了,并且pod名称也改变了
访问测试
curl 10.244.2.22
这时候在看我们之前写入的文字不见了
hostpath
hostPath类型的存储卷是指将工作节点上的某文件系统的目录或文件挂载于Pod中的一种存储卷,独立于Pod资源的生命周期,具有持久性。在Pod删除时,数据不会丢失。
hostPath类型则是映射node文件系统中的文件或者目录到pod里。在使用hostPath类型的存储卷时,也可以设置type字段,支持的类型有文件、目录、File、Socket、CharDevice和BlockDevice。
其实这个功能就相当于docker中的-v 目录映射,只不过在k8s中的时候,pod会漂移,当pod漂移到其他node节点的时候,pod不会跨节点的去读取目录。所以说hostpath只能算一种半持久化的存储方式
使用场景
- 当运行的容器需要访问Docker内部结构时,如使用hostPath映射/var/lib/docker到容器;
- 当在容器中运行cAdvisor时,可以使用hostPath映射/dev/cgroups到容器中;
使用示例
案例说明
这里定义了一个deploy资源对象(vol-hostpath-deploy),在其内部定义了一个容器,将nginx的html文件映射到外部,我们外面给html写入文件,访问nginx来验证是否能够访问。
创建资源清单
vi vol-hostpath-deploy.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: vol-hostpath-deploy
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
volumes: #定义存储卷
- name: html # 定义存储名称
hostPath: # 定义存储类型
path: /tmp/k8s/data/volumn1 # 宿主机存储路径
type: DirectoryOrCreate # 不存在路径创建路径
containers:
- name: nginx
image: nginx:1.12
ports:
- containerPort: 80
volumeMounts: #在容器中定义挂载存储卷的名和路径
- name: html
mountPath: /usr/share/nginx/html
创建deploy
kubectl apply -f vol-hostpath-deploy.yml
kubectl get pods -o wide
写入文件
查看pod发现pod在node1节点,登录node1节点写入文件
cd /tmp/k8s/data/volumn1/
echo "This is a text to test whether the pod is persistent" > index.html
访问测试
curl 10.244.1.17
测试存储卷
登录nginx
kubectl exec vol-emptydir-deploy-86cd768757-5mtzx -c nginx -it -- /bin/sh
cd /usr/share/nginx/html
cat index.html
echo "hello hostpath" > hello
登录宿主机查看
登录pod所在的节点的宿主机目录查看文件是否存在
cat hello
发现写入的文件是存在的
测试文件持久性
删除Pod
手动删除容器模拟容器销毁,用于是pod是被控制器管理的,删除后会被重建新的pod
kubectl delete pod vol-hostpath-deploy-595699bdb4-4m8s4
访问测试
我们知道hostpath是在宿主机创建文件的,现在我们发现pod漂移到了node2节点,我们尝试访问,并登录容器查看
curl 10.244.2.23
发现文件不存在符合我们的预期
再次删除容器
因为上一次删除漂移到了node2,我们再次删除让其漂移到node1
kubectl delete pod vol-hostpath-deploy-595699bdb4-gd8sw
访问测试
curl 10.244.1.18
总结
可以发现容器被删除后,新建的pod也可以看到我们映射的目录,我们写入的文件是存在的
这有个缺点就是不能够跨容器去读取数据,如果删除后的pod被调度到其他节点的话,原来的数据也就没有了,如果能不受节点的影响,并且挂载的数据不会随生命周期的结束而结束,我们应该怎么解决呢?就是我们后面讲到的持久化存储了
NFS 存储卷
nfs存储卷用于将事先存在的NFS服务器上导出的存储空间挂载到Pod中供容器使用。
与emptyDir不同的是,当pod资源删除时emptyDir也会被删除,而NFS在Pod对象删除时仅是被卸载而非删除。这就意味NFS能够允许我们提前对数据进行处理,而且这些数据可以在Pod之间相互传递,并且NFS可以同时被多个Pod挂载并进行读写。
NFS 安装
NFS 简介
NFS 是Network File System的缩写,即网络文件系统。一种使用于分散式文件系统的协定,由Sun公司开发,于1984年向外公布。功能是通过网络让不同的机器、不同的操作系统能够彼此分享个别的数据,让应用程序在客户端通过网络访问位于服务器磁盘中的数据,是在类Unix系统间实现磁盘文件共享的一种方法。
NFS在文件传送或信息传送过程中依赖于RPC协议,RPC远程过程调用 (Remote Procedure Call) 是能使客户端执行其他系统中程序的一种机制,NFS本身是没有提供信息传输的协议和功能的。
NFS应用场景,常用于高可用文件共享,多台服务器共享同样的数据,可扩展性比较差,本身高可用方案不完善,取而代之的数据量比较大的可以采用MFS、TFS、HDFS、GFS等等分布式文件系统。
服务端安装
安装NFS服务端
yum install nfs-utils rpcbind -y
配置NFS
NFS安装完毕,需要创建共享目录,共享目录在vi /etc/exports文件里面配置,配置方式如下
vi /etc/exports
/tmp/k8s/data/volumn1/ *(rw,no_root_squash,no_all_squash,sync)
可配置参数如下
- /tmp/k8s/data/volumn1/: 表示需要共享的目录
- IP:表示允许哪个客户端访问
IP后括号里的设置表示对该共享文件的权限
生效配置
exportfs -r
重启NFS服务
service rpcbind restart;service nfs restart
查看NFS注册
rpcinfo -p localhost
选项与参数:
- -p :针对某 IP (未写则预设为本机) 显示出所有的 port 与 porgram 的信息;
- -t :针对某主机的某支程序检查其 TCP 封包所在的软件版本;
- -u :针对某主机的某支程序检查其 UDP 封包所在的软件版本;
客户端安装
安装nfs-utils
yum -y install nfs-utils
查看共享目录
检查ntfs是否能够正常访问
showmount -e 192.168.64.161
挂载目录
Linux客户端,如何想使用这个NFS文件系统,需要在客户端挂载,挂载命令为(为了提高NFS的稳定性,使用TCP协议挂载,NFS默认用UDP协议):
mount -t nfs 192.168.64.161:/tmp/k8s/data/volumn1 /tmp/k8s/data/volumn1 -o proto=tcp -o nolock
开启自动挂载
开启自动挂载,在/etc/fstab下加上如下配置
vi /etc/fstab
192.168.64.161:/tmp/k8s/data/volumn1 /tmp/k8s/data/volumn1 nfs defaults 0 0
测试NFS
服务端写入
cd /tmp/k8s/data/volumn1
echo "This is a text to test whether the pod is persistent" > index.html
客户端读取
cd /tmp/k8s/data/volumn1
cat index.html
客户端操作
客户端写入一个新的文件并删除index.html
echo "hello NFS" > hello
服务端查看
在服务端查看变更
cat hello
配置资源清单
vi vol-nfs-deploy.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: vol-nfs-deploy
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
volumes: #定义存储卷
- name: html # 定义存储名称
hostPath: # 定义存储类型
path: /tmp/k8s/data/volumn1 # 宿主机存储路径
type: DirectoryOrCreate # 不存在路径创建路径
containers:
- name: nginx
image: nginx:1.12
ports:
- containerPort: 80
volumeMounts: #在容器中定义挂载存储卷的名和路径
- name: html
mountPath: /usr/share/nginx/html
创建deploy
kubectl apply -f vol-nfs-deploy.yml
kubectl get pods -o wide
写入文件
查看pod发现pod在node1节点,登录node1节点写入文件
cd /tmp/k8s/data/volumn1/
echo "This is a text to test whether the pod is persistent" > index.html
访问测试
curl 10.244.1.19
测试存储卷
登录nginx
kubectl exec vol-nfs-deploy-595699bdb4-d9z2h -c nginx -it -- /bin/sh
cd /usr/share/nginx/html
cat index.html
echo "hello nfspath" > hello
登录宿主机查看
登录pod所在的节点的宿主机目录查看文件是否存在
cat hello
发现写入的文件是存在的
测试文件持久性
删除Pod
手动删除容器模拟容器销毁,用于是pod是被控制器管理的,删除后会被重建新的pod
kubectl delete pod vol-nfs-deploy-595699bdb4-g5xwr
访问测试
因为我们使用了NFS的方式存储数据,任何节点都可以访问,我们的pod由node1漂移到了node2
curl 10.244.2.25
通过上面测试可以看出,此前创建的index.html及其数据在Pod资源重建后依然存在,且不论pod资源调度到哪个节点。这表明在删除Pod资源时,其关联的外部存储卷并不会被一同删除。如果需要删除此类的数据,需要用户通过存储系统的管理接口手动进行。
PVC与PV
介绍
前面提到Kubernetes提供那么多存储接口,但是首先Kubernetes的各个Node节点能管理这些存储,但是各种存储参数也需要专业的存储工程师才能了解,由此我们的Kubernetes管理变的更加复杂。由此kubernetes提出了PV和PVC的概念,这样开发人员和使用者就不需要关注后端存储是什么,使用什么参数等问题。如下图:
PV
PersistentVolume(PV)是集群中已由管理员配置的一段网络存储。
集群中的资源就像一个节点是一个集群资源。PV是诸如卷之类的卷插件,但是具有独立于使用PV的任何单个Pod的生命周期。该API对象捕获存储的实现细节,即NFS,ISCSI或云提供商特定的存储系统。
PVC
PersistentVolumeClaim(PVC)是用户存储的请求。它类似于Pod。Pod消耗节点资源,PVC消耗存储资源。Pod可以请求特定级别的资源(CPU和内存)。权限要求可以请求特定的大小和访问模式。
虽然PersistentVolumeClaims允许用户使用抽象存储资源,但是常见的是,用户需要具有不同属性(如性能)的PersistentVolumes,用于不同的问题。集群管理员需要能够提供多种不同于PersistentVolumes的PersistentVolumes,而不仅仅是大小和访问模式,而不会使用户了解这些卷的实现细节。对于这些需求,存在StorageClass资源。
StorageClass为管理员提供了一种描述他们提供的存储的“类”的方法。不同的类可能映射到服务质量级别,或备份策略,或者由集群管理员确定的任意策略。Kubernetes本身对于什么类别代表是不言而喻的。这个概念有时在其它存储系统中称为“配置文件”
生命周期
PV是集群中的资源。PVC是对这些资源的请求,也是对资源的索赔检查。PV和PVC之间的相互作用遵循这个生命周期:Provisioning—>Binding—>Using—>Releasing—>Recycling
供应准备
PV有两种提供方式:静态或者动态
- Static:集群管理员创建多个PV。它们携带可供集群用户使用的真实存储的详细信息。它们存在于Kubernetes API中,可用于消费。
- Dynamic:当管理员创建的静态PV都不匹配用户的PersistentVolumesClaim时,集群可能会尝试为PVC动态配置卷。此配置基于StorageClasses:PVC必须请求一个类,并且管理员必须已经创建并配置该类才能进行动态配置。要求该类的声明有效地位自己禁用动态配置。
绑定Binding
用户创建PVC并指定需要的资源和访问模式。在找到可用PV之前,PVC会保持未绑定状态。
使用Using
用户可在Pod中像volume一样使用PVC。
释放Releasing
用户删除PVC来回收存储资源,PV将变成“released”状态。由于还保留着之前的数据,这些数据要根据不同的策略来处理,否则这些存储资源无法被其它PVC使用
回收Recycling
PV可以设置三种回收策略:保留(Retain)、回收(Recycle)和删除(Delete)
创建PV
创建资源清单
vi pv-nfs.yml
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-nfs-001
labels:
name: pv001 # pv增加标签
spec:
nfs:
path: /tmp/k8s/data/volumn1
server: 192.168.64.161
readOnly: false
accessModes: ["ReadWriteOnce","ReadWriteMany"]
capacity:
storage: 2Gi
persistentVolumeReclaimPolicy: Retain
创建PV
kubectl apply -f pv-nfs.yml
创建PVC
创建PVC并和PV进行绑定
编辑资源清单
vi pvc-nfs.yml
#创建PVC
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nfs-pvc
spec:
accessModes: ["ReadWriteMany"]
resources:
requests:
storage: 2Gi #指定PVC大小为6Gi
selector: #这里通过标签选择器指定了所使用的pv卷为key为name,value为pv001的pv资源
matchLabels:
name: pv001
创建PVC
kubectl apply -f pvc-nfs.yml
配置资源清单
vi pvc-nfs-deploy.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: pvc-nfs-deploy
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
volumes: #定义存储卷
- name: html # 定义存储名称
persistentVolumeClaim: #通过该字段定义使用pvc
claimName: nfs-pvc #指定pvc的名称
readOnly: false #关闭只读
containers:
- name: nginx
image: nginx:1.12
ports:
- containerPort: 80
volumeMounts: #在容器中定义挂载存储卷的名和路径
- name: html
mountPath: /usr/share/nginx/html
创建deploy
kubectl apply -f pvc-nfs-deploy.yml
kubectl get pods -o wide
写入文件
查看pod发现pod在node1节点,登录node1节点写入文件
cd /tmp/k8s/data/volumn1/
echo "This is a text to test whether the pod is persistent" > index.html
访问测试
curl 10.244.2.26
测试存储卷
登录nginx
kubectl exec pvc-nfs-deploy-588b4cfdf-xmkvz -c nginx -it -- /bin/sh
cd /usr/share/nginx/html
cat index.html
echo "hello nfspath" > hello
登录宿主机查看
登录pod所在的节点的宿主机目录查看文件是否存在
cat hello
发现写入的文件是存在的
测试文件持久性
删除Pod
手动删除容器模拟容器销毁,用于是pod是被控制器管理的,删除后会被重建新的pod
kubectl delete pod pvc-nfs-deploy-588b4cfdf-xmkvz
访问测试
因为我们使用了NFS的方式存储数据,任何节点都可以访问,我们的pod由node1漂移到了node2
curl 10.244.2.26
能够成功访问,符合我们的预期