在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创建分为静态和动态,如图

kubesphere配置host kubernetes hostpath_nginx

静态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

官方文档:

配置Pod以使用PV作为存储