K8s实现数据持久化

前言

在一切虚拟化解决方案中,数据的持久化都是需要我们非常关心的问题,docker如此,K8s也不例外。在k8s中,有一个数据卷的概念。

k8s数据卷主要解决了以下两方面问题:

  • 数据持久性:通常情况下,容器运行起来后,写入到其文件系统的文件时暂时性的。当容器崩溃后,kebelet将这个容器kill掉,然后生成一个新的容器,此时,新运行的容器将没有原来容器内的文件,因为容器是重新从镜像创建的。
  • 数据共享:同一个pod中运行的容器之间,经常会存在共享文件/文件夹的需求。

在k8s中,Volume(数据卷)存在明确的生命周期(与包含该数据卷的容器组(pod)相同)。因此Volume的生命周期比同一容器组(pod)中任意容器的生命周期要更长,不管容器重启了多少次,数据都被保留下来。当然,如果pod不存在了,数据卷自然退出了。此时,根据pod所使用的数据卷类型不同,数据可能随着数据卷的退出而删除,也可能被真正持久化,并在下次容器组重启时仍然可以使用。

从根本上来说,一个数据卷仅仅是一个可以被pod访问的目录或文件。这个目录是怎么来的,取决于该数据卷的类型(不同类型的数据卷使用不同的存储介质)。同一个pod中的两个容器可以将一个数据卷挂载到不同的目录下。

一、数据卷类型

k8s目前支持28种数据卷类型(其中大多数特定于云环境),这里将写下在k8s中常用的几种数据卷类型。

1、emptyDir

emptyDir类型的数据卷在创建pod时分配给该pod,并且直到pod被移除,该数据卷才被释放。该数据卷初始分配时,始终是一个空目录。同一个pod中的不同容器都可以对该目录执行读写操作,并且共享其中的数据(尽管不同容器可能将该数据卷挂载到容器中的不同路径)。当pod被删除后,emptyDir数据卷中的数据将被永久删除。(PS:容器奔溃时,kubelet并不会删除pod,而仅仅是将容器重启,因此emptyDir中的数据在容器崩溃并重启后,仍然是存在的)。

emptyDir的使用场景如下:

  • 空白的初始空间,例如合并/排序算法中,临时将数据保存在磁盘上。
  • 长时间计算中存储检查点(中间结果),以便容器崩溃时,可以从上一次存储的检查点(中间结果)继续进行,而不是从头开始。
  • 作为两个容器的共享存储,使得第一个内容管理的容器可以将生成的数据存入其中,同时由一个webserver容器对外提供这些页面。
  • 默认情况下,emptyDir数据卷存储在node节点的存储介质(机械硬盘、SSD或网络存储)上。

emptyDir的使用示例

[[email protected] ~]# vim emtydir.yaml     #pod的yaml文件如下

apiVersion: v1
kind: Pod
metadata:
  name: read-write
spec:
  containers:
  - name: write         #定义一个名为write的容器
    image: busybox
    volumeMounts:
    - mountPath: /write    #当数据持久化类型为emtydir时,这里的路径指的是容器内的路径
      name: share-volume     #指定本地的目录名
    args:        #容器运行后,进行写操作
    - /bin/sh
    - -c
    - echo "emtydir test" > /write/hello;sleep 30000

  - name: read           #定义一个名为read的容器
    image: busybox
    volumeMounts:
    - mountPath: /read
      name: share-volume      #指定本地的目录名
    args:                #容器运行后,进行读操作
    - /bin/sh
    - -c
    - cat /read/hello; sleep 30000
  volumes:               #这里的volumes是指对上面挂载的进行解释
  - name: share-volume    #这里的名字必须和上面pod的mountPath下的name值对应
    emptyDir: {}            #这里表示是个空目录,主要是定义了一个数据持久化的类型
[[email protected] ~]# kubectl apply -f emtydir.yaml    #执行yaml文件
[[email protected] ~]# kubectl exec -it read-write -c write /bin/sh  #进入第一个pod
/ # cat /write/hello        #确认yaml文件执行的命令是否生效
emtydir test

[[email protected] ~]# kubectl exec -it read-write -c read /bin/sh  #进入第二个容器名为read的容器查看
/ # cat /read/hello        #查看指定挂载的目录下是否和write容器中的内容一致
emtydir test
#至此,起码可以确认这两个pod是挂载了同一个本地目录,文件内容都一致。
#那么,现在看看具体挂载的是本地哪个目录?
[[email protected] ~]# kubectl get pod -o wide    #先通过此命令查看pod是运行在哪个节点上的
#我这里是运行在node01节点的,所以接下来需要到node01节点上进行查看

#node01节点操作如下:
[[email protected] ~]# docker ps             #通过此命令查看出运行的容器ID号
CONTAINER ID        IMAGE              #省略部分内容
6186a08c6d5f        busybox
5f19986f0879        busybox
[[email protected] ~]# docker inspect 6186a08c6d5f      #查看第一个容器的详细信息
 "Mounts": [          #找到mount字段
            {
                "Type": "bind",
                "Source": "/var/lib/kubelet/pods/86b67ff4-9ca0-4f40-86d8-6778cfe949ec/volumes/kubernetes.io~empty-dir/share-volume",
#上面的source就是指定的本地目录
                "Destination": "/read",
                "Mode": "Z",
                "RW": true,
                "Propagation": "rprivate"

[[email protected] ~]# docker inspect 5f19986f0879    #查看第二个容器的详细信息

        "Mounts": [    #同样定位到mount字段
            {
                "Type": "bind",
                "Source": "/var/lib/kubelet/pods/86b67ff4-9ca0-4f40-86d8-6778cfe949ec/volumes/kubernetes.io~empty-dir/share-volume",
#可以看到,上面指定的本地目录和第一个容器指定的是同一个目录
                "Destination": "/write",
                "Mode": "Z",
                "RW": true,
                "Propagation": "rprivate"
            },
#至此,已经可以确定这两个容器的挂载目录共享的是同一个本地目录
[[email protected] ~]# cat /var/lib/kubelet/pods/86b67ff4-9ca0-4f40-86d8-6778cfe949ec/volumes/kubernetes.io~empty-dir/share-volume/hello
#查看本地该目录下的内容,和pod中的一致
emtydir test

至此,emptyDir的特性就已经验证了,只要这个pod中还有一个容器在运行,那么这个本地的数据就不会丢失,但如果这个pod被删除,那么本地的数据也将不复存在。

验证如下:

node01上删除一个pod并再次查看本地目录:

[[email protected] ~]# docker rm -f 6186a08c6d5f    #删除一个pod
6186a08c6d5f
[[email protected] ~]# cat /var/lib/kubelet/pods/86b67ff4-9ca0-4f40-86d8-6778cfe949ec/volumes/kubernetes.io~empty-dir/share-volume/hello
#查看本地目录,发现文件还在
emtydir test

在master上将此pod删除,再次去node01节点上查看本地目录是否存在:

#master上删除pod
[[email protected] ~]# kubectl delete -f emtydir.yaml
#在node01上再次查看本地目录,会提示不存在这个目录
[[email protected] ~]# cat /var/lib/kubelet/pods/86b67ff4-9ca0-4f40-86d8-6778cfe949ec/volumes/kubernetes.io~empty-dir/share-volume/hello
cat: /var/lib/kubelet/pods/86b67ff4-9ca0-4f40-86d8-6778cfe949ec/volumes/kubernetes.io~empty-dir/share-volume/hello: 没有那个文件或目录

emptyDir总结:

同个pod里面的不同容器,共享同一个持久化目录,当pod节点删除时,volume的内容也会被删除。但如果仅仅是容器被销毁,pod还在,则volume不会受到任何影响。说白了,emptyDir的数据持久化的生命周期和使用的pod一致。一般是作为临时存储使用。

2、HostPath数据卷类型

hostPath 类型的数据卷将 Pod(容器组)所在节点的文件系统上某一个文件或目录挂载进容器组(容器内部),类似于docker中的bind mount挂载方式。

这种数据持久化的方式,使用场景不多,因为它增加了pod与节点之间的耦合。

绝大多数容器组并不需要使用 hostPath 数据卷,但是少数情况下,hostPath 数据卷非常有用:

适用场景如下:

  • 某容器需要访问 Docker,可使用 hostPath 挂载宿主节点的 /var/lib/docker
  • 在容器中运行 cAdvisor,使用 hostPath 挂载宿主节点的 /sys

总言而之,一般对K8s集群本身的数据持久化和docker本身的数据持久化会使用这种方式。

由于其使用场景比较少,这里就不举例了。

3、Persistent 数据卷类型

PersistentVolume(PV存储卷)是集群中的一块存储空间,由集群管理员管理或者由Storage class(存储类)自动管理,PV和pod、deployment、Service一样,都是一个资源对象。

既然有了PV这个概念,那么PVC(PersistentVolumeClaim)这个概念也不得不说一下,PVC代表用户使用存储的请求,应用申请PV持久化空间的一个申请、声明。K8s集群可能会有多个PV,你需要不停的为不同的应用创建多个PV。

比如说,pod是消耗node节点的计算资源,而PVC存储卷声明是消耗PV的存储资源。Pod可以请求的是特定数量的计算资源(CPU或内存等),而PVC请求的是特定大小或特定访问模式(只能被单节点读写/可被多节点只读/可被多节点读写)的存储资源。

PV和PVC的关系

PV(存储卷)和PVC(存储卷声明)的关系如下图所示:

上图中的解释如下:

  • PV是集群中的存储资源,通常由集群管理员创建和管理;
  • StorageClass用于对PV进行分类,如果配置正确,Storage也可以根据PVC的请求动态创建PV;
  • PVC是使用该资源的请求,通常由应用程序提出请求,并指定对应的StorageClass和需求的空间大小;
  • PVC可以作为数据卷的一种,被挂载到pod中使用。

存储卷声明(PVC)的管理过程

PV和PVC的管理过程描述如下:
1、在主机上划分出一个单独的目录用于PV使用,并且定义其可用大小
2、创建PVC这个资源对象,以便请求PV的存储空间
3、pod中添加数据卷,数据卷关联到PVC;
4、Pod中包含容器,容器挂载数据卷

其实上面解释那么多,可能还是云里雾里的,下面是一个使用案例,仅供参考。

案例大概过程如下:
底层存储采用nfs存储,然后在nfs的目录下划分1G的容量供PV调度。然后通过创建PVC来申请PV的存储资源空间,最后创建pod测试,使用PVC声明的存储资源来实现数据的持久化。

1)搭建nfs存储

为了方便操作,我直接在master上搭建nfs存储。

[[email protected] ~]# yum -y install nfs-utils
[[email protected] ~]# systemctl enable rpcbind
[[email protected] ~]# vim /etc/exports
/nfsdata *(rw,sync,no_root_squash)
[[email protected] ~]# systemctl start nfs-server
[[email protected] ~]# systemctl enable nfs-server
[[email protected] ~]# showmount -e
Export list for master:
/nfsdata *

2)创建PV资源对象

[[email protected] ~]# vim test-pv.yaml    #编辑PV的yaml文件

apiVersion: v1
kind: PersistentVolume
metadata:
  name: test-pv
spec:
  capacity:
    storage: 1Gi          #该PV可分配的容量为1G
  accessModes:
    - ReadWriteOnce              #访问模式为只能以读写的方式挂载到单个节点
  persistentVolumeReclaimPolicy: Recycle   #回收策略为Recycle
  storageClassName: nfs            #定义存储类名字
  nfs:                                #这里和上面定义的存储类名字需要一致
    path: /nfsdata/test-pv       #指定nfs的目录
    server: 192.168.20.6           #nfs服务器的IP
#关于上述的具体解释
#capacity:指定PV的大小
#AccessModes:指定访问模式
    #ReadWriteOnce:只能以读写的方式挂载到单个节点(单个节点意味着只能被单个PVC声明使用)
    #ReadOnlyMany:能以只读的方式挂载到多个节点
    #ReadWriteMany:能以读写的方式挂载到多个节点
#persistentVolumeReclaimPolicy:PV的回收策略
    #Recycle:清除PV中的数据,然后自动回收。
    #Retain:需要手动回收。
    #Delete:删除云存储资源。(云存储专用)
    #PS:注意这里的回收策略是指,在PV被删除后,在这个PV下所存储的源文件是否删除。
#storageClassName:PV和PVC关联的依据。
[[email protected] ~]# kubectl apply -f test-pv.yaml     #执行yaml文件
[[email protected] ~]# kubectl get pv test-pv    #既然PV是一个资源对象,那么自然可以通过此方式查看其状态
NAME      CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE
test-pv   1Gi        RWO            Recycle          Available           nfs                     38s
#查看PV的状态必须为Available才可以正常使用

3)创建PVC资源对象

[[email protected] ~]# vim test-pvc.yaml         #编写yaml文件

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: test-pvc
spec:
  accessModes:          #定义访问模式,必须和PV定义的访问模式一致
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi          #直接请求使用最大的容量
  storageClassName: nfs      #这里的名字必须和PV定义的名字一致
[[email protected] ~]# kubectl apply -f test-pvc.yaml     #执行yaml文件

#再次查看PV及PVC的状态(状态为bound,表示该PV正在被使用)
[[email protected] ~]# kubectl get pvc      #查看PVC的状态
NAME       STATUS   VOLUME    CAPACITY   ACCESS MODES   STORAGECLASS   AGE
test-pvc   Bound    test-pv   1Gi        RWO            nfs            2m10s
[[email protected] ~]# kubectl get pv      #查看PV的状态
NAME      CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM              STORAGECLASS   REASON   AGE
test-pv   1Gi        RWO            Recycle          Bound    default/test-pvc   nfs                     8m24s

4)创建一个Pod

这里创建的pod使用刚刚创建的PV来实现数据的持久化。

[[email protected] ~]# vim test-pod.yaml       #编写pod的yaml文件
apiVersion: v1
kind: Pod
metadata:
  name: test-pod
spec:
  containers:
  - name: test-pod
    image: busybox
    args:
    - /bin/sh
    - -c
    - sleep 30000
    volumeMounts:
    - mountPath: /testdata
      name: volumedata     #这里自定义个名称
  volumes:
    - name: volumedata      #这里的是上面定义的名称解释,这两个名称必须一致
      persistentVolumeClaim:
        claimName: test-pvc
[[email protected] ~]# kubectl apply -f test-pod.yaml        #执行yaml文件
[[email protected] ~]# kubectl get pod     #查看pod的状态,发现其一直处于ContainerCreating状态
#怎么回事呢?
NAME       READY   STATUS              RESTARTS   AGE
test-pod   0/1     ContainerCreating   0          23s
#当遇到pod状态不正常时,一般我们可以采用三种方式来排错
#第一就是使用kubectl  describe命令来查看pod的详细信息
#第二就是使用kubectl logs命令来查看pod的日志
#第三就是查看宿主机本机的message日志
#这里我采用第一种方法排错
[[email protected] ~]# kubectl describe pod test-pod
#输出的最后一条信息如下:
mount.nfs: mounting 192.168.20.6:/nfsdata/test-pv failed, reason given by server: No such file or directory
#原来是我们在挂载nfs存储目录时,指定的目录并不存在
#那就在nfs服务器上(这里是本机)进行创建相关目录咯
[[email protected] ~]# mkdir -p /nfsdata/test-pv      #创建对应目录
[[email protected] ~]# kubectl get pod test-pod   #然后再次查看pod的状态
#如果pod的状态还是正在创建,那么就是因为运行该pod的节点上的kubelet组件还没有反应过来
#如果要追求pod的启动速度,可以手动将pod所在节点的kubelet组件进行重启即可。
[[email protected] ~]# kubectl get pod test-pod    #稍等片刻,再次查看,发现其pod已经running了
NAME       READY   STATUS    RESTARTS   AGE
test-pod   1/1     Running   0          8m

5)测试其数据持久化的效果

[[email protected] ~]# kubectl exec -it test-pod /bin/sh   #进入pod
/ # echo "test pv pvc" > /testdata/test.txt       #向数据持久化的目录写入测试信息
#回到nfs服务器,查看共享的目录下是否有容器中写入的信息
[[email protected] ~]# cat /nfsdata/test-pv/test.txt   #确定是有的
test pv pvc
#现在查看到pod容器的所在节点,然后到对应节点将其删除
[[email protected] ~]# kubectl get pod -o wide       #我这里是运行在node02节点
NAME       READY   STATUS    RESTARTS   AGE   IP           NODE     NOMINATED NODE   READINESS GATES
test-pod   1/1     Running   0          11m   10.244.2.2   node02   <none>           <none>
#在node02节点查看到其pod容器的ID号,然后将其删除
[[email protected] ~]# docker ps      #获取容器的ID号
[[email protected] ~]# docker rm -f dd445dce9530   #删除刚刚创建的容器
#回到nfs服务器,发现其本地目录下的数据还是在的
[[email protected] ~]# cat /nfsdata/test-pv/test.txt
test pv pvc
#那么现在测试,将这个pod删除,nfs本地的数据是否还在?
[[email protected] ~]# kubectl delete -f test-pod.yaml
[[email protected] ~]# cat /nfsdata/test-pv/test.txt      #哦吼,数据还在
test pv pvc
#那现在要是将PVC删除呢?
[[email protected] ~]# kubectl delete -f test-pvc.yaml
[[email protected] ~]# cat /nfsdata/test-pv/test.txt       #哦吼,数据不在了。
cat: /nfsdata/test-pv/test.txt: 没有那个文件或目录

总结:由于我们在创建pv这个资源对象时,采用的回收策略是清除PV中的数据,然后自动回收,而PV这个资源对象是由PVC来申请使用的,所以不管是容器也好,pod也好,它们的销毁并不会影响用于实现数据持久化的nfs本地目录下的数据,但是,一旦这个PVC被删除,那么本地的数据就会随着PVC的销毁而不复存在,也就是说,采用PV这种数据卷来实现数据的持久化,它这个数据持久化的生命周期是和PVC的生命周期是一致的。

———————— 本文至此结束,感谢阅读 ————————

原文地址:https://blog.51cto.com/14154700/2450847

时间: 2024-08-30 12:02:21

K8s实现数据持久化的相关文章

k8s存储数据持久化,emptyDir,hostPath,基于Nfs服务的PV,PVC

在docker和K8S中都存在容器是有生命周期的,因此数据卷可以实现数据持久化. 数据卷解决的主要问题: 1.数据持久性:当我们写入数据时,文件都是暂时性的存在,当容器崩溃后,host就会将这个容器杀死,然后重新从镜像创建容器,数据就会丢失. 2.数据共享:在同一个Pod中运行容器,会存在共享文件的需求. 数据卷的类型: 1.emptyDiremptyDir数据卷类似于docker数据持久化的docker manager volume,该数据卷初分配时,是一个空目录,同一个Pod中的容器可以对该

K8s数据持久化之自动创建PV

在前两篇实现k8s的数据持久化的流程为:搭建nfs底层存储===>创建PV====>创建PVC===>创建pod.最终pod中的container实现数据的持久化. 上述流程中,看似没什么问题,但细想一下,PVC在向PV申请存储空间的时候,是根据指定的pv名称.访问模式.容量大小来决定具体向哪个PV来申请空间的,如果PV的容量为20G,定义的访问模式是WRO(只允许以读写的方式挂载到单个节点),而PVC申请的存储空间为10G,那么一旦这个PVC是向上面的PV申请的空间,也就是说,那个PV

k8s之volumes持久化存储

k8s之数据持久化 kubernetes存储卷:我们知道默认情况下容器的数据都是非持久化的,在容器销毁以后数据也跟着丢失,所以docker提供了volume机制以便将数据持久化存储.类似的,k8s提供了更强大的volume机制和丰富的插件,解决了容器数据持久化和容器间共享数据的问题. volume:我们经常会说:容器和 Pod 是短暂的.其含义是它们的生命周期可能很短,会被频繁地销毁和创建.容器销毁时,保存在容器内部文件系统中的数据都会被清除.为了持久化保存容器的数据,可以使用k8s volum

k8s实践(七):存储卷和数据持久化(Volumes and Persistent Storage)

环境说明: 主机名 操作系统版本 ip docker version kubelet version 配置 备注 master Centos 7.6.1810 172.27.9.131 Docker 18.09.6 V1.14.2 2C2G master主机 node01 Centos 7.6.1810 172.27.9.135 Docker 18.09.6 V1.14.2 2C2G node节点 node02 Centos 7.6.1810 172.27.9.136 Docker 18.09.

K8s之MySQL实现数据持久化

这个是一个只写配置及验证的博文...... 博文大纲:1.搭建nfs存储2.创建PV3.创建PVC4.确认pv及pvc的状态5.创建pod+svc(service)6.进入MySQL数据库,添加测试数据7.手动删除节点上的容器,验证数据库内的数据是否还存在8.模拟MySQL容器所在的节点宕机,验证数据是否会丢失9.client端访问MySQL数据库 k8s集群环境如下: master节点IP:192.168.20.6 node01节点IP:192.168.20.7 node02节点IP:192.

k8s数据持久化

k8s数据持久化 Docker容器是有生命周期的,因此数据卷可以实现数据持久化 数据卷主要解决的问题: 数据持久性:当我们写入数据时,文件都是暂时性的存在,当容器崩溃后,host就会将这个容器杀死,然后重新从镜像创建容器,数据就会丢失 数据共享:在同一个Pod中运行容器,会存在共享文件的需求 存储类(Storage class)是k8s资源类型的一种,它是有管理员为管理PV更加方便创建的一个逻辑组,可以按照存储系统的性能高低,或者综合服务质量,备份策略等分类.不过k8s本身不知道类别到底是什么,

k8s数据持久化之statefulset的数据持久化,并自动创建PV与PVC

一:Statefulset StatefulSet是为了解决有状态服务的问题,对应的Deployment和ReplicaSet是为了无状态服务而设计,其应用场景包括:1.稳定的持久化存储,即Pod重新调度后还是能访问到相同的持久化数据,基于PVC来实现2.稳定的网络标志,即Pod重新调度后其PodName和HostName不变,基于Headless Service(即没有Cluster IP的Service)来实现3.有序部署,有序扩展,即Pod是有顺序的,在部署或者扩展的时候要依据定义的顺序依

k8s数据持久化之Secret

一.Secret资源对象:解决了密码.token.密钥等敏感数据的配置问题,而不需要把这些敏感数据暴露到镜像或者Pod Spec中.Secret可以以Volume或者环境变量的方式使用.用来保存一些敏感信息,比如数据库的用户名密码或者密钥.Secret有三种类型:1.Service Account:用来访问Kubernetes API,由Kubernetes自动创建,并且会自动挂载到Pod的/run/secrets/kubernetes.io/serviceaccount目录中.2.Opaque

Kubernetes数据持久化之Storage Class(存储类)自动创PV

通过博文Kubernetes的存储之Volume可以了解到Kubernets实现数据持久化的流程为:搭建NFS底层存储-->创建PV-->创建PVC-->创建pod最终将pod中的container实现数据的持久化! 从上述流程中,看似没有什么问题,但是仔细研究就会发现:PVC在向PV申请存储空间时,是根据指定PV的名称.访问模式.容量大小来决定具体向哪个PV申请空间的. 打比方说:如果PV的容量是20G,定义的访问模式是WRO(只允许以读写的方式挂载到单个节点),而PVC申请的存储空间