Claustro → gerenciamento simples de cluster OTP

Quase todos os aplicativos de negócios bem-sucedidos, mais cedo ou mais tarde, entram em uma fase em que o dimensionamento horizontal é necessário. Em muitos casos, você pode simplesmente iniciar uma nova instância e reduzir a média de carga. Mas também há casos menos triviais em que precisamos garantir que diferentes nós se conheçam e distribuam cuidadosamente a carga de trabalho.

Claustro → gerenciamento simples de cluster OTP

Aconteceu tanta sorte que Erlang, que escolhemos por sua sintaxe agradável e entusiasmo em torno dele, tem um design de primeira classe suporte para sistemas distribuídos. Em teoria, isso parece completamente trivial:

A passagem de mensagens entre processos em diferentes nós, bem como entre links e monitores, é transparente [...]

Na prática, tudo é um pouco mais complicado. Distribuído Erlang foi desenvolvido quando "contêiner" significava uma grande caixa de ferro para transporte e "estivador" era simplesmente sinônimo de estivador. EM IP4 havia muitos endereços desocupados, as interrupções na rede geralmente eram causadas por ratos mastigando o cabo e o tempo médio de atividade do sistema de produção era medido em décadas.

Agora somos todos incrivelmente autossuficientes, empacotados e distribuídos Erlang em um ambiente onde os endereços IP dinâmicos são distribuídos com base no princípio da grande aleatoriedade, e os nós podem aparecer e desaparecer conforme o capricho do calcanhar esquerdo do escalonador. Para evitar pilhas de código clichê em todos os projetos que executam um sistema distribuído Erlang, para combater o ambiente hostil é necessária ajuda.

Nota: Estou ciente de que há libcluster. É muito legal, tem mais de mil estrelas, o autor é famoso na comunidade e tudo mais. Se os métodos oferecidos por este pacote para criar e manter um cluster são suficientes para você, fico feliz por você. Infelizmente, preciso de muito mais. Quero controlar a configuração detalhadamente e não ser um espectador externo no teatro da reorganização do cluster.

Requisitos

O que eu pessoalmente precisava era de uma biblioteca que assumisse o gerenciamento do cluster e tivesse as seguintes propriedades:

  • trabalho transparente com uma lista de nós codificada e descoberta dinâmica por meio de serviços Erlang;
  • retorno de chamada totalmente funcional para cada mudança de topologia (nó lá, nó aqui, instabilidade da rede, divisões);
  • interface transparente para lançar um cluster com nomes longos e curtos, como acontece com :nonode@nohost;
  • Suporte Docker pronto para uso, sem a necessidade de escrever código de infraestrutura.

O último significa que depois de testar o aplicativo localmente em :nonode@nohost, ou em um ambiente distribuído artificialmente usando test_cluster_task, eu só quero correr docker-compose up --scale my_app=3 e veja como ele executa três instâncias no docker sem nenhuma alteração de código. Eu também quero aplicativos dependentes como mnesia - quando a topologia muda, nos bastidores eles reconstroem o cluster ao vivo, sem qualquer esforço adicional do aplicativo.

Claustro não pretendia ser uma biblioteca capaz de tudo, desde apoiar um cluster até fazer café. Não é uma solução mágica que visa cobrir todos os casos possíveis, ou ser uma solução academicamente completa no sentido que os teóricos de CS colocado neste termo. Esta biblioteca foi projetada para servir a um propósito muito claro, mas faz perfeitamente seu trabalho não muito grande. Este objectivo será proporcionar total transparência entre o ambiente de desenvolvimento local e um ambiente elástico distribuído cheio de contentores hostis.

Abordagem escolhida

Claustro destina-se a ser executado como um aplicativo, embora usuários avançados possam trabalhar com montagem e manutenção do cluster manualmente, executando diretamente Cloister.Manager na árvore supervisora ​​do aplicativo de destino.

Quando executada como um aplicativo, a biblioteca depende de config, de onde lê os seguintes valores básicos:

config :cloister,
  otp_app: :my_app,
  sentry: :"cloister.local", # or ~w|n1@foo n2@bar|a
  consensus: 3,              # number of nodes to consider
                             #    the cluster is up
  listener: MyApp.Listener   # listener to be called when
                             #    the ring has changed

Os parâmetros acima significam literalmente o seguinte: Claustro usado para aplicação OTP :my_appusa descoberta de serviço erlang para conectar nós, pelo menos três, e MyApp.Listener módulo (implementando @behaviour Cloister.Listener) está configurado para receber notificações sobre alterações de topologia. Uma descrição detalhada da configuração completa pode ser encontrada em documentação.

Com esta configuração, o aplicativo Claustro vontade lançamento em etapas, atrasando o processo de inicialização do aplicativo principal até que o consenso seja alcançado (três nós estão conectados e conectados, como no exemplo acima). Isso dá ao aplicativo principal a oportunidade de assumir que, quando for iniciado, o cluster já estará disponível. Sempre que a topologia mudar (haverá muitas delas, porque os nós não iniciam de forma totalmente síncrona), o manipulador será chamado MyApp.Listener.on_state_change/2. Na maioria das vezes realizamos uma ação quando recebemos uma mensagem de status %Cloister.Monitor{status: :up}, que significa: “Olá, o cluster está montado”.

Na maioria dos casos, a instalação consensus: 3 é ideal porque mesmo que esperemos que mais nós se conectem, o retorno de chamada passará status: :rehashingstatus: :up em qualquer nó recém-adicionado ou removido.

Ao iniciar no modo de desenvolvimento, você só precisa definir consensus: 1 и Claustro ficará feliz em pular a espera pela montagem do cluster quando vir :nonode@nohostOu :node@hostOu :[email protected] - dependendo de como o nó foi configurado (:none | :shortnames | :longnames).

Gerenciamento Distribuído de Aplicativos

Aplicativos distribuídos que não estão isolados geralmente incluem dependências distribuídas, como mnesia. É fácil para nós lidar com a reconfiguração deles a partir do mesmo retorno de chamada on_state_change/2. Aqui, por exemplo, está uma descrição detalhada de como reconfigurar mnesia na hora documentação Claustro.

A principal vantagem de usar Claustro é que ele executa todas as operações necessárias para reconstruir o cluster após uma mudança de topologia sob o capô. A aplicação simplesmente é executada em um ambiente distribuído já preparado, com todos os nós conectados, independentemente de sabermos os endereços IP e, portanto, os nomes dos nós com antecedência, ou se eles foram atribuídos/alterados dinamicamente. Isso não requer absolutamente nenhuma configuração especial do docker e, do ponto de vista do desenvolvedor de aplicativos, não há diferença entre executar em um ambiente distribuído ou em um ambiente local. :nonode@nohost. Você pode ler mais sobre isso em documentação.

Embora o tratamento complexo de alterações de topologia seja possível através de uma implementação personalizada MyApp.Listener, sempre pode haver casos extremos em que essas limitações da biblioteca e vieses de configuração provam ser os pilares da implementação. Está tudo bem, basta pegar o acima libcluster, que é de uso mais geral, ou até mesmo lidar com o cluster de baixo nível você mesmo. O objetivo desta biblioteca de códigos não é cobrir todos os cenários possíveis, mas usar o cenário mais comum sem complicações desnecessárias e o incômodo de copiar e colar.

Nota: neste ponto do original havia a frase “Feliz clustering!”, e o Yandex, com o qual eu traduzo (não preciso consultar dicionários), me ofereceu a opção “Feliz clustering!” Talvez seja impossível imaginar uma tradução melhor, especialmente à luz da actual situação geopolítica.

Fonte: habr.com

Adicionar um comentário