k8s存储: (持久化)
docker容器是有生命周期的。
volume
1,存储类(Storage class)是k8s资源类型的一种,它是有管理员为管理PV更加方便创建的一个逻辑组,可以按照存储系统的性能高低,或者综合服务质量,备份策略等分类。不过k8s本身不知道类别到底是什么,它这是作为一个描述。
2,存储类的好处之一就是支持PV的动态创建,当用户用到持久性存储时,不必再去提前创建PV,而是直接创建PVC就可以了,非常的方便。
3,存储类对象的名称很重要,并且出了名称之外,还有3个关键字段
Provisioner(供给方):
及提供了存储资源的存储系统。k8s内建有多重供给方,这些供给方的名字都以“kubernetes.io”为前缀。并且还可以自定义。
Parameters(参数):存储类使用参数描述要关联到的存储卷,注意不同的供给方参数也不同。
reclaimPolicy:PV的回收策略,可用值有Delete(默认)和Retain
简介
1, 由于容器本身是非持久化的,因此需要解决在容器中运行应用程序遇到的一些问题。首先,当容器崩溃时,kubelet将重新启动容器,但是写入容器的文件将会丢失,容器将会以镜像的初始状态重新开始;第二,在通过一个Pod中一起运行的容器,通常需要共享容器之间一些文件。Kubernetes通过存储卷解决上述的两个问题。
2, 在Docker有存储卷的概念卷,但Docker中存储卷只是磁盘的或另一个容器中的目录,并没有对其生命周期进行管理。Kubernetes的存储卷有自己的生命周期,它的生命周期与使用的它Pod生命周期一致。因此,相比于在Pod中运行的容器来说,存储卷的存在时间会比的其中的任何容器都长,并且在容器重新启动时会保留数据。当然,当Pod停止存在时,存储卷也将不再存在。在Kubernetes支持多种类型的卷,而Pod可以同时使用各种类型和任意数量的存储卷。在Pod中通过指定下面的字段来使用存储卷:
spec.volumes:通过此字段提供指定的存储卷
spec.containers.volumeMounts:通过此字段将存储卷挂接到容器中
环境介绍
主机 |
IP地址 |
服务 |
master |
192.168.1.21 |
k8s |
node01 |
192.168.1.22 |
k8s |
node02 |
192.168.1.23 |
k8s |
1.emptyDir(空目录):类似docker 数据持久化的:docer manager volume
使用场景:在同一 个Pod里,不同的容器,共享数据卷。
如果容器被删除,数据仍然存在,如果Pod被 删除,数据也会被删除。
<1> 介绍
一个emptyDir 第一次创建是在一个pod被指定到具体node的时候,并且会一直存在在pod的生命周期当中,正如它的名字一样,它初始化是一个空的目录,pod中的容器都可以读写这个目录,这个目录可以被挂在到各个容器相同或者不相同的的路径下。当一个pod因为任何原因被移除的时候,这些数据会被永久删除。注意:一个容器崩溃了不会导致数据的丢失,因为容器的崩溃并不移除pod.
emptyDir的使用场景如下:
- 空白的初始空间,例如合并/排序算法中,临时将数据保存在磁盘上。
- 长时间计算中存储检查点(中间结果),以便容器崩溃时,可以从上一次存储的检查点(中间结果)继续进行,而不是从头开始。
- 作为两个容器的共享存储,使得第一个内容管理的容器可以将生成的数据存入其中,同时由一个webserver容器对外提供这些页面。
- 默认情况下,emptyDir数据卷存储在node节点的存储介质(机械硬盘、SSD或网络存储)上。
<2>emptyDir 磁盘的作用:
(1)普通空间,基于磁盘的数据存储
(2)作为从崩溃中恢复的备份点
(3)存储那些那些需要长久保存的数据,例web服务中的数据
默认的,emptyDir 磁盘会存储在主机所使用的媒介上,可能是SSD,或者网络硬盘,这主要取决于你的环境。当然,我们也可以将emptyDir.medium的值设置为Memory来告诉Kubernetes 来挂在一个基于内存的目录tmpfs,因为
tmpfs速度会比硬盘块度了,但是,当主机重启的时候所有的数据都会丢失。
测试编写一个yaml文件
[root@master yaml]# vim emptyDir.yaml
apiVersion: v1
kind: Pod
metadata:
name: producer-consumer
spec:
containers:
- image: busybox
name: producer
volumeMounts:
- mountPath: /producer_dir
name: shared-volume
args:
- /bin/sh
- -c
- echo "hello k8s" > /producer_dir/hello; sleep 30000
- image: busybox
name: consumer
volumeMounts:
- mountPath: /consumer_dir
name: shared-volume
args:
- /bin/sh
- -c
- cat /consumer_dir/hello; sleep 30000
volumes:
- name: shared-volume
emptyDir: {}
执行一下
[root@master yaml]# kubectl apply -f emptyDir.yaml
查看一下
[root@master yaml]# kubectl get pod
查看日志
[root@master yaml]# kubectl logs producer-consumer producer
[root@master yaml]# kubectl logs producer-consumer consumer
查看挂载的目录
node节点查看容器名,并通过容器名查看挂载的目录
[root@node01 shared-volume]# docker ps
[root@node01 shared-volume]# docker inspect k8s_consumer_producer-consumer_default_9ec83f9e-e58b-4bf8-8e16-85b0f83febf9_0
进入挂载目录查看一下
2.hostPath Volume:类似docker 数据持久化的:bind mount
<1> 介绍
hostPath宿主机路径,就是把pod所在的宿主机之上的脱离pod中的容器名称空间的之外的宿主机的文件系统的某一目录和pod建立关联关系,在pod删除时,存储数据不会丢失。
<2> 作用
如果Pod被删除,数据会保留,相比较emptyDir要好一点。不过一旦host崩溃,hostPath也无法访问 了。
docker或者k8s集群本身的存储会采用hostPath这种方式。
适用场景如下:
某容器需要访问 Docker,可使用 hostPath 挂载宿主节点的 /var/lib/docker
在容器中运行 cAdvisor,使用 hostPath 挂载宿主节点的 /sys
3.Persistent Volume| PV(持久卷) 提前做好的,数据持久化的数据存放目录。
Psesistent Volume Claim| PVC( 持久卷使用声明|申请)
PersistentVolume(PV)是集群中已由管理员配置的一段网络存储。 集群中的资源就像一个节点是一个集群资源。 PV是诸如卷之类的卷插件,但是具有独立于使用PV的任何单个pod的生命周期。 该API对象捕获存储的实现细节,即NFS,iSCSI或云提供商特定的存储系统。
PVC和PV的概念
我们前面提到kubernetes提供那么多存储接口,但是首先kubernetes的各个Node节点能管理这些存储,但是各种存储参数也需要专业的存储工程师才能了解,由此我们的kubernetes管理变的更加复杂的。由此kubernetes提出了PV和PVC的概念,这样开发人员和使用者就不需要关注后端存储是什么,使用什么参数等问题。如下图:
PersistentVolume(PV)是集群中已由管理员配置的一段网络存储。 集群中的资源就像一个节点是一个集群资源。 PV是诸如卷之类的卷插件,但是具有独立于使用PV的任何单个pod的生命周期。 该API对象捕获存储的实现细节,即NFS,iSCSI或云提供商特定的存储系统。
PersistentVolumeClaim(PVC)是用户存储的请求。PVC的使用逻辑:在pod中定义一个存储卷(该存储卷类型为PVC),定义的时候直接指定大小,pvc必须与对应的pv建立关系,pvc会根据定义去pv申请,而pv是由存储空间创建出来的。pv和pvc是kubernetes抽象出来的一种存储资源。
虽然PersistentVolumeClaims允许用户使用抽象存储资源,但是常见的需求是,用户需要根据不同的需求去创建PV,用于不同的场景。而此时需要集群管理员提供不同需求的PV,而不仅仅是PV的大小和访问模式,但又不需要用户了解这些卷的实现细节。 对于这样的需求,此时可以采用StorageClass资源。这个在前面就已经提到过此方案。
PV是集群中的资源。 PVC是对这些资源的请求,也是对资源的索赔检查。 PV和PVC之间的相互作用遵循这个生命周期:
Provisioning(配置)---> Binding(绑定)--->Using(使用)---> Releasing(释放) ---> Recycling(回收)
(1)基于nfs服务来做的PV
nfs使的我们可以挂在已经存在的共享到的我们的Pod中,和emptyDir不同的是,emptyDir会被删除当我们的Pod被删除的时候,但是nfs不会被删除,仅仅是解除挂在状态而已,这就意味着NFS能够允许我们提前对数据进行处理,而且这些数据可以在Pod之间相互传递.并且,nfs可以同时被多个pod挂在并进行读写
注意:必须先保证NFS服务器正常运行在我们进行挂在nfs的时候
下载nfs所需安装包
[root@node02 ~]# yum -y install nfs-utils rpcbind
创建共享目录
[root@master ~]# mkdir /nfsdata
创建共享目录的权限
[root@master ~]# vim /etc/exports
/nfsdata *(rw,sync,no_root_squash)
开启nfs和rpcbind
[root@master ~]# systemctl start nfs-server.service
[root@master ~]# systemctl start rpcbind
测试一下
[root@master ~]# showmount -e
<1>创建nfs-pv的yaml文件
[root@master yaml]# cd yaml/
[root@master yaml]# vim nfs-pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: test-pv
spec:
capacity: #pv容量的大小
storage: 1Gi
accessModes: #访问pv的模式
- ReadWriteOnce #能以读-写mount到单个的节点
persistentVolumeReclaimPolicy: Recycle
storageClassName: nfs
nfs:
path: /nfsdata/pv1
server: 192.168.1.21
accessModes:(PV支持的访问模式)
- ReadWriteOnce: 能以读-写mount到单个的节点
- ReadWriteMany: 能以读-写mount到多个的节点。
- ReadOnlyOnce: 能以只读的方式mount到单个节点。
persistentVolumeReclaimPolicy : (PV存储空间的回收策略是什么)
Recycle: 自动清除数据。
Retain: 需要管理员手动回收。
Delete: 云存储专用。
<2>执行一下
[root@master yaml]# kubectl apply -f nfs-pv.yaml
<3>查看一下
[root@master yaml]# kubectl get pv
<1>创建nfs-pvc的yaml文件
PersistentVolumeClaim(PVC)是用户存储的请求。PVC的使用逻辑:在pod中定义一个存储卷(该存储卷类型为PVC),定义的时候直接指定大小,pvc必须与对应的pv建立关系,pvc会根据定义去pv申请,而pv是由存储空间创建出来的。pv和pvc是kubernetes抽象出来的一种存储资源。
[root@master yaml]# vim nfs-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: test-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
storageClassName: nfs
<2>执行一下
[root@master yaml]# kubectl apply -f nfs-pvc.yaml
<3>查看一下
[root@master yaml]# kubectl get pvc
[root@master yaml]# kubectl get pv
(2)创建一个pod资源
[root@master yaml]# vim pod.yaml
kind: Pod
apiVersion: v1
metadata:
name: test-pod
spec:
containers:
- name: pod1
image: busybox
args:
- /bin/sh
- -c
- sleep 30000
volumeMounts:
- mountPath: "/mydata"
name: mydata
volumes:
- name: mydata
persistentVolumeClaim:
claimName: test-pvc
<1> 执行一下
[root@master yaml]# kubectl apply -f pod.yaml
<2>查看一下
[root@master yaml]# kubectl get pod -o wide
可以看到现在没有开启成功
查看一下test-pod的信息看看是哪里的问题
[root@master yaml]# kubectl describe pod test-pod
那是因为pv的本地挂载目录没有创建好
[root@master yaml]# mkdir /nfsdata/pv1/
//要和nfs-pv.yaml的名字一样
重新创建一下pod
[root@master yaml]# kubectl delete -f pod.yaml
[root@master yaml]# kubectl apply -f pod.yaml
[root@master yaml]# kubectl get pod -o wide
(3)test-pod创建hello创建文件并添加内容
[root@master yaml]# kubectl exec test-pod touch /mydata/hello
进入容器
[root@master yaml]# kubectl exec -it test-pod /bin/sh
/ # echo 123 > /mydata/hello
/ # exit
挂载目录查看一下
[root@master yaml]# cat /nfsdata/pv1/hello
和刚刚的一样
(4)测试回收策略
删除pod和pvc,pv
[root@master yaml]# kubectl delete pod test-pod
[root@master yaml]# kubectl delete pvc test-pvc
[root@master yaml]# kubectl delete pv test-pv
查看一下
[root@master yaml]# kubectl get pv
[root@master yaml]# cat /nfsdata/pv1/hello
文件已被回收
(5)修改pv的回收策略为手动
修改
[root@master yaml]# vim nfs-pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: test-pv
spec :
capacity :
storage: 1Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain #修改
storageClassName: nfs
nfs:
path: /nfsdata/pv1
server: 192.168.1.21
执行一下
[root@master yaml]# kubectl apply -f nfs-pv.yaml
创建pod
[root@master yaml]# kubectl apply -f pod.yaml
查看一下
[root@master yaml]# kubectl describe pod test-pod
创建pvc
[root@master yaml]# kubectl apply -f nfs-pvc.yaml
查看一下pod
[root@master yaml]# kubectl get pod
(6)test-pod创建hello创建文件并添加内容
[root@master yaml]# kubectl exec test-pod touch /mydata/k8s
查看一下挂载目录
[root@master yaml]# ls /nfsdata/pv1/
删除pod和pvc,pv,再次查看挂载目录
[root@master yaml]# kubectl delete pod test-pod
[root@master yaml]# kubectl delete pvc test-pvc
[root@master yaml]# kubectl delete pv test-pv
查看挂载目录
[root@master yaml]# ls /nfsdata/pv1/
内容还在
4.mysql对数据持久化的应用
下面演示如何为 MySQL 数据库提供持久化存储,步骤为:
- 创建 PV 和 PVC。
- 部署 MySQL。
- 向 MySQL 添加数据。
- 模拟节点宕机故障,Kubernetes 将 MySQL 自动迁移到其他节点。
- 验证数据一致性。
(1)通过之前的yaml文件,创建pv和pvc
[root@master yaml]# kubectl apply -f nfs-pv.yaml
[root@master yaml]# kubectl apply -f nfs-pvc.yaml
查看一下
[root@master yaml]# kubectl get pv
[root@master yaml]# kubectl get pvc
(2)编写一个mysql的yaml文件
[root@master yaml]# vim mysql.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: test-mysql
spec:
selector:
matchLabels: #支持等值的标签
app: mysql
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: test-mysql
spec:
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- image: mysql:5.6
name: mysql
env:
- name: MYSQL_ROOT_PASSWORD
value: 123.com
volumeMounts:
- name: mysql-storage
mountPath: /var/lib/mysql
volumes:
- name: mysql-storage
persistentVolumeClaim:
claimName: test-pvc
执行一下
[root@master yaml]# kubectl apply -f mysql.yaml
查看一下
[root@master yaml]# kubectl get pod
(3)进入mysql容器
① 切换到数据库 mysql。
② 创建数据库表 my_id。
③ 插入一条数据。
④ 确认数据已经写入。
关闭 k8s-node2,模拟节点宕机故障。
[root@master yaml]# kubectl exec -it test-mysql-569f8df4db-rkpwm -- mysql -u root -p123.com
创建数据库
mysql> create database yun33;
切换数据库
mysql> use yun33;
创建表
mysql> create table my_id( id int(4));
在表中插入数据
mysql> insert my_id values(9527);
查看表
mysql> select * from my_id;
(4)查看本地的挂载目录
[root@master yaml]# ls /nfsdata/pv1/
查看一下pod
[root@master yaml]# kubectl get pod -o wide -w
挂起node01
(5)查看node02上面数据是否和刚才一样(验证数据的一致性)
进入数据库
[root@master yaml]# kubectl exec -it test-mysql-569f8df4db-nsdnz -- mysql -u root -p123.com
查看数据库
mysql> show databases;
查看表
mysql> show tables;
mysql> select * from my_id;
可以看到数据还在
5. 排错方法
kubectl describe
//查看详细信息,找出问题
kubectl logs
//查看日志,找出问题
/var/ log/messages
//查看该节点的kubelet的日志。
6. 小结
本章我们讨论了 Kubernetes 如何管理存储资源。
emptyDir 和 hostPath 类型的 Volume 很方便,但可持久性不强,Kubernetes 支持多种外部存储系统的 Volume。
PV 和 PVC 分离了管理员和普通用户的职责,更适合生产环境。我们还学习了如何通过 StorageClass 实现更高效的动态供给。
最后,我们演示了如何在 MySQL 中使用 PersistentVolume 实现数据持久性。
1. PV的访问控制类型
accessModes:(PV支持的访问模式)
- ReadWriteOnce: 能以读-写mount到单个的节点
- ReadWriteMany: 能以读-写mount到多个的节点。
- ReadOnlyOnce: 能以只读的方式mount到单个节点。
2. PV的空间回收策略
persistentVolumeReclaimPolicy : (PV存储空间的回收策略是什么)
Recycle: 自动清除数据。
Retain: 需要管理员手动回收。
Delete: 云存储专用。
3. PV和PVC相互关联
是通过accessModes和storageClassName模块关联的