OpenEBS es un proyecto de la CNCF que proporciona almacenamiento persistente para Kubernetes ejecutándose como pods dentro del propio clúster. Desde la versión 4 se organiza en dos familias: Local PV (Hostpath, LVM y ZFS), que guarda los datos en el disco del nodo donde corre el pod, y Replicated PV Mayastor, que replica cada volumen entre varios nodos y lo sirve por NVMe over TCP. En este tutorial instalarás OpenEBS con Helm en nodos Ubuntu 24.04, crearás volúmenes Local PV Hostpath y después activarás Mayastor para tener volúmenes replicados.

Requisitos previos

Para seguir esta guía necesitas:

  • Un clúster Kubernetes 1.23 o superior con nodos Ubuntu 24.04 LTS, por ejemplo sobre VPS de CubePath. Para Mayastor, al menos tres nodos worker x86_64.
  • kubectl con permisos de administrador del clúster y Helm 3 en tu equipo.
  • Acceso SSH con un usuario con sudo a los nodos.
  • Solo para Mayastor: en cada nodo de almacenamiento, 2 núcleos de CPU libres para el motor de E/S, al menos 4 GB de RAM (de los que 2 GiB se reservan como hugepages) y un disco o partición vacío dedicado, sin sistema de archivos ni particiones montadas.

Paso 1: Instalar OpenEBS con Local PV Hostpath

Añade el repositorio del chart oficial:

helm repo add openebs https://openebs.github.io/openebs
helm repo update

Instala OpenEBS empezando solo con los motores locales. Mayastor se activa en el paso 4, cuando los nodos estén preparados; si lo activas sin hugepages sus pods no arrancan:

helm install openebs openebs/openebs \
  --namespace openebs \
  --create-namespace \
  --set engines.replicated.mayastor.enabled=false

Comprueba que los pods están en Running:

kubectl -n openebs get pods

Deberías ver el provisionador openebs-localpv-provisioner y, si no los has desactivado, los controladores y pods de nodo de LVM y ZFS Local PV, todos en Running.

El chart crea la StorageClass openebs-hostpath:

kubectl get storageclass
NAME               PROVISIONER        RECLAIMPOLICY   VOLUMEBINDINGMODE      ALLOWVOLUMEEXPANSION   AGE
openebs-hostpath   openebs.io/local   Delete          WaitForFirstConsumer   false                  70s

Los drivers LVM y ZFS solo se usan si creas StorageClasses para ellos, así que puedes dejarlos instalados. Si prefieres no tenerlos, añade --set engines.local.lvm.enabled=false --set engines.local.zfs.enabled=false al comando de instalación.

Paso 2: Crear un volumen Local PV Hostpath

Local PV Hostpath crea un directorio por volumen en el nodo, por defecto bajo /var/openebs/local. Ofrece el rendimiento del disco local sin sobrecarga, pero los datos no se replican: si el nodo se pierde, el volumen también. Es adecuado para bases de datos que ya replican por su cuenta (PostgreSQL con réplicas, Kafka, Elasticsearch) o para cachés.

Crea un namespace de pruebas:

kubectl create namespace demo-openebs

Crea un manifiesto con un PVC y un pod que lo usa:

nano hostpath-demo.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: datos-locales
  namespace: demo-openebs
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: openebs-hostpath
  resources:
    requests:
      storage: 5Gi
---
apiVersion: v1
kind: Pod
metadata:
  name: escritor
  namespace: demo-openebs
spec:
  containers:
    - name: app
      image: busybox:1.36
      command: ["sh", "-c", "date >> /datos/log.txt && sleep 3600"]
      volumeMounts:
        - name: datos
          mountPath: /datos
  volumes:
    - name: datos
      persistentVolumeClaim:
        claimName: datos-locales
kubectl apply -f hostpath-demo.yaml
kubectl -n demo-openebs wait --for=condition=Ready pod/escritor --timeout=120s

La StorageClass usa WaitForFirstConsumer, así que el PV no se crea hasta que el pod se programa en un nodo. Comprueba que el PVC está enlazado y en qué nodo está el pod:

kubectl -n demo-openebs get pvc datos-locales
kubectl -n demo-openebs get pod escritor -o wide
NAME            STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS       AGE
datos-locales   Bound    pvc-0b7c2f3e-91a4-4d0e-8f7a-5e2c6d1b3a90   5Gi        RWO            openebs-hostpath   30s

En ese nodo, el directorio del volumen contiene el archivo que escribió el pod:

sudo ls /var/openebs/local/

StorageClass Hostpath en otro directorio

Si tienes un disco de datos montado, por ejemplo en /mnt/datos, crea una StorageClass que guarde ahí los volúmenes. La ruta se define en la anotación cas.openebs.io/config:

nano storageclass-hostpath-datos.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: hostpath-datos
  annotations:
    openebs.io/cas-type: local
    cas.openebs.io/config: |
      - name: StorageType
        value: hostpath
      - name: BasePath
        value: /mnt/datos
provisioner: openebs.io/local
reclaimPolicy: Delete
volumeBindingMode: WaitForFirstConsumer
kubectl apply -f storageclass-hostpath-datos.yaml

El directorio indicado en BasePath debe existir en todos los nodos donde puedan programarse pods que usen esta clase.

Paso 3: Preparar los nodos para Mayastor

Mayastor necesita hugepages de 2 MiB, el módulo del kernel nvme_tcp y una etiqueta que indique en qué nodos debe ejecutarse su motor de E/S. Ejecuta en cada nodo de almacenamiento:

echo 'vm.nr_hugepages = 1024' | sudo tee /etc/sysctl.d/20-openebs-hugepages.conf
sudo sysctl --system

Carga el módulo nvme_tcp y déjalo configurado para el arranque:

sudo modprobe nvme_tcp
echo nvme_tcp | sudo tee /etc/modules-load.d/nvme_tcp.conf

Comprueba ambos:

grep HugePages_Total /proc/meminfo
lsmod | grep nvme_tcp
HugePages_Total:    1024
nvme_tcp               53248  0

El kubelet solo detecta las hugepages al arrancar, así que reinícialo en cada nodo (en k3s el servicio es k3s o k3s-agent, y en RKE2 rke2-server o rke2-agent):

sudo systemctl restart kubelet

Desde tu equipo, etiqueta los nodos que ejecutarán Mayastor y verifica que el kubelet anuncia las hugepages:

kubectl label node worker-1 worker-2 worker-3 openebs.io/engine=mayastor
kubectl get node worker-1 -o jsonpath='{.status.allocatable.hugepages-2Mi}{"\n"}'
2Gi

Paso 4: Activar Mayastor

Actualiza la release existente para activar el motor replicado, conservando el resto de valores:

helm upgrade openebs openebs/openebs \
  --namespace openebs \
  --reuse-values \
  --set engines.replicated.mayastor.enabled=true

Mayastor despliega bastantes componentes (API REST, agentes, etcd, el io-engine en cada nodo etiquetado y el driver CSI). Espera unos minutos y comprueba que los pods io-engine están listos:

kubectl -n openebs get pods -o wide | grep io-engine

Todos deben estar en Running con todos sus contenedores listos, uno por nodo etiquetado.

Paso 5: Crear un DiskPool en cada nodo

Mayastor no usa directorios: gestiona dispositivos de bloque completos agrupados en DiskPool, uno por nodo. Localiza el identificador estable del disco vacío en cada nodo, ya que los nombres como /dev/sdb pueden cambiar entre reinicios:

ls -l /dev/disk/by-id/

Crea un manifiesto con un pool por nodo, sustituyendo los identificadores de ejemplo por los tuyos:

nano diskpools.yaml
apiVersion: openebs.io/v1beta2
kind: DiskPool
metadata:
  name: pool-worker-1
  namespace: openebs
spec:
  node: worker-1
  disks: ["aio:///dev/disk/by-id/tu_disco_worker_1"]
---
apiVersion: openebs.io/v1beta2
kind: DiskPool
metadata:
  name: pool-worker-2
  namespace: openebs
spec:
  node: worker-2
  disks: ["aio:///dev/disk/by-id/tu_disco_worker_2"]
---
apiVersion: openebs.io/v1beta2
kind: DiskPool
metadata:
  name: pool-worker-3
  namespace: openebs
spec:
  node: worker-3
  disks: ["aio:///dev/disk/by-id/tu_disco_worker_3"]
kubectl apply -f diskpools.yaml
kubectl -n openebs get diskpools

Al cabo de unos segundos, los tres pools deben mostrar el estado Online y su capacidad total. Si alguno se queda en otro estado, revisa los eventos con kubectl -n openebs describe diskpool <nombre>.

Paso 6: Crear una StorageClass replicada y probarla

Crea una StorageClass con tres réplicas servidas por NVMe over TCP:

nano storageclass-mayastor-3r.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: mayastor-3r
provisioner: io.openebs.csi-mayastor
parameters:
  protocol: nvmf
  repl: "3"
allowVolumeExpansion: true
reclaimPolicy: Delete
volumeBindingMode: Immediate
kubectl apply -f storageclass-mayastor-3r.yaml

Crea un PVC de prueba y un pod que lo monte:

nano mayastor-demo.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: datos-replicados
  namespace: demo-openebs
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: mayastor-3r
  resources:
    requests:
      storage: 5Gi
---
apiVersion: v1
kind: Pod
metadata:
  name: escritor-replicado
  namespace: demo-openebs
spec:
  containers:
    - name: app
      image: busybox:1.36
      command: ["sh", "-c", "date >> /datos/log.txt && sleep 3600"]
      volumeMounts:
        - name: datos
          mountPath: /datos
  volumes:
    - name: datos
      persistentVolumeClaim:
        claimName: datos-replicados
kubectl apply -f mayastor-demo.yaml
kubectl -n demo-openebs wait --for=condition=Ready pod/escritor-replicado --timeout=180s
kubectl -n demo-openebs exec escritor-replicado -- cat /datos/log.txt
Thu Sep 25 10:42:17 UTC 2026

Comprueba que la capacidad usada ha aumentado en los tres pools, lo que indica que cada nodo tiene una réplica:

kubectl -n openebs get diskpools

Solución de problemas

PVC Hostpath en Pending. Es normal hasta que un pod lo usa, por el modo WaitForFirstConsumer. Si sigue así con el pod creado, revisa los eventos con kubectl -n demo-openebs describe pvc datos-locales y los logs del provisionador con kubectl -n openebs logs deploy/openebs-localpv-provisioner.

Los pods io-engine no arrancan o no se crean. Comprueba que el nodo tiene la etiqueta openebs.io/engine=mayastor, que allocatable muestra hugepages-2Mi: 2Gi y que has reiniciado el kubelet tras configurar las hugepages. Revisa los eventos con kubectl -n openebs describe pod <pod-io-engine>.

El pod con un volumen Mayastor no monta. El nodo donde corre el pod necesita el módulo nvme_tcp aunque no tenga pool. Cárgalo como en el paso 3 en todos los nodos worker.

DiskPool que no pasa a Online. Suele ser un disco con particiones o firmas de un sistema de archivos anterior, o una ruta by-id incorrecta. Verifica la ruta en el nodo y limpia el disco con sudo wipefs -a /dev/disk/by-id/<id> si estás seguro de que no contiene nada que necesites.

Conclusión

Has instalado OpenEBS con Local PV Hostpath para volúmenes locales rápidos y Mayastor para volúmenes replicados en tres nodos, y has comprobado ambos con un pod real. A partir de aquí puedes marcar mayastor-3r como StorageClass predeterminada, probar la tolerancia a fallos apagando uno de los nodos mientras el pod escribe, o configurar snapshots CSI con una VolumeSnapshotClass del driver io.openebs.csi-mayastor.