【k8s知识】【存储】k8s之持久化
一、持久化的必要性
场景:K8S运行的是一个一个Pod,K8S对Pod自动化管理,一个Pod挂了,另外一个Pod就会马上拉起来,假如运行Mysql的Pod挂了,马上重新拉起来,那原来Pod中存储的数据还会存在吗?或者说新拉起来的Pod会进行数据恢复吗?答案是:NO! 如果没有持久化存储,那兄弟,你真正的做到了从删库到跑路!
二、k8s的存储分类
三、k8s存储简介
1、emptydir---临时存储
(1)概念
当pod的存储方案设定为emptydir的时候,pod启动时,就会在pod所在节点的磁盘空间开辟出一块空卷,最开始里面是什么都没有的,pod启动后容器产生的数据会存放到那个空卷中。空卷变成了一个临时卷,供pod内的容器读取和写入数据,一旦pod容器消失,节点上开辟出的这个临时卷就会随着pod的销毁而销毁。
(2)用途
充当临时存储空间,当pod内容器产生的数据不需要做持久化存储的时候用emptydir
(3)使用
# emptydir.yaml --- apiVersion: v1 kind: Namespace metadata: name: dev --- apiVersion: v1 kind: Pod metadata: name: test-emptydir namespace: dev spec: containers: - image: nginx:1.20.0 name: test-emptydir volumeMounts: - mountPath: /usr/share/nginx/html #挂载到容器中的路径 name: cache-volume volumes: - name: cache-volume emptyDir: {} #指定存储方式为emptydir
(4)验证
# 1 在调度节点node3操作 # [root@k8s-node3 ~]# docker ps -a | grep empty d336ffaa01b5 7ab27dbbfbdf "/docker-entrypoint.…" 6 minutes ago Up 6 minutes k8s_test-emptydir_test-emptydir_dev_425715f8-2f4c-487a-a910-52371d91bf13_0 # 2 查看容器详细信息 # [root@k8s-node3 ~]# docker inspect d336ffaa01b5 ....... "Mounts": [ { "Type": "bind", "Source": "/var/lib/kubelet/pods/425715f8-2f4c-487a-a910-52371d91bf13/volumes/kubernetes.io~empty-dir/cache-volume", # 宿主机的源路径 "Destination": "/usr/share/nginx/html", # 挂载到容器内部的路径 "Mode": "", "RW": true, "Propagation": "rprivate" ...... # 3 此时源路径为空文件夹 # [root@k8s-node3 ~]# ls /var/lib/kubelet/pods/425715f8-2f4c-487a-a910-52371d91bf13/volumes/kubernetes.io~empty-dir/cache-volume # 4 进入到源路径 # [root@k8s-node3 ~]# cd /var/lib/kubelet/pods/425715f8-2f4c-487a-a910-52371d91bf13/volumes/kubernetes.io~empty-dir/cache-volume # 5 创建index.html # [root@k8s-node3 cache-volume]# echo 2021-5-31 > index.html # 6 测试访问 # [root@k8s-node3 cache-volume]# curl 10.244.3.156 2021-5-31 #===========然后到master节点删除pod============ # 7 删除pod # [root@k8s-master yaml]# kubectl delete pod/test-emptydir -n dev # 使用命令行删除pod # 或者应用yaml文件删除pod # kubectl delete -f emptydir.yaml pod "test-emptydir" deleted # 8 然后再回node3查看--发现路径已经被删除-随pod一起删除 # [root@k8s-node3 ]# ls /var/lib/kubelet/pods/425715f8-2f4c-487a-a910-52371d91bf13/volumes/kubernetes.io~empty-dir/cache-volume ls: 无法访问/var/lib/kubelet/pods/425715f8-2f4c-487a-a910-52371d91bf13/volumes/kubernetes.io~empty-dir/cache-volume: 没有那个文件或目录 # 9 因为emptydir是随pod创建而创建,然后再随pod删除而删除 # [root@k8s-node3 ]# ls /var/lib/kubelet/pods/425715f8-2f4c-487a-a910-52371d91bf13/ ls: 无法访问/var/lib/kubelet/pods/425715f8-2f4c-487a-a910-52371d91bf13/: 没有那个文件或目录
2、hostpath---半持久化
(1)概念
hostPath类型则是映射node文件系统中的文件或者目录到pod里,其实这个功能就相当于docker中的-v 目录映射,只不过在k8s中的时候,pod会漂移,当pod漂移到其他node节点的时候,pod不会跨节点的去读取目录。所以说hostpath只能算一种半持久化的存储方式。
(2)用途
当运行的容器需要访问Docker内部结构时,如使用hostPath映射/var/lib/docker到容器;
当在容器中运行cAdvisor时,可以使用hostPath映射/dev/cgroups到容器中;
(3)使用
# hostpath.yaml apiVersion: v1 kind: Pod metadata: name: test-hostpath namespace: dev spec: containers: - name: test-hostpath image: nginx:1.20.0 hostname: test-hostpath volumeMounts: - name: test-hostpath # 取个名字 mountPath: /usr/share/nginx/html # 挂载到容器中的路径 volumes: - name: test-hostpath # 取个名字必须与上面volumeMounts中name的一致 hostPath: # directory location on host path: /data # node节点上宿主机的路径 # this field is optional type: DirectoryOrCreate # path的属性,下面会介绍可选属性的值
(4)验证
宿主机:
# [root@k8s-node3 ]# ls / # 发现data目录已经自动创建 bin boot data dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var # 进入 宿主机/data文件夹是空的 # [root@k8s-node3 ]# cd /data # [root@k8s-node3 data]# ls | wc -l 0 # 在/data目录下创建一个测试的index.html文件 # [root@k8s-node3 data]# echo "`date` test hostpath" > /data/index.html # 访问测试 # [root@k8s-node3 data]# curl 10.244.3.158 # 访问pod的ip地址发现生效了,其实到这里已经证明hostpath挂载好了 2021年 05月 31日 星期一 15:55:10 CST test hostpath
查看pod
# 查找容器 # [root@k8s-node3 data]# docker ps -a | grep test-hostpath 99d9a0d1ba9a 7ab27dbbfbdf "/docker-entrypoint.…" 10 minutes ago Up 10 minutes k8s_test-hostpath_test-hostpath_dev_3f252f5b-f87d-4e02-9da0-d7ffefe3eeed_0 # 查看容器详细信息 # [root@k8s-node3 data]#docker inspect 99d9a0d1ba9a ...... "Mounts": [ { "Type": "bind", "Source": "/data", # 宿主机的目录 "Destination": "/usr/share/nginx/html", # 容器中的目录 "Mode": "", "RW": true, "Propagation": "rprivate" } ....... # 注意 type: DirectoryOrCreate # path的属性,如果path存在则为上面的结果,如果宿主机path目录不存在,是由k8s自动创建的 # 那么此处Mounts的信息会有所不同,但是都能实现同样的挂载效果 # =====path不存在由k8s自动创建的效果如下=====当path存在后,由k8s自动映射,如果path在pod存在之前就存在则直接映射到容器中 ...... "Mounts": [ { "Type": "bind", "Source": "/var/lib/kubelet/pods/3f252f5b-f87d-4e02-9da0-d7ffefe3eeed/volumes/kubernetes.io~projected/kube-api-access-xqbhv", "Destination": "/var/run/secrets/kubernetes.io/serviceaccount", "Mode": "ro", "RW": false, "Propagation": "rprivate" }, ...... # 进入容器内部查看 # docker exec -it 99d9a0d1ba9a /bin/bash # root@test-hostpath:/# cd /usr/share/nginx/html/ # root@test-hostpath:/usr/share/nginx/html# ls index.html # 果然,宿主机和Pod内都存在
删除pod,重新拉起一个pod
# master 节点操作 # [root@k8s-master yaml]# kubectl delete -f hostpath.yaml pod "test-hostpath" deleted # [root@k8s-node3 ~]# ls /data # 删除pod但是index.html文件依然存在 index.html # 再次应用yaml文件创建pod kubectl apply -f hostpath.yaml [root@k8s-master yaml]# kubectl get pods -n dev -o wide # 如果pod没有调度到node3,为了方便,可强制指定调度到node3,否则pod调度到其他节点则看不到实验效果了 NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES test-hostpath 1/1 Running 0 18s 10.244.3.160 k8s-node3 <none> <none> # 再次进入容器查看 # [root@k8s-node3 ~]# docker ps -a | grep test-hostpath f94596e2ff3b 7ab27dbbfbdf "/docker-entrypoint.…" 2 minutes ago Up 2 minutes k8s_test-hostpath_test-hostpath_dev_a4be3f59-fbd8-4f99-8d97-fdeb7ad8c4e4_0 # [root@k8s-node3 ~]# docker exec -it f94596e2ff3b /bin/bash root@test-hostpath:/# ls /usr/share/nginx/html/ index.html # 果然,index.html存在!
存在问题:
这种共享宿主机存储的方法似乎可以解决Mysql数据库数据恢复的场景,我们似乎可以万事大吉了!But,有的老铁会问:如果我得宿主机挂了怎么办?或者Pod没有在上一次节点上拉起,而是在新的节点上拉起,那数据都不在一个宿主机上,无法恢复。
3、pv---持久化存储
(1)概念
既然Host类型
的持久化存储无法解决节点宕机或者pod在另外的机器上拉起导致数据无法恢复的Bug,那我们就应该思考一个问题:既然我无法在宿主机持久化,那我在集群之外的服务器上存储数据,让我的Pod关联到这个数据存储服务器上,同时我对这个数据存储服务器做高可用,岂不美哉?
PV :PersistentVolume(持久化卷),是对底层的共享存储的一种抽象,PV由管理员进行创建和配置,它和具体的底层的共享存储技术的实现方式有关,比如 Ceph、GlusterFS、NFS等,都是通过插件机制完成与共享存储的对接。
PV 是什么?它是一种插件,它能够支持多种数据存储服务器,通过PV,我们能在K8S集群中,把我们的数据持久化到外部的服务器中。
PV/PVC结合NFS使用实践(持久存储):简单来说,要使用持久化存储,就需要使用pvc去跟pv去申请,然后pv查看自己有没有合适的存储空间卷,有合适的就与pvc进行绑定。pv与pvc是一一对应绑定的。现在我们用一幅图来说明pvc,pv,nfs的关系
创建顺序:后端存储—pv—pvc—pod
(2)使用
1)nfs服务器搭建
一、简介
一句话说就是多台服务器的数据保存在同一个存储服务器上。这样无论用户请求在哪一台服务器上看到的数据都是一样的,
NFS Network File System 网络文件系统。
NFS 主要功能是通过局域网络让不同的主机系统之间可以共享文件或目录
NFS 系统和 Windows 网络共享、网络驱动器类似, 只不过 windows用于局域网, NF 用于企业集群架构中, 如果是大型网站, 会用到更复杂的分布式文件系统 glusterfs,Ceph等
二、搭建
·找一台centos 7机器,执行以下脚本,搭建 NFS服务器:
# 操作节点为NFS服务器 # 安装nfs yum -y install nfs-utils rpcbind # 创建nfs目录 mkdir -p /nfs/data/ mkdir -p /nfs/data/k8s # 授予权限 chmod -R 777 /nfs/data # 编辑export文件 # vim /etc/exports /nfs/data *(rw,no_root_squash,sync) # 这里给的是root权限---生产环境不推荐 # 或者/nfs/data 0.0.0.0/0(rw,sync,all_squash) # 所有用户权限被映射成服务端上的普通用户nobody,权限被压缩 # 使得配置生效 exportfs -r # 查看生效 exportfs # 启动rpcbind、nfs服务 systemctl start rpcbind && systemctl enable rpcbind #端口是111 systemctl start nfs && systemctl enable nfs # 端口是 2049 # 查看rpc服务的注册情况 rpcinfo -p localhost # showmount测试 showmount -e ip(ip地址)
·在K8S集群所有node节点上安装NFS客户端
# 操作节点为k8s集群所有node节点 yum -y install nfs-utils rpcbind # systemctl start nfs && systemctl enable nfs
2)定义pv
# 定义PV apiVersion: v1 kind: PersistentVolume # 类型为PV metadata: name: nginx-pv # pv的名称 spec: accessModes: # 访问模式 - ReadWriteMany # PV以read-write挂载到多个节点 capacity: # 容量 storage: 2Gi # pv可用的大小 nfs: path: /nfs/data/ # NFS的路径 server: 10.0.0.16 # NFS服务器地址
3)定义PVC
# 定义PVC,用于消费PV apiVersion: v1 kind: PersistentVolumeClaim # 类型 metadata: name: nginx-pvc # PVC的名称 namespace: dev spec: accessModes: # 访问模式 - ReadWriteMany # PVC以read-write挂载到多个节点 resources: requests: storage: 2Gi # PVC允许申请的大小
4)PV、PVC实践
# vim nginx-pv-demo.yaml # 定义PV --- apiVersion: v1 kind: PersistentVolume metadata: name: nginx-pv # pv的名称 spec: accessModes: # 访问模式 - ReadWriteMany # PV以read-write挂载到多个节点 capacity: # 容量 storage: 2Gi # pv可用的大小 nfs: path: /nfs/data/ # NFS的挂载路径 server: 10.0.0.16 # NFS服务器地址 --- # 定义PVC,用于消费PV apiVersion: v1 kind: PersistentVolumeClaim # 类型 metadata: name: nginx-pvc # PVC 的名字 namespace: dev # 命名空间 spec: accessModes: # 访问模式 - ReadWriteMany # PVC以read-write挂载到多个节点 resources: requests: storage: 2Gi # PVC允许申请的大小 --- # 定义Pod,指定需要使用的PVC apiVersion: apps/v1 kind: Deployment metadata: name: nginx-pvc namespace: dev # 如果前面的PVC指定了命名空间这里必须指定与PVC一致的命名空间,否则PVC不可用 spec: selector: matchLabels: app: nginx-pvc template: metadata: labels: app: nginx-pvc spec: containers: - name: nginx-test-pvc image: nginx:1.20.0 imagePullPolicy: IfNotPresent ports: - name: web-port containerPort: 80 protocol: TCP volumeMounts: - name: nginx-persistent-storage # 取个名字,与下面的volumes的名字要一致 mountPath: /usr/share/nginx/html # 容器中的路径 volumes: - name: nginx-persistent-storage persistentVolumeClaim: claimName: nginx-pvc # 引用前面声明的PVC
5)查看PV、PVC
[root@k8s-master yaml]# kubectl get pv,pvc -n dev -o wide NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE VOLUMEMODE persistentvolume/nginx-pv 2Gi RWX Retain Bound dev/nginx-pvc 57s Filesystem NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE VOLUMEMODE persistentvolumeclaim/nginx-pvc Bound nginx-pv 2Gi RWX 57s Filesystem # STATUS状态都是绑定状态了 # 查看pod,发现pod已调度到node3节点 root@k8s-master yaml]# kubectl get pods -n dev -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE nginx-54b75bbf54-474k5 1/1 Running 0 8m53s 10.244.3.162 k8s-node3 <none> <none>
(3)验证
1)pod中创建文件,nfs服务器查看文件
# 为了方便,我这里直接在node3上使用docker命令操作, # 也可以在master节点,使用kubectl exec -it pod/nginx-test-pvc -n dev -- bash 进入pod容器操作 # node3节点操作 # 查询容器 # [root@k8s-node3 ~]# docker ps -a | grep nginx-test-pvc 2abd9b083a0b 7ab27dbbfbdf "/docker-entrypoint.…" 19 minutes ago Up 19 minutes k8s_nginx-test-pvc_nginx-54b75bbf54-474k5_dev_f8d48ac1-eab0-4c34-91fa-addbc143c7e3_0 # 进入容器 [root@k8s-node3 ~]# docker exec -it 2abd9b083a0b /bin/bash # 在容器中,挂载目录创建一个index.html文件,我这里容器中挂载路径是/usr/share/nginx/html root@nginx-54b75bbf54-474k5:/# echo "`date` test pv-pvc-nfs storage" > /usr/share/nginx/html/index.html # 在NFS服务器/nfs/data 目录下查看文件是否存在 # [root@nfs-master opt]# ls /nfs/data/ index.html
2)NFS数据存储目录里面创建文件,在pod中查看文件
# nfs服务器 # [root@nfs-master opt]# cd /nfs/data/ # 进入文件共享目录,创建一个新的文件test.html # [root@nfs-master data]# echo "add another file to /nfs/data path " > test.html # node3节点容器里查看文件 # root@nginx-54b75bbf54-474k5:/# cd /usr/share/nginx/html/ # 进入容器中挂载路径查看文件 # root@nginx-54b75bbf54-474k5:/usr/share/nginx/html# ls index.html test.html
3)删除Pod,NFS服务器查看文件
# master节点操作 # 手动删除之前的pod,k8s会自动再创建一个pod # [root@k8s-master yaml]# kubectl delete pod/nginx-54b75bbf54-474k5 -n dev pod "nginx-54b75bbf54-474k5" deleted # [root@k8s-master yaml]# kubectl get pods -n dev -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES nginx-54b75bbf54-h7r7r 1/1 Running 0 5s 10.244.2.146 k8s-node2 <none> <none> # 这里验证有两种方式,方式一,直接访问pod的ip地址 curl 10.244.2.146/index.html # [root@k8s-node1 ~]# curl 10.244.2.146/index.html Tue Jun 1 03:35:15 UTC 2021 test pv-pvc-nfs storage # [root@k8s-node1 ~]# curl 10.244.2.146/test.html add another file to /nfs/data path # 能够访问到文件,说明验证成功 # 验证方式二,是进入容器中查看文件 # 本次pod被调度到了node2 # [root@k8s-node2 ~]# docker ps -a | grep nginx-test-pvc 0409738da703 7ab27dbbfbdf "/docker-entrypoint.…" 2 minutes ago Up 2 minutes k8s_nginx-test-pvc_nginx-54b75bbf54-h7r7r_dev_bac97d1f-484a-4e40-8073-9a1da56dc871_0 # 进入容器 # [root@k8s-node2 ~]# docker exec -it 0409738da703 /bin/bash # 查看文件 # root@nginx-54b75bbf54-h7r7r:/# ls /usr/share/nginx/html/ index.html test.html
4)回收PV
# master节点操作 # 先删除pod--必须先删除pod再删除pvc,因为pod绑定了pvc,如果先删pvc会出现pvc一直处于删除状态而无法删除 # [root@k8s-master yaml]# kubectl delete deploy/nginx-pvc -n dev deployment.apps "nginx-pvc" deleted # 再删除pvc [root@k8s-master ~]# kubectl delete pvc/nginx-pvc -n dev persistentvolumeclaim "nginx-pvc" deleted
PVC被删除后,发现集群启动了一个新 Pod,这个 Pod 的作用就是清除 PV的数据。这个时候再看 NFS 服务器目录 /nfs/data中已经没有数据了
[root@nfs-master /]# cd /nfs/data
[root@nfs-master data]# ls