在Kubernetes中,使用Volume提供容器的共享存储,但是与Docker设计不同的是,Volume的生命周期与Pod一致,即:Pod在Volume就在,Pod被删除,Volume根据其类型决定删不删数据。
Volume Plugins
插件分类 | 主要用途 | 卷插件 | 数据是否会随Pod删除而删除 |
临时存储 | 主要用于存储一些临时文件,类似于在操作系统中创建的tempDir | EmptyDir | 是 |
本地存储 | 用于将一些K8s中定义的配置通过Volume映射到容器中使用 | ConfigMap/DownwardAPI/Secret | 是 |
本地存储 | 使用宿主机上的存储资源 | HostPath/Local | 否 |
自建存储平台 | 自己搭建的存储平台 | CephFS/Ginder/GlusyerFS/NFS/RBD | 否 |
云厂商插件 | 云厂商提供的插件 | awsElasticBlockStore/AzureDisk/AzureFile/GCEersisenDisk | 否 |
HostPath
HostPath中定义的是宿主机的真实的绝对路径,就会存在同一个节点上多个Pod共用一个HostPath的情况,可能会造成数据混乱,读写异常等等。
例子
apiVersion: v1
kind: Pod
metadata:
name: hostpath-demo
namespace: demo
spec:
containers:
- image: nginx:1.19.2
name: container-demo
volumeMounts:
- mountPath: /test-pd
name: hostpath-volume
volumes:
- name: hostpath-volume
hostPath:
path: /data # 对应宿主机上的绝对路径
type: Directory # 可选字段,默认是 Directory
持久化存储-PV(Persistent Volume)
Kubernetes 中引入了一个专门的对象 Persistent Volume(简称 PV),将计算和存储进行分离,可以使用不同的控制器来分别管理。
通过 PV,可以和 Pod 自身的生命周期进行解耦。一个 PV 可以被几个 Pod 同时使用,即使 Pod 被删除后,PV 这个对象依然存在,其他新的 Pod 依然可以复用
PV创建分为静态和动态,如图
静态PV
管理员通过手动的方式在后端存储平台上创建好对应的 Volume,然后通过 PV 定义到 Kubernetes 中去。开发者通过 PVC 来使用。
例子
PV创建
apiVersion: v1
kind: PersistentVolume
metadata:
name: task-pv-volume # pv 的名字
labels: # pv 的一些label
type: local
spec:
storageClassName: manual
capacity: # 该 pv 的容量
storage: 10Gi
accessModes: # 该 pv 的接入模式
- ReadWriteOnce
hostPath: # 该 pv 使用的 hostpath 类型,还支持通过 CSI 接入其他 plugin
path: "/mnt/data"
这里定义了一个名为task-pv-volume的 PV,PV 是集群的资源,并不属于某个 namespace。其中storageClassName这个字段是某个StorageClass对象的名字。
其中,accessMode
可以指定PV的访问挂载方式
- ReadWriteOnce(RWO)表示该卷只可以以读写方式挂载到一个 Pod 内;
- ReadOnlyMany( ROX)表示该卷可以挂载到多个节点上,并被多个 Pod 以只读方式挂载;
- ReadWriteMany(RWX)表示卷可以被多个节点以读写方式挂载供多个 Pod 同时使用。
PVC创建
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: task-pv-claim
namespace: demo
spec:
storageClassName: manual
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 3Gi
创建好了以后,Kubernetes 会为 PVC 匹配满足条件的 PV。我们在 PVC 里面指定storageClassName为 manua,这个时候就只会去匹配storageClassName同样为 manual 的 PV。一旦发现合适的 PV 后,就可以绑定到该 PV 上。
PV 一般会有如下五种状态:
- Pending 表示目前该 PV 在后端存储系统中还没创建完成;
- Available 即闲置可用状态,这个时候还没有被绑定到任何 PVC 上;
- Bound 就像上面例子里似的,这个时候已经绑定到某个 PVC 上了;
- Released 表示已经绑定的 PVC 已经被删掉了,但资源还未被回收掉;
- Failed 表示回收失败。
同样,对于 PVC 来说,也有如下三种状态:
- Pending 表示还未绑定任何 PV;
- Bound 表示已经和某个 PV 进行了绑定;
- Lost 表示关联的 PV 失联。
在Pod中使用PV
apiVersion: v1
kind: Pod
metadata:
name: task-pv-pod
namespace: demo
spec:
volumes:
- name: task-pv-storage
persistentVolumeClaim:
claimName: task-pv-claim
containers:
- name: task-pv-container
image: nginx:1.14.2
ports:
- containerPort: 80
name: "http-server"
volumeMounts:
- mountPath: "/usr/share/nginx/html"
name: task-pv-storage
静态 PV 最大的问题就是使用起来不够方便,都是管理员提前创建好一批指定规格的 PV,无法做到按需创建。使用过程中,经常会遇到由于资源大小不匹配,规格不对等,造成 PVC 无法绑定 PV 的情况。同时还会造成资源浪费,比如一个只需要 1G 空间的 Pod,绑定了 10G 的 PV。
动态PV
例子
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast-rbd-sc
annotation:
storageclass.kubernetes.io/is-default-class: "true"
provisioner: kubernetes.io/rbd # 必填项,用来指定volume plugin来创建PV的物理资源
parameters: # 一些参数
monitors: 10.16.153.105:6789
adminId: kube
adminSecretName: ceph-secret
adminSecretNamespace: kube-system
pool: kube
userId: kube
userSecretName: ceph-secret-user
userSecretNamespace: default
fsType: ext4
imageFormat: "2"
imageFeatures: "layering"
你可以通过注释storageclass.kubernetes.io/is-default-class来指定默认的 StorageClass。这样新创建出来的 PVC 中的 storageClassName 字段就会自动使用默认的 StorageClass。
首先我们定义了一个 StorageClass。当用户创建好 Pod 以后,指定了 PVC,这个时候 Kubernetes 就会根据 StorageClass 中定义的 Provisioner 来调用对应的 plugin 来创建 PV。PV 创建成功后,跟 PVC 进行绑定,挂载到 Pod 中使用。
StatefulSet 中使用 PV 和 PVC
例子
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
spec:
serviceName: "nginx"
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: k8s.gcr.io/nginx-slim:0.8
ports:
- containerPort: 80
name: web
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: www
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 1Gi
官方文档: