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

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

Temos o prazer de anunciar que a Flant está expandindo sua contribuição para ferramentas de código aberto para Kubernetes, lançando versão alfa do driver CSI (Interface de armazenamento de contêiner) para Yandex.Cloud.

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

Introdução

Por que isso?

Dentro da nossa empresa, desde o início da utilização do Kubernetes em produção (ou seja, há vários anos), temos desenvolvido a nossa própria ferramenta (deckhouse), que, aliás, também pretendemos disponibilizar em breve como um projeto Open Source . Com sua ajuda, configuramos e configuramos uniformemente todos os nossos clusters, e atualmente já existem mais de 100 deles, em uma ampla variedade de configurações de hardware e em todos os serviços em nuvem disponíveis.

Clusters que utilizam deckhouse possuem todos os componentes necessários ao funcionamento: balanceadores, monitoramento com gráficos convenientes, métricas e alertas, autenticação de usuários através de provedores externos para acesso a todos os dashboards, e assim por diante. Não faz sentido instalar um cluster tão “bombado” em uma solução gerenciada, pois muitas vezes isso é impossível ou levará à necessidade de desabilitar metade dos componentes.

NB: Esta é a nossa experiência e é bastante específica. Não estamos de forma alguma sugerindo que todos implantem clusters Kubernetes por conta própria, em vez de usar soluções prontas. A propósito, não temos experiência real na operação do Kubernetes do Yandex e não daremos nenhuma avaliação deste serviço neste artigo.

O que é e para quem?

Então, já falamos sobre a abordagem moderna de armazenamento no Kubernetes: como funciona o CSI? и como surgiu a comunidade para esta abordagem.

Atualmente, muitos grandes provedores de serviços em nuvem desenvolveram drivers para usar seus discos em nuvem como volume persistente no Kubernetes. Se o fornecedor não tiver esse driver, mas todas as funções necessárias forem fornecidas por meio da API, nada impedirá que você mesmo implemente o driver. Foi o que aconteceu com Yandex.Cloud.

Tomamos como base para o desenvolvimento Driver CSI para nuvem DigitalOcean e algumas ideias de drivers para GCP, já que a interação com a API dessas nuvens (Google e Yandex) possui uma série de semelhanças. Em particular, a API e GCPe Yandex retornar um objeto Operation para rastrear o status de operações de longa duração (por exemplo, criação de um novo disco). Para interagir com a API Yandex.Cloud, use SDK Yandex.Cloud Go.

O resultado do trabalho realizado publicado no GitHub e pode ser útil para aqueles que, por algum motivo, usam sua própria instalação do Kubernetes em máquinas virtuais Yandex.Cloud (mas não em um cluster gerenciado pronto) e gostariam de usar (encomendar) discos por meio do CSI.

Implementação

Principais recursos

Atualmente o driver suporta as seguintes funções:

  • Ordenar discos em todas as zonas do cluster de acordo com a topologia dos nós do cluster;
  • Remoção de discos encomendados anteriormente;
  • Redimensionamento offline para discos (Yandex.Cloud não suporta aumentando os discos montados na máquina virtual). Para obter informações sobre como o driver teve que ser modificado para tornar o redimensionamento o mais simples possível, veja abaixo.

No futuro, planejamos implementar suporte para criação e exclusão de instantâneos de disco.

A principal dificuldade e como superá-la

A falta de capacidade de aumentar discos em tempo real na API Yandex.Cloud é uma limitação que dificulta a operação de redimensionamento para PV (Volume Persistente): neste caso, é necessário que o pod da aplicação que utiliza o disco seja parado, e isso pode causar inatividade dos aplicativos.

Conforme Especificações CSI, se o controlador CSI relatar que pode redimensionar discos apenas “offline” (VolumeExpansion.OFFLINE), então o processo de aumento do disco deve ser assim:

Se o plugin tiver apenas VolumeExpansion.OFFLINE a capacidade de expansão e o volume estão atualmente publicados ou disponíveis em um nó, então ControllerExpandVolume DEVE ser chamado SOMENTE após:

  • O plugin tem controlador PUBLISH_UNPUBLISH_VOLUME capacidade e ControllerUnpublishVolume foi invocado com sucesso.

SE NÃO

  • O plugin NÃO possui controlador PUBLISH_UNPUBLISH_VOLUME capacidade, o plugin tem nó STAGE_UNSTAGE_VOLUME capacidade, e NodeUnstageVolume foi concluído com sucesso.

SE NÃO

  • O plugin NÃO possui controlador PUBLISH_UNPUBLISH_VOLUME capacidade, nem nó STAGE_UNSTAGE_VOLUME capacidade, e NodeUnpublishVolume foi concluído com sucesso.

Basicamente, isso significa que você precisa desconectar o disco da máquina virtual antes de expandi-lo.

Contudo, infelizmente implementação A especificação CSI via sidecars não atende a estes requisitos:

  • Em um contêiner lateral csi-attacher, que deveria ser responsável pela presença do intervalo necessário entre as montagens, essa funcionalidade simplesmente não é implementada no redimensionamento offline. Uma discussão sobre isso foi iniciada aqui.
  • O que exatamente é um contêiner sidecar neste contexto? O plug-in CSI em si não interage com a API Kubernetes, mas apenas responde às chamadas gRPC enviadas a ele por contêineres secundários. Mais recente estão sendo desenvolvidos pela comunidade Kubernetes.

No nosso caso (plugin CSI), a operação de ampliação do disco fica assim:

  1. Recebemos uma chamada gRPC ControllerExpandVolume;
  2. Estamos tentando aumentar o disco na API, mas recebemos um erro sobre a impossibilidade de realizar a operação porque o disco está montado;
  3. Armazenamos o identificador do disco no mapa, que contém os discos para os quais a operação de aumento precisa ser executada. Abaixo, por questões de brevidade, chamaremos este mapa de volumeResizeRequired;
  4. Remova manualmente o pod que está usando o disco. O Kubernetes irá reiniciá-lo. Para que o disco não tenha tempo de montar (ControllerPublishVolume) antes de concluir a operação de aumento ao tentar montar, verificamos se o disco fornecido ainda está em volumeResizeRequired e retornar um erro;
  5. O driver CSI tenta executar novamente a operação de redimensionamento. Se a operação foi bem-sucedida, remova o disco do volumeResizeRequired;
  6. Porque O ID do disco está faltando em volumeResizeRequired, ControllerPublishVolume passa com sucesso, o disco é montado, o pod é iniciado.

Tudo parece bastante simples, mas como sempre há armadilhas. Amplia discos redimensionador externo, que em caso de erro durante a operação usa uma fila com um aumento exponencial no tempo limite de 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)},
  )
}

Isso pode resultar periodicamente na extensão da operação de expansão do disco por mais de 15 minutos e, portanto, na indisponibilidade do pod correspondente.

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

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

Não consideramos necessário iniciar uma discussão com urgência e corrigir o redimensionador externo, porque o redimensionamento offline de discos é um retrocesso que em breve desaparecerá de todos os provedores de nuvem.

Como começar a usar?

O driver é compatível com Kubernetes versão 1.15 e superior. Para que o motorista funcione, os seguintes requisitos devem ser atendidos:

  • Bandeira --allow-privileged definido como valor true para servidor API e kubelet;
  • Incluído --feature-gates=VolumeSnapshotDataSource=true,KubeletPluginsWatcher=true,CSINodeInfo=true,CSIDriverRegistry=true para servidor API e kubelet;
  • Propagação de montagem (propagação de montagem) deve estar habilitado 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 descrito no README. A instalação envolve a criação de objetos no Kubernetes a partir de manifestos.

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

  • Especifique o identificador do diretório no manifesto (folder-id) Yandex.Cloud (veja a documentação);
  • Para interagir com a API Yandex.Cloud, o driver CSI usa uma conta de serviço. No manifesto, o segredo deve ser passado chaves autorizadas da conta de serviço. Na documentação descrito, como criar uma conta de serviço e obter chaves.

Contudo - tentee ficaremos felizes em receber feedback e novos problemasse você encontrar algum problema!

Suporte adicional

Como resultado, gostaríamos de ressaltar que implementamos este driver CSI não por um grande desejo de nos divertir escrevendo aplicativos em Go, mas por uma necessidade urgente dentro da empresa. Não nos parece prático manter nossa própria implementação, então se Yandex demonstrar interesse e decidir continuar apoiando o driver, teremos prazer em transferir o repositório para eles.

Além disso, Yandex provavelmente possui sua própria implementação do driver CSI em seu cluster Kubernetes gerenciado, que pode ser lançado em código aberto. Também vemos esta opção de desenvolvimento como favorável - a comunidade poderá usar um driver comprovado de um provedor de serviços, e não de uma empresa terceirizada.

PS

Leia também em nosso blog:

Fonte: habr.com

Adicionar um comentário