Complementos de volume para o almacenamento de Kubernetes: de Flexvolume a CSI

Complementos de volume para o almacenamento de Kubernetes: de Flexvolume a CSI

Cando Kubernetes aínda era v1.0.0, había complementos de volume. Eran necesarios para conectar sistemas a Kubernetes para almacenar datos de contedores persistentes (permanentes). O seu número era pequeno, e entre os primeiros estaban provedores de almacenamento como GCE PD, Ceph, AWS EBS e outros.

Os complementos entregáronse xunto con Kubernetes, polo que recibiron o seu nome: in-tree. Non obstante, para moitos, o conxunto existente destes complementos resultou ser insuficiente. Os artesáns engadiron complementos sinxelos ao núcleo de Kubernetes mediante parches, despois de que reuniron o seu propio Kubernetes e instalárono nos seus servidores. Pero co paso do tempo, os desenvolvedores de Kubernetes decatáronse diso peixe o problema non se pode resolver. A xente necesita vara de pesca. E no lanzamento de Kubernetes v1.2.0 apareceu...

Complemento Flexvolume: caña de pescar mínima

Os desenvolvedores de Kubernetes crearon o complemento FlexVolume, que era un marco lóxico de variables e métodos para traballar con controladores Flexvolume implementados por desenvolvedores de terceiros.

Detémonos e vexamos máis de cerca o que é o controlador FlexVolume. Isto é certo ficheiro executable (ficheiro binario, script Python, script Bash, etc.), que, cando se executa, toma argumentos da liña de comandos como entrada e devolve unha mensaxe con campos previamente coñecidos en formato JSON. Por convención, o primeiro argumento da liña de comandos é sempre un método, e os restantes son os seus parámetros.

Complementos de volume para o almacenamento de Kubernetes: de Flexvolume a CSI
Diagrama de conexión para recursos compartidos CIFS en OpenShift. Controlador Flexvolume: no centro

Conxunto mínimo de métodos parece 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á o escenario no que actuará o kubelet no futuro ao chamar ao condutor. Tamén hai métodos especiais expandvolume и expandfs, que se encargan de redimensionar dinámicamente o volume.

Como exemplo dos cambios que engade o método expandvolume, e con ela podes familiarizarte coa posibilidade de cambiar o tamaño dos volumes en tempo real a nosa solicitude de extracción en Rook Ceph Operator.

E aquí tes un exemplo da implementación do controlador Flexvolume para traballar 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

Entón, despois de preparar o ficheiro executable real, cómpre facelo cargue o controlador ao clúster de Kubernetes. O controlador debe estar situado en cada nodo do clúster segundo un camiño predeterminado. Por defecto seleccionouse:

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

... pero ao usar diferentes distribucións de Kubernetes (OpenShift, Rancher...) o camiño pode ser diferente.

Problemas de Flexvolume: como lanzar unha cana de pescar correctamente?

A carga do controlador Flexvolume nos nodos do clúster resultou ser unha tarefa non trivial. Unha vez realizada a operación manualmente unha vez, é doado atoparse cunha situación na que aparecen novos nodos no clúster: debido á adición dun novo nodo, ao escalado horizontal automático ou, o que é peor, á substitución dun nodo por un mal funcionamento. Neste caso, deberíase traballar co almacenamento nestes nodos é imposible, ata que aínda lles engadas manualmente o controlador Flexvolume.

A solución a este problema foi unha das primitivas de Kubernetes: DaemonSet. Cando aparece un novo nodo no clúster, contén automaticamente un pod do noso DaemonSet, ao que se une un volume local ao longo do camiño para atopar controladores Flexvolume. Tras a creación exitosa, o pod copia os ficheiros necesarios para que o controlador funcione no disco.

Aquí tes un exemplo de tal DaemonSet para crear 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>

... e un exemplo de script Bash para configurar o 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

É importante non esquecer que a operación de copia non é atómico. Hai unha gran probabilidade de que o kubelet comece a usar o controlador antes de que se complete o seu proceso de aprovisionamento, o que provoca que o sistema falle. O enfoque correcto é copiar primeiro os ficheiros do controlador cun nome diferente e despois usar unha operación de cambio de nome atómico.

Complementos de volume para o almacenamento de Kubernetes: de Flexvolume a CSI
Diagrama de traballo con Ceph no operador Rook: o controlador Flexvolume no diagrama está situado dentro do axente Rook

O seguinte problema cando se usan controladores Flexvolume é o da maior parte do almacenamento nun nodo do clúster debe estar instalado o software necesario para iso (por exemplo, o paquete ceph-common para Ceph). Inicialmente, o complemento Flexvolume non foi deseñado para implementar sistemas tan complexos.

Unha solución orixinal a este problema pódese ver na implementación do controlador Flexvolume do operador Rook:

O controlador en si está deseñado como un cliente RPC. O socket IPC para a comunicación está situado no mesmo directorio que o propio controlador. Lembramos que para copiar ficheiros de controladores sería bo usar DaemonSet, que conecta o directorio co controlador como volume. Despois de copiar os ficheiros do controlador de torre necesarios, este pod non morre, pero conéctase ao socket IPC a través do volume adxunto como un servidor RPC completo. O paquete ceph-common xa está instalado dentro do contenedor do pod. O socket IPC garante que o kubelet se comunicará exactamente co pod que se atopa no mesmo nodo. Todo xenial é sinxelo!...

Adeus, os nosos agarimosos... plugins na árbore!

Os desenvolvedores de Kubernetes descubriron que o número de complementos para almacenar no núcleo é de vinte. E un cambio en cada un deles, dun xeito ou doutro, pasa polo ciclo completo de lanzamento de Kubernetes.

Resulta que para usar a nova versión do complemento de almacenamento, cómpre actualizar todo o clúster. Ademais disto, podes sorprenderte de que a nova versión de Kubernetes sexa de súpeto incompatible co núcleo de Linux que estás a usar... Así que enxugas as bágoas e, apretando os dentes, coordinas coa túa dirección e cos usuarios o tempo para actualizar o núcleo de Linux e o clúster de Kubernetes. Con posibles paradas na prestación dos servizos.

A situación é máis que cómica, non cres? Quedou claro para toda a comunidade que o enfoque non funcionaba. Por decisión voluntaria, os desenvolvedores de Kubernetes anuncian que xa non se aceptarán novos complementos para traballar co almacenamento no núcleo. Ademais, como xa sabemos, identificáronse unha serie de deficiencias na implementación do complemento Flexvolume...

O último complemento engadido para volumes en Kubernetes, CSI, foi chamado para pechar o problema co almacenamento de datos persistente dunha vez por todas. A súa versión alfa, máis coñecida como Out-of-Tree CSI Volume Plugins, anunciouse no lanzamento Kubernetes 1.9.

Interfaz de almacenamento de contenedores ou caña giratoria CSI 3000!

En primeiro lugar, gustaríame sinalar que CSI non é só un complemento de volume, senón un real estándar sobre a creación de compoñentes personalizados para traballar con almacéns de datos. Os sistemas de orquestración de contedores como Kubernetes e Mesos debían "aprender" a traballar con compoñentes implementados segundo este estándar. E agora xa aprendín Kubernetes.

Cal é a estrutura do complemento CSI en Kubernetes? O complemento CSI funciona con controladores especiais (controladores CSI) escrito por desenvolvedores terceiros. Un controlador CSI en Kubernetes debería constar como mínimo de dous compoñentes (pods):

  • Controlador — xestiona almacenamentos persistentes externos. Implémentase como servidor gRPC, para o que se usa o primitivo StatefulSet.
  • Nodo — é responsable de montar o almacenamento persistente nos nodos do clúster. Tamén se implementa como servidor gRPC, pero usa o primitivo DaemonSet.

Complementos de volume para o almacenamento de Kubernetes: de Flexvolume a CSI
Como funciona o complemento CSI en Kubernetes

Podes coñecer algúns outros detalles do traballo de CSI, por exemplo, no artigo "Comprensión do C.S.I.' tradución do cal publicamos hai un ano.

As vantaxes desta implementación

  • Para cousas básicas como rexistrar un controlador para un nodo, os desenvolvedores de Kubernetes implementaron un conxunto de contedores. Xa non necesitas xerar unha resposta JSON con capacidades, como se fixo co complemento Flexvolume.
  • En lugar de "deslizar" ficheiros executables nos nodos, agora cargamos pods no clúster. Isto é o que esperamos inicialmente de Kubernetes: todos os procesos ocorren dentro de contedores despregados usando primitivos de Kubernetes.
  • Xa non precisa desenvolver un servidor RPC e un cliente RPC para implementar controladores complexos. O cliente foi implementado para nós polos desenvolvedores de Kubernetes.
  • Pasar argumentos para traballar sobre o protocolo gRPC é moito máis cómodo, flexible e fiable que pasalos a través de argumentos da liña de comandos. Para comprender como engadir compatibilidade coas métricas de uso de volume a CSI engadindo un método gRPC estandarizado, podes ler: a nosa solicitude de extracción para o controlador vsphere-csi.
  • A comunicación prodúcese a través de sockets IPC, para non confundirse se o kubelet enviou a solicitude ao pod correcto.

Esta lista che lembra algo? As vantaxes de CSI son resolvendo eses mesmos problemas, que non se tiveron en conta ao desenvolver o complemento Flexvolume.

Descubrimentos

CSI como estándar para implementar complementos personalizados para interactuar cos almacéns de datos foi moi ben recibido pola comunidade. Ademais, polas súas vantaxes e versatilidade, os controladores CSI créanse incluso para sistemas de almacenamento como Ceph ou AWS EBS, complementos para traballar cos que se engadiron na primeira versión de Kubernetes.

A principios de 2019, complementos na árbore foron declarados obsoletos. Pretendemos seguir admitindo o complemento Flexvolume, pero non desenvolveremos novas funcionalidades para el.

Nós mesmos xa temos experiencia no uso de ceph-csi, vsphere-csi e estamos preparados para engadir a esta lista. Ata o momento, CSI está a facer fronte ás tarefas que se lle encomendaron cun estrondo, pero agardaremos e veremos.

Non esquezas que todo o novo é un bo replanteo do vello!

PS

Lea tamén no noso blog:

Fonte: www.habr.com

Engadir un comentario