概述
写在前面
在这个星期的一次面试中,面试官向我问及如果在一个具有很大磁盘的物理机上部署多个容器应用,其存储空间是否应保持共享;通过对k8s的储存模型的学习之后,本应能轻易给出答案,而当时我对这个问题的回答并不完善(当时我正处于牙疼之中,脑子一片空白——这也是原因之一),所以我借此机会写下这篇文章,警醒自己学而时习之。
kubernetes的临时存储
显然,了解临时存储对解答前文中的问题没有任何帮助(x),但为了文章以及个人知识体系的完整性,我打算先就临时存储写一个段落
emptydir——临时存储
当pod的存储方案设定为emptydir的时候,pod启动时,就会在pod所在节点的磁盘空间开辟出一块空卷,最开始里面是什么都没有的,pod启动后容器产生的数据会存放到那个空卷中。空卷变成了一个临时卷。
供pod内的容器读取和写入数据,一旦pod容器消失,节点上开辟出的这个临时卷就会随着pod的销毁而销毁。
emptydir的用途
- 充当临时存储空间,当pod内容器产生的数据不需要做持久化存储的时候用emptydir
- 设制检查点以从崩溃事件中恢复未执行完毕的长计算
一般来说emptydir的用途都是用来充当临时存储空间,例如一些不需要数据持久化的微服务,我们都可以用emptydir来当做微服务pod的存储方案。
借助本地储存实现的持久化存储——hostPath
k8s的hostPath存储方式,是一种映射本地的文件或目录进入pod中的方式,这种方式与docker的Data volume存储方式非常相似。
使用hostPath存储的好处在于,在Pod挂掉之后,可以重新拉去镜像制作新的Pod并接回原有的hostPath,此时不会造成硬盘数据的丢失。
其缺点在于,在云原生的大趋势下,一种借助宿主机本地存储的存储方案显然不应成为最优方案,将数据持久化在本地不便于“上云”,这种不易于迁移到其它节点的设计违背了云原生设计的初衷。
kubernetes的持久化存储
PV的设计与实现
在k8s中,PV描述的是持久化存储数据卷,这个API对象主要定义的是一个持久化存储在宿主机上的目录,比如一个NFS的挂载目录。通常情况下,运维人员事先在k8s集群内创建PV对象以待用,比如,运维人员可以定义一个NFS类型的PV。
PVC的设计与实现
PVC对象通常由平台的用户创建,或者以PVC模板的方式成为StatefulSet的一部分,然后由StatefulSet控制器负责创建带编号的PVC。
用户创建的PVC要真正被容器使用,就必须先和某个符合条件的PV进行绑定,此时必须要满足以下两个条件:
- PV的大小必须满足PVC的要求,即PV和PVC的spec字段
- PV和PVC的storageClassName必须一致
在对PV和PVC进行绑定之后,Pod就能像使用hostPath一样使用这个PVC了。
在张磊的《深入剖析Kubernetes》一书中指出,可以以“面向对象”的思想来理解PVC和PV的设计。可以将PVC理解为持久化存储的“接口”,它提供了对某种持久化存储的描述,但不提供具体的实现,而这个持久化存储的实现部分由PV完成。这样做的好处在于,平台的用户只需要和PVC这一接口沟通,而不必关心具体的存储实现是NFS还是Ceph。
持久化存储控制器Volume Controller
显然,我们更希望PVC能自动绑定符合条件的PV,而并非每次启动Pod前都需要程序员手动绑定PV和PVC。Kubernetes提供了一项专门处理持久化存储的控制器——Volume Controller。它维护着多个控制循环,其中一个循环扮演用于绑定PVC和PV,其名为PersistentVolumeController。这一插件会不断查看当前每一个PVC是否处于Bound(已绑定)状态,如果不是,它会遍历所有可用的PV,并尝试进行绑定。
概述(写在后面)
显然,到此为止,我们已经可以很好地回答前文概述中谈及的问题,通过PVC和PV形成的存储持久化机制,其各个容器间的存储共享是同一个Pod的容器共享存储资源,不同Pod内的容器不共享存储资源,那么对于概述中提及的场景,可以类比于在这个宿主机上运行minikube,各个容器间是否共享存储资源,取决于这些容器是否应被放置于同一个Pod中——这需要取决于各个容器的实际业务应用与关联。
未完待续
张磊的《深入剖析Kubernnetes》中,对k8s的存储原理还有更深入的讲解与分析,尤其是对于本地化存储卷和k8s的存储插件,本文暂不涉及上述内容,或许几个月内我会对这部分内容再进行整理,完善这方面的笔记。
参考文章
《深入剖析Kubernetes》,张磊 著,人民邮电出版社