【云原生】Kubernetes之持久化

Kubernetes 持久化存储

文章目录

一、为什么要做持久化存储

  • 在k8s中部署的应用都是以Pod容器的形式运行的,假如我们部署的MySQL、Redis等数据库,需要对这些数据库产生的数据做备份。因为Pod是有生命周期的,如果Pod不挂在数据卷,那Pod删除或重启后这些数据会随之消失,如果想要长久的保留这些数据就要用到Pod数据持久化存储。

二、都有哪些存储

2.1、emptyDir

2.1.1、什么是emptyDir
  • emptyDir是一个临时存储卷,与Pod的声明周期绑定在一起,如果Pod被删除了,这意味着数据也被随之删除。
2.1.2、emptyDir作用
  • 可以实现持久化
  • 同一个Pod的多个容器可以实现数据共享,多个不同的Pod之间不能进行数据通信
  • 随着Pod的生命周期而存在,当我们删除Pod时,其数据也会被随之删除
2.1.3、emptyDir的应用场景
  • 临时缓存空间,比如基于磁盘的归并排序
  • 为较耗时计算任务提供检查点,以便任务能方便的从崩溃前状态恢复执行
  • 存储Web访问日志及错误日志等信息
2.1.4、emptyDir优缺点

优点

  • 可以实现同一个Pod内多个容器之间数据共享
  • 当Pod内的某个容器被强制删除时,数据并不会丢失,因为Pod没有删除

缺点

  • 当Pod被删除时,数据也会随之删除
  • 不同的Pod之间无法实现数据共享
2.1.5、emptyDir的使用方式
bash 复制代码
[root@master ~]# cat emptyDir.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: test
spec:
  containers:
  - name: test-pod
    image: busybox:1.28
    imagePullPolicy: IfNotPresent
    # 定义容器内的挂载点
    volumeMounts:
    # 挂载的卷名称未data
    - name: data
    # 将data数据卷挂载到内容内部的/opt目录下
      mountPath: /opt/
    command: ["sh","-c","sleep 1000"]
  # 创建一个卷
  volumes:
  # 卷名字叫data,刚好与容器挂载的卷名字一致
  - name: data
  # emptyDir是一个空目录,用于临时存储数据。{}表示使用默认配置
    emptyDir: {}
bash 复制代码
# 查看临时目录存在的位置,可用如下方法
[root@master ~]# kubectl get pod -o wide
NAME   READY   STATUS    RESTARTS   AGE   IP           NODE    NOMINATED NODE   READINESS GATES
test   1/1     Running   0          32s   10.244.2.2   node1   <none>           <none>


# 查看 Pod 的 uid,指定的机器指定目录下会生成带有 uid 号的目录
[root@master ~]# kubectl get pod test -o yaml | grep uid
  uid: b6b8a98d-9324-4869-a443-4e7dbe24f232


# 登录 node1 机器
[root@node1 ~]# tree /var/lib/kubelet/pods/b6b8a98d-9324-4869-a443-4e7dbe24f232/
/var/lib/kubelet/pods/b6b8a98d-9324-4869-a443-4e7dbe24f232/
├── containers
│   └── test-pod
│       └── 2e82889a
├── etc-hosts
├── plugins
│   └── kubernetes.io~empty-dir
│       ├── data
│       │   └── ready
│       └── wrapped_kube-api-access-n7cgv
│           └── ready
└── volumes
    ├── kubernetes.io~empty-dir
    │   └── data
    └── kubernetes.io~projected
        └── kube-api-access-n7cgv
            ├── ca.crt -> ..data/ca.crt
            ├── namespace -> ..data/namespace
            └── token -> ..data/token

11 directories, 7 files
bash 复制代码
# 在容器里面写文件,对应的emptyDir目录会有相应文件
[root@master ~]# kubectl exec -it test -- sh -c 'date > /opt/time.txt'
[root@master ~]# kubectl exec -it test -- ls /opt/time.txt
/opt/time.txt
[root@master ~]# kubectl exec -it test -- cat /opt/time.txt
Fri Jul  5 00:25:52 UTC 2024


# 登录到node1机器查看
[root@node1 ~]# cat /var/lib/kubelet/pods/b6b8a98d-9324-4869-a443-4e7dbe24f232/volumes/kubernetes.io~empty-dir/data/time.txt 
Fri Jul  5 00:25:52 UTC 2024

2.2、hostPath

2.2.1、什么是hostPath
  • hostPath Volume是指Pod挂载宿主机上的目录或文件。hostPath Volume使得容器可以使用宿主机的文件系统进行存储,hostpath(宿主机路径):节点级别的存储卷,在Pod被删除,这个存储卷还是存在的,不会被删除,所以只要同一个Pod被调度到同一个节点上来,在Pod被删除重新被调度到这个节点之后,对应的数据依然存在的
2.2.2、hostPath应用场景
  • Pod中容器想要访问宿主机文件
2.2.3、hostPath优缺点

优点

  • 可以实现同一个Pod不同容器之间的数据共享
  • 可以实现同一个Node节点不同Pod之间的数据共享

缺点

  • 无法满足跨节点Pod之间的数据共享
2.2.4、hostPath的使用方式
bash 复制代码
[root@master ~]# cat hostPath.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: test
spec:
  containers:
  - name: test-pod
    image: busybox:1.28
    imagePullPolicy: IfNotPresent
    # 定义容器内部的挂载点
    volumeMounts:
    # 挂载卷的名字叫 data
    - name: data
    # 叫 data 卷挂载到容器中/opt
      mountPath: /opt
    # 容器执行的命令,主要是让容器运行
    command: ["sh","-c","sleep 1000"]
  # 定义了Pod中挂载的卷
  volumes:
  # 卷的名称
  - name: data
  # 指定宿主机的卷存储路径
    hostPath: 
  # 宿主机上的路径,这个Pod被分配到什么Node节点,那么就会自动在该Node节点/下面创建data目录
      path: "/data"
  # 卷类型,意味着如果宿主机上/data目录不存在Kubernetes会自动创建它
      type: DirectoryOrCreate
bash 复制代码
# 在容器当中写入数据,对应Node节点/data目录下就会出现数据,最终实现持久化存储
[root@master ~]# kubectl exec -it test -- sh -c 'date > /opt/time.txt'
[root@master ~]# kubectl exec -it test -- cat /opt/time.txt
Fri Jul  5 00:39:34 UTC 2024
[root@master ~]# kubectl get pod -o wide
NAME   READY   STATUS    RESTARTS   AGE     IP           NODE    NOMINATED NODE   READINESS GATES
test   1/1     Running   0          5m54s   10.244.1.4   node2   <none>           <none>


# 登录 node2 机器查看
[root@node2 ~]# ls /data/
time.txt
[root@node2 ~]# cat /data/time.txt 
Fri Jul  5 00:39:34 UTC 2024

2.3、PV以及PVC

  • 前面和大家一起学习了一些基本的资源对象的使用方法,前面我们也和大家降到了有状态的应用和数据有持久化的应用,我们有通过hostPath 或者 emptyDir的方式来持久化我们的数据,但是显然我们还需要更加可靠的存储保存应用持久化数据,这样的容器在重建后,依赖可以使用之前的数据。但是显然存储资源和CPU资源以及内存资源有很多大不同,为了屏蔽底层的技术实现细节,让用户更加方便的使用,Kubernetes便引入了PV和PVC两个重要的资源对象来实现对存储的管理。这也是我们这节课和大家讲解的核心:PV 和 PVC
2.3.1、什么是PV
  • PV的全称是:PersistentVolume(持久化卷),是对底层的共享存储的一种抽象,PV由管理员进行创建和配置,它和具体的底层的共享存储技术的实现方式有关,比如Ceph、GlusterFS、NFS等,都是通过插件机制完成与共享存储的对接。
  • PersistentVolume(PV)是集群中的一块存储,由管理员配置或使用存储类动态配置。它是集群中的资源,就像Pod是K8S集群资源一样。PV是容量插件,入Volumes,其声明周期独立于使用PV的任务单个Pod
2.3.2、什么是PVC
  • PersistentVolumeClaim(PVC)是一个持久化存储卷,我们在创建Pod时可以定义这个类型的存储卷。它类似于一个Pod。Pod消耗节点的资源,PVC消耗PV资源。Pod可以请求特定级别的资源(CPU和内存)。pvc在申请pv的时候也可以请求特定大小和访问模式(例如,可以依次读写或多次只读)
2.3.3、PV的供应方式

可以通过两种方式配置PV:静态或动态

  • 静态的:

​ 集群管理员创建了许多PV,它们包含可供集群用户使用的实际存储的详细信息。它们存在于Kubernetes API中,可供使用

  • 动态的:

​ 当管理员创建PV都不匹配用户的PersistentVolumeClaim(PVC)时,集群可能会尝试为PVC专门动态配置卷。此配置基于StorageClasses,PVC必须请求存储类,管理员必须创建并配置该类,以便进行动态配置。

2.3.4、绑定
  • 用户创建pvc并指定需要的资源和访问模式。在找到可用pv之前,pvc会保持未绑定状态

  • 每个持久卷会处于以下阶段(Phase)之一:

    Available

    卷是一个空闲资源,尚未绑定到任何申领

    Bound

    该卷已经绑定到某申领

    Released

​ 所绑定的申领已被删除,但是关联存储资源尚未被集群回收

Failed

​ 卷的自动回收操作失败

2.3.5、使用流程
  • 需要主调一个存储服务器,把它划分成多个存储空间

  • K8S管理员可以把这些存储空间定义成多个PV

  • 在Pod中使用PVC类型的存储卷之前需要先创建PVC,通过定义需要使用的PV的大小和对应的访问模式,找到合适的PV

  • PVC被创建之后,就可以当成存储卷来使用了,我们在定义Pod时就可以使用这个PVC的存储卷

  • PVC和PV它们时一一对应的关系,PV如果被PVC绑定了,就不能被其他PVC使用了

  • 我们在创建PVC的时候,应该确保和底下的PV能绑定,如果没有合适的PV,那么PVC就会处于pending(等待)状态

2.3.6、回收策略
  • 我们创建Pod时如果使用PVC做为存储卷,那么它会和PV绑定,当删除Pod,PVC和PV绑定就会解除,解除之后和PVC绑定的PV卷里的数据需要怎么处理,目前,卷可以保留,回收或删除:

Retain

​ 当删除PVC的时候,PV仍然存在,处于released状态,但是它不能被其他PVC绑定使用,里面的数据还是存在的,当我们下次再使用的时候,数据还是存在的,这个是默认的回收策略

Recycle 简单擦除(rm -rf /thevolume/*)

Delete

​ 删除PVC时即会从Kubernetes中移除PV,也会从相关的外部设施中删除存储资产

2.3.7、访问模式
  • PersistentVolume(PV)卷可以用资源提供者所支持的任何方式挂载到宿主系统上。如下表所示,提供者(驱动)的能力不同,每个PV卷的访问模式都会设置为对应卷所支持的模式值。例如,NFS可以支持多个读写客户,但是某个特定的NFS PV卷可能在服务器上以只读的方式导出。每个PV卷都会获得自身的访问模式集合,描述的是特定PV卷的能力

  • 访问模式有:

ReadWriteOnce

​ 卷可以被一个节点以读写方式挂载。ReadWriteOnce访问模式也允许运行在同一节点上的多个Pod访问卷

ReadOnlyMant

​ 卷可以被多个节点以只读方式挂载

ReadWriteMany

​ 卷可以被多个节点以读写方式挂载

ReadWriteOncePod

​ 特性状态:Kubernetes v1.27beat

​ 卷可以被单个Pod以读写方式挂载。如果你想确保整个集群中只有一个Pod可以读取或写入该PVC,请使用 ReadWriteOncePod访问模式。这只支持CSI卷以及需要kubernetes 1.22以上版本

在命令行接口(CLI)中,访问模式也使用以下缩写形式:

  • RWO -ReadWriteOnce

  • ROX -ReadOnlyMany

  • RWX -ReadWriteMany

  • RWOP -ReadWriteOncePod

2.3.8 示例
  • 这里面为了方便演示,决定使用相对简单的NFS这种存储资源
2.3.8.1、资源列表
  • 这里面Kubernetes集群由Master、Node1、Node2节点组成,本节课不在演示Kubernetes的部署了,只解释NFS的部署,以及配置PV、PVC相关内容
操作系统 主机名 配置 IP
CentOS 7.9 master 2C4G 192.168.93.101
CentOS 7.9 node1 2C4G 192.168.93.102
CentOS 7.9 node2 2C4G 192.168.93.103
CentOS 7.9 nfs 2C4G 192.168.93.104
2.3.8.2、部署NFS
  • NFS节点操作
2.3.8.2.1、关闭防火墙
bash 复制代码
systemctl stop firewalld
systemctl disable firewalld
2.3.8.2.2、关闭内核安全机制
bash 复制代码
setenforce 0
sed -i "s/.*SELINUX=.*/SELINUX=disabled/g" /etc/selinux/config
2.3.8.2.3、安装
bash 复制代码
[root@nfs ~]# yum -y install nfs-utils rpcbind
[root@nfs ~]# mkdir -p /data/volumes
[root@nfs ~]# cat > /etc/exports << EOF
/data/volumes 192.168.93.0/24(rw,no_root_squash)
EOF
[root@nfs ~]# systemctl enable nfs --now
bash 复制代码
# 所有Kubernetes集群中的节点需要安装以下软件包用以支持NFS
yum -y install nfs-utils rpcbind
2.3.8.3、PV、PVC应用示例
  • 该yaml文件一共创建了3个资源,分别是PV、PVC、Pod
  • 启动PV的名字叫test-pv,指定的存储大小是1G(从NFS的/data/volumes分配的空间),回收策略是Delete,访问模式是ReadWriteMany
  • PVC叫test-pvc,请求的存储是1G,访问模式是ReadWriteMany
  • Pod挂载test-pvc这个PVC到/opt目录
bash 复制代码
[root@master ~]# cat pv-pvc-test.yaml 
apiVersion: v1
# 定义资源对象(PV)
kind: PersistentVolume
metadata:
# PV的名字
  name: test-pv
spec:
# 定义持久卷的存储容量,这里是1G
  capacity:
    storage: 1G
  # 定义回收策略
  persistentVolumeReclaimPolicy: Delete
  # 定义访问模式
  accessModes: ["ReadWriteMany"]
  # 指定持久卷的后端存储是NFS
  nfs:
  # NFS服务器上的路径
    path: /data/volumes
  # NFS服务器的IP地址
    server: 192.168.93.104
---
apiVersion: v1
# 定义资源对象(PVC)
kind: PersistentVolumeClaim
metadata:
# PVC的名字
  name: test-pvc
spec:
  # ReadWriteMany表示PVC期望的访问模式与PV匹配
  accessModes: ["ReadWriteMany"]
  # 定义PVC所需的存储资源
  resources:
  # PVC请求的最小存储容量
    requests:
      storage: 1G
   # PVC促成农户资源上限(不能超过1G)
    limits:
      storage: 1G
---
apiVersion: v1
kind: Pod
metadata:
  name: test
spec:
  containers:
  - name: test-pod
    image: busybox:1.28
    imagePullPolicy: IfNotPresent
    volumeMounts:
    - name: data
      mountPath: /opt
    command: ["sh","-c","sleep 1000"]
  volumes:
  # 定义存储卷的名字
  - name: data
    persistentVolumeClaim:
    # claimName:test-pvc表明这个卷使用名为test-pvc的持久化声明
      claimName: test-pvc
bash 复制代码
[root@master ~]# kubectl apply -f pv-pvc-test.yaml 
persistentvolume/test-pv created
persistentvolumeclaim/test-pvc created
pod/test created
bash 复制代码
# 查看Pod
[root@master ~]# kubectl get pod
NAME   READY   STATUS    RESTARTS   AGE
test   1/1     Running   0          15s


# 查看PVC
[root@master ~]# kubectl get pvc
NAME       STATUS   VOLUME    CAPACITY   ACCESS MODES   STORAGECLASS   AGE
test-pvc   Bound    test-pv   1G         RWX                           27s


# 查看PV
[root@master ~]# kubectl get pv
NAME      CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM              STORAGECLASS   REASON   AGE
test-pv   1G         RWX            Delete           Bound    default/test-pvc                           56s
bash 复制代码
# 查看PVC相关信息
[root@master ~]# kubectl describe pvc test-pvc
Name:          test-pvc
Namespace:     default
StorageClass:  
Status:        Bound
Volume:        test-pv
Labels:        <none>
Annotations:   pv.kubernetes.io/bind-completed: yes
               pv.kubernetes.io/bound-by-controller: yes
Finalizers:    [kubernetes.io/pvc-protection]
Capacity:      1G
Access Modes:  RWX
VolumeMode:    Filesystem
Used By:       test
Events:        <none>


# 查看PV相关信息
[root@master ~]# kubectl describe pvc test-pv
Name:          test-pvc
Namespace:     default
StorageClass:  
Status:        Bound
Volume:        test-pv
Labels:        <none>
Annotations:   pv.kubernetes.io/bind-completed: yes
               pv.kubernetes.io/bound-by-controller: yes
Finalizers:    [kubernetes.io/pvc-protection]
Capacity:      1G
Access Modes:  RWX
VolumeMode:    Filesystem
Used By:       test
Events:        <none>
bash 复制代码
# 写入数据进行验证
[root@master ~]# kubectl exec -it test -- sh -c 'date > /opt/time.txt'

[root@master ~]# kubectl exec -it test -- cat /opt/time.txt
Fri Jul  5 01:55:08 UTC 2024
bash 复制代码
# 查看Pod被被配到那个node节点上
[root@master ~]# kubectl get pod -o wide
NAME   READY   STATUS    RESTARTS   AGE    IP           NODE    NOMINATED NODE   READINESS GATES
test   1/1     Running   0          4m3s   10.244.1.5   node2   <none>           <none>


# 登录到node2
[root@nfs ~]# cat /data/volumes/time.txt 
Fri Jul  5 01:55:08 UTC 2024
相关推荐
运维老郭6 小时前
【K8s运维实战】Kubernetes临时存储卷实战:emptyDir核心用法与gitRepo弃用迁移指南
运维·云原生·kubernetes
AAA@峥10 小时前
容器数据不丢失:Docker 分层存储 + Volume 共享、备份迁移完整指南
运维·docker·容器
十年磨一剑~11 小时前
docker 为何每次 docker tag才能 docker push
docker·容器·eureka
gs8014013 小时前
记一次多 Agent 架构在单节点 K8s 触发的 PID 耗尽与 Pod 驱逐(Evicted)大摸排
容器·架构·kubernetes
bukeyiwanshui14 小时前
20260701 k8s Metric Server
容器·贪心算法·kubernetes
江湖有缘14 小时前
基于Docker环境部署tillywork开源工作管理工具
docker·容器·开源
spider_xcxc16 小时前
云上网络基石(阿里云):深入解读阿里云VPC及其应用生态
网络·阿里云·云计算·k8s·运维开发·虚拟化
鸿儒51716 小时前
MR-50国产化GPU docker配置方法
docker·容器·mr
Zhu75818 小时前
在k8s集群部署ApacheHadoop单节点单数据副本
云原生·容器·kubernetes