LVM volumes
You can use a VolumeGroup to virtualize local disks and partition them into Logical Volume Manager (LVM) volumes for your applications. This topic describes how to use LVM volumes.
Background
Both HostPath and LocalVolume allow pods to access host storage, but they have limitations. To address these limitations, ACK provides an LVM volume solution. The main limitations of HostPath and LocalVolume are listed below:
Kubernetes does not manage the lifecycle of these local storage volumes. An administrator must manually manage and perform operations and maintenance (O&M) on the storage volumes.
When multiple pods share local storage, they must either share a storage directory or use separate subdirectories. This prevents capacity isolation.
When multiple pods share local storage, metrics such as input/output operations per second (IOPS) and throughput apply to the entire storage space and cannot be configured for individual pods.
When a new pod uses local storage, the scheduler is unaware of the remaining storage space on each node. This can lead to inefficient volume scheduling.
Features
LVM volume lifecycle management: Automatic creation, deletion, mounting, and unmounting of volumes.
LVM volume scale-out.
LVM volume monitoring.
IOPS limits for LVM volumes.
Node local storage management: Automatic O&M for VolumeGroups.
Cluster capacity awareness for LVM volumes.
Notes
LVM local storage volumes do not support data migration across nodes. They are not suitable for high availability (HA) scenarios.
Capacity awareness for VolumeGroup local storage is an optional feature. The initialization of a VolumeGroup depends on local storage resources. You must initialize the VolumeGroup manually or automatically initialize local storage resources. Both methods require a deep understanding of local storage resources. If you are not familiar with local storage resources, use cloud storage resources such as disks or Container Network File System (CNFS) instead.
Architecture
The CSI-Provisioner and CSI-Plugin components implement basic LVM features, such as volume lifecycle management, scale-out, mounting, and formatting.
Component | Details |
Storage manager | Manages O&M and capacity statistics for node local storage. This component is optional. The Worker can manage O&M for the VolumeGroup. |
Storage information center | Saves node local storage information, including capacity and VolumeGroup details. |
Local storage scheduler | Enables cluster storage capacity awareness for new PersistentVolumeClaims (PVCs). |
Step 1: Create a ServiceAccount for the plugin and provisioner components
For clusters that run Kubernetes 1.22 or later, you must create a new ServiceAccount. For clusters that run Kubernetes 1.20 or earlier, you must add Role-Based Access Control (RBAC) permissions for Secrets to clusterrole/alicloud-csi-plugin.
Clusters of version 1.22 and later
Create a ServiceAccount and grant it permissions using the following YAML file:
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: alibaba-cloud-csi-local
namespace: kube-system
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: alibaba-cloud-csi-local
rules:
- apiGroups: [""]
resources: ["persistentvolumes"]
verbs: ["get", "list", "watch", "update", "create", "delete", "patch"]
- apiGroups: [""]
resources: ["persistentvolumeclaims"]
verbs: ["get", "list", "watch", "update"]
- apiGroups: [""]
resources: ["persistentvolumeclaims/status"]
verbs: ["get", "list", "watch", "update", "patch"]
- apiGroups: ["storage.k8s.io"]
resources: ["storageclasses"]
verbs: ["get", "list", "watch"]
- apiGroups: ["storage.k8s.io"]
resources: ["csinodes"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["events"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
- apiGroups: [""]
resources: ["nodes"]
verbs: ["get", "update", "patch", "list", "watch"]
- apiGroups: ["storage.k8s.io"]
resources: ["volumeattachments"]
verbs: ["get", "list", "watch", "update", "patch"]
- apiGroups: ["coordination.k8s.io"]
resources: ["leases"]
verbs: ["get", "create", "list", "watch", "delete", "update"]
- apiGroups: ["storage.k8s.io"]
resources: ["volumeattachments/status"]
verbs: ["patch"]
- apiGroups: ["storage.k8s.io"]
resources: ["storageclasses"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: alibaba-cloud-csi-local
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: alibaba-cloud-csi-local
subjects:
- kind: ServiceAccount
name: alibaba-cloud-csi-local
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: alibaba-cloud-csi-local
namespace: kube-system
rules:
- apiGroups: [""]
resources: ["secrets"]
resourceNames: ["csi-local-plugin-cert"]
verbs: ["get"]
- apiGroups: [""]
resources: ["secrets"]
verbs: ["create"]
- apiGroups: [""]
resources: ["configmaps"]
resourceNames: ["csi-plugin", "ack-cluster-profile"]
verbs: ["get"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: alibaba-cloud-csi-local
namespace: kube-system
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: alibaba-cloud-csi-local
subjects:
- kind: ServiceAccount
name: alibaba-cloud-csi-local
namespace: kube-systemClusters of version 1.20 and earlier
The csi-plugin component shares the same ServiceAccount. First, add RBAC permissions for Secrets to clusterrole/alicloud-csi-plugin.
Check whether
clusterrole/alicloud-csi-pluginhas thecreatepermission for Secrets.echo `JSONPATH='{range .rules[*]}{@.resources}:{@.verbs} \r\n {end}' \ && kubectl get clusterrole alicloud-csi-plugin -o jsonpath="$JSONPATH";` | grep secretsExpected output:
["secrets"]:["get","list"]If
clusterrole/alicloud-csi-plugindoes not have thecreatepermission for Secrets, run the following command to add this permission.kubectl patch clusterrole alicloud-csi-plugin --type='json' -p='[{"op": "add", "path": "/rules/0", "value":{ "apiGroups": [""], "resources": ["secrets"], "verbs": ["create"]}}]'Expected output:
clusterrole.rbac.authorization.k8s.io/alicloud-csi-plugin patchedVerify that the
createpermission for Secrets is added toclusterrole/alicloud-csi-plugin.echo `JSONPATH='{range .rules[*]}{@.resources}:{@.verbs} \r\n {end}' \ && kubectl get clusterrole alicloud-csi-plugin -o jsonpath="$JSONPATH";` | grep secretsExpected output:
["secrets"]:["create"] ["secrets"]:["get","list"]The output indicates that the
createpermission for Secrets is successfully added toclusterrole/alicloud-csi-plugin.
Step 2: Deploy the plugin and provisioner components
The Container Storage Interface (CSI) solution consists of two components: the plugin, which mounts and unmounts volumes, and the provisioner, which creates volumes and persistent volume (PV) objects.
Replace {{ regionId }} in the following YAML files with the ID of the region where your cluster is located.
Clusters of version 1.22 and later
Clusters of version 1.20 and earlier
Step 3: Use an LVM storage volume
When you use the Container Storage Interface (CSI) provisioner to automatically create a PersistentVolume (PV), note the following:
The StorageClass must specify the VolumeGroup name.
If you want the PV to be created on a specific node, add the annotation volume.kubernetes.io/selected-node: nodeName to the PersistentVolumeClaim (PVC).
Create a StorageClass using the following YAML file.
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" writeIOPS: "10000" writeBPS: "1M" reclaimPolicy: Delete volumeBindingMode: WaitForFirstConsumer allowVolumeExpansion: trueParameter
Description
volumeType
Specifies that the local storage type is LVM.
vgName
Required. The name of the VolumeGroup.
fsType
The file system type to be formatted.
lvmType
The type of LVM to create. Valid values are `linear` and `striping`.
writeIOPS
When you dynamically create a logical volume (LV) using the StorageClass, this parameter limits the number of write IOPS for each LV.
writeBPS
When you dynamically create an LV using the StorageClass, this parameter limits the write BPS for each LV. The unit is bytes.
Create a PVC using the following YAML file.
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: lvm-pvc spec: accessModes: - ReadWriteOnce resources: requests: storage: 2Gi storageClassName: csi-localCreate an application using the following YAML file.
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: anolis-registry.cn-zhangjiakou.cr.aliyuncs.com/openanolis/nginx:1.14.1-8.6 volumeMounts: - name: lvm-pvc mountPath: "/data" volumes: - name: lvm-pvc persistentVolumeClaim: claimName: lvm-pvcCheck the PVC and PV information.
Check the PVC information.
kubectl get pvcExpected output:
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE lvm-pvc Bound disk-afacf7a9-3d1a-45da-b443-24f8fb35**** 2Gi RWO csi-local 16sCheck the PV information.
kubectl get pvExpected output:
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE disk-afacf7a9-3d1a-45da-b443-24f8fb35**** 2Gi RWO Delete Bound default/lvm-pvc csi-local 12s
Check the application status.
Check the pod information.
kubectl get podExpected output:
NAME READY STATUS RESTARTS AGE deployment-lvm-9f798687c-m**** 1/1 Running 0 9sCheck the mount details of the pod.
kubectl exec -ti deployment-lvm-9f798687c-m**** -- df /dataExpected output:
Filesystem 1K-blocks Used Available Use% Mounted on /dev/mapper/volumegroup1-disk--afacf7a9--3d1a--45da--b443--24f8fb35**** 1998672 6144 1976144 1% /dataList the directories under
/data.ls /dataExpected output:
lost+found
Verify the persistent storage of the LVM volume.
Create a `test` directory in
/dataand then list the contents of the directory.touch /data/test ls /dataExpected output:
lost+found testLog out of the pod.
exitDelete the pod. The system automatically recreates the pod.
kubectl delete pod deployment-lvm-9f798687c-m****Expected output:
pod "deployment-lvm-9f798687c-m****" deletedCheck the pod information.
kubectl get podExpected output:
NAME READY STATUS RESTARTS AGE deployment-lvm-9f798687c-j**** 1/1 Running 0 2m19sCheck the mount details of the pod.
kubectl exec deployment-lvm-9f798687c-j**** -- ls /dataExpected output:
lost+found test
Expand the LVM volume.
Check the PVC information.
kubectl get pvcExpected output:
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE lvm-pvc Bound disk-afacf7a9-3d1a-45da-b443-24f8fb35**** 2Gi RWO csi-local 6m50sExpand the PVC to 4 GiB.
kubectl patch pvc lvm-pvc -p '{"spec":{"resources":{"requests":{"storage":"4Gi"}}}}'Expected output:
persistentvolumeclaim/lvm-pvc patchedCheck the PVC information.
kubectl get pvcExpected output:
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE lvm-pvc Bound disk-afacf7a9-3d1a-45da-b443-24f8fb35**** 4Gi RWO csi-local 7m26sVerify that the LVM volume is expanded to 4 GiB.
kubectl exec deployment-lvm-9f798687c-j**** -- df /dataExpected output:
Filesystem 1K-blocks Used Available Use% Mounted on /dev/mapper/volumegroup1-disk--afacf7a9--3d1a--45da--b443--24f8fb35**** 4062912 8184 4038344 1% /data
Monitor the LVM volume.
curl -s localhost:10255/metrics | grep lvm-pvcExpected output:
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+06You can ingest this monitoring data into Prometheus and display it on the frontend. For more information, see Open source Prometheus monitoring.