Nossa experiência no desenvolvimento de um driver CSI em Kubernetes para Yandex.Cloud



Temos o prazer de anunciar que o Flant está reabastecendo sua contribuição para as ferramentas de código aberto do Kubernetes, lançando uma versão alfa do driver CSI (Container Storage Interface) para Yandex.Cloud.

Mas antes de passarmos aos detalhes da implementação, responderemos à pergunta de por que isso é necessário quando o Yandex já possui o serviço Serviço Gerenciado para Kubernetes .

Introdução


Por que é isso?


Dentro da nossa empresa, desde o início da operação do Kubernetes em produção (ou seja, por vários anos), nossa própria ferramenta (deckhouse) está em desenvolvimento, que, a propósito, também planejamos disponibilizar como projeto de código aberto em um futuro próximo. Com sua ajuda, configuramos e configuramos uniformemente todos os nossos clusters e, no momento, existem mais de 100 deles, além disso, nas mais diversas configurações de ferro e em todos os serviços em nuvem disponíveis.

Os clusters nos quais o deckhouse é usado têm todos os componentes necessários para o trabalho: balanceadores, monitoramento com gráficos, métricas e alertas convenientes, autenticação do usuário através de provedores externos para acessar todos os painéis e assim por diante. Não faz sentido colocar um cluster "bombeado" em uma solução gerenciada, pois muitas vezes é impossível ou levará à necessidade de desativar metade dos componentes.

NB : Esta é a nossa experiência, e é bastante específica. De maneira alguma afirmamos que todos devem se envolver independentemente na implantação do cluster Kubernetes em vez de usar soluções prontas. A propósito, não temos experiência real na operação do Kubernetes a partir do Yandex e não faremos nenhuma avaliação deste serviço neste artigo.

O que é e para quem?


Então, nós já conversamos sobre a abordagem moderna de armazenamento no Kubernetes: como o CSI funciona e como a comunidade chegou a essa abordagem.

Atualmente, muitos grandes provedores de serviços em nuvem desenvolveram drivers para usar suas unidades em nuvem como um volume persistente no Kubernetes. Se o fornecedor não tiver esse driver, mas ao mesmo tempo todas as funções necessárias forem fornecidas por meio da API, nada o impedirá de implementar o driver por conta própria. E assim aconteceu com o Yandex.Cloud.

Como base para o desenvolvimento, adotamos o driver CSI para a nuvem DigitalOcean e algumas idéias do driver para GCP , já que a interação com a API dessas nuvens (Google e Yandex) tem várias semelhanças. Em particular, a API eGCP e Yandex retornam um objeto Operationpara rastrear o status de operações demoradas (por exemplo, criando um novo disco). Para interagir com a API Yandex.Cloud , o SDK do Yandex.Cloud Go é usado .

O resultado do trabalho realizado é publicado no GitHub e pode ser útil para quem, por algum motivo, usa sua própria instalação do Kubernetes nas máquinas virtuais Yandex.Cloud (mas não é um cluster gerenciado pronto) e gostaria de usar (solicitar) discos via CSI.

Implementação


Características principais


Atualmente, o driver suporta as seguintes funções:

  • Ordenando discos em todas as zonas do cluster de acordo com a topologia de nós no cluster;
  • Removendo discos encomendados anteriormente;
  • Redimensionamento offline para discos (o Yandex. Cloud não suporta o aumento de discos montados em uma máquina virtual). Sobre como modificar o driver para redimensionar o mais simples possível, veja abaixo.

No futuro, está planejado implementar o suporte para a criação e remoção de discos de captura instantânea.

A principal dificuldade e sua superação


A falta de capacidade de expandir discos em tempo real na API Yandex.Cloud é uma limitação que complica a operação de redimensionamento do PV (Volume Persistente): nesse caso, é necessário que o pod do aplicativo que usa o disco seja parado e isso pode causar uma simples formulários.

De acordo com a especificação CSI , se o controlador CSI relatar que só pode redimensionar os discos "offline" ( VolumeExpansion.OFFLINE), o processo de aumento do disco deve ser assim:

Se o plugin tiver apenas VolumeExpansion.OFFLINEcapacidade de expansão e o volume estiver atualmente publicado ou disponível em um nó, ControllerExpandVolumeDEVE ser chamado SOMENTE após:

  • O plug-in tem PUBLISH_UNPUBLISH_VOLUMEcapacidade de controlador e ControllerUnpublishVolumefoi chamado com sucesso.

SE NÃO

  • O plug-in NÃO possui PUBLISH_UNPUBLISH_VOLUMEcapacidade de controlador , o plug-in tem STAGE_UNSTAGE_VOLUMEcapacidade de e NodeUnstageVolumefoi concluído com êxito.

SE NÃO

  • O plug-in NÃO possui PUBLISH_UNPUBLISH_VOLUMEcapacidade de controlador , nem STAGE_UNSTAGE_VOLUMEcapacidade de , e NodeUnpublishVolumefoi concluída com êxito.

Em essência, isso significa a necessidade de desconectar o disco da máquina virtual antes de aumentá-lo.

No entanto, infelizmente, a implementação da especificação CSI através do side-car não atende a estes requisitos:

  • No sidecar-container csi-attacher, que deve ser responsável pela presença do espaço necessário entre as montagens, essa funcionalidade simplesmente não é implementada com o redimensionamento offline. Uma discussão sobre isso foi iniciada aqui .
  • O que é um contêiner lateral neste contexto? O próprio plug-in CSI não interage com a API do Kubernetes, mas responde apenas às chamadas de gRPC que os contêineres laterais enviam para ele. Estes últimos estão sendo desenvolvidos pela comunidade Kubernetes.

No nosso caso (plugin CSI), a operação para aumentar o disco é a seguinte:

  1. Recebemos uma chamada de gRPC ControllerExpandVolume;
  2. Estamos tentando aumentar o disco na API, mas obtemos um erro sobre a impossibilidade de executar a operação, pois o disco está montado;
  3. Salvamos o identificador de disco no mapa que contém os discos para os quais você precisa executar uma operação de aumento. Por questões de brevidade, chamaremos este mapa como volumeResizeRequired;
  4. Exclua manualmente o pod que usa o disco. O Kubernetes irá reiniciá-lo. Para que o disco não tenha tempo de montar ( ControllerPublishVolume) antes da conclusão da operação de aumento ao tentar montar, verificamos se esse disco ainda está dentro volumeResizeRequirede retornamos um erro;
  5. O driver CSI está tentando executar novamente a operação de redimensionamento. Se a operação foi bem-sucedida, exclua o disco de volumeResizeRequired;
  6. Porque está faltando o identificador de disco volumeResizeRequired, ControllerPublishVolumeé bem-sucedido, o disco está montado e o pod é iniciado.

Tudo parece bastante simples, mas como sempre, existem armadilhas. O redimensionador externo está envolvido na expansão do disco , que, em caso de erro durante a operação, usa uma fila com um aumento exponencial no tempo limite de espera até 1000 segundos:

func DefaultControllerRateLimiter() RateLimiter {
  return NewMaxOfRateLimiter(
  NewItemExponentialFailureRateLimiter(5*time.Millisecond, 1000*time.Second),
  // 10 qps, 100 bucket size.  This is only for retry speed and its only the overall factor (not per item)
  &BucketRateLimiter{Limiter: rate.NewLimiter(rate.Limit(10), 100)},
  )
}

Periodicamente, isso pode levar ao fato de que a operação de aumentar o disco é estendida por mais de 15 minutos e, portanto, a inacessibilidade do pod correspondente.

A única opção que nos permitiu reduzir o tempo de inatividade potencial de maneira fácil e indolor foi usar nossa versão do redimensionador externo com um limite de tempo limite máximo de 5 segundos :

workqueue.NewItemExponentialFailureRateLimiter(5*time.Millisecond, 5*time.Second)

Não consideramos necessário iniciar urgentemente uma discussão e corrigir o redimensionador externo, porque os discos de redimensionamento offline são um atavismo que em breve desaparecerá de todos os provedores de nuvem.

Como começar a usar?


O driver é suportado no Kubernetes versão 1.15 e superior. Para o driver funcionar, os seguintes requisitos devem ser atendidos:

  • O sinalizador é --allow-privilegeddefinido como o valor truepara o servidor da API e o kubelet;
  • Incluído --feature-gates=VolumeSnapshotDataSource=true,KubeletPluginsWatcher=true,CSINodeInfo=true,CSIDriverRegistry=truepara servidor de API e kubelet;
  • Montagem de propagação ( propagação de montagem ) deve ser incluída no cluster. Ao usar o Docker, o daemon deve ser configurado para permitir montagens compartilhadas.

Todas as etapas necessárias para a instalação em si são descritas em README . Instalação é a criação de objetos no Kubernetes a partir de manifestos.

Para que o driver funcione, você precisará do seguinte:

  • Indique o identificador do diretório de catálogo Yandex.Cloud ( folder-id) no manifesto ( consulte a documentação );
  • Para interagir com a API Yandex.Cloud no driver CSI, uma conta de serviço é usada. No manifesto secreto, você deve passar as chaves autorizadas para a conta de serviço. A documentação descreve como criar uma conta de serviço e obter as chaves.

Em geral, experimente e teremos o maior prazer em receber feedback e novos problemas, se você encontrar algum problema!

Suporte adicional


Como resultado, gostaríamos de observar que implementamos esse driver CSI não por um grande desejo de se divertir escrevendo aplicativos no Go, mas por causa da necessidade urgente dentro da empresa. Não parece aconselhável oferecer suporte à nossa própria implementação; portanto, se o Yandex mostrar interesse e decidir continuar dando suporte ao driver, teremos o prazer de transferir o repositório à sua disposição.

Além disso, provavelmente, o Yandex no cluster gerenciado Kubernetes possui sua própria implementação do driver CSI, que pode ser lançado em código aberto. Essa opção de desenvolvimento também parece favorável para nós - a comunidade poderá usar o driver comprovado do provedor de serviços e não de uma empresa de terceiros.

PS


Leia também no nosso blog:

Source: https://habr.com/ru/post/undefined/


All Articles