OpenEBS LVM LocalPV on Single Node OpenShift (vSphere 7)
A guide to installing OpenEBS LVM LocalPV CSI on SNO with snapshot support, using a locally attached disk. Tested on OCP 4.21, OpenEBS LVM LocalPV Helm chart, vSphere 7.
Prerequisites
- Single Node OpenShift (SNO) cluster
- A second disk attached to the VM (in this guide:
/dev/sdd, 200GB) - Helm installed on your jumpbox
ocCLI with cluster-admin access
Phase 0: Prepare the Disk
Open a debug shell on the SNO node and create a volume group:
oc debug node/<node-name>
chroot /host
pvcreate /dev/sdd
vgcreate lvmvg /dev/sdd
# Create a thin pool (use ~95% of VG capacity)
lvcreate -L 189G -T lvmvg/thinpool
# Verify
vgs
lvs
exit
exit
Note: OpenEBS will also auto-create its own thin pool (lvmvg_thinpool) when it first provisions a volume. Use the auto-created pool name in the StorageClass (see Phase 3).Phase 1: Load the dm-snapshot Kernel Module
OpenEBS requires the dm-snapshot kernel module for snapshot support. Load it manually first to verify:
oc debug node/<node-name>
chroot /host
modprobe dm-snapshot
lsmod | grep dm_snapshot
exit
exit
Then make it persistent across reboots via a MachineConfig:
cat <<EOF | oc apply -f -
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
metadata:
labels:
machineconfiguration.openshift.io/role: master
name: 99-master-dm-snapshot
spec:
config:
ignition:
version: 3.2.0
systemd:
units:
- contents: |
[Unit]
Description=Load dm-snapshot module
[Service]
Type=oneshot
ExecStart=/usr/sbin/modprobe dm-snapshot
[Install]
WantedBy=multi-user.target
enabled: true
name: dm-snapshot.service
EOF
Note: This will trigger a node reboot on SNO. Wait for the node to return to Ready before continuing.Phase 2: Install OpenEBS LVM LocalPV via Helm
Create the namespace and add the Helm repo:
oc new-project openebs
helm repo add openebs-lvmlocalpv https://openebs.github.io/lvm-localpv
helm repo update
Install the chart. The snapshot CRD flag is required because OCP/Rook-Ceph already owns the snapshot CRDs:
helm install openebs-lvmlocalpv openebs-lvmlocalpv/lvm-localpv \
--namespace openebs \
--set openebs-crds.csi.volumeSnapshots.enabled=false \
--set crds.csi.volumeSnapshots.enabled=false \
--set lvmController.replicas=1 \
--set lvmController.podAntiAffinity.enabled=false
Phase 3: Apply OpenShift SCCs
OpenEBS requires privileged access. Label the namespace and grant SCCs to the service accounts:
oc label namespace openebs \
pod-security.kubernetes.io/enforce=privileged \
pod-security.kubernetes.io/audit=privileged \
pod-security.kubernetes.io/warn=privileged --overwrite
oc adm policy add-scc-to-user privileged \
system:serviceaccount:openebs:openebs-lvm-node-sa
oc adm policy add-scc-to-user privileged \
system:serviceaccount:openebs:openebs-lvm-controller-sa
Verify both pods are running:
oc get pods -n openebs
Expected output:
openebs-lvmlocalpv-lvm-localpv-controller-xxx 5/5 Running
openebs-lvmlocalpv-lvm-localpv-node-xxx 2/2 Running
Phase 4: Create StorageClass and VolumeSnapshotClass
After first provisioning a PVC, check which thin pool OpenEBS auto-created:
oc debug node/<node-name> -- chroot /host lvs -o lv_name,pool_lv lvmvg
Use that pool name (e.g. lvmvg_thinpool) in the StorageClass:
cat <<EOF | oc apply -f -
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: openebs-lvmpv
provisioner: local.csi.openebs.io
parameters:
storage: "lvm"
volgroup: "lvmvg"
thinprovision: "yes"
thinpoolname: "lvmvg_thinpool"
allowVolumeExpansion: true
volumeBindingMode: Immediate
EOF
Create the VolumeSnapshotClass:
cat <<EOF | oc apply -f -
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshotClass
metadata:
name: openebs-lvmpv-snapclass
driver: local.csi.openebs.io
deletionPolicy: Delete
EOF
Phase 5: Patch the CDI StorageProfile (KubeVirt)
If running OpenShift Virtualization, patch the StorageProfile to prevent CDI warnings:
oc patch storageprofile openebs-lvmpv --type=merge -p '{
"spec": {
"claimPropertySets": [
{
"accessModes": ["ReadWriteOnce"],
"volumeMode": "Filesystem"
},
{
"accessModes": ["ReadWriteOnce"],
"volumeMode": "Block"
}
]
}
}'
Verify:
oc get storageprofile openebs-lvmpv -o yaml | grep -A 10 status
You should see cloneStrategy: snapshot auto-selected.
Phase 6: Verify
Create a test PVC and pod:
cat <<EOF | oc apply -f -
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: lvm-test-pvc
namespace: default
spec:
accessModes:
- ReadWriteOnce
storageClassName: openebs-lvmpv
resources:
requests:
storage: 1Gi
EOF
cat <<EOF | oc apply -f -
apiVersion: v1
kind: Pod
metadata:
name: lvm-test-pod
namespace: default
spec:
containers:
- name: busybox
image: busybox
command: ["sh", "-c", "while true; do echo hello >> /data/test.txt; sleep 5; done"]
volumeMounts:
- mountPath: /data
name: lvm-vol
volumes:
- name: lvm-vol
persistentVolumeClaim:
claimName: lvm-test-pvc
EOF
Test a snapshot:
cat <<EOF | oc apply -f -
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
name: lvm-test-snap
namespace: default
spec:
volumeSnapshotClassName: openebs-lvmpv-snapclass
source:
persistentVolumeClaimName: lvm-test-pvc
EOF
oc get volumesnapshot lvm-test-snap -n default
READYTOUSE: true confirms snapshots are working.
Gotchas
| Issue | Cause | Fix |
|---|---|---|
| Helm install fails with snapshot CRD conflict | Rook-Ceph/OCP owns snapshot CRDs | Add --set openebs-crds.csi.volumeSnapshots.enabled=false --set crds.csi.volumeSnapshots.enabled=false |
| Node daemonset pod pending (SCC) | Missing privileged SCC | Grant SCC to openebs-lvm-node-sa and openebs-lvm-controller-sa |
Snapshot stuck READYTOUSE: false |
dm-snapshot kernel module not loaded |
modprobe dm-snapshot on host + MachineConfig for persistence |
| CDI StorageProfile incomplete warning | CDI doesn't know StorageClass capabilities | Patch StorageProfile with claimPropertySets |
| OpenEBS uses wrong thin pool | Auto-creates lvmvg_thinpool instead of manual pool |
Check lvs after first PVC and use auto-created pool name in StorageClass |