Plug-ins de volume para armazenamento Kubernetes: do Flexvolume ao CSI

Plug-ins de volume para armazenamento Kubernetes: do Flexvolume ao CSI

Na época em que o Kubernetes ainda era v1.0.0, existiam plug-ins de volume. Eles eram necessários para conectar sistemas ao Kubernetes para armazenar dados de contêineres persistentes (permanentes). Seu número era pequeno e entre os primeiros estavam provedores de armazenamento como GCE PD, Ceph, AWS EBS e outros.

Os plug-ins foram entregues junto com o Kubernetes, por isso receberam esse nome - in-tree. No entanto, para muitos, o conjunto existente de tais plugins revelou-se insuficiente. Os artesãos adicionaram plug-ins simples ao núcleo do Kubernetes usando patches, após os quais montaram seus próprios Kubernetes e os instalaram em seus servidores. Mas com o tempo, os desenvolvedores do Kubernetes perceberam que peixe o problema não pode ser resolvido. As pessoas necessitam cana de pesca. E no lançamento do Kubernetes v1.2.0 apareceu...

Plugin Flexvolume: vara de pesca mínima

Os desenvolvedores do Kubernetes criaram o plugin FlexVolume, que era uma estrutura lógica de variáveis ​​​​e métodos para trabalhar com drivers Flexvolume implementados por desenvolvedores terceiros.

Vamos parar e dar uma olhada mais de perto no que é o driver FlexVolume. Este é um certo arquivo executável (arquivo binário, script Python, script Bash, etc.), que, quando executado, recebe argumentos de linha de comando como entrada e retorna uma mensagem com campos pré-conhecidos no formato JSON. Por convenção, o primeiro argumento da linha de comando é sempre um método e os argumentos restantes são seus parâmetros.

Plug-ins de volume para armazenamento Kubernetes: do Flexvolume ao CSI
Diagrama de conexão para compartilhamentos CIFS no OpenShift. Driver Flexvolume - Bem no Centro

Conjunto mínimo de métodos Parece que este:

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 cenário em que o kubelet atuará no futuro ao chamar o driver. Existem também métodos especiais expandvolume и expandfs, que são responsáveis ​​por redimensionar dinamicamente o volume.

Como exemplo das mudanças que o método adiciona expandvolume, e com ele a capacidade de redimensionar volumes em tempo real, você pode se familiarizar com nossa solicitação pull no Operador Rook Ceph.

E aqui está um exemplo de implementação do driver Flexvolume para trabalhar com 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ão, depois de preparar o arquivo executável real, você precisa carregue o driver para o cluster Kubernetes. O driver deve estar localizado em cada nó do cluster de acordo com um caminho predeterminado. Por padrão foi selecionado:

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

... mas ao usar diferentes distribuições do Kubernetes (OpenShift, Rancher...) o caminho pode ser diferente.

Problemas de Flexvolume: como lançar uma vara de pescar corretamente?

Carregar o driver Flexvolume para nós de cluster acabou não sendo uma tarefa trivial. Tendo feito a operação manualmente uma vez, é fácil encontrar uma situação em que novos nós aparecem no cluster: devido à adição de um novo nó, escalonamento horizontal automático ou - o que é pior - substituição de um nó devido a um mau funcionamento. Neste caso, o trabalho com o armazenamento nesses nós deve ser feito é impossível, até que você ainda adicione manualmente o driver Flexvolume a eles.

A solução para este problema foi uma das primitivas do Kubernetes - DaemonSet. Quando um novo nó aparece no cluster, ele contém automaticamente um pod do nosso DaemonSet, ao qual um volume local é anexado ao longo do caminho para localizar os drivers Flexvolume. Após a criação bem-sucedida, o pod copia no disco os arquivos necessários para que o driver funcione.

Aqui está um exemplo de DaemonSet para criar um plugin 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 um exemplo de script Bash para definir o layout do driver 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 não esquecer que a operação de cópia não é atômico. Há uma grande chance de que o kubelet comece a usar o driver antes que seu processo de provisionamento seja concluído, causando falha no sistema. A abordagem correta é primeiro copiar os arquivos do driver com um nome diferente e, em seguida, usar uma operação de renomeação atômica.

Plug-ins de volume para armazenamento Kubernetes: do Flexvolume ao CSI
Diagrama de trabalho com Ceph no operador Rook: o driver Flexvolume no diagrama está localizado dentro do agente Rook

O próximo problema ao usar drivers Flexvolume é que para a maior parte do armazenamento em um nó de cluster o software necessário para isso deve ser instalado (por exemplo, o pacote ceph-common para Ceph). Inicialmente, o plugin Flexvolume não foi projetado para implementar sistemas tão complexos.

Uma solução original para este problema pode ser vista na implementação do driver Flexvolume do operador Rook:

O próprio driver foi projetado como um cliente RPC. O soquete IPC para comunicação está localizado no mesmo diretório do próprio driver. Lembramos que para copiar os arquivos do driver seria bom utilizar o DaemonSet, que conecta o diretório ao driver como um volume. Depois de copiar os arquivos necessários do driver rook, este pod não morre, mas se conecta ao soquete IPC por meio do volume anexado como um servidor RPC completo. O pacote ceph-common já está instalado dentro do contêiner do pod. O soquete IPC garante que o kubelet se comunicará exatamente com o pod localizado no mesmo nó. Tudo que é engenhoso é simples!..

Adeus, nossos afetuosos... plugins na árvore!

Os desenvolvedores do Kubernetes descobriram que o número de plug-ins para armazenamento no núcleo é vinte. E uma mudança em cada um deles, de uma forma ou de outra, passa por todo o ciclo de lançamento do Kubernetes.

Acontece que para usar a nova versão do plugin de armazenamento, você precisa atualizar todo o cluster. Além disso, você pode se surpreender ao saber que a nova versão do Kubernetes de repente se tornará incompatível com o kernel Linux que você está usando... Então você enxuga as lágrimas e, cerrando os dentes, coordena com sua gerência e usuários o tempo para atualize o kernel Linux e o cluster Kubernetes. Com possíveis paralisações na prestação de serviços.

A situação é mais que cômica, não acha? Ficou claro para toda a comunidade que a abordagem não estava funcionando. Por uma decisão deliberada, os desenvolvedores do Kubernetes anunciam que novos plugins para trabalhar com armazenamento não serão mais aceitos no kernel. Além disso, como já sabemos, foram identificadas uma série de deficiências na implementação do plugin Flexvolume...

O último plugin adicionado para volumes no Kubernetes, CSI, foi projetado para resolver o problema do armazenamento persistente de dados de uma vez por todas. Sua versão alfa, mais conhecida como Out-of-Tree CSI Volume Plugins, foi anunciada no lançamento Kubernetes 1.9.

Interface de armazenamento de contêiner ou haste giratória CSI 3000!

Em primeiro lugar, gostaria de salientar que o CSI não é apenas um plugin de volume, mas um verdadeiro Стандарт na criação de componentes personalizados para trabalhar com data warehouses. Sistemas de orquestração de contêineres como Kubernetes e Mesos deveriam “aprender” como trabalhar com componentes implementados de acordo com esse padrão. E agora já aprendi Kubernetes.

Qual é a estrutura do plugin CSI no Kubernetes? O plugin CSI funciona com drivers especiais (Drivers CSI) escrito por desenvolvedores terceirizados. Um driver CSI no Kubernetes deve consistir no mínimo em dois componentes (pods):

  • Responsável pelo Tratamento — gerencia armazenamentos persistentes externos. É implementado como um servidor gRPC, para o qual a primitiva é usada StatefulSet.
  • Node — é responsável por montar o armazenamento persistente nos nós do cluster. Também é implementado como um servidor gRPC, mas usa o primitivo DaemonSet.

Plug-ins de volume para armazenamento Kubernetes: do Flexvolume ao CSI
Como funciona o plugin CSI no Kubernetes

Você pode conhecer alguns outros detalhes do trabalho do CSI, por exemplo, no artigo “Compreendendo o C.S.I." tradução do qual publicamos há um ano.

As vantagens de tal implementação

  • Para coisas básicas como registrar um driver para um nó, os desenvolvedores do Kubernetes implementaram um conjunto de contêineres. Você não precisa mais gerar uma resposta JSON com recursos, como foi feito para o plugin Flexvolume.
  • Em vez de “colocar” arquivos executáveis ​​nos nós, agora carregamos pods para o cluster. Isto é o que esperamos inicialmente do Kubernetes: todos os processos ocorrem dentro de contêineres implantados usando primitivas do Kubernetes.
  • Você não precisa mais desenvolver um servidor RPC e um cliente RPC para implementar drivers complexos. O cliente foi implementado para nós por desenvolvedores do Kubernetes.
  • Passar argumentos para trabalhar no protocolo gRPC é muito mais conveniente, flexível e confiável do que passá-los por meio de argumentos de linha de comando. Para entender como adicionar suporte para métricas de uso de volume ao CSI adicionando um método gRPC padronizado, você pode ler: nossa solicitação pull para driver vsphere-csi.
  • A comunicação ocorre por meio de soquetes IPC, para não confundir se o kubelet enviou a solicitação para o pod correto.

Essa lista te lembra alguma coisa? As vantagens do CSI são resolvendo esses mesmos problemas, que não foram levados em consideração no desenvolvimento do plugin Flexvolume.

Descobertas

CSI como padrão para implementação de plug-ins personalizados para interação com data warehouses foi recebido calorosamente pela comunidade. Além disso, devido às suas vantagens e versatilidade, os drivers CSI são criados até mesmo para sistemas de armazenamento como Ceph ou AWS EBS, plugins para trabalhar com os quais foram adicionados na primeira versão do Kubernetes.

No início de 2019, plug-ins na árvore foram declarados obsoletos. Planejamos continuar a oferecer suporte ao plugin Flexvolume, mas não desenvolveremos novas funcionalidades para ele.

Nós mesmos já temos experiência no uso de ceph-csi, vsphere-csi e estamos prontos para adicionar algo a esta lista! Até agora, o CSI está lidando com as tarefas que lhe são atribuídas com força, mas vamos esperar para ver.

Não se esqueça que tudo que é novo é um bom repensar do antigo!

PS

Leia também em nosso blog:

Fonte: habr.com

Adicionar um comentário