Longhorn es una solución de almacenamiento distribuido de bloques para Kubernetes. Recientemente ha sido incluido en la incubadora de la CNCF.

Longhorn proporciona métricas para Prometheus y es el complemento perfecto para proporcionar almacenamiento a las aplicaciones desplegadas sobre Kubernetes.

En esta entrada automatizamos las instrucciones oficiales de despliegue usando Helm para desplegarlo sobre Kubernetes.

La última versión del script se puede encontrar en el repositorio de GitHub vagrant/k3s-ubuntu-cluster/

TL;DR;

El script es:

#!/usr/bin/env bash

function getKubeconfig {
    scriptDir="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
    if test -f "kubeconfig"
    then
        echo "Using $scriptDir/kubeconfig"
        export KUBECONFIG=$scriptDir/kubeconfig
    elif test -f $HOME/.kube/config
    then
        echo "Using default kubeconfig at $HOME/.kube/config"
    elif [[ -z "$KUBECONFIG" ]]
    then
        echo "ERROR - Unable to find a valid kubeconfig"
        exit 1
    else
        echo "Using \$KUBECONFIG=$KUBECONFIG"
    fi
}


function installHelmChart {
    helmChart="$1"
    helmRepoChart="$2"
    chartNamespace="$3"

    checkRelease=$(helm status $helmChart --namespace $chartNamespace 2>/dev/null| grep -i status | awk '{ print $2 }')

    if [ "$checkRelease" != "deployed" ]
    then
        echo "...Installing longhorn"
        helm install $helmChart $helmRepoChart --namespace $chartNamespace --create-namespace
    else
        echo "... $helmChart is already installed in the namespace $chartNamespace"
    fi
}

function setDefaultStorageClass {
    defaultStorageClass="$1"
    storageClassList=$(kubectl get storageclass -o name | awk -F '/' '{print $2}')

    for storageclass in $storageClassList
    do
        if [ "$storageclass" = "$defaultStorageClass" ]
        then
            echo "Set default storageClass for $storageclass"
            kubectl patch storageclass $storageclass -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'
        else
            echo "Removing default storageClass for $storageclass"
            kubectl patch storageclass $storageclass -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"false"}}}'
        fi
    done
}

function main {
    getKubeconfig

    # Those commands are idempotent
    helm repo add longhorn https://charts.longhorn.io
    helm repo update
    
    installHelmChart "longhorn" "longhorn/longhorn" "longhorn-system"
    setDefaultStorageClass "longhorn"
}

main

Algo de contexto

El script de instalación de Longhorn forma parte del proceso de automatización del despliegue y configuración de un clúster de Kubernetes usando Vagrant para demos/laboratorios/pruebas.

El proceso empieza desplegando varias máquinas virtuales mediante Vagrant; a continuación se instala Kubernetes (k3s) usando k3sup.

k3sup puede generar un fichero de configuración kubeconfig específico para el clúster desplegado o fusionar la configuración con un fichero kubeconfig existente.

El script consta de tres funciones (bueno, en realidad 4, pero la última es main que podría omitirse):

getKubeconfig

En mi caso, al tratarse en general de clústers temporales, uso el fichero kubeconfig creado por k3sup mediante la variable $KUBECONFIG.

Mediante la función getKubeconfig el script trata de localizar un fichero kubeconfig que Helm pueda usar para conectar con el clúster.

Primero, busca en la carpeta en la que se encuentra el script. De nuevo, esto es porque el script y k3sup se encuentran en la misma carpeta.

Si no se encuentra el fichero local kubeconfig, se intenta usar el fichero kubeconfig de la carpeta $HOME del usuario; el script usa el contexto actual (current context) definido en el fichero.

Finalmente, se examina la variable de entorno $KUBECONFIG y si no esta definida, salimos del script (no podremos conectar al clúster con Helm).

isHelmAppInstalled

Si se intenta instalar una chart que ya ha sido deplegada, Helm genera un error.

Para evitarlo, primero comprobamos si Longhorn está desplegado en el clúster analizando la salida del comando helm status:

helm status -n longhorn-system longhorn
NAME: longhorn
LAST DEPLOYED: Sat Nov 13 19:33:28 2021
NAMESPACE: longhorn-system
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
Longhorn is now installed on the cluster!

Please wait a few minutes for other Longhorn components such as CSI deployments, Engine Images, and Instance Managers to be initialized.

Visit our documentation at https://longhorn.io/docs/

Filtrando el resultado del comando, buscamos el valor del campo STATUS para verificar si la chart está instalada o no.

setDefaultStorageClass

Aunque la instalación de Longhorn establece la storageClass longhorn como storageClass por defecto en el clúster, no se modifican las anotaciones de otras storageClass presentes.

Esto puede provocar problemas, como indica la documentación oficial de Kubernetes:

If two or more of them are marked as default, a PersistentVolumeClaim without storageClassName explicitly specified cannot be created.

El resultado de la instalación de Longhorn es que varias storageClass pueden estar marcadas como storageClass por defecto del clúster. Abrí el issue Múltiples storageClass por defecto al instalar Longhorn (que ya está cerrado):

$ kubectl get storageclass
NAME                   PROVISIONER             RECLAIMPOLICY   VOLUMEBINDINGMODE      ALLOWVOLUMEEXPANSION   AGE
local-path (default)   rancher.io/local-path   Delete          WaitForFirstConsumer   false                  48d
longhorn (default)     driver.longhorn.io      Delete          Immediate              true                   72m

La solución es obtener la lista de storageClass definidas en el clúster, marcar sólo una como storageClass por defecto (en nuestro caso, longhorn) y para el resto, marcar como false la anotación is-default-class.

Resumen

El script obtiene la configuración del fichero kubeconfig (o de la variable de entorno $KUBECONFIG) para que Helm pueda conectar con el clúster y desplegar Longhorn.

Añadimos el repositorio de la chart de Longhorn, actualizamos y finalmente nos aseguramos que sólo hay una storageClass por defecto en el clúster.

Así podemos automatizar la instalación de Longhorn en los clústers de Kubernetes.