您可以在本地盘上通过VolumeGroup进行磁盘虚拟化,并通过LVM数据卷切分磁盘给应用使用。本文介绍如何使用LVM数据卷。

背景信息

使用HostPath和LocalVolume都可以实现Pod对主机存储空间的访问,但均具有其局限性:
  • Kubernetes没有提供上述本地存储卷的生命周期管理能力,需要管理员手动管理、运维存储卷。
  • 多个Pod共享一个本地存储的时候,需要共享存储目录或者分别使用其子目录,无法做到容量隔离。
  • 多个Pod共享一个本地存储的时候,IOPS、吞吐等指标共享了整个存储空间,无法进行限制。
  • 新建Pod使用本地存储时,不知道各个节点存储空间余量,不能进行合理的存储卷调度。

为此ACK提供了LVM数据卷方案,以解决上述问题。

功能介绍

  • LVM数据卷生命周期管理:卷自动创建、删除、挂载、卸载。
  • LVM数据卷扩容功能。
  • LVM卷的监控能力。
  • LVM卷的IOPS限制。
  • 节点本地存储管理:自动运维VolumeGroup。
  • LVM卷的集群容量感知能力。

注意事项

  • LVM为本地存储类型,不适用于高可用数据场景。
  • VolumeGroup运维、本地存储容量感知为可选项(暂缓提供)。

实现架构

实现架构
基础的LVM功能(卷的生命周期管理、扩容、挂载、格式化等)由CSI-Provisioner和CSI-Plugin实现。
单元 详情
存储管理器 节点本地存储(VolumeGroup)的运维管理以及容量统计。可以不用该组件,由Worker管理运维VolumeGroup。
存储信息中心 保存节点本地存储信息,包括容量、VolumeGroup等信息。
本地存储调度器 实现新建PVC对集群存储容量感知功能。

插件部署

LVM CSI插件分为2个组件:Plugin(负责挂载、卸载LVM卷)和Provisioner(负责创建LVM卷和PV对象)。

apiVersion: storage.k8s.io/v1beta1
kind: CSIDriver
metadata:
  name: localplugin.csi.alibabacloud.com
spec:
  attachRequired: false
  podInfoOnMount: true
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
  labels:
    app: csi-local-plugin
  name: csi-local-plugin
  namespace: kube-system
spec:
  selector:
    matchLabels:
      app: csi-local-plugin
  template:
    metadata:
      labels:
        app: csi-local-plugin
    spec:
      containers:
      - args:
        - --v=5
        - --csi-address=/csi/csi.sock
        - --kubelet-registration-path=/var/lib/kubelet/csi-plugins/localplugin.csi.alibabacloud.com/csi.sock
        env:
        - name: KUBE_NODE_NAME
          valueFrom:
            fieldRef:
              apiVersion: v1
              fieldPath: spec.nodeName
        image: registry.cn-hangzhou.aliyuncs.com/acs/csi-node-driver-registrar:v1.2.0
        imagePullPolicy: Always
        name: driver-registrar
        volumeMounts:
        - mountPath: /csi
          name: plugin-dir
        - mountPath: /registration
          name: registration-dir
      - args:
        - --endpoint=$(CSI_ENDPOINT)
        - --v=5
        - --nodeid=$(KUBE_NODE_NAME)
        - --driver=localplugin.csi.alibabacloud.com
        env:
        - name: KUBE_NODE_NAME
          valueFrom:
            fieldRef:
              apiVersion: v1
              fieldPath: spec.nodeName
        - name: SERVICE_PORT
          value: "11290"
        - name: CSI_ENDPOINT
          value: unix://var/lib/kubelet/csi-plugins/localplugin.csi.alibabacloud.com/csi.sock
        image: registry.cn-hangzhou.aliyuncs.com/acs/csi-plugin:v1.18.8.45-1c5d2cd1-aliyun
        imagePullPolicy: Always
        name: csi-localplugin
        securityContext:
          allowPrivilegeEscalation: true
          capabilities:
            add:
            - SYS_ADMIN
          privileged: true
        volumeMounts:
        - mountPath: /var/lib/kubelet
          mountPropagation: Bidirectional
          name: pods-mount-dir
        - mountPath: /dev
          mountPropagation: HostToContainer
          name: host-dev
        - mountPath: /var/log/
          name: host-log
        - mountPath: /mnt
          mountPropagation: Bidirectional
          name: quota-path-dir
      hostNetwork: true
      hostPID: true
      serviceAccount: csi-admin
      tolerations:
      - operator: Exists
      volumes:
      - hostPath:
          path: /var/lib/kubelet/csi-plugins/localplugin.csi.alibabacloud.com
          type: DirectoryOrCreate
        name: plugin-dir
      - hostPath:
          path: /var/lib/kubelet/plugins_registry
          type: DirectoryOrCreate
        name: registration-dir
      - hostPath:
          path: /var/lib/kubelet
          type: Directory
        name: pods-mount-dir
      - hostPath:
          path: /dev
          type: ""
        name: host-dev
      - hostPath:
          path: /var/log/
          type: ""
        name: host-log
      - hostPath:
          path: /mnt
          type: Directory
        name: quota-path-dir
  updateStrategy:
    rollingUpdate:
      maxUnavailable: 10%
    type: RollingUpdate

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  labels:
    app: csi-local-provisioner
  name: csi-local-provisioner
  namespace: kube-system
spec:
  selector:
    matchLabels:
      app: csi-local-provisioner
  strategy:
    rollingUpdate:
      maxSurge: 25%
      maxUnavailable: 25%
    type: RollingUpdate
  template:
    metadata:
      labels:
        app: csi-local-provisioner
    spec:
      affinity:
        nodeAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - preference:
              matchExpressions:
              - key: node-role.kubernetes.io/master
                operator: Exists
            weight: 1
      containers:
      - args:
        - --csi-address=$(ADDRESS)
        - --feature-gates=Topology=True
        - --volume-name-prefix=local
        - --strict-topology=true
        - --timeout=150s
        - --extra-create-metadata=true
        - --enable-leader-election=true
        - --leader-election-type=leases
        - --retry-interval-start=500ms
        - --v=5
        env:
        - name: ADDRESS
          value: /socketDir/csi.sock
        image: registry.cn-hangzhou.aliyuncs.com/acs/csi-provisioner:v1.6.0-b6f763a43-aliyun
        imagePullPolicy: Always
        name: external-local-provisioner
        volumeMounts:
        - mountPath: /socketDir
          name: socket-dir
      - args:
        - --v=5
        - --csi-address=$(ADDRESS)
        - --leader-election
        env:
        - name: ADDRESS
          value: /socketDir/csi.sock
        image: registry.cn-hangzhou.aliyuncs.com/acs/csi-resizer:v0.3.0
        imagePullPolicy: Always
        name: external-local-resizer
        volumeMounts:
        - mountPath: /socketDir/
          name: socket-dir
      hostNetwork: true
      serviceAccount: csi-admin
      tolerations:
      - effect: NoSchedule
        operator: Exists
        key: node-role.kubernetes.io/master
      - effect: NoSchedule
        operator: Exists
        key: node.cloudprovider.kubernetes.io/uninitialized
      volumes:
      - hostPath:
          path: /var/lib/kubelet/csi-plugins/localplugin.csi.alibabacloud.com
          type: DirectoryOrCreate
        name: socket-dir

使用LVM数据卷示例

使用CSI-Provisioner自动创建PV,有以下特点:
  • StorageClass中需要指定VolumeGroup名字。
  • 如果期望创建的PV在某个节点,需要给PVC添加Label:volume.kubernetes.io/selected-node: nodeName
  1. 使用以下模板创建StorageClass。
    apiVersion: storage.k8s.io/v1
    kind: StorageClass
    metadata:
        name: csi-local
    provisioner: localplugin.csi.alibabacloud.com
    parameters:
        volumeType: LVM
        vgName: volumegroup1
        fsType: ext4
        lvmType: "striping"
    reclaimPolicy: Delete
    volumeBindingMode: WaitForFirstConsumer
    allowVolumeExpansion: true
    参数 描述
    volumeType 表示本地存储类型为LVM(后续将支持其他类型本地存储)。
    vgName 必选,VolumeGroup的名字。
    lvmType 生成的LVM类型,支持linear(线性)、striping(条带化)。
    fsType 格式文件系统类型。
  2. 使用以下模板创建PVC。
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
      name: lvm-pvc
    spec:
      accessModes:
      - ReadWriteOnce
      resources:
        requests:
          storage: 2Gi
      storageClassName: csi-local
  3. 使用以下模板创建应用。
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: deployment-lvm
      labels:
        app: nginx
    spec:
      selector:
        matchLabels:
          app: nginx
      template:
        metadata:
          labels:
            app: nginx
        spec:
          containers:
          - name: nginx
            image: nginx:1.7.9
            volumeMounts:
              - name: lvm-pvc
                mountPath: "/data"
          volumes:
            - name: lvm-pvc
              persistentVolumeClaim:
                claimName: lvm-pvc
  4. 查看应用状态。
    执行以下命令查看Pod信息。
    kubectl get pod
    返回结果如下:
    NAME                             READY   STATUS    RESTARTS   AGE
    deployment-lvm-9f798687c-mqfht   1/1     Running   0          9s
    执行以下命令查看PVC信息。
    kubectl get pvc
    返回结果如下:
    NAME      STATUS   VOLUME                                      CAPACITY   ACCESS MODES   STORAGECLASS   AGE
    lvm-pvc   Bound    disk-afacf7a9-3d1a-45da-b443-24f8fb3599c1   2Gi        RWO            csi-local      16s
    执行以下命令查看PV信息。
    kubectl get pv
    返回结果如下:
    NAME                                        CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM             STORAGECLASS   REASON   AGE
    disk-afacf7a9-3d1a-45da-b443-24f8fb3599c1   2Gi        RWO            Delete           Bound    default/lvm-pvc   csi-local               12s
    执行以下命令查看Pod挂载详情。
    kubectl exec -ti deployment-lvm-9f798687c-mqfht sh df /data
    返回结果如下:
    Filesystem                                                              1K-blocks  Used Available Use% Mounted on
    /dev/mapper/volumegroup1-disk--afacf7a9--3d1a--45da--b443--24f8fb3599c1   1998672  6144   1976144   1% /data
    执行以下命令列出/data下的目录。
    ls /data
    返回结果如下:
    lost+found
    执行以下命令在/data下新增test目录并查看。
    touch /data/test
    ls /data
    返回结果如下:
    lost+found  test
    执行以下命令退出登录。
    exit
    执行以下命令删除Pod重建。
    kubectl delete pod deployment-lvm-9f798687c-mqfht
    返回结果如下:
    pod "deployment-lvm-9f798687c-mqfht" deleted
    执行以下命令查看Pod信息。
    kubectl get pod
    返回结果如下:
    NAME                             READY   STATUS    RESTARTS   AGE
    deployment-lvm-9f798687c-jsdnk   1/1     Running   0          2m19s
    执行以下命令查看Pod挂载详情。
    kubectl exec deployment-lvm-9f798687c-jsdnk ls /data
    返回结果如下:
    lost+found  
    test
  5. 扩容LVM卷。
    执行以下命令查看PVC信息。
    kubectl get pvc
    返回结果如下:
    NAME      STATUS   VOLUME                                      CAPACITY   ACCESS MODES   STORAGECLASS   AGE
    lvm-pvc   Bound    disk-afacf7a9-3d1a-45da-b443-24f8fb3599c1   2Gi        RWO            csi-local      6m50s
    执行以下命令将PVC扩容到4 GiB。
    kubectl patch pvc lvm-pvc -p '{"spec":{"resources":{"requests":{"storage":"4Gi"}}}}'
    返回结果如下:
    persistentvolumeclaim/lvm-pvc patched
    执行以下命令查看PVC信息。
    kubectl get pvc
    返回结果如下:
    NAME      STATUS   VOLUME                                      CAPACITY   ACCESS MODES   STORAGECLASS   AGE
    lvm-pvc   Bound    disk-afacf7a9-3d1a-45da-b443-24f8fb3599c1   4Gi        RWO            csi-local      7m26s
    执行以下命令查看LVM卷扩容到4 GiB。
    kubectl exec deployment-lvm-9f798687c-jsdnk df /data
    返回结果如下:
    Filesystem                                                              1K-blocks  Used Available Use% Mounted on
    /dev/mapper/volumegroup1-disk--afacf7a9--3d1a--45da--b443--24f8fb3599c1   4062912  8184   4038344   1% /data
  6. 执行以下命令监控LVM卷。
    curl -s localhost:10255/metrics | grep lvm-pvc
    返回结果如下:
    kubelet_volume_stats_available_bytes{namespace="default",persistentvolumeclaim="lvm-pvc"} 1.917165568e+09
    kubelet_volume_stats_capacity_bytes{namespace="default",persistentvolumeclaim="lvm-pvc"} 1.939816448e+09
    kubelet_volume_stats_inodes{namespace="default",persistentvolumeclaim="lvm-pvc"} 122400
    kubelet_volume_stats_inodes_free{namespace="default",persistentvolumeclaim="lvm-pvc"} 122389
    kubelet_volume_stats_inodes_used{namespace="default",persistentvolumeclaim="lvm-pvc"} 11
    kubelet_volume_stats_used_bytes{namespace="default",persistentvolumeclaim="lvm-pvc"} 5.873664e+06

    可以将上述监控数据接入Prometheus并在前端展示。详情请参见开源Prometheus监控