不完美 Kubernetes Storage - Part 3

258 阅读6分钟

不完美 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:

-w804

然后再看看Dynamic Provisioning:

-w808

可以看到,在新设计的概念下,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

The Next

part4