容器化一个应用比较麻烦的地方,就是对于有状态的服务的管理,最常见的状态就是存储状态
。
pv、pvc 存储体系
-
PV 描述的是持久化存储数据卷,这个API对象主要定义的是一个持久化存储在宿主机上的目录。
通常情况下,PV由运维人员负责创建,如下例(NFS为例):apiVersion: v1 kind: PersistentVolume metadata: name: nfs spec: storageClassName: manual capacity: storage: 1Gi accessModes: - ReadWriteMany nfs: server: 10.244.1.4 path: "/"
-
PVC描述的是Pod希望使用的持久化存储的属性,比如,Volume 存储的大小,可读写权限等。
PVC通常是由开发人员创建;或者以 PVC 模版的方式成为 StatefulSet 的一部分, 然后由 StatefulSet 控制器负责创建带编号的 PVC。apiVersion: v1 kind: PersistentVolumeClaim metadata: name: nfs spec: accessModes: - ReadWriteMany storageClassName: manual resources: requests: storage: 1Gi
创建的PVC只有和对应的PV绑定才可以使用
绑定条件:
- PV 的存储大小和权限必须满足 PVC 的要求。
- PV 和 PVC 的 storageClassName 必须一致。如果没有指定 storageClassName 则默认是 "default" 或者 ""。
如果PVC没有绑定,则声明使用该PVC的Pod无法启动。
成功绑定之后,Pod 就是声明PVC绑定的持久化存储了,使用方法如下:
apiVersion: v1
kind: Pod
metadata:
labels:
role: web-frontend
spec:
containers:
- name: web
image: nginx
ports:
- name: web
containerPort: 80
volumeMounts:
- name: nfs
mountPath: "/usr/share/nginx/html"
volumes:
- name: nfs
persistentVolumeClaim:
claimName: nfs
Pod 只需要在 volumes 字段里声明要使用的 PVC 的name,等Pod创建后,Kubelet会将 PVC 绑定的 PV, 例如上面的 NFS 类型的 volume 挂载到容器内目录。
PVC 和 PV 的设计,其实跟“面向对象”的思想完全一致
PVC 可以理解为持久化存储的接口,提供了对存储的描述,但不提供具体实现;而PV来负责存储的具体实现。
StorageClass
问题
当每次创建 PVC 声明使用存储时,都需要去手动的创建 PV,来满足 PVC 的使用。
解决方法
可以用一种机制来根据用户声明的存储使用量(PVC)来动态的创建对应的持久化存储卷(PV)。k8s 用 StorageClass 来实现动态创建 持久化存储。
实现原理:
存储控制器 Volume Controller,是用来专门处理持久化存储的控制器,其一个子控制循环 PersistentVolumeController 负责实现 PV 和 PVC 的绑定。
PersistentVolumeController 会 watch kube-apiserver 的 PVC 对象。如果发现有 PVC对象创建,则会查看所有可用的 PV, 如果有则绑定,若没有,则会使用 StorageClass 的配置和 PVC 的描述创建 PV 进行绑定。
所谓将一个 PV 与 PVC 进行“绑定”,其实就是将这个PV对象的名字,填在了 PVC 对象的 spec.volumeName 字段上