Uma das features mais importantes de uma plataforma de APIs é o trace. Por meio do trace, é possível extrair métricas, insights, fazer troubleshooting e acompanhar a saúde das APIs. Aqui na Sensedia, utilizamos a stack da Elastic [https://www.elastic.co/]. Nosso cenário tem alguns números interessantes:8.6 TB de dados4.6 Bilhões de documentos43 Milhões de documentos ingeridos/dia - fora as réplicasÉ óbvio que com o volume, vieram os desafios. A tarefa de administrar o ElasticSearch (ES daqui para frente) não é nada simples. Neste artigo, abordo alguns pontos importantes sobre o uso, manutenção e monitoramento de um cluster ES.
Atualmente, nosso cluster é composto por 16 nodes(previsão de crescimento a curto prazo :) ):* 3 node masters dedicados* 3 node clients* 10 node dataOs node master tem a função de gerenciar o cluster. Para ser sincero, somente um node master é eleito e os outros nodes são elegíveis a master (caso o master atual falhe). A decisão do número ímpar é para prevenir o chamado split brain. Já os nodes clients tem a função de receber as requests, processá-las e responder aos solicitantes. Por último, os nodes data são responsáveis por armazenar os dados. É importante notar que os nodes estão divididos em duas zonas geográficas diferentes.
Utilizamos AWS como principal parceiro cloud. Em 2017, foi disponibilizado a família i3[https://aws.amazon.com/ec2/instance-types/i3/]. Esse tipo de instância é otimizada para uso de storage e utiliza NVMe [https://searchstorage.techtarget.com/definition/NVMe-non-volatile-memory-express]. O IO é muito maior. Isso mudou nossas vidas (literalmente). O ES utiliza a engine Lucene e este, salva os dados por segmentos. Isso implica num IO agressivo no disco. Utilizar esse tipo de instância exige determinados cuidados. Esses discos são efêmeros. Isso significa que ao reiniciar um node, todos os dados são apagados. Por esse (e vários outros) motivos, devemos utilizar réplicas (trataremos sobre isso mais a frente).
O lucene abre um segmento para cada escrita no cluster. Esse segmento é imutável, o que significa que na próxima escrita, um novo segmento será aberto. De tempos em tempos, o lucene faz um merge desses segmentos. Esse processo é feito criando um terceiro segmento com o conteúdo dos dois segmentos antigos e os apaga logo em seguida. Isso significa que o filesystem deve trabalhar bem grande quantidade de arquivos pequenos e inodes. Para bases pequenas, o ext4 deve ser mais do que suficiente. Para bases grandes, o ZFS ou XFS podem ser melhores opções. Importante notar que esses file system, normalmente, possuem um custo de CPU maior.ElasticSearch IndexElasticsearch ShardsElasticsearch ShardsElasticsearch ShardsElasticsearch ShardsLucene indexLucene indexLucene indexLucene indexSegmentSegmentSegmentSegmentSegmentSegmentSegmentSegment
Existe a opção de utiliza Java client para interagir com os nodes client (utilizando a porta 9300 TCP). Essa opção porém, é ligeiramente mais rápida que a interface REST. Utilizar http traz diversos ganhos:* Possibilidade de fazer balanceamento server-side* Acompanhamento de métricas como latência, quantidade de erros x sucesso etc* Também suporta bulk insertNas palavras da própria Elastic:
We strongly encourage the use of HTTP over the node protocol for a number of reasons. HTTP is only marginally slower, yet far easier to administer and work with. When using the HTTP protocol one may upgrade Elasticsearch versions without having to upgrade Logstash in lock-step.
Ao fazer um bulk insert, somente um segmento é criado. Isso implica em uma quantidade menores de merge e, por consequência, melhor performance na indexação.
Obviamente é possível ter uma interface de escrita direta com o ES direto na aplicação, porém, você terá que se preocupar com itens essenciais como o bulk insert. Uma excelente opção é usar um broker em conjunto com um consumer. Uma excelente ferramenta para isso é o logstash [https://www.elastic.co/products/logstash]. Ele permite, nativamente, a parametrização de diversas configurações que podem ajudar na performance do cluster no momento de inserção de dados. Além disso, possui diversos filtros e a possibilidade de inserir código customizado utilizando ruby.
Os dados no ES são salvos em índices. Cada índice pode ser considerado como um "database". Que possui sua próprias configurações e pode/deve ter cuidados especiais. As configurações do índice são setadas no momento de sua criação. Não é possível alterá-lo após a criação (seria necessário reindex os dados, que, na prática, é criar um novo índice). É muito importante mapear os dados inseridos e dividi-los em índices. Isso é crucial para sustentabilidade e operação do cluster a médio/longo prazo.
Os índices são armazenados em shards. Por padrão, o ES cria 5 shards para cada índice. Isso pode (e deve) ser otimizado. O número excessivo de shards é prejudicial a saúde do cluster. Não há um consenso a respeito do tamanho ideal de um shard, mas há um denominador indicando que cada shard deve possuir entre 30 ~ 50 GB no máximo. Utilizando essa métrica, diminuímos a quantidade de shards em ~60%. Isso trouxe um ganho enorme na performance do cluster.
Cada shard pode possuir uma ou mais réplicas. Como utilizamos duas zonas geográficas, utilizamos 1 réplica. Imagine um cenário onde os shards não são otimizados? O número de shards é multiplicado pelo número de réplicas. Isso seria ainda mais prejudicial à performance do cluster.
Essa configuração é extremamente importante. Por meio dela, definimos que a réplica de um determinado shard deve estar em uma zona geográfica diferente. Isso garante a redundância total do dados em outro data centes. Sem isso, corremos o risco (e isso vai acontecer) da réplica de um shard ser alocado em uma máquina na mesma região. Caso essa região fique indisponível, nossa estratégia de redundância cai por terra.
O ES suporta a criação de templates[https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-templates.html] para criação de índices. Esses templates são configurados via API. Seu uso trás diversos benefícios, haja visto que podemos otimizar a forma como os dados são salvos. Além disso, podemos aplicar configurações aos índices (algo muito importante, pois deve ser feito no momento da criação do índice). A quantidade de shards e réplicas é definido via template. A tipagem de dados também é bem importante para uma melhor performance para indexação no índice invertido[https://www.elastic.co/guide/en/elasticsearch/guide/current/inverted-index.html].
Utilizamos diversas ferramentas para monitorar o ES:* API nativa. O ES possui uma API que externaliza diversas informações importantes.* Zabbix. Existem templates com exporters externos que trazem informações sobre a saúde do cluster, uso de memória, quantidade de merges etc.* Prometheus + Grafana. O exporter do prometheus para ES é bem completo e trás um detalhamento bem grande sobre o cluster.* Cerebro. Esse plugin é excelente para acompanhar o cluster e executar algumas ações. Trás uma visão gráfica a respeito dos índices x shards x réplicas.
* Heap de memória* Quantidade de merges* Status do cluster [https://www.elastic.co/guide/en/elasticsearch/reference/current/cluster-health.html]* IO de disco* Events rate* Latência das consultas
O curator é uma ferramenta que auxilia na execução de atividades operacionais no cluster. Com ele, podemos fazer backup e expurgo de dados. A ferramenta tem suporte nativo a expurgo de dados por tempo, tamanho e outros critérios.
O ES possui suporte a plugins para estender funcionalidades. Utilizamos o plugin cloud-aws-repository para fazer snapshots diários do cluster. Esse plugin permite que os dados do snapshot sejam salvos diretamente em um bucket do S3. Também utilizamos o curator para acionar as APIs de backup.
Obviamente, sempre existe espaço para melhoria e estamos evoluindo constantemente o cluster. Com esses inputs, atingimos um nível interessante de operação e sustentabilidade de um cluster com essa quantidade de dados. Nossas queries tem um tempo de resposta muito bom e, em 99% do tempo, nosso trace é near real time. Nossos downtimes também foram reduzidos à zero (exceto programados rs).