Volume


本节我们讨论 Kubernetes 的存储模型 Volume,学习如何将各种持久化存储映射到容器。

我们经常会说:容器和 Pod 是短暂的。
其含义是它们的生命周期可能很短,会被频繁地销毁和创建。容器销毁时,保存在容器内部文件系统中的数据都会被清除。

为了持久化保存容器的数据,可以使用 Kubernetes Volume。

Volume 的生命周期独立于容器,Pod 中的容器可能被销毁和重建,但 Volume 会被保留。

本质上,Kubernetes Volume 是一个目录,这一点与 Docker Volume 类似。当 Volume 被 mount 到 Pod,Pod 中的所有容器都可以访问这个 Volume。Kubernetes Volume 也支持多种 backend 类型,包括 emptyDir、hostPath、GCE Persistent Disk、AWS Elastic Block Store、NFS、Ceph 等,完整列表可参考 https://kubernetes.io/docs/concepts/storage/volumes/#types-of-volumes

Volume 提供了对各种 backend 的抽象,容器在使用 Volume 读写数据的时候不需要关心数据到底是存放在本地节点的文件系统中呢还是云硬盘上。对它来说,所有类型的 Volume 都只是一个目录。

我们将从最简单的 emptyDir 开始学习 Kubernetes Volume。

为什么需要存储卷


容器部署过程中一般有以下三种数据:
• 启动时需要的初始数据,例如配置文件
• 启动过程中产生的临时数据,该临时数据需要多个容器间共享
• 启动过程中产生的持久化数据,例如MySQL的data目录

数据卷概述


• Kubernetes中的Volume提供了在容器中挂载外部存储的能力
Pod需要设置卷来源(spec.volume)和挂载点(spec.containers.volumeMounts)两个信息后才可以使用相应的Volume

数据卷类型大致分类


• 本地(hostPath,emptyDir等)
• 网络(NFS,Ceph,GlusterFS等)
• 公有云(AWS EBS等)
• K8S资源(configmap,secret等)

Kubernetes支持存储卷类型中,emptyDir存储卷的生命周期与其所属的Pod对象相同,它无法脱离Pod对象的生命周期提供数据存储功能,因此emptyDir通常仅用于数据缓存或临时存储。不过基于emptyDir构建的gitRepo存储卷可以在Pod对象的生命周期起始时从响应的Git仓库中复制相应的数据文件到底层的emptyDir中,从而使得它具有了一定意义上的持久性。

emptyDir存储卷


Kubernetes支持存储卷类型中,emptyDir存储卷的生命周期与其所属的Pod对象相同,它无法脱离Pod对象的生命周期提供数据存储功能,因此emptyDir通常仅用于数据缓存或临时存储。不过基于emptyDir构建的gitRepo存储卷可以在Pod对象的生命周期起始时从响应的Git仓库中复制相应的数据文件到底层的emptyDir中,从而使得它具有了一定意义上的持久性。

emptyDir存储卷是Pod对象生命周期中的一个临时目录,类似于Docker上的docker挂载卷,在Pod对象启动时即被创建,而在Pod对象被移除时会被一并删除。不具有持久能力的emptyDir存储卷只能用于某些特殊场景中,例如,用一Pod内的多个容器间文件的共享,或者作为容器数据的临时存储目录用于数据缓存系统等。

emptyDir存储卷则定义于.spec.volumes.emptyDir嵌套字段中,可用字段主要包含两个,具体如下:

medium此目录所在存储介质的类型,可取值为defaultMemory,默认为default,表示使用节点的默认存储介质:Memory 表示基于RAM的临时文件系统tmpfs,空间受于内存,但性能非常好,通常用于为容器中的应用提供缓存空间

sizeLimit:当前存储卷的空间限额,默认值为 nil,表示不限制;不过在 medium 字段为 Memory时,建议定义此限额。

emptyDir


emptyDir 是最基础的 Volume 类型。正如其名字所示,一个 emptyDir Volume 是 Host 上的一个空目录。

emptyDir Volume 对于容器来说是持久的,对于 Pod 则不是。当 Pod 从节点删除时,Volume 的内容也会被删除。但如果只是容器被销毁而 Pod 还在,则 Volume 不受影响。emptyDir一般用于测试,或者缓存场景。 

也就是说:emptyDir Volume 的生命周期与 Pod 一致。

Pod 中的所有容器都可以共享 Volume,它们可以指定各自的 mount 路径。下面通过例子来实践 emptyDir,配置文件如下:

[root@k8s-master ~]# cat emptyDir.yml
apiVersion: v1
kind: Pod
metadata: name: producer-consumer
spec:containers:- name: producerimage: busyboxvolumeMounts: - name: shared-volumemountPath: /producer_dirargs:- /bin/sh- -c- echo "hello this is producer" > /producer_dir/hello ; sleep 3600- name: consumerimage: busyboxvolumeMounts:- name: shared-volumemountPath: /consumer_dirargs:- /bin/sh- -c- cat  /consumer_dir/hello ; sleep 3600volumes:- name: shared-volumeemptyDir: {}[root@k8s-master ~]# kubectl apply -f emptyDir.yml
pod/producer-consumer created

这里我们模拟了一个 producer-consumer 场景。Pod 有两个容器 producer和 consumer,它们共享一个 Volumeproducer 负责往 Volume 中写数据,consumer 则是从 Volume 读取数据。

  1. 文件最底部 volumes 定义了一个 emptyDir 类型的 Volume shared-volume
  2. producer 容器将 shared-volume mount 到 /producer_dir 目录。
  3. producer 通过 echo 将数据写到文件 hello 里。
  4. consumer 容器将 shared-volume mount 到 /consumer_dir 目录。
  5. consumer 通过 cat 从文件 hello 读数据。
[root@k8s-master ~]# kubectl logs producer-consumer consumer
hello this is producer

kubectl logs 显示容器 consumer 成功读到了 producer 写入的数据,验证了两个容器共享 emptyDir Volume。

因为 emptyDir 是 Docker Host 文件系统里的目录,其效果相当于执行了 docker run -v /producer_dir 和 docker run -v /consumer_dir。通过 docker inspect 查看容器的详细配置信息,我们发现两个容器都 mount 了同一个目录:

        "Mounts": [{"Type": "bind","Source": "/var/lib/kubelet/pods/620cd011-1d40-47af-8a1a-1beb131e135f/volumes/kubernetes.io~empty-dir/shared-volume","Destination": "/consumer_dir","Mode": "Z","RW": true,"Propagation": "rprivate"},{"Type": "bind","Source": "/var/lib/kubelet/pods/620cd011-1d40-47af-8a1a-1beb131e135f/volumes/kubernetes.io~empty-dir/shared-volume","Destination": "/producer_dir","Mode": "Z","RW": true,"Propagation": "rprivate"},

这里 /var/lib/kubelet/pods/620cd011-1d40-47af-8a1a-1beb131e135f/volumes/kubernetes.io~empty-dir/shared-volume 就是 emptyDir 在 Host 上的真正路径。

emptyDir 是 Host 上创建的临时目录,其优点是能够方便地为 Pod 中的容器提供共享存储,不需要额外的配置。但它不具备持久性,如果 Pod 不存在了,emptyDir 也就没有了。根据这个特性,emptyDir 特别适合 Pod 中的容器需要临时共享存储空间的场景,比如前面的生产者消费者用例。

[root@k8s-master ~]# kubectl describe pod producer-consumer
Volumes:shared-volume:Type:       EmptyDir (a temporary directory that shares a pod's lifetime)

下面是一个使用了emptyDir存储卷的简单示例


1.创建Pod对象配置清单


apiVersion: v1
kind: Pod
metadata:name: vol-emptydir-pod
spec:volumes:- name: htmlemptyDir: { }containers:- name: nginximage: nginx:latestvolumeMounts:- name: htmlmountPath: /usr/share/nginx/html- name: pagegenimage: alpinevolumeMounts:- name: htmlmountPath: /htmlcommand: [ "/bin/sh", "-c" ]args:                        #定义循环,每10秒向/html/文件中追加写入当前主机名和时间- while true; doecho $(hostname) $(date) >> /html/index.html;sleep 10;done

上面示例中定义的存储卷名称为html,挂载于容器nginx的 /usr/share/nginx/html目录,以及容器pagegen的/html目录。容器pagegen每隔10秒向存储卷上的index.html文件中追加一行信息,而容器nginx中的nginx进程则以其站点主页。如下图所示:

2.创建Pod对象

kubectl apply -f vol-emptydir.yaml

3.查看Pod状态 Pod对象的详细信息中会显示存储卷的相关状态,包括其是否创建成功(在Events字段中输出)、相关的类型及参数(在Volumes字段中输出)以及容器中挂载状态等信息(在Containers字段中输出),如下面命令所示:

kubectl describe pods/vol-emptydir-pod
Containers:nginx:Mounts:/usr/share/nginx/html from html (rw)/var/run/secrets/kubernetes.io/serviceaccount from default-token-xxqkj (ro)pagegen:Command:/bin/sh-cArgs:while true; do echo $(hostname) $(date) >> /html/index.html; sleep 10; doneMounts:/html from html (rw)/var/run/secrets/kubernetes.io/serviceaccount from default-token-xxqkj (ro)

4.访问Pod中Nginx pagegen容器每隔10秒向 html/index.html 追加写入信息,Nginx容器挂载的也是此临时存储,所以Nginx的网页文件也是从这里获取。

curl http://172.20.1.18
vol-emptydir-pod Fri Jun 12 02:47:29 UTC 2020
vol-emptydir-pod Fri Jun 12 02:47:39 UTC 2020
vol-emptydir-pod Fri Jun 12 02:47:49 UTC 2020
vol-emptydir-pod Fri Jun 12 02:47:59 UTC 2020
vol-emptydir-pod Fri Jun 12 02:48:09 UTC 2020
vol-emptydir-pod Fri Jun 12 02:48:19 UTC 2020

5.进入容器 以下分别进入Nginx容器以及pagegen容器查看其挂载

通过 -c 来指定容器名称进入指定容器

kubectl exec -it pods/vol-emptydir-pod -c nginx -- /bin/sh
# ls /usr/share/nginx/html
index.html
# head -3 /usr/share/nginx/html/index.html
vol-emptydir-pod Fri Jun 12 02:47:29 UTC 2020
vol-emptydir-pod Fri Jun 12 02:47:39 UTC 2020
vol-emptydir-pod Fri Jun 12 02:47:49 UTC 2020

进入pagegen容器

kubectl exec -it pods/vol-emptydir-pod -c pagegen -- /bin/sh
/ # ls /html/
index.html
/ # head -3 /html/index.html
vol-emptydir-pod Fri Jun 12 02:47:29 UTC 2020
vol-emptydir-pod Fri Jun 12 02:47:39 UTC 2020
vol-emptydir-pod Fri Jun 12 02:47:49 UTC 2020
/ # ps aux
PID   USER     TIME  COMMAND1 root      0:00 /bin/sh -c while true; do echo $(hostname) $(date) >> /html/index.html; sleep 10; done286 root      0:00 /bin/sh303 root      0:00 sleep 10304 root      0:00 ps aux

作为边车 (sidecar)的容器pagegen,其每隔10秒生成一行信息追加到存储卷上的index.html文件中,因此,通过主容器nginx的应用访问到文件内存也会处理不停的变动中。另外,emptyDir存储卷也可以基于RAM创建tmpfs文件系统的存储卷,常用于为容器的应用提高高性能缓存,下面是一个配置示例:


cat vol-emptydir.yaml
apiVersion: v1
kind: Pod
metadata:name: vol-emptydir-pod
spec:volumes:- name: htmlemptyDir:medium: Memory                #指定临时存储到内存sizeLimit: 256Mi              #给予的内存空间大小containers:- name: nginximage: nginx:latestvolumeMounts:- name: htmlmountPath: /usr/share/nginx/html- name: pagegenimage: alpinevolumeMounts:- name: htmlmountPath: /htmlcommand: [ "/bin/sh", "-c" ]args:- while true; doecho $(hostname) $(date) >> /html/index.html;sleep 10;done

emptyDir卷简单易用,但仅能用于临时存储,另外还存在一些类型的存储卷构建在emptyDir之上,并额外提供了emptyDir没有的功能。

如果还是不懂可以看下面这个例子


定义一个emptyDir存储大小为1G,将其挂载到redis的/data目录中

[root@k8s-master emptydir]# cat emptydir-redis.yaml
apiVersion: v1
kind: Pod
metadata:name: emptydir-redislabels:volume: emptydirannotations:kubernetes.io/storage: emptyDir
spec:containers:- name: emptydir-redisimage: redis:latestimagePullPolicy: IfNotPresentports:- name: redis-6379-portprotocol: TCPcontainerPort: 6379volumeMounts:   #将定义的驱动emptydir-redis挂载到容器的/data目录,通过名字方式关联- name: emptydir-redismountPath: /datavolumes:          #定义一个存储,驱动类型为emptyDir,大小1G- name: emptydir-redisemptyDir:sizeLimit: 1Gi

生成redis pod,并查看describe pod的详情信息

[root@k8s-master emptydir]# kubectl apply -f emptydir-redis.yaml
pod/emptydir-redis created
[root@k8s-master emptydir]# kubectl describe pod emptydir-redis #执行kubectl describe pods emptydir-redis查看容器的存储挂载信息[root@k8s-master emptydir]# kubectl describe pod emptydir-redis
Name:         emptydir-redis
Namespace:    default
Priority:     0
Node:         k8s-master/192.168.179.99
Start Time:   Thu, 29 Oct 2020 15:43:57 +0800
Labels:       volume=emptydir
Annotations:  kubernetes.io/storage: emptyDir
Status:       Running
IP:           10.244.0.15
IPs:IP:  10.244.0.15
Containers:emptydir-redis:Container ID:   docker://7e568233b435ad661971463cf7bb4acb87de7df99fc53b451732e81ec8cb0e7cImage:          redis:latestImage ID:       docker-pullable://redis@sha256:a0494b60a0bc6de161d26dc2d2f9d2f1c5435e86a9e5d48862a161131affa6bdPort:           6379/TCPHost Port:      0/TCPState:          RunningStarted:      Thu, 29 Oct 2020 15:44:52 +0800Ready:          TrueRestart Count:  0Environment:    <none>Mounts:         #挂载信息,将emptydir-redis挂载到/data目录,且是rw读写状态/data from emptydir-redis (rw)/var/run/secrets/kubernetes.io/serviceaccount from default-token-dfmvc (ro)
Conditions:Type              StatusInitialized       True Ready             True ContainersReady   True PodScheduled      True
Volumes:            #定义了一个EmptyDir类型的存储,大小为1Giemptydir-redis:   Type:       EmptyDir (a temporary directory that shares a pod's lifetime)Medium:     SizeLimit:  1Gidefault-token-dfmvc:Type:        Secret (a volume populated by a Secret)SecretName:  default-token-dfmvcOptional:    false

向redis中写入数据

[root@k8s-master emptydir]# kubectl get pod emptydir-redis -o wide
NAME             READY   STATUS    RESTARTS   AGE   IP            NODE         NOMINATED NODE   READINESS GATES
emptydir-redis   1/1     Running   0          11m   10.244.0.15   k8s-master   <none>           <none>#安装客户端redis-cli
[root@k8s-master emptydir]# yum install redis -y[root@k8s-master emptydir]# redis-cli -h 10.244.0.15 -p 6379
10.244.0.15:6379>
#向redis中写入两个key
10.244.0.15:6379> set volume emptydir
OK
10.244.0.15:6379> set username happylu
OK
10.244.0.15:6379> get volume
"emptydir"
10.244.0.15:6379> get username
"happylu"

登陆到pod中,可以直接kill redis-server进程,进程一般为1,进程被kill后kubelet会自动将进程重启

[root@k8s-master emptydir]# kubectl exec -it  emptydir-redis -- /bin/sh
# kill 1
# command terminated with exit code 137

pod异常重启后,再次登录redis并查看redis中的数据内容,发现数据没有丢失。

[root@k8s-master ~]# kubectl get pod
NAME                     READY   STATUS      RESTARTS   AGE
emptydir-redis           0/1     Completed   1          27m[root@k8s-master ~]# kubectl get pod
NAME                     READY   STATUS    RESTARTS   AGE
emptydir-redis           1/1     Running   2          30m#pod重启后,再次登录redis并查看redis中的数据内容,发现数据没有丢失。
10.244.0.15:6379> keys *
1) "volume"
2) "username"

emptyDir实际是宿主机上创建的一个目录,将目录以bind mount的形势挂载到容器中,跟随容器的生命周期。查看存储内容如下:

[root@k8s-master ~]# docker inspect  d64ee8367b49"Mounts": [{"Type": "bind","Source": "/var/lib/kubelet/pods/28800e94-3474-4a0e-9e35-d5585b60a133/volumes/kubernetes.io~empty-dir/emptydir-redis","Destination": "/data","Mode": "","RW": true,"Propagation": "rprivate"},

查看目录的信息:

[root@k8s-master ~]# cd /var/lib/kubelet/pods/28800e94-3474-4a0e-9e35-d5585b60a133/volumes/kubernetes.io~empty-dir/emptydir-redis
[root@k8s-master emptydir-redis]# ls
dump.rdb

Pod删除后,volume的信息也随之删除

[root@k8s-master emptydir]# kubectl delete -f emptydir-redis.yaml
pod "emptydir-redis" deleted[root@k8s-master emptydir]# cd /var/lib/kubelet/pods/28800e94-3474-4a0e-9e35-d5585b60a133/volumes/kubernetes.io~empty-dir/emptydir-redis
-bash: cd: /var/lib/kubelet/pods/28800e94-3474-4a0e-9e35-d5585b60a133/volumes/kubernetes.io~empty-dir/emptydir-redis: No such file or directory

总结


emptyDir是host上定义的一块临时存储,通过bind mount的形式挂载到容器中使用,容器重启数据会保留,容器删除则volume会随之删除。

Kubernetes 本地存储卷 pod volume emptyDir相关推荐

  1. K8S 快速入门(十三)实战篇:存储 卷(Volume)

    Volume 官网文档 卷 1. 为什么要用Volume? k8s中Container 中的文件在磁盘上是临时存放的, 这就带来了一些列的问题 当一个容器损坏之后, kubelet会重启这个容器, 但 ...

  2. Kubernetes—动态存储卷配置(StorageClass资源)(十二)

    StorageClass的介绍   StorageClass是对存储资源的一个抽象定义.与静态模式的存储卷配置(就是集群管理员手动去创建持久卷PV),StorageClass是一种动态模式的存储卷配置 ...

  3. kubernetes系列10—存储卷详解

    kubernetes系列10-存储卷详解 1.认识存储卷 1.1 背景 默认情况下容器中的磁盘文件是非持久化的,容器中的磁盘的生命周期是短暂的,这就带来了一系列的问题:第一,当一个容器损坏之后,kub ...

  4. Docker存储卷volume(V18.X)

    文章目录 1. 简介 1.1 介绍 1.2 作用 1.3 分类 2. 存储卷使用 2.1 设置卷的命令 2.1.1 -v或`--volume`选项指定卷 2.1.2 `--mount`指定卷 2.1. ...

  5. k8s存储卷(empyDir;hostPath;nfs)

    文章目录 一: emptyDir 存储卷 1.1 为什么使用存储卷 1.2 emptyDir 存储卷的作用 1.3 示例 二: hostPath卷 2.1 作用 2.2 示例 三: nfs共享存储 3 ...

  6. Kubernetes持久化存储PV、PVC和StorageClass介绍

    PV和PVC Kubernetes Volume提供了非常好的数据持久化方案,不过对于大型Kubernetes集群来说管理上还有不方便之处.Volume方案需要创建Pod或者Deployment的管理 ...

  7. 14、Kubernetes持久化存储

    文章目录 前言 步骤 持久化服务器上操作 设置挂载路径 Node节点上操作 PV和PVC 实现流程 举例 前言 之前我们有提到数据卷:emptydir ,是本地存储,pod重启,数据就不存在了,需要对 ...

  8. Docker存储卷(V18.X)

    Docker存储卷(V18.X) 原文: Docker存储卷(V18.X) 简介 介绍 Docker的存储卷称之为volume,本质上容器上的一个或者多个目录,而这些目录绕过了联合文件系统,与宿主机中 ...

  9. @kubernetes(k8s)数据持久化Volume存储卷(emptyDir、hostPath、NFS、StorageClass)

    文章目录 kubernetes(k8s)数据持久化Volume 一.数据持久化 1.Vlolume概述 2.数据卷的分类 3.常用的四种数据卷 4.Pod使用Volume步骤: 5. volume基本 ...

最新文章

  1. mongo备份索引_【MongoDB学习之四】索引 聚合 备份与恢复 监控
  2. 嵌入式linux段错误,在嵌入式Linux上使用C Std Lib时出现异常的段错误
  3. java mongodb 使用场景_mongodb使用场景一般是什么?mongodb有哪些基本命令?
  4. 静态方法中调用spring容器中的对象
  5. TCP/IP详解学习笔记(1)
  6. Yearn正在评估恢复yDAI池的方式
  7. Android中CursorLoader的使用、原理及注意事项
  8. vue指令和特殊特性
  9. Windows Message ID 常量列表
  10. cf500B New Year Permutation
  11. 计算机基础(01)基础知识
  12. python 中文分析句子成分_中文句子结构分析
  13. 海纳百川下载器使用方法图文详解
  14. 论QQ如何发大菜狗表情
  15. layim即时通讯实例各功能整合
  16. cad移动时捕捉不到基点_CAD很难学吗?其实不然,掌握这10个知识你就能走天下!...
  17. 安卓手机刷入面具Magisk
  18. 计算数组中某个元素的所在位置
  19. Android开发之如何自定义垂直方向的SeekBar
  20. login.php 什么意思,php is_login()做什么用的;

热门文章

  1. 量化投资学习——股指期货研究(七)
  2. 软件验收管理工作内容
  3. 受微软信任的交叉证书已过期,如何再给内核驱动数字签名
  4. 微软研究让员工真正快乐的原因,结果让人震惊
  5. signature今日头条php实现,今日头条_signature 求解
  6. 《深度工作》一书摘抄
  7. 【组合数+隔板法】2021牛客寒假算法基础集训营5 B 比武招亲(上)
  8. 字体样式,文本样式,背景样式,边框样式,线性渐变,伪类选择器,css权重
  9. 配置 chronyd时间同步服务器
  10. JNI内存方面说明以及相关类型手动释放内存