作者:Michelle Au(谷歌),Matt Schallert(Uber),Celina Ward(Uber)
本地持久卷(Local Persistent Volume)功能已在Kubernetes 1.14中提升为GA。最初在Kubernetes 1.7中作为alpha引入,然后在Kubernetes 1.10中作为beta引入。GA的程碑表明Kubernetes用户可能依赖该功能及其API用于生产。GA功能受Kubernetes弃用政策保护。
https://kubernetes.io/docs/reference/using-api/deprecation-policy/
什么是本地持久卷?
本地持久卷表示直接连接到单个Kubernetes节点的本地磁盘。
Kubernetes提供了一个功能强大的卷插件系统,使Kubernetes工作负载能够使用各种块和文件存储来保存数据。这些插件中的大多数都支持远程存储 - 这些远程存储系统独立于产生数据的Kubernetes节点而保留数据。远程存储通常无法为本地直连存储提供一致的高性能保证。使用本地持久卷插件,Kubernetes工作负载现在可以使用应用程序开发者习惯使用的相同卷API来使用高性能本地存储。
它与HostPath卷有什么不同?
为了更好地理解本地持久卷的优点,将其与HostPath卷进行比较非常有用。HostPath卷将文件或目录从主机节点的文件系统挂载到Pod中。类似地,本地永久卷将本地磁盘或分区挂载到Pod中。
最大的区别是Kubernetes调度程序了解本地持久卷所属的节点。使用HostPath卷时,调度程序可能会将引用HostPath卷的pod移动到其他节点,从而导致数据丢失。但是使用本地持久卷,Kubernetes调度程序可确保始终将使用本地永久卷的Pod安排到同一节点。
虽然HostPath卷可以通过持久卷声明(PVC)引用,也可以直接在pod定义中内联,但本地永久卷只能通过PVC引用。这提供了额外的安全性优势,因为Persistent Volume对象由管理员管理,从而阻止Pod能够访问主机上的任何路径。
其他好处包括支持在挂载期间格式化块设备,以及使用fsGroup支持卷所有权。
GA有什么新功能?
从1.10开始,我们主要致力于提高功能的稳定性和可扩展性,以便生产就绪。
唯一的主要功能是能够指定裸盘并让Kubernetes自动格式化并挂载文件系统。这减少了在将设备提供给Kubernetes之前必须格式化和挂载设备的先前负担。
GA的局限性
在GA,本地持久卷不支持动态卷配置。但是,有一个外部控制器可用于帮助管理节点上各个磁盘的本地PersistentVolume生命周期。这包括创建PersistentVolume对象、清理并重用应用程序释放的磁盘。
https://kubernetes.io/docs/concepts/storage/dynamic-provisioning/
https://github.com/kubernetes-sigs/sig-storage-local-static-provisioner
如何使用本地持久卷?
工作负载可以使用与远程存储后端相同的PersistentVolumeClaim接口来请求本地持久卷。这样可以轻松地跨群集、云和本地环境交换存储后端。
首先,应创建一个设置volumeBindingMode的StorageClass: WaitForFirstConsumer以启用卷拓扑感知调度。此模式指示Kubernetes等待绑定PVC,直到安排使用它的Pod为止。
https://kubernetes.io/docs/concepts/storage/storage-classes/#volume-binding-mode
代码语言:javascript复制kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: local-storage
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer
然后,可以配置并运行外部静态配置程序,以便为节点上的所有本地磁盘创建PV。
代码语言:javascript复制$ kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
local-pv-27c0f084 368Gi RWO Delete Available local-storage 8s
local-pv-3796b049 368Gi RWO Delete Available local-storage 7s
local-pv-3ddecaea 368Gi RWO Delete Available local-storage 7s
之后,工作负载可以通过使用volumeClaimTemplates创建PVC和Pod或StatefulSet来开始使用PV。
代码语言:javascript复制apiVersion: apps/v1
kind: StatefulSet
metadata:
name: local-test
spec:
serviceName: "local-service"
replicas: 3
selector:
matchLabels:
app: local-test
template:
metadata:
labels:
app: local-test
spec:
containers:
- name: test-container
image: k8s.gcr.io/busybox
command:
- "/bin/sh"
args:
- "-c"
- "sleep 100000"
volumeMounts:
- name: local-vol
mountPath: /usr/test-pod
volumeClaimTemplates:
- metadata:
name: local-vol
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: "local-storage"
resources:
requests:
storage: 368Gi
一旦StatefulSet启动并运行,PVC全部绑定:
代码语言:javascript复制$ kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
local-vol-local-test-0 Bound local-pv-27c0f084 368Gi RWO local-storage 3m45s
local-vol-local-test-1 Bound local-pv-3ddecaea 368Gi RWO local-storage 3m40s
local-vol-local-test-2 Bound local-pv-3796b049 368Gi RWO local-storage 3m36s
当不再需要磁盘时,可以删除PVC。外部静态配置器将清理磁盘并使PV可再次使用。
代码语言:javascript复制$ kubectl patch sts local-test -p '{"spec":{"replicas":2}}'
statefulset.apps/local-test patched
$ kubectl delete pvc local-vol-local-test-2
persistentvolumeclaim "local-vol-local-test-2" deleted
$ kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
local-pv-27c0f084 368Gi RWO Delete Bound default/local-vol-local-test-0 local-storage 11m
local-pv-3796b049 368Gi RWO Delete Available local-storage 7s
local-pv-3ddecaea 368Gi RWO Delete Bound default/local-vol-local-test-1 local-storage 19m
你可以在Kubernetes网站上找到该功能的完整文档。
https://kubernetes.io/docs/concepts/storage/volumes/#local
哪些是合适的用例?
本地永久卷相对于远程持久存储的主要好处是性能:与远程存储系统相比,本地磁盘通常提供更高的IOPS和吞吐量以及更低的延迟。
但是,使用本地持久卷时需要考虑一些重要的限制和注意事项:
- 使用本地存储将你的应用程序绑定到特定节点,使你的应用程序更难调度。使用本地存储的应用程序应指定高优先级,以便在必要时可以抢占不需要本地存储的较低优先级的pod。
- 如果该节点或本地卷遇到故障并且无法访问,则该pod也将变得不可访问。可能需要手动干预、外部控制器或操作员才能从这些情况中恢复。
- 虽然大多数远程存储系统实现同步复制,但大多数本地磁盘产品不提供数据持久性保证。意味着丢失磁盘或节点可能会导致该磁盘上的所有数据丢失
出于这些原因,只应考虑在应用层处理数据复制和备份的工作负载的本地持久存储,从而使应用程序能够适应节点或数据故障并且不可用,尽管在单个磁盘级别上缺少此类保证。
良好工作负载的示例包括软件定义的存储系统和复制数据库。其他类型的应用程序应继续使用高可用性、可远程访问的持久存储。
Uber如何使用本地存储
M3,Uber的内部指标平台,大规模试用本地持久卷,以评估M3DB - 一个由Uber创建的开源、分布式时间序列数据库。M3DB的一个显着特点是能够将其度量标准分割为分区,将它们复制三倍,然后将副本均匀地分散到不同的故障域中。
在使用本地持久卷的试验之前,M3DB仅在Uber托管环境中运行。随着时间,出现了内部使用案例需要能够在具有较少依赖性的环境中运行M3DB。所以团队开始探索各种选择。作为一个开源项目,我们希望为社区提供一种尽可能轻松地运行M3DB的方法,使用开源堆栈,同时满足M3DB对高吞吐量、低延迟存储的需求,以及扩展自身的能力出。
Kubernetes本地持久卷界面具有高性能、低延迟的保证,很快就成为构建之上的完美抽象。使用本地持久卷,单个M3DB实例可以轻松处理每秒高达600k的写入。这为群集上的尖峰留下了足够的空间,通常每秒处理几百万个度量标准。
由于M3DB还可以优雅地处理丢失单个节点或卷,因此本地持久卷的有限数据持久性保证不是问题。如果节点发生故障,M3DB会找到合适的替代,新节点将从其两个对等体开始流式传输数据。
由于Kubernetes调度程序对卷拓扑的智能处理,M3DB能够以编程方式将其副本均匀地分散到所有可用云区域中的多个本地持久卷中,或者在本地群集的情况下,跨所有可用的服务器机架。
Uber的运营经验
如上所述,虽然本地持久卷提供了许多好处,但它们还需要在生产中提交之前仔细规划并仔细考虑约束。在考虑我们针对M3DB的本地量化策略时,Uber必须考虑一些事情。
首先,我们必须考虑Kubernetes集群中节点的硬件配置文件。例如,每个节点集群有多少本地磁盘?他们将如何分区?
本地静态配置器README提供指导以帮助回答这些问题。最好能够将完整磁盘专用于每个本地卷(用于IO隔离)和每个卷的完整分区(用于容量隔离)。在我们可以混合和匹配本地磁盘的云环境中,这更容易。但是,如果在本地使用本地卷,则硬件限制可能是一个限制因素,具体取决于可用磁盘的数量及其特征。
https://github.com/kubernetes-sigs/sig-storage-local-static-provisioner/#best-practices
在首次测试本地卷时,我们希望彻底了解使用本地存储的pod对中断的影响(自愿和非自愿),因此我们开始测试一些故障情况。我们发现当节点保持可用时,而本地卷不可用时(例如在磁盘上执行维护时),使用本地卷的pod将陷入ContainerCreating状态,直到它可以挂载卷。如果某个节点不可用,例如,如果该节点已从群集中删除或已耗尽(drained),则使用该节点上的本地卷的pod将处于“未知(Unknown)”或“待处理(Pending)”状态,具体取决于该节点是否优雅地删除。
从这些临时状态恢复pod意味着必须删除将pod绑定到其本地卷的PVC,然后删除该pod以便重新安排它(或等到节点和磁盘再次可用)。我们在为M3DB构建operator时考虑到了这一点,当重新调度pod时,会对集群拓扑进行更改,以便新节点从其余两个节点优雅地流式传输数据。最终我们计划完全自动删除和重新安排过程。
pod状态的警报可以帮助引起对卡住本地卷的注意,特定于工作负载的控制器或operator可以自动修复它们。由于这些限制,最好从自动升级或修复中排除具有本地卷的节点,事实上,一些云提供商明确提到这是最佳做法。
本地和云之间的可移植性
本地卷在Uber决定使用Kubernetes构建M3DB编排方面发挥了重要作用,部分原因在于它是一种存储抽象,在本地和云环境中的工作方式相同。远程存储解决方案在云提供商之间具有不同的特征,一些用户可能不希望在他们自己的数据中心中使用网络存储。另一方面,本地磁盘相对普遍存在并提供更可预测的性能特征。
通过使用云中的本地磁盘编排M3DB,使用Kubernetes更容易启动和运行,我们相信我们仍然可以使用operator在我们的本地环境中运行M3DB而无需任何修改。当我们继续研究我们如何在本地运行Kubernetes时,解决了这样一个重要的未决问题是一个很大的缓解。
本地持久卷的下一步是什么?
正如我们在Uber的M3DB上看到的那样,本地持久卷已成功用于生产环境。随着本地持久卷的采用不断增加,SIG Storage继续寻求改进功能的方法的反馈。
最常见的问题之一是控制器可以帮助从故障节点或磁盘中恢复,当前主要是手动过程(或者必须内置到operator中的东西)。SIG Storage正在研究创建一个可以由具有简单和类似恢复过程的工作负载使用的通用控制器。
另一个普遍的问题是使用lvm支持动态配置。这可以简化磁盘管理并提高磁盘利用率。SIG Storage正在评估此功能可行性的性能权衡。
进来参与
如果你对此功能有反馈意见或有兴趣参与设计和开发,请加入Kubernetes Storage Special-Interest-Group(SIG)。我们正在快速成长,并始终欢迎新的贡献者。
https://github.com/kubernetes/community/blob/master/sig-storage/README.md
特别感谢所有为此功能达到GA的贡献者,包括Chuqiang Li(lichuqiang)、Dhiraj Hedge(dhirajh)、Ian Chakeres(ianchakeres)、JanŠafránek(jsafrane)、Michelle Au(msau42)、Saad Ali(saad-ali)、Yecheng Fu(cofyc)和Yuquan Ren(nickrenren)。