
Nos tempos em que o Kubernetes ainda estava na versão v1.0.0, existiam plugins para volumes. Eles eram necessários para conectar sistemas de armazenamento ao Kubernetes para gerenciar dados persistentes (permanentes) de contêineres. Sua quantidade era pequena, e entre os primeiros estavam provedores de armazenamento como GCE PD, Ceph, AWS EBS e outros.
Os plugins eram fornecidos junto com o Kubernetes, sendo por isso chamados de in-tree. No entanto, muitos acharam que o conjunto existente de plugins era insuficiente. Habilidosos adicionavam plugins simples ao núcleo do Kubernetes por meio de patches, após o que construíam seu próprio Kubernetes e o instalavam em seus servidores. Mas com o tempo, os desenvolvedores do Kubernetes perceberam que não resolveriam o problema. As pessoas precisavam de uma vara de pescar. E na versão do Kubernetes v1.2.0 ela apareceu...
Plugin Flexvolume: uma vara de pescar básica
Os desenvolvedores do Kubernetes criaram o plugin FlexVolume, que era uma camada lógica composta por variáveis e métodos para trabalhar com drivers Flexvolume implementados por desenvolvedores terceiros.
Vamos parar e examinar mais de perto o que é um driver FlexVolume. É um determinado arquivo executável (arquivo binário, script Python, script Bash, etc.), que ao ser executado aceita argumentos de linha de comando e retorna uma mensagem com campos pré-definidos no formato JSON. O primeiro argumento na linha de comando, de acordo com a convenção, sempre é o método, e os demais argumentos são seus parâmetros.

Esquema de conexão de CIFS Shares no OpenShift. O driver Flexvolume está bem no meio.
Conjunto mínimo de métodos é assim:
flexvolume_driver mount # responsável por conectar o volume ao pod
# Formato da mensagem retornada:
{
"status": "Success"/"Failure"/"Not supported",
"message": "Por que esse status foi retornado",
}
flexvolume_driver unmount # responsável por desconectar o volume do pod
# Formato da mensagem retornada:
{
"status": "Success"/"Failure"/"Not supported",
"message": "Por que esse status foi retornado",
}
flexvolume_driver init # responsável pela inicialização do plugin
# Formato da mensagem retornada:
{
"status": "Success"/"Failure"/"Not supported",
"message": "Por que esse status foi retornado",
// Determina se o driver usa métodos attach/deatach
"capabilities":{"attach": True/False}
} Uso dos métodos attach e detach determinará o cenário pelo qual, no futuro, o kubelet agirá ao chamar o driver. Também existem métodos especiais expandvolume e expandfs, que são responsáveis pela alteração dinâmica do tamanho do volume.
Como exemplo das mudanças que o método agrega, expandvolume, e junto com isso — a capacidade de executar a mudança de tamanhos de volumes em tempo real, você pode consultar no Rook Ceph Operator.
Aqui está um exemplo de implementação de um driver Flexvolume para trabalhar com NFS:
usage() {
err "Uso inválido. Uso: "
err "t$0 init"
err "t$0 mount <diretório de montagem> <parâmetros json>"
err "t$0 unmount <diretório de montagem>"
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": "Sucesso"}'
exit 0
fi
mkdir -p ${MNTPATH} &> \/dev\/null
mount -t nfs ${NFS_SERVER}:\/ ${SHARE} ${MNTPATH} &> \/dev\/null
if [ $? -ne 0 ]; then
err "{ "status": "Falha", "message": "Falha ao montar ${NFS_SERVER}:${SHARE} em ${MNTPATH}"}"
exit 1
fi
log '{"status": "Sucesso"}'
exit 0
}
unmount() {
MNTPATH=$1
if [ $(ismounted) -eq 0 ] ; then
log '{"status": "Sucesso"}'
exit 0
fi
umount ${MNTPATH} &> \/dev\/null
if [ $? -ne 0 ]; then
err "{ "status": "Falhou", "message": "Falha ao desmontar volume em ${MNTPATH}"}"
exit 1
fi
log '{"status": "Sucesso"}'
exit 0
}
op=$1
if [ "$op" = "init" ]; then
log '{"status": "Sucesso", "capabilities": {"attach": false}}'
exit 0
fi
if [ $# -lt 2 ]; then
usage
fi
shift
case "$op" in
mount)
domount $*
;;
unmount)
unmount $*
;;
*)
log '{"status": "Não suportado"}'
exit 0
esac
exit 1Portanto, após a preparação do próprio arquivo executável, é necessário distribuir o driver no cluster Kubernetes. O driver deve estar presente em cada nó do cluster de acordo com o caminho previamente estabelecido. Por padrão, foi escolhido:
/usr/libexec/kubernetes/kubelet-plugins/volume/exec/имя_поставщика_хранилища~имя_драйвера/
… mas ao usar diferentes distribuições do Kubernetes (OpenShift, Rancher…) o caminho pode ser diferente.
Problemas com Flexvolume: como configurar corretamente?
Instalar o driver Flexvolume nos nós do cluster se mostrou uma tarefa não trivial. Após realizar a operação uma vez manualmente, é fácil se deparar com a 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ó por falha. Nesse caso, a operação de armazenamento nesses nós deve ser realizada é impossível, enquanto você ainda estiver no modo manual e não adicionar o driver Flexvolume neles.
A solução para esse problema foi um dos primitivas do Kubernetes — DaemonSetQuando um novo nó é adicionado ao cluster, um pod do nosso DaemonSet é automaticamente alocado nele, ao qual se conecta um volume local no caminho para encontrar os drivers de Flexvolume. Após a criação bem-sucedida do pod, ele copia os arquivos necessários para o funcionamento do driver no disco.
Aqui está um exemplo de um DaemonSet para implementar o plugin Flexvolume:
apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
name: flex-set
spec:
template:
metadata:
name: flex-deploy
labels:
app: flex-deploy
spec:
containers:
- image:
name: flex-deploy
securityContext:
privileged: true
volumeMounts:
- mountPath: /flexmnt
name: flexvolume-mount
volumes:
- name: flexvolume-mount
hostPath:
path:… e um exemplo de script Bash para implementar o driver de 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ômica.Há uma grande probabilidade de que o kubelet comece a usar o driver antes que o processo de preparação esteja concluído, o que causará um erro no funcionamento do sistema. A abordagem correta seria primeiro copiar os arquivos do driver com um nome diferente e, em seguida, usar uma operação atômica de renomeação.

O esquema de trabalho com Ceph no operador Rook: o driver de Flexvolume no esquema está dentro do agente Rook.
O próximo problema ao usar drivers de Flexvolume é que, para a maioria dos storages no nó do cluster, o software necessário deve estar instalado. (por exemplo, o pacote ceph-common para Ceph). Originalmente, o plugin de Flexvolume não foi pensado para implementar sistemas tão complexos.
A solução original para esse problema pode ser vista na implementação do driver de Flexvolume do operador Rook:
O driver em si é implementado como um cliente RPC. O socket IPC para comunicação está no mesmo diretório que o próprio driver. Lembramos que, para copiar os arquivos do driver, seria bom usar um DaemonSet que conecta um diretório com o driver como volume. Após copiar os arquivos necessários do driver, esse pod não morre, mas se conecta ao socket IPC através do volume conectado como um servidor RPC completo. O pacote ceph-common já está instalado dentro do container do pod. O socket IPC garante que o kubelet se comunique exatamente com o pod que está no mesmo nó que ele. Tudo o que é genial é simples!...
Adeus, nossos adoráveis… plugins in-tree!
Os desenvolvedores do Kubernetes descobriram que o número de plugins de armazenamento integrados no núcleo é de vinte. E cada alteração neles, de alguma forma, passa pelo ciclo de lançamento completo do Kubernetes.
Acontece que, para usar a nova versão do plugin de armazenamento, é necessário atualizar todo o cluster. Além disso, você pode se surpreender ao descobrir que a nova versão do Kubernetes de repente se torna incompatível com o núcleo do Linux em uso... E assim você seca suas lágrimas e, rangendo os dentes, negocia com a gerência e os usuários o momento da atualização do núcleo do Linux e do cluster do Kubernetes, com a possibilidade de paralisação na prestação de serviços.
A situação é mais do que cômica, não acha? Para toda a comunidade, ficou claro que essa abordagem não funciona. Por decisão da equipe de desenvolvimento do Kubernetes, foi declarado que novos plugins para trabalhar com armazenamento não seriam mais aceitos no núcleo. Além disso, como já sabemos, o plugin Flexvolume apresentou uma série de falhas na implementação...
A última adição ao plugin de volumes no Kubernetes foi projetada para encerrar de uma vez por todas a questão dos armazenamento persistente — o CSI. Sua versão alpha, mais completa, chamada de Out-of-Tree CSI Volume Plugins, foi anunciada no lançamento .
Interface de Armazenamento de Contêiner, ou girando o CSI 3000!
Primeiro, é importante notar que o CSI não é apenas um plugin de volume, mas um verdadeiro para a criação de componentes personalizados para trabalhar com armazenamento de dados.A expectativa era que os sistemas de orquestração de contêineres, como Kubernetes e Mesos, deveriam "aprender" a trabalhar com componentes implementados de acordo com esse padrão. E assim o Kubernetes já aprendeu.
Qual é a estrutura do plugin CSI no Kubernetes? O plugin CSI funciona com drivers especiais (drivers CSI), escritos por desenvolvedores externos. O driver CSI no Kubernetes deve consistir, no mínimo, em dois componentes (pods):
- Controlador — que gerencia armazenamento persistente externo. É liberado como um servidor gRPC, utilizando o primitivo
StatefulSet. - Nó — que é responsável por montar armazenamento persistente nos nós do cluster. Também é implementado como um servidor gRPC, mas utiliza o primitivo
DaemonSet.

Esquema de funcionamento do plugin CSI no Kubernetes
Sobre alguns outros detalhes do funcionamento do CSI, você pode aprender, por exemplo, no artigo "», publicamos há um ano.
Os benefícios dessa implementação
- Para tarefas básicas — como registrar um driver para um nó — os desenvolvedores do Kubernetes implementaram um conjunto de contêineres. Não é mais necessário construir manualmente a resposta JSON com capabilities, como era feito para o plugin Flexvolume.
- Em vez de "instalar" arquivos executáveis nos nós, agora disponibilizamos pods no cluster. Isso é exatamente o que esperamos do Kubernetes: todos os processos ocorrem dentro de contêineres implantados com os primitivas do Kubernetes.
- Para implementar drivers complexos, não é mais necessário desenvolver um servidor RPC e um cliente RPC. O cliente foi implementado pelos desenvolvedores do Kubernetes.
- A passagem de argumentos para operação pelo protocolo gRPC é muito mais conveniente, flexível e confiável do que a passagem através de argumentos da linha de comando. Para entender como adicionar suporte a métricas de uso de volume no CSI através da adição de um método gRPC padronizado, você pode conferir para o driver vsphere-csi.
- A comunicação ocorre através de sockets IPC, para evitar confusões sobre a qual pod o kubelet enviou a solicitação.
Esta lista não lhe diz nada? As vantagens do CSI são a solução para aqueles problemas, que não foram levados em conta durante o desenvolvimento do plugin Flexvolume.
Como resultado, obtemos um serviço de "monitoramento como serviço", automatizado na linha de produção de integração contínua. O desenvolvedor ou testador precisa apenas definir a lista de métricas em um arquivo JSON, e tudo o mais acontece automaticamente. Obtemos um controle de qualidade transparente dos lançamentos: todas as notificações sobre desempenho, consumo de recursos ou regressões arquitetônicas.
O CSI, como padrão para a implementação de plugins de usuário para interagir com sistemas de armazenamento, foi recebido calorosamente pela comunidade. Além disso, devido às suas vantagens e versatilidade, drivers CSI estão sendo criados até mesmo para sistemas de armazenamento como Ceph ou AWS EBS, cujos plugins foram adicionados desde a primeira versão do Kubernetes.
No início de 2019, os plugins in-tree . O suporte ao plugin Flexvolume continuará, mas não haverá desenvolvimento de novas funcionalidades para ele.
Já temos experiência com ceph-csi, vsphere-csi e estamos prontos para adicionar a essa lista! Até agora, o CSI tem lidado muito bem com as tarefas atribuídas, e veremos como irá evoluir.
Não se esqueça de que tudo o que é novo é o bem repensado que já existia!
P.S.
Confira também em nosso blog:
- «»;
- «»;
- «».
Fonte: habr.com
