29、K8S-数据存储之EmptyDir、hostPath

发布时间 2023-03-23 22:01:15作者: 小粉优化大师

1、基础知识

1.1、Docker存储

1.1.1、简介

Docker的文件系统 与Docker容器具有相同的生命周期,但是Docker容器肯定会遇到同时运行到多节
点场景中,这个时候,会因为节点崩溃、服务崩溃、网络原因,导致容器异常退出,所以一旦我们将数据存储到容器内部,肯定会导致数据丢失。
- Docker镜像是只读的文件,Docker容器可读可写,但是不能够数据持久化。 所以为了避免这种数据异常丢失的情况,我们一般会将 容器的数据(程序状态数据、应用存储数据等),存储在容器的文件系统之外。
- 通过引入外部的存储空间来实现 数据的可持久化效果。

1.2、外部存储

1.2.1、host机制

- 通过数据卷 或者 数据卷容器,将当前宿主机上面的文件系统目录与容器里面的工作目录进行一一的关联。
- 即使我们对本地是磁盘做 raid等冗余功能,但是这种等级的安全很鸡肋。

1.2.2、网络存储机制

- 通过网络的方式,将外部的存储空间挂载到当前宿主机,然后借助于host机制实现容器数据的可持久化。

1.2.3、Docker存储流程图

1.3、k8s数据卷

1.3.1、k8s集群中的容器数据存储图

1.3.2、k8s集群中的容器数据存储关键点

有两个关键点:
1、容器引擎对共享式存储设备的支持类型:
 - 多路并行读写 - 多个容器内可以同时对同一个存储设备进行读写操作
 - 多路只读 - 多个容器内可以同时对同一个存储设备进行只读操作
 - 单路读写 - 多个容器内可以通过同一个中间容器对同一个存储设备进行读写操作
2、pod内部容器使用存储卷有两步:
 - 在Pod上定义存储卷,并关联至目标存储服务上
 - 在需要用到存储卷的容器上,挂载其所属的Pod的存储卷

1.3.3、k8s支持的存储类型

类型            举例
本地数据卷      emptyDir、hostPath、local等
云存储数据卷    gcePersistentDisk、awsElasticBlockStore等
网络存储卷      NFS、gitRepo、NAS、SAN等
分布式存储卷    GlusterFS、CephFS、rdb(块设备)、iscsi等
信息数据卷      flocker、secret等


k8s还支持 CSI(Container Storage Interface)接口方式,来实现更大范围的存储功能扩展。
比如: Rancher 是为使用容器的公司打造的容器管理平台。Rancher 简化了使用 Kubernetes 的流程,开 发者可以随处运行 Kubernetes,满足 IT 需求规范,赋能 DevOps 团队。这个团队研发的Longhorn就是要给非常好的存储平台。
Longhorn是一个轻量级且功能强大的云原生Kubernetes分布式存储平台,可以在任意基础设施上运 行。Longhorn与Rancher结合使用,将帮助您在Kubernetes环境中轻松、快速和可靠地部署高可用性持久化块存储。

1.3.4、资源对象属性介绍

spec:
  volumes:
  - name <string>      # 存储卷名称标识,仅可使用DNS标签格式的字符,在当前Pod中必须唯一
    VOL_TYPE <Object>  # 存储卷插件及具体的目标存储供给方的相关配置
  containers:
  - name: …
    image: …
    volumeMounts:
    - name <string>        # 要挂载的存储卷的名称,必须匹配存储卷列表中某项的定义
      mountPath <string>   # 容器文件系统上的挂载点路径
      readOnly <boolean>   # 是否挂载为只读模式,默认为“否”
      subPath <string>     # 挂载存储卷上的一个子目录至指定的挂载点
      subPathExpr <string> # 挂载由指定的模式匹配到的存储卷的文件或目录至挂载点

1.4、k8s存储机制

1.4.1、方案劣势

虽然这种借助于k8s存储插件的方式,为pod应用引入各种存储机制,但是这种方案有天然的劣势:
 1 要保证所有的pod节点支持后端的指定类型的存储服务。
 2 所有的后端指定服务的配置都要在pod上自定义
 3 这些卷插件将pod定制的配置属性传递给kubelet,然后实现对应的效果。
 -- 从字面上来理解,如果一个开发者要开发一个pod对象,必须首先是一个存储专家,而这是不可能的。
 
所以,使用k8s的门槛高多了。而k8s为了避免这种为用户带来不好影响的现象,引入了两个非常重要的存储资源对象 -- PV 和 PVC

1.4.2、存储方案

 

1、由专业的存储管理员来管理所有的存储后端:
 - 在专用的存储设备上,创建各种类型级别的PV(Persistent Volume)
 - 或者通过存储模板文件SC(storageclasses)来自动创建大量不同类型的PV对象。
2、由开发人员定制需要的PVC(Persistent Volume Claim),然后关联到pod上
3、Pod通过PVC到PV上请求一块独立大小的网络存储空间,然后直接使用

2、EmptyDir-实践

2.1、基础知识

2.1.1、特点解析

一个emptyDir volume在pod被调度到某个Node时候自动创建的,无需指定宿主机上对应的目录。特点如下:
1、跟随 Pod 初始化而来,开始是空数据卷
2、Pod 移除,emptyDir中数据随之永久消除
3、emptyDir 一般做本地临时存储空间使用
4、emptyDir 数据卷介质种类跟当前主机的磁盘一样。

2.1.2、属性解析

kubectl explain pod.spec.volumes.emptyDir
  medium   指定媒介类型,主要有 disk Memory(默认) 两种
  sizeLimit 现在资源使用情况
   
kubectl explain pod.spec.containers.volumeMounts
  mountPath   挂载到容器中的路径
  name 指定挂载的volumes名称
  readOnly   是否只读挂载
  subPath   是否挂载子路径

2.1.3、配置样式

# volume配置样式
 volumes:
 - name: volume_name
   emptyDir: {}
# volume使用样式
 containers:
   - volumeMounts:
       - name: volume_name
         mountPath: /path/to/container/
                  
注意:关于volume和容器的关系,每个容器都可以单独挂载多个volume,每种volume都可以被多个容器挂载

2.2、emptyDir-简单实践

2.2.1、定义资源配置清单

cat >storage-empty.yml<<'EOF'
apiVersion: v1
kind: Pod
metadata:
  name: nginx-volume
  labels:
    name: nginx-volume
spec:
  volumes:
  - name: empty-volume
    emptyDir: {}
  containers:
    - name: nginx-volume
      image: 192.168.10.33:80/k8s/my_nginx:v1
      volumeMounts:
       - name: empty-volume
         mountPath: /nginx/www/empty/
EOF

2.2.2、应用资源配置清单

# 应用资源配置清单 
master1 storage]# kubectl apply  -f storage-empty.yml 

master1 storage]# kubectl get pods
NAME           READY   STATUS    RESTARTS   AGE
nginx-volume   1/1     Running   0          69s

# 查看挂载的信息
master1 storage]# kubectl describe pod nginx-volume 
...
    Mounts:
      /nginx/www/empty/ from empty-volume (rw)
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-kd8dw (ro)
...

# 查看目录是否存在
master1 storage]# kubectl exec nginx-volume -- ls /nginx/www
empty

master1 storage]# kubectl exec -it nginx-volume -- /bin/bash
root@nginx-volume:/# 

2.2.3、文件在哪里?

# 往文件目录写入数据
master1 storage]# kubectl exec -it nginx-volume -- /bin/bash
root@nginx-volume:/# echo "nihao" > /nginx/www/nihao.txt

# 到节点查询文件位置
node1 ~]# find / -name "nihao.txt"
/run/containerd/io.containerd.runtime.v2.task/k8s.io/ec0ce61aa680a89da7d97ea631332e131b05840913e4d691a984516bf35a3e0f/rootfs/nginx/www/nihao.txt
/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/2060/fs/nginx/www/nihao.txt

2.3、emptyDir-两个镜像共享一个volume实践

2.3.1、定义资源配置清单

cat >storage-nginx-volume.yml<<'EOF'
apiVersion: v1
kind: Pod
metadata:
  name: volume-test
spec:
  volumes:
  - name: nginx-index
    emptyDir: {}

  containers:
  - name: nginx
    image: 192.168.10.33:80/k8s/my_nginx:v1
    volumeMounts:
    - name: nginx-index
      mountPath: /usr/share/nginx/html/

  - name: busybox
    image: 192.168.10.33:80/k8s/busybox:latest
    volumeMounts:
    - name: nginx-index
      mountPath: /data/
    command:
    - "/bin/sh"
    - "-c"
    - "while true; do echo $(date) > /data/index.html; sleep 2; done"
EOF

2.3.2、应用资源配置清单

master1 ]# kubectl apply -f storage-nginx-volume.yml 
pod/volume-test created

master1 ]# kubectl get pods -o wide
NAME          READY   STATUS    RESTARTS   AGE   IP             NODE    NOMINATED NODE   READINESS GATES
volume-test   2/2     Running   0          4s    10.244.3.227   node1   <none>           <none>

master1 ]# curl  10.244.3.227
Thu Mar 23 12:00:55 UTC 2023

3、hostPath-实践

3.1、基础知识

3.1.1、场景需求

hostPath将宿主机上的目录挂载到Pod中作为数据的存储目录,一般用在如下场景:
1、容器应用程序中某些文件需要永久保存
2、某些容器应用需要用到容器的内部数据结构,可将宿主机的/var/lib/docker挂载到Pod中
3、Pod删除,宿主机文件不受影响
hostPath使用注意事项:
1、不同宿主机的文件内容不一定完全相同,所以Pod迁移前后的访问效果不一样
2、宿主机的目录不属于资源对象的资源,所以我们对资源设置的资源配额限制对hostPath目录无效

3.1.2、配置属性

kubectl explain pod.spec.volumes.hostPath
   path 指定宿主机的路径
   type 指定路径的类型,一共有7种,默认的类型是没有指定空字符串,默认配置,在关联hostPath存储卷之前不进行任何检查,如果宿主机没有对应的目录,也会自动创建
   DirectoryOrCreate 宿主机上不存在,创建此0755权限的空目录,属主属组均为kubelet  
   Directory 必须存在挂载已存在的目录  
   FileOrCreate 宿主机上不存在挂载文件,就创建0644权限的空文件,属主和属组同为kubelet 
   File 必须存在文件
   Socket 事先必须存在的Socket文件路径
   CharDevice 事先必须存在的字符设备文件路径
   BlockDevice 事先必须存在的块设备文件路径
 参考资料:https://kubernetes.io/docs/concepts/storage/volumes#hostpath

3.1.3、配置格式

volumes:
 - name: volume_name
   hostPath:
     path: /path/to/host

3.2、hostPath-定制redis环境,将数据的备份-实践

3.2.1、定义资源配置清单

cat >hostpath-redis.yml<<'EOF'
apiVersion: v1
kind: Pod
metadata:
  name: hostpath-redis
spec:
  nodeName: node2
  volumes:
  - name: redis-backup
    hostPath:
     path: /backup/redis
  containers:
    - name: hostpath-redis
      image: 192.168.10.33:80/k8s/redis:latest
      volumeMounts:
       - name: redis-backup
         mountPath: /data
EOF

关键点:
    spec.containers.volumeMounts的name属性和spec.volumes的那么属性完全一致,因为他们是基于name来关联的。  
注意:
    redis的镜像将数据保存到了容器的/data目录下。

3.2.2、应用资源配置清单并且查询目录的挂载情况

master1 ]# kubectl apply -f hostpath-redis.yml 
pod/hostpath-redis created

master1 ]# kubectl get pods -o wide
NAME             READY   STATUS    RESTARTS   AGE   IP             NODE    NOMINATED NODE   READINESS GATES
hostpath-redis   1/1     Running   0          43s   10.244.4.100   node2   <none>           <none>

# 本地目录存在
node2 ~]# ll /backup/redis/
total 0


# 查看pod的描述信息
master1 ]# kubectl describe pod hostpath-redis
...
    Environment:  <none>
    Mounts:
      /data from redis-backup (rw)
Volumes:
  redis-backup:
    Type:          HostPath (bare host directory volume)
    Path:          /backup/redis
...

3.2.3、给redis写入数据

# 写数据并且持久化入盘
master1 storage]# kubectl exec -it hostpath-redis -- /bin/bash
root@hostpath
-redis:/data# redis-cli 127.0.0.1:6379> set volume hostpath OK 127.0.0.1:6379> set name cyc OK 127.0.0.1:6379> BGSAVE Background saving started 127.0.0.1:6379> exit

3.2.4、删除pod,再重新创建pod验证数据是否还存在

# 删除pod重新创建,看看数据是否存在
[root@master1 storage]# kubectl delete  -f hostpath-redis.yml 
pod "hostpath-redis" deleted
[root@master1 storage]# kubectl apply -f hostpath-redis.yml pod/hostpath-redis created
[root@master1 storage]# kubectl get pods NAME READY STATUS RESTARTS AGE hostpath
-redis 1/1 Running 0 3s
[root@master1 storage]# kubectl exec
-it hostpath-redis -- /bin/bash
root@hostpath
-redis:/data# redis-cli 127.0.0.1:6379> get name "cyc" 127.0.0.1:6379> get volume "hostpath" 127.0.0.1:6379>