Uma nova era para a coordenação de cluster no Elasticsearch

Um dos motivos pelos quais o Elasticsearch se tornou tão amplamente difundido se deve a seu bom desempenho no escalonamento de apenas um pequeno cluster com alguns nós para um grande cluster com centenas de nós. Em seu núcleo está o subsistema de coordenação de cluster. O Elasticsearch versão 7 contém um novo subsistema de coordenação de cluster que oferece muitos benefícios em comparação com as versões anteriores. Este artigo trata das melhorias feitas nesse subsistema na versão 7. Ele descreve como usar o novo subsistema, como as alterações afetam as atualizações a partir da versão 6 e como essas melhorias evitam que você coloque acidentalmente os dados em risco. E conclui com uma amostra da teoria que descreve a maneira como o novo subsistema funciona.

O que é coordenação de cluster?

Um cluster do Elasticsearch pode executar muitas tarefas que exigem que inúmeros nós funcionem em conjunto. Por exemplo, cada pesquisa deve ser roteada para todos os shards certos para garantir que os resultados sejam precisos. Cada réplica deve ser atualizada quando você indexa ou exclui alguns documentos. Cada solicitação de cliente deve ser encaminhada do nó que a recebe aos nós que podem manipulá-la. Cada um dos nós tem sua própria visão geral do cluster para que possa executar pesquisas, indexação e outras atividades coordenadas. Essa visão geral é conhecida como estado de cluster. O estado de cluster determina aspectos como os mapeamentos e as configurações para cada índice, os shards que estão alocados a cada nó e as cópias de shard que estão em sincronia. É muito importante manter essas informações consistentes em todo o cluster. Muitos recursos recentes, incluindo replicação baseada em número de sequência e replicação entre clusters, funcionam corretamente apenas porque podem depender da consistência do estado do cluster.

O subsistema de coordenação funciona escolhendo um nó específico para ser o mestre do cluster. Esse nó mestre eleito garante que todos os nós em seu cluster recebam atualizações no estado do cluster. Isso é mais complicado do que pode parecer inicialmente, porque sistemas distribuídos como o Elasticsearch devem estar preparados para lidar com muitas situações estranhas. Às vezes os nós são executados lentamente, pausam para uma coleta de lixo ou repentinamente perdem potência. As redes sofrem devido a partições, perda de pacote, períodos de alta latência ou podem entregar mensagens em ordem diferente daquela em que estas foram enviadas. Pode ocorrer mais de um problema como esse de uma só vez e às vezes de maneira intermitente. Apesar de tudo isso, o subsistema de coordenação de cluster deve ser capaz de garantir que cada nó tenha uma visão consistente do estado do cluster.

Também é importante que o Elasticsearch seja resiliente às falhas dos nós individuais. Ele obtém essa resiliência considerando atualizações de estado do cluster como bem-sucedidas depois de um quórum de nós tê-las aceito. Um quórum é um subconjunto cuidadosamente escolhido dos nós elegíveis como mestres em um cluster. A vantagem de exigir a resposta de somente um subconjunto dos nós é que alguns dos nós pode falhar sem afetar a disponibilidade do cluster. Os quóruns devem ser cuidadosamente escolhidos para que o cluster não consiga eleger dois mestres independentes que tomem decisões inconsistentes, por fim resultando em perda de dados.

Normalmente recomendamos que os clusters tenham três nós elegíveis como mestres de modo que, se um dos nós falhar, os outros dois ainda possam formar seguramente um quórum e garantir o andamento. Se um cluster tiver menos de três nós elegíveis como mestres, ele não poderá tolerar seguramente a perda de nenhum deles. Em contrapartida, se um cluster tiver muito mais do que três nós elegíveis como mestres, as eleições e atualizações de estado do cluster poderão levar mais tempo.

Evolução ou revolução?

O Elasticsearch nas versões 6.x e anteriores usam um subsistema de coordenação de cluster chamado Zen Discovery. Esse subsistema evoluiu e amadureceu ao longo dos anos e sustenta com sucesso clusters grandes e pequenos. Entretanto, há algumas melhorias que queríamos fazer que exigiam algumas alterações mais fundamentais na maneira como ele funciona.

O Zen Discovery permite que o usuário escolha quantos nós elegíveis como mestres formam um quórum usando a configuração discovery.zen.minimum_master_nodes. É de extrema importância definir essa configuração corretamente em cada nó e atualizá-la corretamente à medida que o cluster é escalonado dinamicamente. O sistema não poderá detectá-lo se um usuário tiver definido essa configuração equivocadamente, e na prática é muito fácil esquecer de ajustá-la depois de adicionar ou remover nós. O Zen Discovery tenta proteger contra esse tipo de configuração errada esperando alguns segundos em cada eleição de mestre e geralmente é muito conservador com outros tempos limites atingidos também. Isso significa que, se um nó mestre eleito falhar, o cluster ficará indisponível por pelo menos alguns segundos cruciais antes de eleger um substituto. Se o cluster não puder eleger um mestre, às vezes será muito difícil entender a causa.

Para o Elasticsearch 7.0, reformulamos e recriamos o subsistema de coordenação de cluster:

  • A configuração minimum_master_nodes é removida em favor de permitir que o próprio Elasticsearch escolha quais nós podem formar um quórum. 
  • Eleições de mestre típicas agora levam bem menos de um segundo para serem concluídas. 
  • A expansão e redução de clusters fica mais segura e fácil e deixa muito menos espaço para configurar o sistema de maneira que cause riscos de perda de dados. 
  • Os nós registram logs de seu status com muito mais clareza para ajudar a diagnosticar por que não podem se associar a um cluster ou por que um mestre não pode ser eleito.

À medida que os nós são adicionados ou removidos, o Elasticsearch mantém automaticamente um nível ideal de tolerância a falhas atualizando a [configuração de votação] do cluster (https://www.elastic.co/guide/en/elasticsearch/reference/7.0/modules-discovery-voting.html). A configuração de votação é um conjunto de nós elegíveis como mestres cujos votos são contados ao tomar uma decisão. Normalmente a configuração de votação contém todos os nós elegíveis como mestres no cluster. Os quóruns são maiorias simples da configuração de votação: todas as atualizações de estado do cluster precisam da concordância de mais de metade dos nós na configuração de votação. Como o sistema gerencia a configuração de votação e, portanto, seus quóruns, ele pode evitar qualquer chance de configuração errada que possa resultar em perda de dados, mesmo se nós são adicionados ou removidos.

Se um nó não puder descobrir um nó mestre e não vencer uma eleição em si, a partir da versão 7.0, o Elasticsearch registrará logs periodicamente com uma mensagem de aviso descrevendo o status atual em detalhes suficientes para ajudar a diagnosticar muitos problemas comuns.

Além disso, o Zen Discovery tinha um modo de falha muito raro, registrado na página de status de resiliência do Elasticsearch como "Repeated network partitions can cause cluster state updates to be lost" (Partições de rede repetidas podem causar a perda de atualizações de estado do cluster), o que não pode mais ocorrer. Esse item agora está marcado como resolvido.

Como usar isso?

Se você iniciar alguns nós recém-instalados do Elasticsearch com configurações totalmente padrão, eles buscarão automaticamente outros nós executados no mesmo host e formarão um cluster após alguns segundos. Se você iniciar mais nós no mesmo host, por padrão eles descobrirão esse cluster e se associarão a ele também. Isso torna tão fácil iniciar um cluster de desenvolvimento de vários nós com o Elasticsearch versão 7.0 quanto acontecia com as versões anteriores.

Esse mecanismo de formação de clusters totalmente automático funciona bem em um único host, mas não é robusto o suficiente para usar em produção ou outros ambientes distribuídos. Há um risco de que os nós possam não fazer a descoberta mútua a tempo e em vez disso possam formar dois ou mais clusters independentes. A partir da versão 7.0, se você quiser iniciar um novo cluster que tenha nós em mais de um host, deverá especificar o conjunto inicial de nós elegíveis como mestres que o cluster deve usar como configuração de votação em sua primeira eleição. Isso é conhecido como cluster bootstrapping e só é obrigatório na primeira vez em que o cluster se forma. Os nós que já se associaram a um cluster armazenam a configuração de votação em suas pastas de dados e a reusam após uma reinicialização, e os nós recém-iniciados que estão se associando a um cluster existente podem receber essas informações de um mestre eleito do cluster.

Para fazer bootstrap de um cluster, defina a configuração cluster.initial_master_nodes com os nomes ou endereços IP do conjunto inicial de nós elegíveis como mestres. Você pode fornecer essa configuração na linha de comando ou no arquivo elasticsearch.yml de um ou mais dos nós elegíveis como mestres. Você também precisará configurar o subsistema de descoberta para que os nós saibam como se descobrir entre si.

Se initial_master_nodes não estiver definido, os nós novos serão iniciados esperando poder descobrir um cluster existente. Se um nó não puder descobrir um cluster para se associar a ele, registrará um log periodicamente com uma mensagem de aviso indicando

master not discovered yet, this node has not previously joined a bootstrapped (v7+) cluster,
and [cluster.initial_master_nodes] is empty on this node

Não há mais nenhum ritual especial necessário para adicionar novos nós elegíveis como mestres a um cluster. Basta configurar os novos nós para descobrir o cluster existente e iniciá-los, assim o cluster adaptará de maneira segura e automaticamente sua configuração de votação quando os novos nós se associarem. Também é seguro remover os nós simplesmente parando-os desde que você não pare metade ou mais dos nós elegíveis como mestres de uma só vez. Se você precisar parar metade ou mais dos nós elegíveis como mestres ou tiver necessidades de escalonamento ou orquestração mais complexas, haverá um procedimento de escalonamento mais direcionado que usa uma API para ajustar a configuração de votação diretamente.

Como fazer a atualização?

Você pode atualizar um cluster do Elasticsearch da versão 6 para a versão 7 através de uma atualização sem interrupção ou uma reinicialização de cluster completo. Recomendamos uma atualização sem interrupção porque isso permite executar a atualização nó a nó, enquanto o cluster permanece disponível. Você deve atualizar o cluster da versão 6 para a versão 6.7 antes de executar uma atualização sem interrupção para a versão 7. Uma reinicialização de cluster completo permite atualizar para a versão 7 a partir de qualquer versão 6.x, mas ela envolve encerrar o cluster inteiro e iniciá-la novamente. Em qualquer dos casos, há muito mais alterações no Elasticsearch entre as versões 6 e 7 do que melhorias na coordenação de cluster descritas aqui. Para garantir uma atualização tranquila, você sempre deve seguir cuidadosamente as [instruções de atualização] detalhadas (https://www.elastic.co/guide/en/elasticsearch/reference/7.0/setup-upgrade.html).

Se você executar uma atualização sem interrupção, o cluster bootstrapping ocorrerá automaticamente, com base no número de nós do cluster e em qualquer configuração minimum_master_nodes existente. Isso significa que é importante garantir que essa configuração seja definida corretamente antes de iniciar a atualização. Não é necessário definir initial_master_nodes aqui porque o cluster bootstrapping ocorre automaticamente ao executar essa atualização sem interrupção. Os nós elegíveis como mestres da versão 7 preferirão votar a favor dos nós de versão 6.7 nas eleições de mestre, por isso você normalmente pode esperar que um nó de versão 6.7 seja o mestre eleito durante a atualização até ter atualizado cada um dos nós elegíveis como mestres.

Se você executar uma atualização de reinicialização de cluster completo, deverá fazer bootstrapping no cluster atualizado conforme descrito anteriormente: para iniciar o cluster recém-atualizado, primeiro defina initial_master_nodes com os nomes ou endereços IP dos nós elegíveis como mestres.

Nas versões 6 e anteriores, há algumas outras configurações que permitem configurar o comportamento do Zen Discovery no namespace discovery.zen.*. Algumas dessas configurações não têm mais nenhum efeito e foram removidas. Outras foram renomeadas. Se uma configuração tiver sido renomeada, o nome antigo estará obsoleto na versão 7 e você deverá ajustar a configuração para usar os novos nomes:

| Old name | New name | | --- | --- | | discovery.zen.ping.unicast.hosts | discovery.seed_hosts | | discovery.zen.hosts_provider | discovery.seed_providers | | discovery.zen.no_master_block | cluster.no_master_block |

O novo subsistema de coordenação de cluster inclui um novo mecanismo de detecção de falhas. Isso significa que as configurações de detecção de falhas do Zen Discovery no namespace discovery.zen.fd.* não têm mais nenhum efeito. A maioria dos usuários deve usar a configuração de detecção de falhas padrão nas versões 7 e posteriores, mas se você precisar fazer quaisquer alterações, poderá fazê-las usando as configurações cluster.fault_detection.*.

Segurança é prioridade

As versões do Elasticsearch anteriores à versão 7.0 às vezes permitiam executar acidentalmente uma sequência de etapas que colocavam a consistência do cluster em risco. Em contrapartida, as versões 7.0 e posteriores deixarão você totalmente ciente de que pode estar fazendo uma ação insegura e exigirão confirmação de que você realmente quer prosseguir.

Por exemplo, um cluster do Elasticsearch 7.0 não fará a recuperação automática se metade ou mais dos nós elegíveis como mestres estiverem permanentemente perdidos. É comum ter três nós elegíveis como mestres em um cluster, permitindo que o Elasticsearch tolere a perda de um deles sem causar inatividade. Se dois deles estiverem permanentemente perdidos, o nó remanescente não poderá fazer nenhum progresso com segurança.

As versões do Elasticsearch anteriores à 7.0 silenciosamente permitiam que um cluster se recuperasse dessa situação. Os usuários podiam colocar o cluster de volta online iniciando novos nós elegíveis como mestres vazios para substituir qualquer quantidade de nós perdidos. Uma recuperação automatizada a partir da perda permanente de metade ou mais dos nós elegíveis como mestres é insegura, porque em nenhum dos nós remanescentes temos a certeza de ter uma cópia do estado de cluster mais recente. Isso pode resultar em perda de dados. Por exemplo, uma cópia de shard pode ter sido removida do conjunto em sincronia. Se nenhum dos nós remanescentes souber disso, essa cópia de shard inválida poderá ser alocada como primária. A parte mais perigosa disso era que os usuários ficavam completamente desapercebidos de que essa sequência de etapas havia colocado em risco o cluster. Podem passar semanas ou meses até um usuário perceber alguma inconsistência.

No Elasticsearch 7.0 e versões posteriores, esse tipo de atividade insegura é muito mais restrita. Os clusters preferirão permanecer indisponíveis em vez de assumir esse tipo de risco. Na rara situação em que não há backups, ainda será possível executar esse tipo de operação insegura se for absolutamente necessário. Bastam apenas algumas etapas adicionais para confirmar se você está ciente dos riscos e evitar a chance de executar uma operação insegura por acidente.

Se você tiver perdido metade ou mais dos nós elegíveis como mestres, a primeira alternativa será tentar colocar de volta online os nós elegíveis como mestres. Se os diretórios de dados dos nós ainda estiverem intatos, a melhor coisa a fazer será iniciar novos nós usando esses diretórios de dados. Se isso for possível, o cluster se formará seguramente outra vez usando o estado de cluster mais atualizado.

A próxima alternativa é tentar restaurar o cluster de um [instantâneo] recente (https://www.elastic.co/guide/en/elasticsearch/reference/7.0/modules-snapshots.html). Isso coloca o cluster em um estado ideal conhecido, mas perde quaisquer dados gravados desde que você tirou o instantâneo. Você pode indexar quaisquer dados ausentes novamente, já que conhece qual é o período de tempo ausente. Os instantâneos são incrementais, por isso você pode executá-los com muita frequência. Não é incomum tirar um instantâneo a cada 30 minutos para limitar a quantidade de dados perdidos nesse tipo de recuperação.

Se nenhuma dessas ações de recuperação for possível, o último recurso será a ferramenta de recuperação insegura elasticsearch-node. Trata-se de uma ferramenta de linha de comando que um administrador de sistema pode executar para realizar ações inseguras como eleger um mestre inválido de uma minoria. Tornando bem explícitas as etapas que podem romper a consistência, o Elasticsearch 7.0 elimina o risco de causar acidentalmente a perda de dados através de uma série de operações inseguras.

Como funciona?

Se você tiver familiaridade com a teoria de sistemas distribuídos, poderá reconhecer a coordenação de cluster como exemplo de um problema que pode ser resolvido usando [consenso distribuído] (https://en.wikipedia.org/wiki/Consensus_(computer_science)).  O consenso distribuído não era tão bem compreendido quando se iniciou o desenvolvimento do Elasticsearch, mas houve um avanço significativo nos últimos anos.

O Zen Discovery adotou muitas ideias dos algoritmos do consenso distribuído, mas o fez de maneira orgânica, e não estritamente seguindo o modelo prescrito pela teoria. Ele também tem tempos limites bastante conservadores, o que às vezes o faz se recuperar muito lentamente após uma falha. A introdução de um novo subsistema de coordenação de cluster na versão 7.0 nos ofereceu a oportunidade de seguir o modelo teórico de forma muito mais próxima.

A coordenação distribuída é conhecida por ser um problema difícil de se resolver corretamente. Dependíamos intensamente de métodos formais para validar nossos designs com antecedência, com um arsenal de ferramentas automatizadas que permitiam sólidas garantias em termos de precisão e segurança. Você pode encontrar as especificações formais do novo algoritmo de coordenação de cluster do Elasticsearch em nosso repositório público de modelos formais do Elasticsearch. O [módulo de segurança] do núcleo (https://github.com/elastic/elasticsearch-formal-models/blob/ca26d5cb4ce9fd8c8b032a11bc849b52a812b6e5/ZenWithTerms/tla/ZenWithTerms.tla) do algoritmo é simples e conciso, e há uma correspondência direta de um para um entre o modelo formal e o código de produção no repositório do Elasticsearch.

Se você tiver conhecimento da família de algoritmos de consenso distribuído que inclui o Paxos, o Raft, o Zab e o Viewstamped Replication (VR), o módulo de segurança do núcleo parecerá familiar. Ele modela um único registro regravável e usa uma noção de um termo de mestre que cria um paralelo entre os ballots do Paxos, os termos do Raft e as visões do VR. O módulo de segurança do núcleo e seu modelo formal também trata de cluster bootstrapping, persistência entre reinicializações de nós e reconfiguração dinâmica. Todos esses recursos são importantes para garantir que o sistema se comporte corretamente em todas as circunstâncias.

Em torno desse núcleo teoricamente robusto, criamos uma camada de liveness para garantir que, independentemente de quais falhas ocorram no cluster, depois que a rede for restaurada e nós suficientes estiverem online, um mestre será eleito e poderá publicar atualizações de estado de cluster. A camada de liveness usa inúmeras técnicas avançadas para evitar muitos problemas comuns. O agendador de eleições é adaptável, alterando o comportamento de acordo com as condições da rede para evitar um excesso de eleições contestadas. Um turno de pré-votação ao estilo do Raft suprime eleições sem vencedores antes mesmo que comecem, evitando a interrupção por nós zumbi. A detecção de atrasos evitará que os nós interrompam o cluster se ficarem muito longe atrás do mestre. A detecção de falhas bidirecional ativa garante que os nós no cluster sempre possam se comunicar mutuamente. A maioria das atualizações de estado do cluster é publicada com eficiência como pequenos diffs, evitando a necessidade de copiar todo o estado de cluster de um nó a outro. Líderes que sejam dispensados de maneira elegante abdicarão explicitamente em favor de um sucessor de sua escolha, reduzindo a inatividade durante um failover deliberado evitando a necessidade de uma eleição completa. Nós desenvolvemos a infraestrutura de testes para simular com eficiência os efeitos de interrupções patológicas que poderiam durar segundos, minutos ou horas, o que nos permite verificar se o cluster sempre se recupera rapidamente depois que a interrupção é resolvida.

Por que não o Raft?

Uma pergunta comum que nos fazem é por que simplesmente não "conectamos" em um algoritmo de consenso distribuído padrão como o Raft. Existem muitos algoritmos bem conhecidos, cada um deles oferecendo vantagens e desvantagens diferentes. Nós cuidadosamente avaliamos e nos inspiramos em toda a literatura disponível. Uma das nossas primeiras provas de conceito usava um protocolo bastante próximo ao Raft. Aprendemos com essa experiência que as alterações necessárias para sua total integração com o Elasticsearch se revelaram bastante substanciais. Muitos dos algoritmos padrão também prescrevem algumas decisões de design que seriam abaixo das ideais para o Elasticsearch. Por exemplo:

  • São frequentemente estruturados em torno de um log de operações, enquanto a coordenação de cluster do Elasticsearch naturalmente mais se baseia diretamente no estado do cluster em si. Isso permite otimizações vitais como batching (combinar operações relacionadas em uma única transmissão) de maneira mais simples do que seria possível se fossem baseadas em operações.
  • Frequentemente têm uma capacidade bem restrita para expandir ou reduzir clusters, exigindo uma sequência de etapas para alcançar muitas tarefas de manutenção, enquanto a coordenação de cluster do Elasticsearch pode seguramente executar reconfigurações arbitrárias em uma única etapa. Isso simplifica o sistema adjacente evitando estados intermediários problemáticos.
  • Frequentemente se concentram intensamente na segurança, deixam abertos os detalhes de como garantir liveness e não descrevem como o cluster deve reagir se encontrar um nó sem integridade. As verificações de integridade do Elasticsearch são complexas, depois de serem usadas e refinadas em campo por muitos anos, e era importante para nós preservar seu comportamento existente. Na realidade for exigido muito menos esforço para implementar as propriedades de segurança do sistema do que garantir liveness. A maior parte do esforço de implementação se concentrou nas propriedades de liveness do sistema.
  • Um dos objetivos do projeto foi oferecer suporte a uma atualização sem interrupção de inatividade zero de um cluster de versão 6.7 executando o Zen Discovery para um cluster de versão 7 executando o novo subsistema de coordenação. Não parecia viável adaptar algum dos algoritmos padrão em um que permitisse esse tipo de atualização sem interrupção.

Uma implementação completa de nível industrial de um algoritmo de consenso distribuído exige esforço substancial para se desenvolver e precisa ir além do que prega a literatura acadêmica. É inevitável que personalizações serão necessárias na prática, mas os protocolos de coordenação são complexos e quaisquer personalizações correm o risco de implantar erros. Por fim fez bastante sentido do ponto de vista da engenharia tratar essas personalizações como o desenvolvimento de um novo protocolo.

Resumo

O Elasticsearch 7.0 é fornecido com um novo subsistema de coordenação de cluster que é mais rápido, seguro e simples de usar. Ele oferece suporte a atualizações em andamento de inatividade zero a partir da versão 6.7 e proporciona a base para a replicação de dados resiliente. Para experimentar o novo subsistema de coordenação de cluster, faça download da versão beta 7.0 mais recente, consulte os documentos, experimente ele e faça seus comentários.