【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

 

posted @ 2023-01-18 15:19  liubingPersonalBlog  阅读(244)  评论(0编辑  收藏  举报