Upgrade sem degradação do cluster de Elasticsearch
Como trocar uma peça ou consertar um carro enquanto ele está à 150 km/h em uma rodovia movimentada? — essa pergunta define bem o assunto deste artigo.
Muitas vezes (as vezes mais do que imaginamos) precisamos fazer o upgrade ou a manutenção de um serviço de alta disponibilidade que atende diversas aplicações (simples ou críticas) em produção, sem afetar nenhuma delas e sem comprometer a consistência do serviço. Sabemos (ou não, o que pode ser pior) o quão complexo e problemático essa tarefa pode ser.
Caso de uso com Elasticsearch
O elasticsearch é um motor de busca extremamente poderoso, tem seu processamento distribuído e a capacidade de se recuperar rapidamente de falhas sem necessidade de intervenção humana. Mas, alterar sua configuração, atualizar sua versão, adicionar plugins, dentre outras ações, pode exigir uma reinicialização completa do cluster para que tenham efeito. Vamos entender como fazer isso da melhor maneira.
Rolling Upgrade
Para tal tarefa, utilizamos a técnica de Rolling Upgrade, que significa, no contexto de aplicações distribuídas, permitir a atualização/manutenção de um serviço ou aplicação sem downtime. Seu conceito possui basicamente 4 etapas que devem ser feitas de maneira sequencial, instância por instância, até percorrer o cluster completamente:
- Drenagem das conexões com a instância;
- Remoção da instância do cluster;
- Atualização/manutenção da instância;
- Inserção da instância novamente no cluster.
Alguns pontos importantes de citar sobre essas etapas:
- A drenagem e a remoção, em alguns casos, podem acontecer juntas;
- A atualização, normalmente quando o serviço em execução está em memória, pode ser executada antes da drenagem/remoção. Caso a atualização/manutenção somente seja passível de ser executada com a instância parada, o processo deve ser mantido na ordem inicial.
Executando Rolling Upgrade em um Cluster de Elasticsearch
Os passos abaixo devem ser executados em todas as instâncias/nós do cluster, independentemente do tipo dessa instância (seja um nó coordenador (client node), de dados (data node) ou gerenciador (master node). Após executar o procedimento em uma instância, aguarde a recomposição do cluster para o status green para prosseguir para o próximo nó. Durante parte do processo o seu cluster estará com o status yellow, o que significa que ele está operacional com todos shards primários ativos, porém com réplicas desalocadas.
1. Desativar a alocação de shards
O master tentará fazer a realocação dos shards da instância para outra instância, como sabemos que estamos fazendo a manutenção e em poucos instantes o nó voltará para o cluster, desabilitando a alocação de shards garantimos que processamentos desnecessários de I/O sejam feitos e que, após atualizado, o cluster retornará mais rapidamente para o estado saudável (green); Podemos desativar a alocação via API enviando a seguinte requisição:
PUT _cluster/settings
{
"persistent": {
"cluster.routing.allocation.enable": "none"
}
}
2. Parar (se possível) aplicações de indexação
Quanto menos indexação estiver ocorrendo durante a indisponibilidade da instância, mais rápida será a inicialização do nó, a recuperação dos shards e a saúde do cluster; Se for possível, pare os processos de indexação, seja o logstash, beats, outro ETL/data shipper ou aplicação/processo responsável pela indexação de dados. Se não for possível, tudo bem, isso não impedirá os próximos passos.
3. Executar synced flush
Executar um synced flush, ou pessimamente traduzido para nivelamento sincronizado, significa, resumidamente, equilibrar os shards primários com suas réplicas (normal flush) e marcar os todos esses shards com um id único sync_id
. O benefício dessa operação é ter uma rápida comparação dos shards utilizando esse identificador durante o processo de reinicialização ou recuperação, garantindo que eles estão equilibrados e iguais em um certo momento mais próximo.
Podemos executar um synced flush via API enviando:
POST _flush/synced
Devemos ter atenção se houve alguma falha na resposta, se tiver, precisamos repetir a operação até que ocorra sucesso em todos os shards. Veja o exemplo de uma resposta onde ocorrem falhas:
{
"_shards": {
"total": 418,
"successful": 384,
"failed": 34
}
}
4. Parar o serviço
Agora podemos parar a instância atual para fazer a manutenção/atualização. Veja um exemplo de como parar o serviço em distribuições linux que utilizam systemd
para gerenciar serviços:
sudo systemctl stop elasticsearch
5. Executar atualização ou manutenção na instância
É nesta etapa que você faz a atualização efetiva ou manutenção da instância de acordo com a sua necessidade.
Importante ressaltar a obrigação de sempre planejar milimetricamente cada manutenção. Ter atenção em fazer uma cópia (backup) das configurações ou do estado anterior do que foi modificado e uma estratégia de rollback na manga.
Lembrando que, na maioria dos casos, é possível efetuar a manutenção antes da primeira etapa, já que o elasticsearch pode sofrer diversos tipos de alterações (como em plugins, configurações, upgrade de versão, etc) com a instância em execução e somente tendo efeito nas alterações após a reinicialização do serviço.
6. Iniciar o serviço
Terminamos de fazer as alterações, então, já podemos iniciar a instância. Abaixo exemplo da inicialização do serviço:
sudo systemctl start elasticsearch
7. Monitorar inicialização da instância
É importante aguardar a inicialização da instância para reabilitar a alocação. Você pode verificar o status do serviço garantindo que está em execução e, após alguns segundos, obter os dados das instâncias participantes do cluster verificando se todos estão aparecendo, garantindo que estão se comunicando e no cluster.
sudo systemctl status elasticsearch
curl http://localhost:9200/_cat/nodes?v
Caso o segundo comando não tenha resultado imediato, aguarde mais alguns segundos e tente novamente. O tempo pode variar de acordo com a sua capacidade de hardware. Se estiver demorando ou tiver dúvidas sobre o estado do nó, de uma olhada nos logs do elasticsearch (normalmente localizado em /var/log/elasticsearch
) pra ver se realmente está tudo bem.
8. Ativar a alocação de shards
A partir do momento que a instância estiver de volta no cluster, devemos reabilitar a alocação de shards, enviando via API:
PUT _cluster/settings
{
"persistent": {
"cluster.routing.allocation.enable": "all"
}
}
9. Monitorar a saúde do cluster
Acesse o estado do cluster via API (se possível, tenha um dashboard que te mostre esses dados em tempo real):
GET _cluster/health?pretty
Exemplo da resposta:
{
"cluster_name" : "my-cluster",
"status" : "green",
"timed_out" : false,
"number_of_nodes" : 30,
"number_of_data_nodes" : 23,
"active_primary_shards" : 209,
"active_shards" : 418,
"relocating_shards" : 0,
"initializing_shards" : 0,
"unassigned_shards" : 0,
"delayed_unassigned_shards" : 0,
"number_of_pending_tasks" : 0,
"number_of_in_flight_fetch" : 0,
"task_max_waiting_in_queue_millis" : 0,
"active_shards_percent_as_number" : 100.0
}
Aguarde até que o status
esteja green para prosseguir com a manutenção no próximo nó.
Focado em elasticsearch, podemos ler mais sobre Rolling Upgrade na documentação oficial: https://www.elastic.co/guide/en/elasticsearch/reference/current/rolling-upgrades.html
Conclusão
O Rolling Upgrade é bem fácil de ser feito, apesar de exigir um tanto de paciência de acordo com o tamanho do seu cluster. Porém, qualquer erro pode comprometer a disponibilidade do seu serviço ou até mesmo danificar uma instância. Dependendo da criticidade do seu serviço e do problema (leia-se “cagada”) que você causar, seu cluster como um todo também pode ser danificado; Por isso, não se esqueça, o ponto mais importante é o planejamento, para que esse passo-a-passo seja executado com sucesso. Entenda o motivo da manutenção, faça todo planejamento, teste todas as mudanças em um ambiente de homologação, e, se não tiver, teste localmente ou utilizando docker. Garanta que a sua estratégia de rollback nunca saia do papel.