不完美 Kubernetes Storage:Volume, PersistentVolume, PersistentVolumeClaim, Provisioner, StorageClass, VolumeClaimTemplate and StatefulSet - Part 3
回顾
在part2中,我们已经了解了关于Kubernetes Storage的一下概念。
- Volume
- PersistentVolume
- PersistentVolumeClaim
那么请问,哪一种Volume Type随着pod的建立而建立,随着pod的撤销而撤销,主要用于pod内container之间的数据共享呢?
Kubernetes Provisioner
估计可能是PV,PVC搞得怨声载道,在2017年随着v1.6版本的发布,Kubernets项目新增加了dynamic provisioning, Storage Class, 以及Provisioner这三个概念。那么新增的这三个概念是要解决什么问题的?
还是要解决资源分配自动化的问题。让我们回头看看PV,PVC所引起的核心问题:PV要被预先恰当的分配--通常是需要手动完成,然后才能被使用者claim。如果我们回到“Volume Pool”的这个概念,抛弃PV这个间接层,直接由使用者的claim在“Volume Pool”这个预先分配好的大池子里面切一块出来给使用者使用,不就解决了之前PV,PVC的问题了吗?
Kubernetes的本意大抵是如此了罢。但是……但是之前已经有使用了PV,PVC系统的项目在线上运行了,抛弃掉PV这个概念会造成无法向下兼容的大问题,怎么办?聪明的设计师又用起了老套路:增加间接层。
容易理解但是不太准确的解释是:Kubernets所谓的dynamic provisioning,是指在用户Claim的时候,由用户指定的自动资源分配器Provisioner自动的在指定的资源池上分配用户指定规模的PV,然后被用户所使用。
其实自动资源分配器Provisioner并不是由用户直接指定的,用户直接指定的是另一个间接层,Storage Class。Provisioner其实是由Storage Class所指定的。加入这个间接层是为了解决另外一个问题,我们后面会说到。
我们来看一个例子。
# sc-definition.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: google-storage
provisioner: kubernetes.io/gcd-pd
# pvc-definition.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-claim
spec:
accessModes:
- ReadWriteOnce
storageClassName: google-storage
resource:
requests:
storage: 500Mi
# pod-definition.yaml
apiVersion: v1
kind: Pod
metadata:
name: my-pod
spec:
containers:
- name: my-frontend
image: nginx
volumeMounts:
- name: my-volume
mountPath: "/var/www/html"
volumes:
- name: my-volume
persistentVolumeClaim:
claimName: my-claim
上面这个示例的意思是,一个名叫my-pod的pod,在自己内部创建了一个container,这个container挂载了一个volume到自己的/var/www/html目录下;根据pod的指示,这个volume来自于一个叫做my-claim的PVC。
这个叫做my-claim的PVC,告诉一个叫做google-storage的 Storage Class,自己需要500Mi的存储空间。
这个叫做google-storage的Storage Class,指定了一个叫做kubernetes.io/gcd-pd的自动资源分配器provisioner,执行my-claim所需资源的分配。Kubernetes支持很多内建的Provisioner,可以看这里。
之后的事情就是Kubernetes自己默默做的了。Kubernetes使用这个名字叫做kubernetes.io/gcd-pd的provisioner,在这个provisioner所对应的资源池上分配了一个大小500Mi的PV,然后把这个PV一对一的bind到了名字叫做my-claim的PVC上。
可以看到,在新的设计体系里面,PV已经从应用层面被干掉了,它现在只是程序执行过程中的一个中间产物而已。
StorageClass
那为什要加入StorageClass这个概念呢?是为了解决什么问题?
我们知道,一般云服提供商的存储类型是不止一种的,以阿里云为例,有云盘、高效云盘;有时根据存储所部署的地域不同,性能和价格也会不同。StorageClass可以把这些Provisioner的属性封装起来,简化PVC的接口。
再看个例子。
# sc-gce-definition.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: silver
provisioner: kubernetes.io/gce-pd
parameters:
type: pd-standard
replication-type: none
# sc-gce-gold-definition.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: gold
provisioner: kubernetes.io/gcd-pd
parameters:
type: pd-ssd
replication-type: none
# sc-gce-platinum-definition.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: platinum
provisioner: kubernetes.io/gcd-pd
parameters:
type: pd-ssd
replication-type: reginal-pd
在上面的例子中,GCE存储空间被StorageClass分类了。Silver类型就是一般的存储空间,无备份;gold类型使用了ssd,无备份;platinum类型使用了ssd,有备份。
可以看到,StorageClass可以根据Provisioner的属性、参数对Provisioner进行“分类”,这也是StorageClass被称为“Class”的原因。
Static Provisioning 与 Dynamic Provisioning
我们再来回顾一下PV,PVC的概念,与StorageClass,Provisioner的概念进行对照。
为了区分概念,Kubernetes称PV,PVC方法为Static Provisioning;StorageClass Provisioner方法为Dynamic Provisioning
先看看Static Provisioning:

然后再看看Dynamic Provisioning:

可以看到,在新设计的概念下,PV被StorageClass替换掉了。PV只是一个系统运行过程的中间产物。这个中间产物PV被Kubernets系统自动bind到了PVC。
向下兼容
那么新的StorageClass如何向下兼容不需要动态分配的情景呢,例如像hostPath?在StorageClass内可以指定no-provisioner,这样Kubernets可以使用local persistent。其实Local storage与hostPath并非完全相同,这又是kubernetes Storage系统向下兼容并做概念修正和优化的另外一个故事了。
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: local-storage
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer
焦油坑
Kubernets经历过数个版本的系列发布,以及后续版本为了填之前版本的坑,并努力的向下兼容,导致Kubernetes的概念系统在方方面面可能……嗯……有些复杂。Kubernets的文档系统的主要作用是参考手册,是不会把kubernets各个版本发布系统的时间线、缘由,以及一系列kubernetes.io上的blog前后串联起来的。Kubernetes的文档系统只能用类似下面这样“平坦”的方式来介绍概念,而不会告诉读者什么才是Kubnernets设计本意的“工业级”的应用,以及Kubernetes如何被“更正确”的应用。
A PersistentVolume (PV) is a piece of storage in the cluster that has been provisioned by an administrator or dynamically provisioned using Storage Classes. It is a resource in the cluster just like a node is a cluster resource. PVs are volume plugins like Volumes, but have a lifecycle independent of any individual Pod that uses the PV. This API object captures the details of the implementation of the storage, be that NFS, iSCSI, or a cloud-provider-specific storage system.
其实在现在的Kuberents storage系统内,在真实的工业级应用场景,PV只是作为一个中间结果出现,不会出现在yaml当中。它在Kubernets系统中的作用,除了作为调试时的中间结果输出和向下兼容外,并无其他用处。这个意思是,能不用就尽量不要用。
到目前为止,所有的示例都相当的naive,在真实应用环境下我们不会直接创建pod的,而是会使用deployment。这就引出了另外一个概念——StatefulSet。
Kubernets Storage 概念总结
- Volume
- PersistentVolume
- PersistentVolumeClaim
- Static Provisioning
- Dynamic Provisioning
- StorageClass
- Provisioner