Complementos de volumen para el almacenamiento de Kubernetes: de Flexvolume a CSI

Complementos de volumen para el almacenamiento de Kubernetes: de Flexvolume a CSI

Cuando Kubernetes todavía era v1.0.0, había complementos de volumen. Eran necesarios para conectar sistemas a Kubernetes para almacenar datos de contenedores persistentes (permanentes). Su número era pequeño y entre los primeros se encontraban proveedores de almacenamiento como GCE PD, Ceph, AWS EBS y otros.

Los complementos se entregaron junto con Kubernetes, razón por la cual recibieron su nombre: in-tree. Sin embargo, para muchos, el conjunto existente de complementos de este tipo resultó insuficiente. Los artesanos agregaron complementos simples al núcleo de Kubernetes mediante parches, después de lo cual ensamblaron su propio Kubernetes y lo instalaron en sus servidores. Pero con el tiempo, los desarrolladores de Kubernetes se dieron cuenta de que pez el problema no se puede resolver. La gente necesita caña de pescar. Y en el lanzamiento de Kubernetes v1.2.0 apareció...

Complemento Flexvolume: caña de pescar mínima

Los desarrolladores de Kubernetes crearon el complemento FlexVolume, que era un marco lógico de variables y métodos para trabajar con controladores Flexvolume implementado por desarrolladores externos.

Detengámonos y echemos un vistazo más de cerca a qué es el controlador FlexVolume. Esto es cierto Archivo ejecutable (archivo binario, secuencia de comandos Python, secuencia de comandos Bash, etc.), que, cuando se ejecuta, toma argumentos de la línea de comando como entrada y devuelve un mensaje con campos conocidos previamente en formato JSON. Por convención, el primer argumento de la línea de comando es siempre un método y los argumentos restantes son sus parámetros.

Complementos de volumen para el almacenamiento de Kubernetes: de Flexvolume a CSI
Diagrama de conexión para recursos compartidos CIFS en OpenShift. Controlador Flexvolume: justo en el centro

Conjunto mínimo de métodos. Se ve así:

flexvolume_driver mount # отвечает за присоединение тома к pod'у
# Формат возвращаемого сообщения:
{
  "status": "Success"/"Failure"/"Not supported",
  "message": "По какой причине был возвращен именно такой статус",
}

flexvolume_driver unmount # отвечает за отсоединение тома от pod'а
# Формат возвращаемого сообщения:
{
  "status": "Success"/"Failure"/"Not supported",
  "message": "По какой причине был возвращен именно такой статус",
}

flexvolume_driver init # отвечает за инициализацию плагина
# Формат возвращаемого сообщения:
{
  "status": "Success"/"Failure"/"Not supported",
  "message": "По какой причине был возвращен именно такой статус",
  // Определяет, использует ли драйвер методы attach/deatach
  "capabilities":{"attach": True/False}
}

Usando métodos attach и detach definirá el escenario en el que kubelet actuará en el futuro al llamar al conductor. También hay métodos especiales. expandvolume и expandfs, que son responsables de cambiar dinámicamente el tamaño del volumen.

Como ejemplo de los cambios que añade el método expandvolume, y con él la capacidad de cambiar el tamaño de los volúmenes en tiempo real, puede familiarizarse con nuestra solicitud de extracción en Operador Ceph Torre.

Y aquí hay un ejemplo de la implementación del controlador Flexvolume para trabajar con NFS:

usage() {
    err "Invalid usage. Usage: "
    err "t$0 init"
    err "t$0 mount <mount dir> <json params>"
    err "t$0 unmount <mount dir>"
    exit 1
}

err() {
    echo -ne $* 1>&2
}

log() {
    echo -ne $* >&1
}

ismounted() {
    MOUNT=`findmnt -n ${MNTPATH} 2>/dev/null | cut -d' ' -f1`
    if [ "${MOUNT}" == "${MNTPATH}" ]; then
        echo "1"
    else
        echo "0"
    fi
}

domount() {
    MNTPATH=$1

    NFS_SERVER=$(echo $2 | jq -r '.server')
    SHARE=$(echo $2 | jq -r '.share')

    if [ $(ismounted) -eq 1 ] ; then
        log '{"status": "Success"}'
        exit 0
    fi

    mkdir -p ${MNTPATH} &> /dev/null

    mount -t nfs ${NFS_SERVER}:/${SHARE} ${MNTPATH} &> /dev/null
    if [ $? -ne 0 ]; then
        err "{ "status": "Failure", "message": "Failed to mount ${NFS_SERVER}:${SHARE} at ${MNTPATH}"}"
        exit 1
    fi
    log '{"status": "Success"}'
    exit 0
}

unmount() {
    MNTPATH=$1
    if [ $(ismounted) -eq 0 ] ; then
        log '{"status": "Success"}'
        exit 0
    fi

    umount ${MNTPATH} &> /dev/null
    if [ $? -ne 0 ]; then
        err "{ "status": "Failed", "message": "Failed to unmount volume at ${MNTPATH}"}"
        exit 1
    fi

    log '{"status": "Success"}'
    exit 0
}

op=$1

if [ "$op" = "init" ]; then
    log '{"status": "Success", "capabilities": {"attach": false}}'
    exit 0
fi

if [ $# -lt 2 ]; then
    usage
fi

shift

case "$op" in
    mount)
        domount $*
        ;;
    unmount)
        unmount $*
        ;;
    *)
        log '{"status": "Not supported"}'
        exit 0
esac

exit 1

Entonces, después de preparar el archivo ejecutable real, debe cargar el controlador en el clúster de Kubernetes. El controlador debe ubicarse en cada nodo del clúster según una ruta predeterminada. Por defecto estaba seleccionado:

/usr/libexec/kubernetes/kubelet-plugins/volume/exec/имя_поставщика_хранилища~имя_драйвера/

... pero al utilizar diferentes distribuciones de Kubernetes (OpenShift, Rancher...) la ruta puede ser diferente.

Problemas de volumen flexible: ¿cómo lanzar correctamente una caña de pescar?

Cargar el controlador Flexvolume en los nodos del clúster resultó ser una tarea nada trivial. Habiendo realizado la operación manualmente una vez, es fácil encontrarse con una situación en la que aparecen nuevos nodos en el clúster: debido a la adición de un nuevo nodo, escalado horizontal automático o, lo que es peor, reemplazo de un nodo debido a un mal funcionamiento. En este caso, se debe trabajar con el almacenamiento en estos nodos. imposible, hasta que aún les agregue manualmente el controlador Flexvolume.

La solución a este problema fue una de las primitivas de Kubernetes: DaemonSet. Cuando aparece un nuevo nodo en el clúster, automáticamente contiene un pod de nuestro DaemonSet, al que se adjunta un volumen local a lo largo de la ruta para encontrar los controladores Flexvolume. Tras una creación exitosa, el pod copia en el disco los archivos necesarios para que el controlador funcione.

A continuación se muestra un ejemplo de un DaemonSet de este tipo para diseñar un complemento Flexvolume:

apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
  name: flex-set
spec:
  template:
    metadata:
      name: flex-deploy
      labels:
        app: flex-deploy
    spec:
      containers:
        - image: <deployment_image>
          name: flex-deploy
          securityContext:
              privileged: true
          volumeMounts:
            - mountPath: /flexmnt
              name: flexvolume-mount
      volumes:
        - name: flexvolume-mount
          hostPath:
            path: <host_driver_directory>

... y un ejemplo de un script Bash para diseñar el controlador Flexvolume:

#!/bin/sh

set -o errexit
set -o pipefail

VENDOR=k8s.io
DRIVER=nfs

driver_dir=$VENDOR${VENDOR:+"~"}${DRIVER}
if [ ! -d "/flexmnt/$driver_dir" ]; then
  mkdir "/flexmnt/$driver_dir"
fi

cp "/$DRIVER" "/flexmnt/$driver_dir/.$DRIVER"
mv -f "/flexmnt/$driver_dir/.$DRIVER" "/flexmnt/$driver_dir/$DRIVER"

while : ; do
  sleep 3600
done

Es importante no olvidar que la operación de copia no es atómico. Existe una alta probabilidad de que kubelet comience a utilizar el controlador antes de que se complete el proceso de aprovisionamiento, lo que provocará que el sistema falle. El enfoque correcto es copiar primero los archivos del controlador con un nombre diferente y luego utilizar una operación de cambio de nombre atómico.

Complementos de volumen para el almacenamiento de Kubernetes: de Flexvolume a CSI
Diagrama de trabajo con Ceph en el operador Rook: el controlador Flexvolume en el diagrama está ubicado dentro del agente Rook

El siguiente problema al utilizar controladores Flexvolume es que para la mayor parte del almacenamiento en un nodo del clúster se debe instalar el software necesario para ello (por ejemplo, el paquete ceph-common para Ceph). Inicialmente, el complemento Flexvolume no fue diseñado para implementar sistemas tan complejos.

Se puede ver una solución original a este problema en la implementación del controlador Flexvolume del operador Rook:

El controlador en sí está diseñado como un cliente RPC. El conector IPC para la comunicación se encuentra en el mismo directorio que el propio controlador. Recordamos que para copiar archivos de controladores sería bueno utilizar DaemonSet, que conecta el directorio con el controlador a modo de volumen. Después de copiar los archivos necesarios del controlador rook, este pod no muere, sino que se conecta al socket IPC a través del volumen adjunto como un servidor RPC completo. El paquete ceph-common ya está instalado dentro del contenedor del pod. El socket IPC garantiza que el kubelet se comunicará exactamente con el pod que está ubicado en el mismo nodo. ¡Todo lo ingenioso es simple!..

¡Adiós, nuestros cariñosos... complementos en el árbol!

Los desarrolladores de Kubernetes descubrieron que la cantidad de complementos para almacenamiento dentro del núcleo es veinte. Y un cambio en cada uno de ellos, de una forma u otra, pasa por el ciclo completo de lanzamiento de Kubernetes.

Resulta que para utilizar la nueva versión del complemento de almacenamiento, necesitas actualizar todo el cluster. Además de esto, te sorprenderá que la nueva versión de Kubernetes de repente se vuelva incompatible con el kernel de Linux que estás utilizando... Así que te limpias las lágrimas y, apretando los dientes, coordinas con tu dirección y con los usuarios el momento de actualice el kernel de Linux y el clúster de Kubernetes. Con posibles paradas en la prestación de los servicios.

La situación es más que cómica, ¿no crees? Quedó claro para toda la comunidad que el enfoque no estaba funcionando. Por decisión deliberada, los desarrolladores de Kubernetes anuncian que el kernel ya no aceptará nuevos complementos para trabajar con almacenamiento. Además, como ya sabemos, se identificaron una serie de deficiencias en la implementación del complemento Flexvolume...

Se recurrió al último complemento agregado para volúmenes en Kubernetes, CSI, para solucionar el problema con el almacenamiento de datos persistentes de una vez por todas. Su versión alfa, más conocida como Out-of-Tree CSI Volume Plugins, se anunció en el lanzamiento. Kubernetes 1.9.

¡Interfaz de almacenamiento de contenedores o caña giratoria CSI 3000!

En primer lugar, me gustaría señalar que CSI no es solo un complemento de volumen, sino un verdadero стандарт sobre la creación de componentes personalizados para trabajar con almacenes de datos. Se suponía que los sistemas de orquestación de contenedores como Kubernetes y Mesos debían "aprender" a trabajar con componentes implementados de acuerdo con este estándar. Y ahora ya aprendí Kubernetes.

¿Cuál es la estructura del complemento CSI en Kubernetes? El complemento CSI funciona con controladores especiales (controladores CSI) escrito por desarrolladores externos. Un controlador CSI en Kubernetes debe constar como mínimo de dos componentes (pods):

  • Control — gestiona almacenamientos persistentes externos. Se implementa como un servidor gRPC, para lo cual se utiliza la primitiva. StatefulSet.
  • Nodo — es responsable de montar el almacenamiento persistente en los nodos del clúster. También está implementado como un servidor gRPC, pero utiliza la primitiva DaemonSet.

Complementos de volumen para el almacenamiento de Kubernetes: de Flexvolume a CSI
Cómo funciona el complemento CSI en Kubernetes

Puede conocer algunos otros detalles del trabajo de CSI, por ejemplo, en el artículo "Entendiendo el C.S.I." traducción de la cual publicamos hace un año.

Las ventajas de tal implementación.

  • Para cosas básicas como registrar un controlador para un nodo, los desarrolladores de Kubernetes implementaron un conjunto de contenedores. Ya no necesita generar usted mismo una respuesta JSON con capacidades, como se hizo con el complemento Flexvolume.
  • En lugar de "deslizar" archivos ejecutables en los nodos, ahora cargamos pods en el clúster. Esto es lo que esperamos inicialmente de Kubernetes: todos los procesos ocurren dentro de contenedores implementados utilizando primitivas de Kubernetes.
  • Ya no es necesario desarrollar un servidor RPC y un cliente RPC para implementar controladores complejos. El cliente fue implementado por nosotros por desarrolladores de Kubernetes.
  • Pasar argumentos para trabajar a través del protocolo gRPC es mucho más conveniente, flexible y confiable que pasarlos a través de argumentos de línea de comando. Para comprender cómo agregar soporte para métricas de uso de volumen a CSI agregando un método gRPC estandarizado, puede leer: nuestra solicitud de extracción para el controlador vsphere-csi.
  • La comunicación se produce a través de sockets IPC, para no confundir si el kubelet envió la solicitud al pod correcto.

¿Esta lista te recuerda algo? Las ventajas de CSI son resolviendo esos mismos problemas, que no se tuvieron en cuenta al desarrollar el complemento Flexvolume.

Hallazgos

CSI como estándar para implementar complementos personalizados para interactuar con almacenes de datos fue recibido muy calurosamente por la comunidad. Además, debido a sus ventajas y versatilidad, los controladores CSI se crean incluso para sistemas de almacenamiento como Ceph o AWS EBS, cuyos complementos para trabajar se agregaron en la primera versión de Kubernetes.

A principios de 2019, complementos en el árbol han sido declarados obsoletos. Planeamos seguir admitiendo el complemento Flexvolume, pero no desarrollaremos nuevas funciones para él.

Nosotros mismos ya tenemos experiencia en el uso de ceph-csi, vsphere-csi y estamos listos para agregarlos a esta lista. Hasta ahora, CSI está haciendo frente a las tareas que se le asignan con éxito, pero esperaremos y veremos.

¡No olvides que todo lo nuevo es un buen replanteamiento de lo viejo!

PS

Lea también en nuestro blog:

Fuente: habr.com

Añadir un comentario