Não precisa de logs?

O desenvolvimento mudou muito nos últimos anos. Em vez de aplicativos monolíticos, microsserviços e funções vieram. Os bancos de dados de monstros industriais universais degeneraram em alvos estreitos. Docker mudou de idéia sobre a implantação. Mas nossa ideia de logs mudou?

Um dos grandes problemas do Yandex.Verticals eram os logs - 18 TB por dia e 250.000 logs por segundo, tudo é gravado em arquivos. Os logs são heterogêneos porque existem muitas linguagens: Scala, Java, Python, Go. Em seguida, eles são coletados pela Fluent Bit, escreve em Kafka, manipuladores trabalham em uma máquina de ferro, montam a partir de Kafka e escrevem tudo em disco. Além disso, esta é a segunda versão dos logs.



Como resultado, surge um longo problema de pesquisa. Esses logs são pesquisados ​​usando grep. Em alguns serviços, o grep pode chegar a horas. Se você tiver problemas na produção, não procurará seus registros por horas. Para resolver o problema, a Yandex decidiu escrever sua própria bicicleta de entrega de logs para pesquisa. O que aconteceu com Alexei Danilov ( danevge ) - o desenvolvedor da equipe de infraestrutura da Yandex.Verticals. Desenvolve, escreve e suporta projetos auto.ru e Yandex.Real Estate.

Isenção de responsabilidade. O artigo fala sobre desenvolvimento moderno e é adequado para arquitetura de microsserviço. Vários produtos são apresentados aqui - essas são as ferramentas usadas no Yandex.Verticals. Sob outras condições, os análogos são possíveis com mais êxito, mas eles executam quase as mesmas funções.
Nota O artigo é uma versão estendida do relatório de Alexey Danilov, “Logs não são necessários” no RIT ++ 2019 DevOps Conf, que é estilisticamente modificado e complementado com novo material. Você pode encontrar a gravação em vídeo do discurso de Alexey no link do nosso canal do YouTube.

A equipe do Yandex.Vertical tem 300 pessoas, cerca de 100 delas são desenvolvedores. No desenvolvimento, não somos diferentes da maioria das empresas que criam suas próprias soluções de produtos. Microsserviços, todo mundo mora no Docker, um monólito em PHP está acumulando poeira em um canto escuro, implantado via Hashicorp Nomad e mantemos um zoológico de idiomas: Scala, Java, Go, Node.js, Python.

Um dos grandes problemas de infraestrutura do Yandex.Verticals são os logs de aplicativos. Quando abordamos esse problema com seriedade, usamos a terceira versão de sua coleta e processamento. Simplificado, funcionou assim:

  • aplicativos gravados em arquivos;
  • O Fluent Bit leu os arquivos e os enviou linha a linha para o Kafka;
  • Em uma máquina de ferro dedicada, havia um aplicativo que lia o tópico Kafka e escrevia nos arquivos do disco.

Na estação quente, tínhamos 18 TB de toras por dia, ou 250.000 linhas por segundo. Essa é uma quantidade muito grande, o que complica o trabalho com esses dados. A única maneira de analisar isso é grep, pois tudo é armazenado em arquivos. Para aplicativos grandes, a análise pode levar horas. Para problemas na produção, você não tem esse tempo.

Soluções prontas não cabiam em preço, recursos ou velocidade. Eles não podiam lidar de maneira aceitável com o nosso fluxo. É difícil até contar o número de tentativas de cozinhar o Elasticsearch. Suponho que não sabemos como cozinhá-lo. Mas não é disso que precisamos, se para usá-lo como um repositório de logs, são necessárias habilidades (habilidades) especiais.

Nessa situação, decidimos implementar nosso próprio sistema para coletar e analisar logs.

Bicicleta




Nota: Se a próxima bicicleta não for interessante, prossiga imediatamente para a seção "Tipificação".

Formatar


Usamos vários PLs e adoramos microsserviços. Para trabalhar com os logs, formamos uniformemente nosso próprio formato JSON. Ele cobre a maioria das necessidades de trabalho adicional com logs.


Um exemplo de logs com todos os campos possíveis.

Driver de log do Docker


Para coletar os logs, escrevemos nosso próprio driver de log do docker - um aplicativo no Go. Ele é montado de uma maneira especial, entregue por comandos do plugin docker, armazenados no registro e executado em uma única instância executando o Docker.

Como qualquer problema com o driver de log pode afetar negativamente todo o trabalho, tentamos escrever uma implementação mínima. Nosso driver escuta o stdout do contêiner e passa imediatamente os logs para o aplicativo que está próximo. Ele já lida com a parte mais complexa da entrega.

Os problemas


Mencionarei separadamente os problemas de atualização da versão do driver de log do docker.


Captura de tela do Grafana interno.

À esquerda está a proporção de versões instaladas para máquinas. Agora, três versões estão instaladas em todo o hardware - nenhum carro é perdido em lugar nenhum e não há instalações desnecessárias. À direita está o número de contêineres que usam esta ou aquela versão.

O driver da janela de encaixe não pode ser atualizado imediatamente. Para fazer isso, você precisaria reiniciar todos os contêineres e todos os serviços, o que poderia resultar em problemas. Portanto, para instalar a nova versão, basta aguardar a atualização de todos os contêineres.

Esquema geral


Considere o esquema geral de um novo sistema para coletar e entregar logs. Outros detalhes não são tão interessantes.



Os aplicativos gravam logs no formato JSON no stdout. O Docker escuta o pipe do contêiner e o redireciona para o driver do Docker. O driver do Docker lê e re-derrete de forma assíncrona tudo no Pusher.

Empurrador está em todos os carros de ferro. Ele prepara, satura, vira e envia logs para a fila de mensagens Yandex. O fluxo de logs do MQ é analisado por três tipos de trabalhadores e gravado nos repositórios.

Existem três repositórios para registrar logs.

  • Yandex mapReduce para armazenar e analisar logs por um longo período de tempo. Este é um análogo do Hadoop.
  • ClickHouse para armazenar logs durante o último dia.
  • Humio (como um experimento) para armazenar logs para o último dia.

Lucro


O formato geral permite gravar e processar logs da mesma maneira. A coleta de logs é automática, sem usar um disco, e a entrega ocorre na área de alguns segundos. A chave pesquisa de 2 segundos a 5 segundos. Armazenamento e recuperação por um longo período de tempo.

Para volumes menores, considere alternativas: Humio, Splunk e Elastic. Os dois últimos possuem drivers oficiais do Docker. Se você mora na AWS, é o Amazon CloudWatch.

Amazon cloudwatch


O Amazon CloudWatch lida com métricas, eventos e logs. Ele não procura o último, não fornece itens de super busca e não os processa da forma usual. O Amazon CloudWatch processa logs, análises, filtros e exibições em gráficos.


O Amazon CloudWatch converte logs em métricas e gráficos.

O que fazer com os logs?


De volta à nossa bicicleta - ela atende a todos os casos? Não, nossa solução permite encontrar os logs, mas eles exigem uma heterogeneidade muito maior de informações e seus tipos. Os logs são usados ​​em muito mais casos.

Assim que você coletar os logs, a seguinte frase será: "Vamos analisar algo, de alguma forma processá-lo, anotá-lo em algum lugar e começar a ser exibido em gráficos, em painéis". Este é o caminho para o inferno. Especialmente se estamos falando de ferramentas comuns.

Se você imaginar os logs como um certo caos ou log de eventos de qualquer dado, eles não funcionarão.

Esta será uma grande confusão de informações que não podem ser processadas. Um jogo começará na formalização dos logs: "Vamos escrever essas linhas em um formato especial para que seja conveniente analisá-las mais tarde!" Isso também não funciona. Acredite em nós, tentamos.

Digitação


Se você dividir os logs em tipos e processá-los separadamente, poderá encontrar ferramentas que facilitarão o trabalho com eles. Trabalhando não mais como com logs, mas com dados úteis - esse trabalho é mais transparente e conveniente. Alguns tipos de logs podem ser descartados por completo.

Apenas no caso


Esse tipo de registro "ser" é o meu favorito. Se é impossível responder claramente por que essa ou aquela linha é necessária, então elas são. Esse tipo também pode ser chamado de "logs apenas por precaução".

// validate customer func Validate(customer Customer) { // ??? log.debug(“Validate customer %v”, customer) … log.Error(“Customer not valid %v. Reason: %s”, ...) …. } 

Os logs não são comentários que podem ser excluídos. Isso faz parte do código que é mais difícil de modificar, manter e, mais ainda, excluir.

Na melhor das hipóteses, esse log pode se transformar em depuração ou rastreamento. Esse tipo desorganiza o código . Devido ao registro precipitado, posso obter dados pessoais, senhas e cookies dos usuários.

O caminho certo é jogá-los fora e esquecê-los . Mas então nos deparamos com um novo problema. Como analisar a situação com um erro?

Erro fatal / crítico


Para começar, consideramos apenas erros críticos. Esses são erros devido aos quais usuários e desenvolvedores sofrem. O primeiro - quando eles não conseguem concluir a operação. O segundo - quando você precisa fazer correções manualmente.

Por que os logs não se encaixam?

Nenhuma resposta rápida . Se a equipe de desenvolvimento aprender sobre o erro dos usuários por meio do suporte ou do Twitter, é hora de mudar alguma coisa.

Não há contexto . Uma linha separada do log de erros é inútil. Temos que coletar o contexto pouco a pouco. Mesmo assim, pode não ser suficiente, pois esse é o contexto do processo, não o erro.

Não existe uma imagem grande . Nenhuma resposta para as perguntas:

  • com que frequência esse erro ocorre;
  • ocorreu nas réplicas restantes do serviço;
  • foi antes?

Para corrigir esses problemas, use uma ferramenta adequada, por exemplo, sentry.io . Ele permite que você trabalhe com informações de erro representativas e completas (contextuais) com alerting rule personalizável. O site sentinela descreve as diferenças nos logs do uso de sentry.io.

Erro não crítico


Lançamos erros fatais e críticos e agora eles estão escritos no Sentry. Mas houve erros internos - várias bibliotecas ou respostas de serviços de terceiros.

Um bom exemplo é uma nova tentativa bem-sucedida. Suponha que o serviço A esteja voltado para o serviço B, mas, devido a problemas de rede, não foi possível obter uma resposta. Após o erro, o serviço A voltou para o serviço B e recebeu uma resposta válida. O erro na primeira chamada é crítico? Não. Nesse caso, o processo foi concluído com êxito e o usuário pôde usar o serviço.

Se esses erros não são críticos para o serviço funcionar e não afetam o usuário com uma repetição rara, eles não são erros. Isso é uma degradação do serviço, embora a resposta ao usuário tenha chegado 50 ms depois. Este tipo de log refere-se a avisos - aviso.

Advertência


Alertas são informações sobre degradação de um serviço.

Aqui veremos os mesmos problemas inerentes a erros críticos, mas com uma reserva. A reação a um evento individual não é importante - sua quantidade ao longo do tempo é importante.

Considere um exemplo em que um serviço não pode recuperar uma entrada de cache e acessa o armazenamento a frio. Se isso acontecer uma vez por minuto, isso pode ser feito para a operação normal do serviço. Emissões raras não são importantes .

Mas, ao mesmo tempo, você precisa de uma ferramenta para visualizar o panorama geral, precisa de análise em tempo real . Para acompanhar as alterações por um longo período, seria bom ter uma análise retrospectiva também. A degradação acima de um determinado nível (limites) pode afetar adversamente os usuários - você precisa de uma reação com degradação grave .

Não precisamos dos logs marcados como Aviso, mas das métricas de degradação.

A ferramenta de coleta de métricas mais popular é o Prometheus, e você pode usar o Grafana para visualização. Se você precisar de um contexto grande (o mesmo que o erro), o mesmo Sentry fará, mas com os alertas desativados. No entanto, na maioria dos casos, haverá contexto suficiente. Será usado para gráficos - etiquetas Prometheus.

Exemplos.
etiqueta
exemplo 1
exemplo 2
exemplo 3
contexto
db
internal_service
cache
módulo
user_service
user_service
user_service
razão
long_query
tentar novamente
miss_cache
outro
get_user
service_b
user_permisson

Três eventos ocorreram no user_service condicional. Eles afetam a operação do serviço: uma longa solicitação ao banco de dados, acesso repetido à API do serviço service_b e nenhum direito do usuário foi encontrado no cache. Gráficos e alertas serão configurados como importantes para os desenvolvedores do serviço, graças ao contexto.

Rastreamento


Essa é a primeira coisa a começar, se escolhermos o caminho em que você precisa analisar os logs. Por si só, essas informações nos logs são inúteis, porque você precisa criar cadeias de chamadas, ver dados dentro de solicitações, erros nas cadeias de chamadas, tempos de resposta, o número de RPS.

Existem ótimas ferramentas para rastreamento - Jaeger ou Zipkin. Eu recomendo usar o OpenTracing, que ambos suportam.

Você pode coletar o rastreamento de três fontes.

  • Se você usar balanceadores compartilhados, analise os logs deles e envie-os para o Jaeger.
  • Os próprios serviços , se receberem endereços pelo Service Discovery e forem diretamente. Nesse caso, o rastreamento dos serviços é enviado diretamente ao Jaeger.
  • Malha de serviço inteligente. Ele sabe como coletar e enviar um rastreamento, por exemplo, Istio.


Informação inicial


Essas informações são sobre chamadas de serviço da API, inicialização do Cron, consultas ao banco de dados ou chamadas para outros serviços.

 { "_message": "Request: ...; request_id: ...,... ", "_level": "INFO", "_time": "2019-03-08T12:04:05.000+07:00", "_context": "ryawvcHandler", "_tread": "785534" } 

Essas informações pertencem ao bloco "Apenas no caso", mas são separadas porque são mais comuns. Esta informação é necessária para analisar o erro e você pode descartá-lo .

Se as informações sobre chamadas para métodos internos são extremamente importantes e você não pode prescindir dela, mesmo com o contexto coletado em caso de erro, vale a pena instrumentar as chamadas de método como um rastreio.

Tempo de execução


Essas informações são sobre o tempo de execução de métodos, APIs, consultas ao banco de dados ou outros serviços.

 { "_message": "Get customer 12ms", "_level": "INFO", "_time": "2019-03-08T12:04:05.000+07:00", "_context": "ryawvcCustomerRepository", "_tread": "785534" } 

Não há valor nos logs, porque você precisa analisar essas informações, exibi-las nos gráficos e configurar limites. Esse tipo de log precisa ser substituído por métricas , por exemplo, no Prometheus.

Informações comerciais


Essas informações são necessárias para análise de negócios, análise de comportamento do cliente, cálculos financeiros. Nesse local, historicamente usamos a abordagem oposta - logs analisados. Mas este é um bom exemplo do que os logs do aplicativo podem degenerar se você trabalhar com eles dessa maneira.

Para logs com dados corporativos, acordos foram formados com campos fixos no formato TSKV, necessários para análises. Os aplicativos gravaram logs de negócios em um arquivo dedicado. Em seguida, os logs foram lidos e enviados linha a linha para o MQ, e um aplicativo separado os processou e os gravou no banco de dados. Este é um exemplo do que qualquer análise se transforma.

Não funcionará para analisar todo o fluxo de logs na esperança de que os dados converjam.

Convenções, formatos, regras e requisitos de confiabilidade estão surgindo. Isso já se parece um pouco com os logs do aplicativo. Nesse caso, o log se torna a fila de entrega de dados com todos os requisitos subsequentes para o MQ. É perceptível que o middleware na forma de um log é supérfluo aqui.

Uma boa solução é enviar esses dados diretamente para o MQ. Já serão processados, armazenados no armazenamento apropriado e usados ​​pela equipe de análise. Por exemplo, para exibição, usamos o Tableau.

Desempenho


Esse tipo de log raramente é encontrado nos logs do aplicativo e é mais frequentemente coletado como uma métrica. Separadamente, acrescento que, para coletar as métricas básicas específicas do idioma, basta usar a biblioteca do Prometheus. Por padrão, ela coletará tudo o que alcançar. O custo de adicionar essas métricas é pequeno.



Resultado de digitação


Depois de classificar os logs por tipo, podemos pegar ferramentas mais poderosas para trabalhar com eles. Não existem sistemas complexos ou tecnologias espaciais como a Amazon, não há nada que não possa ser criado amanhã. Você provavelmente já possui alguns desses sistemas ou análogos: Sentry está acumulando poeira em algum lugar, Prometheus está trabalhando em algum lugar.



O problema não está na tecnologia, mas em uma armadilha cognitiva quando confiamos nos logs como um meio de representação confiável do estado do nosso sistema. Não é assim, os logs são um conjunto de eventos caóticos.

Há uma exceção - Debug-logs, que pode ser usada em casos raros.

Log de depuração


Os logs de depuração devem ser informações detalhadas. Eles não devem duplicar o que já está sendo enviado para os sistemas que descrevemos acima. Este tipo existe para analisar casos especiais. Por exemplo, um bug incompreensível ocorre na produção e, no momento, não está claro pelas métricas o que está acontecendo.

Ative os logs de depuração a quente, sem reiniciar o serviço . Como estamos falando de vários serviços, não haverá muitos deles. Infraestrutura sofisticada não é necessária. Pilha de ELK suficiente sem "preparação" complicada. Também faz sentido adicionar um alerta ao Sentry com todo o contexto necessário.

Os logs de depuração podem ser usados ​​para desenvolvimento . Mas eles são perfeitamente substituídos por depuração.

Resumir


Nós escrevemos nossa entrega de registro de bicicleta para pesquisa . Não satisfazemos os clientes do serviço - todos eles vêm até nós para analisá-los, coletá-los e agregá-los em algum lugar. Isso pode ser evitado - sistemas complexos de processamento de logs não são necessários.

Os logs brutos são inúteis, mas podem ser transformados em métricas úteis.

Basta criar uma infraestrutura para fornecer métricas e dados úteis em torno dos serviços. Como resultado, serão exibidas métricas úteis que falam sobre serviços e mostram transparentemente tudo o que acontece com eles.

Os erros devem conter o contexto do próprio erro.

Isso ajudará a lidar com isso e corrigi-lo imediatamente. Erros e degradação devem levar à ação , para que os desenvolvedores aprendam instantaneamente sobre os problemas e os corrijam mesmo antes das solicitações irritadas dos usuários.

As ferramentas certas tornarão o trabalho com seus serviços mais agradável e transparente . A depuração tem um lugar para estar, mas você precisa ser rigoroso com ela.

No HighLoad ++ 2019 em novembro, haverá uma seção DevOps - 13 relatórios sobre cargas na AWS, um sistema de monitoramento em Lamoda, transportadores para entrega de modelos, vida útil sem o Kubernetes e muito mais. Veja a lista completa de tópicos e resumos na página separada “ Relatórios ”. E nos encontraremos no DevOpsConf na primavera - inscreva-se no boletim informativo e informe-nos quando determinarmos as datas e o local.

Source: https://habr.com/ru/post/pt468535/


All Articles