Longhorn: Almacenamiento Distribuido para Kubernetes

Longhorn es un sistema de almacenamiento de bloques distribuido nativo de Kubernetes desarrollado por Rancher que convierte los discos locales de los nodos en volúmenes replicados de alta disponibilidad. Con Longhorn puedes crear volúmenes persistentes con réplicas automáticas, instantáneas y backups a S3, todo gestionado mediante una interfaz web intuitiva directamente desde el clúster.

Requisitos Previos

  • Kubernetes 1.21+ con al menos 3 nodos worker
  • Mínimo 1 disco dedicado por nodo (SSD recomendado para producción)
  • open-iscsi instalado en todos los nodos
  • nfs-common instalado en todos los nodos
  • cryptsetup si se va a usar cifrado de volúmenes
  • Mínimo 4 GB de RAM y 2 vCPUs por nodo worker
# Instalar dependencias en todos los nodos worker (Ubuntu/Debian)
apt update
apt install -y open-iscsi nfs-common cryptsetup

# Habilitar el servicio iSCSI
systemctl enable iscsid && systemctl start iscsid

# Para CentOS/Rocky Linux
yum install -y iscsi-initiator-utils nfs-utils cryptsetup
systemctl enable iscsid && systemctl start iscsid

# Verificar que los nodos cumplen los requisitos con el script oficial
curl -sSfL https://raw.githubusercontent.com/longhorn/longhorn/v1.6.0/scripts/environment_check.sh | bash

Instalación de Longhorn

# Instalación mediante Helm (recomendado)
helm repo add longhorn https://charts.longhorn.io
helm repo update

# Crear el namespace
kubectl create namespace longhorn-system

# Instalar Longhorn con valores por defecto
helm install longhorn longhorn/longhorn \
  --namespace longhorn-system \
  --set defaultSettings.defaultReplicaCount=3

# Verificar la instalación
kubectl get pods -n longhorn-system

# Esperar a que todos los pods estén en estado Running
kubectl wait --for=condition=ready pod \
  -l app=longhorn-manager \
  -n longhorn-system \
  --timeout=300s

Instalación con valores personalizados para producción:

cat > longhorn-values.yaml << 'EOF'
defaultSettings:
  # Número de réplicas por volumen (mínimo 3 para HA)
  defaultReplicaCount: 3
  
  # Porcentaje mínimo de espacio libre antes de marcar el nodo como no apto
  storageMinimalAvailablePercentage: 25
  
  # Porcentaje de espacio reservado por Longhorn del disco total
  storageOverProvisioningPercentage: 200
  
  # Habilitar backups automáticos de instantáneas
  recurringJobSelector: '[{"name":"backup","isGroup":true}]'
  
  # Endpoint de backup (S3)
  backupTarget: "s3://mi-bucket-longhorn@us-east-1/"
  backupTargetCredentialSecret: "longhorn-s3-secret"
  
  # Garantizar que las réplicas están en nodos diferentes
  replicaSoftAntiAffinity: false
  
  # Compresor de datos para backups
  backupCompressionMethod: lz4

persistence:
  defaultClass: true
  defaultFsType: ext4
  defaultClassReplicaCount: 3
  defaultDataLocality: best-effort

ingress:
  enabled: true
  ingressClassName: nginx
  host: longhorn.midominio.com
  tls: true
  tlsSecret: longhorn-tls
  annotations:
    nginx.ingress.kubernetes.io/auth-type: basic
    nginx.ingress.kubernetes.io/auth-secret: longhorn-auth

longhornManager:
  priorityClass: system-node-critical
EOF

helm install longhorn longhorn/longhorn \
  --namespace longhorn-system \
  --values longhorn-values.yaml

Configuración del StorageClass

# Ver el StorageClass creado por Longhorn
kubectl get storageclass

# Crear un StorageClass personalizado para SSDs de alto rendimiento
cat > storageclass-ssd.yaml << 'EOF'
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: longhorn-ssd
  annotations:
    storageclass.kubernetes.io/is-default-class: "false"
provisioner: driver.longhorn.io
allowVolumeExpansion: true
reclaimPolicy: Delete
volumeBindingMode: Immediate
parameters:
  # Número de réplicas para este StorageClass
  numberOfReplicas: "3"
  
  # Política de datos en caché (best-effort, strict-local, disabled)
  dataLocality: "best-effort"
  
  # Montar solo en el nodo donde vive la réplica primaria
  fromBackup: ""
  
  # Cifrado del volumen
  encrypted: "true"
  
  # Seleccionar solo nodos con el label ssd=true
  diskSelector: "ssd"
  nodeSelector: "storage=ssd"
EOF

kubectl apply -f storageclass-ssd.yaml

# Crear un PVC que use el StorageClass personalizado
cat > pvc-ejemplo.yaml << 'EOF'
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: datos-aplicacion
  namespace: produccion
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: longhorn-ssd
  resources:
    requests:
      storage: 50Gi
EOF

kubectl apply -f pvc-ejemplo.yaml
kubectl get pvc -n produccion

Backups a S3

# Crear el Secret con las credenciales de S3
kubectl create secret generic longhorn-s3-secret \
  --from-literal=AWS_ACCESS_KEY_ID=tu-access-key \
  --from-literal=AWS_SECRET_ACCESS_KEY=tu-secret-key \
  --from-literal=AWS_ENDPOINTS=https://s3.amazonaws.com \
  -n longhorn-system

# Configurar el target de backup en Longhorn
kubectl patch settings.longhorn.io backup-target \
  -n longhorn-system \
  --type merge \
  -p '{"value":"s3://mi-bucket-longhorn@us-east-1/"}'

kubectl patch settings.longhorn.io backup-target-credential-secret \
  -n longhorn-system \
  --type merge \
  -p '{"value":"longhorn-s3-secret"}'

# Crear un RecurringJob para backups automáticos diarios
cat > backup-job.yaml << 'EOF'
apiVersion: longhorn.io/v1beta2
kind: RecurringJob
metadata:
  name: backup-diario
  namespace: longhorn-system
spec:
  cron: "0 2 * * *"  # Todos los días a las 2:00 AM
  task: "backup"
  groups:
    - default
  retain: 14  # Mantener los últimos 14 backups
  concurrency: 2
  labels:
    backup-type: daily
EOF

kubectl apply -f backup-job.yaml

Instantáneas de Volúmenes

# Crear una instantánea manualmente de un volumen
cat > snapshot.yaml << 'EOF'
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
  name: snapshot-datos-app-$(date +%Y%m%d)
  namespace: produccion
spec:
  volumeSnapshotClassName: longhorn
  source:
    persistentVolumeClaimName: datos-aplicacion
EOF

kubectl apply -f snapshot.yaml

# Listar las instantáneas
kubectl get volumesnapshots -n produccion

# Restaurar un PVC desde una instantánea
cat > pvc-desde-snapshot.yaml << 'EOF'
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: datos-restaurados
  namespace: produccion
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: longhorn-ssd
  resources:
    requests:
      storage: 50Gi
  dataSource:
    name: snapshot-datos-app-20240101
    kind: VolumeSnapshot
    apiGroup: snapshot.storage.k8s.io
EOF

kubectl apply -f pvc-desde-snapshot.yaml

Recuperación ante Desastres

# Restaurar un volumen desde backup en S3 (en un clúster nuevo o diferente)
cat > pvc-desde-backup.yaml << 'EOF'
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: datos-recuperados
  namespace: produccion
  annotations:
    # URL del backup en S3
    longhorn.io/restore-volume-name: "backup-nombre-del-volumen"
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: longhorn
  resources:
    requests:
      storage: 50Gi
  dataSource:
    name: mi-backup
    kind: LonghornBackup
    apiGroup: longhorn.io
EOF

# Ver los backups disponibles en S3
kubectl get backups -n longhorn-system

# Ver el estado de un backup específico
kubectl describe backup <backup-name> -n longhorn-system

# Expandir un volumen en uso (sin reiniciar el pod)
kubectl patch pvc datos-aplicacion \
  -n produccion \
  --type merge \
  -p '{"spec":{"resources":{"requests":{"storage":"100Gi"}}}}'

Monitoreo con Prometheus

# Longhorn expone métricas en el puerto 9500
# Crear un ServiceMonitor para Prometheus Operator
cat > longhorn-servicemonitor.yaml << 'EOF'
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: longhorn
  namespace: longhorn-system
  labels:
    app: longhorn-manager
spec:
  selector:
    matchLabels:
      app: longhorn-manager
  endpoints:
    - port: manager
      path: /metrics
      interval: 30s
EOF

kubectl apply -f longhorn-servicemonitor.yaml

# Verificar que las métricas están disponibles
kubectl port-forward -n longhorn-system svc/longhorn-backend 9500:9500 &
curl -s http://localhost:9500/metrics | grep longhorn_volume

# Métricas importantes de Longhorn:
# longhorn_volume_capacity_bytes       - Capacidad total de los volúmenes
# longhorn_volume_usage_bytes          - Uso actual
# longhorn_volume_state                - Estado del volumen (1=healthy)
# longhorn_node_storage_capacity_bytes - Capacidad del almacenamiento del nodo

Solución de Problemas

Volúmenes en estado Degraded:

# Ver el estado de los volúmenes
kubectl get volumes -n longhorn-system

# Ver qué réplicas están caídas
kubectl get replicas -n longhorn-system | grep -v Running

# Forzar la reconstrucción de una réplica
kubectl edit volume <volume-name> -n longhorn-system
# Cambiar spec.numberOfReplicas y volver al valor original

# Ver logs del manager de Longhorn
kubectl logs -n longhorn-system -l app=longhorn-manager -f

Nodo en estado de mantenimiento:

# Verificar el estado de los nodos en Longhorn
kubectl get nodes.longhorn.io -n longhorn-system

# Habilitar un nodo que está deshabilitado para scheduling
kubectl edit nodes.longhorn.io <node-name> -n longhorn-system
# Cambiar spec.allowScheduling: true

Backup fallido:

# Ver los backups y su estado
kubectl get backups -n longhorn-system -o wide

# Ver los logs del backup controller
kubectl logs -n longhorn-system -l longhorn.io/component=backup-controller

Conclusión

Longhorn transforma el almacenamiento local de los nodos Kubernetes en un sistema de almacenamiento de bloques distribuido, replicado y con backup automático, sin necesidad de infraestructura de almacenamiento externa. Su interfaz web, la integración nativa con Kubernetes y las capacidades de backup a S3 lo convierten en una solución de almacenamiento completa especialmente adecuada para clústeres en bare metal o VPS donde no hay almacenamiento gestionado disponible.