Como nós da CIAN domesticamos terabytes de logs

Como nós da CIAN domesticamos terabytes de logs

Olá a todos, meu nome é Alexander, trabalho na CIAN como engenheiro e estou envolvido na administração de sistemas e automação de processos de infraestrutura. Nos comentários de um dos artigos anteriores, fomos solicitados a informar onde obtemos 4 TB de logs por dia e o que fazemos com eles. Sim, temos muitos logs e um cluster de infraestrutura separado foi criado para processá-los, o que nos permite resolver problemas rapidamente. Neste artigo falarei sobre como o adaptamos ao longo de um ano para trabalhar com um fluxo de dados cada vez maior.

Por onde começamos?

Como nós da CIAN domesticamos terabytes de logs

Nos últimos anos, a carga no cian.ru cresceu muito rapidamente e, no terceiro trimestre de 2018, o tráfego de recursos atingiu 11.2 milhões de usuários únicos por mês. Naquela época, em momentos críticos perdíamos até 40% dos logs, por isso não conseguíamos lidar rapidamente com os incidentes e despendíamos muito tempo e esforço para resolvê-los. Muitas vezes também não conseguíamos encontrar a causa do problema e ele reaparecia depois de algum tempo. Era um inferno e algo precisava ser feito a respeito.

Naquela época, usamos um cluster de 10 nós de dados com ElasticSearch versão 5.5.2 com configurações de índice padrão para armazenar logs. Foi apresentado há mais de um ano como uma solução popular e acessível: então o fluxo de logs não era tão grande, não adiantava criar configurações fora do padrão. 

O processamento dos logs recebidos foi fornecido pelo Logstash em diferentes portas em cinco coordenadores ElasticSearch. Um índice, independentemente do tamanho, consistia em cinco fragmentos. Foi organizada uma rotação horária e diária, como resultado, cerca de 100 novos fragmentos apareciam no cluster a cada hora. Embora não houvesse muitos registros, o cluster funcionou bem e ninguém prestou atenção às suas configurações. 

Os desafios do rápido crescimento

O volume de logs gerados cresceu muito rapidamente, à medida que dois processos se sobrepunham. Por um lado, o número de utilizadores do serviço cresceu. Por outro lado, começamos a mudar ativamente para uma arquitetura de microsserviços, serrando nossos antigos monólitos em C# e Python. Várias dezenas de novos microsserviços que substituíram partes do monólito geraram significativamente mais logs para o cluster de infraestrutura. 

Foi o dimensionamento que nos levou ao ponto em que o cluster se tornou praticamente incontrolável. Quando os logs começaram a chegar a uma taxa de 20 mil mensagens por segundo, a rotação frequente e inútil aumentou o número de shards para 6 mil, e havia mais de 600 shards por nó. 

Isso gerou problemas com a alocação de RAM e, quando um nó travava, todos os shards começavam a se mover simultaneamente, multiplicando o tráfego e carregando outros nós, o que tornava quase impossível gravar dados no cluster. E nesse período ficamos sem toras. E se houvesse algum problema com o servidor, basicamente perdíamos 1/10 do cluster. Um grande número de pequenos índices adicionou complexidade.

Sem logs, não entendíamos os motivos do incidente e poderíamos, mais cedo ou mais tarde, pisar novamente no mesmo rake, e na ideologia de nossa equipe isso era inaceitável, pois todos os nossos mecanismos de trabalho são projetados para fazer exatamente o contrário - nunca repetir os mesmos problemas. Para isso, precisávamos do volume total de logs e de sua entrega quase em tempo real, já que uma equipe de engenheiros de plantão monitorava os alertas não só das métricas, mas também dos logs. Para entender a dimensão do problema, naquela época o volume total de logs era de cerca de 2 TB por dia. 

Estabelecemos a meta de eliminar completamente a perda de logs e reduzir o tempo de entrega ao cluster ELK para um máximo de 15 minutos em caso de força maior (posteriormente contamos com esse número como um KPI interno).

Novo mecanismo de rotação e nós quentes

Como nós da CIAN domesticamos terabytes de logs

Iniciamos a conversão do cluster atualizando a versão do ElasticSearch de 5.5.2 para 6.4.3. Mais uma vez, nosso cluster da versão 5 morreu e decidimos desligá-lo e atualizá-lo completamente - ainda não há logs. Então fizemos essa transição em apenas algumas horas.

A transformação de maior escala neste estágio foi a implementação do Apache Kafka em três nós com um coordenador como buffer intermediário. O corretor de mensagens nos salvou da perda de logs durante problemas com o ElasticSearch. Ao mesmo tempo, adicionamos 2 nós ao cluster e mudamos para uma arquitetura quente-quente com três nós “quentes” localizados em racks diferentes no data center. Redirecionamos os logs para eles usando uma máscara que não deve ser perdida em nenhuma circunstância - nginx, bem como logs de erros do aplicativo. Logs menores foram enviados para os nós restantes - depuração, aviso, etc., e após 24 horas, logs “importantes” dos nós “quentes” foram transferidos.

Para não aumentar o número de índices pequenos, mudamos da rotação temporal para o mecanismo de rollover. Havia muita informação nos fóruns de que a rotação por tamanho do índice não é confiável, então decidimos usar a rotação pelo número de documentos no índice. Analisamos cada índice e registramos a quantidade de documentos após os quais a rotação deveria funcionar. Assim, atingimos o tamanho ideal do fragmento - não mais que 50 GB. 

Otimização de cluster

Como nós da CIAN domesticamos terabytes de logs

No entanto, não nos livramos completamente dos problemas. Infelizmente, ainda apareceram pequenos índices: não atingiram o volume especificado, não foram girados e foram excluídos pela limpeza global de índices com mais de três dias, pois removemos a rotação por data. Isso levou à perda de dados devido ao fato de o índice do cluster ter desaparecido completamente e uma tentativa de gravar em um índice inexistente quebrou a lógica do curador que usamos para gerenciamento. O alias de escrita foi convertido em índice e quebrou a lógica de rollover, causando crescimento descontrolado de alguns índices de até 600 GB. 

Por exemplo, para a configuração de rotação:

сurator-elk-rollover.yaml

---
actions:
  1:
    action: rollover
    options:
      name: "nginx_write"
      conditions:
        max_docs: 100000000
  2:
    action: rollover
    options:
      name: "python_error_write"
      conditions:
        max_docs: 10000000

Se não houvesse nenhum alias de rollover, ocorreu um erro:

ERROR     alias "nginx_write" not found.
ERROR     Failed to complete action: rollover.  <type 'exceptions.ValueError'>: Unable to perform index rollover with alias "nginx_write".

Deixamos a solução desse problema para a próxima iteração e abordamos outra questão: mudamos para a lógica pull do Logstash, que processa os logs recebidos (removendo informações desnecessárias e enriquecendo). Colocamos no docker, que iniciamos via docker-compose, e também colocamos lá o logstash-exporter, que envia métricas ao Prometheus para monitoramento operacional do fluxo de logs. Dessa forma, nos demos a oportunidade de alterar suavemente o número de instâncias de logstash responsáveis ​​​​pelo processamento de cada tipo de log.

Enquanto melhorávamos o cluster, o tráfego do cian.ru aumentou para 12,8 milhões de usuários únicos por mês. Como resultado, descobriu-se que nossas transformações ficaram um pouco atrasadas em relação às mudanças na produção, e nos deparamos com o fato de que os nós “quentes” não conseguiam lidar com a carga e retardavam toda a entrega das toras. Recebemos dados “quentes” sem falhas, mas tivemos que intervir na entrega do restante e fazer um rollover manual para distribuir uniformemente os índices. 

Ao mesmo tempo, dimensionar e alterar as configurações das instâncias do logstash no cluster foi complicado pelo fato de ser um docker-compose local e todas as ações serem executadas manualmente (para adicionar novas extremidades, era necessário passar manualmente por todos os servidores e faça docker-compose up -d em todos os lugares).

Redistribuição de log

Em setembro deste ano, ainda estávamos cortando o monólito, a carga no cluster aumentava e o fluxo de logs se aproximava de 30 mil mensagens por segundo. 

Como nós da CIAN domesticamos terabytes de logs

Começamos a próxima iteração com uma atualização de hardware. Passamos de cinco coordenadores para três, substituímos nós de dados e ganhamos em termos de dinheiro e espaço de armazenamento. Para nós, usamos duas configurações: 

  • Para nós “quentes”: E3-1270 v6/SSD de 960 Gb/32 Gb x 3 x 2 (3 para Hot1 e 3 para Hot2).
  • Para nós “quentes”: E3-1230 v6 / SSD de 4 TB / 32 Gb x 4.

Nessa iteração, movemos o índice com logs de acesso de microsserviços, que ocupa o mesmo espaço que os logs nginx da linha de frente, para o segundo grupo de três nós “quentes”. Agora armazenamos dados em nós “quentes” por 20 horas e depois os transferimos para nós “quentes” para o restante dos logs. 

Resolvemos o problema do desaparecimento de pequenos índices reconfigurando sua rotação. Agora os índices são girados a cada 23 horas em qualquer caso, mesmo que haja poucos dados. Isso aumentou ligeiramente o número de fragmentos (havia cerca de 800 deles), mas do ponto de vista do desempenho do cluster é tolerável. 

Como resultado, havia seis nós “quentes” e apenas quatro nós “quentes” no cluster. Isso causa um ligeiro atraso nas solicitações em longos intervalos de tempo, mas aumentar o número de nós no futuro resolverá esse problema.

Essa iteração também corrigiu o problema da falta de escalonamento semiautomático. Para fazer isso, implantamos um cluster Nomad de infraestrutura – semelhante ao que já implantamos em produção. Por enquanto, a quantidade de Logstash não muda automaticamente dependendo da carga, mas chegaremos a isso.

Como nós da CIAN domesticamos terabytes de logs

Planos para o futuro

A configuração implementada é perfeitamente dimensionada e agora armazenamos 13,3 TB de dados - todos os logs por 4 dias, o que é necessário para análise emergencial de alertas. Convertemos alguns dos logs em métricas, que adicionamos ao Graphite. Para facilitar o trabalho dos engenheiros, contamos com métricas para o cluster de infraestrutura e scripts para reparo semiautomático de problemas comuns. Após aumentar o número de nós de dados, previsto para o próximo ano, passaremos para o armazenamento de dados de 4 para 7 dias. Isso será suficiente para o trabalho operacional, pois procuramos sempre investigar os incidentes o mais rápido possível, e para investigações de longo prazo existem dados de telemetria. 

Em outubro de 2019, o tráfego do cian.ru já havia crescido para 15,3 milhões de usuários únicos por mês. Isso se tornou um teste sério da solução arquitetônica para entrega de logs. 

Agora estamos nos preparando para atualizar o ElasticSearch para a versão 7. Porém, para isso teremos que atualizar o mapeamento de vários índices no ElasticSearch, pois eles migraram da versão 5.5 e foram declarados obsoletos na versão 6 (eles simplesmente não existem na versão 7). Isso significa que durante o processo de atualização certamente ocorrerá algum tipo de força maior, que nos deixará sem registros enquanto o problema é resolvido. Da versão 7, estamos mais ansiosos pelo Kibana com uma interface melhorada e novos filtros. 

Atingimos nosso objetivo principal: paramos de perder logs e reduzimos o tempo de inatividade do cluster de infraestrutura de 2 a 3 falhas por semana para algumas horas de trabalho de manutenção por mês. Todo esse trabalho de produção é quase invisível. No entanto, agora podemos determinar exatamente o que está acontecendo com nosso serviço, podemos fazê-lo rapidamente em modo silencioso e não nos preocupar com a perda dos logs. No geral estamos satisfeitos, felizes e nos preparando para novas façanhas, das quais falaremos mais tarde.

Fonte: habr.com

Adicionar um comentário