Recollendo rexistros de Loki

Recollendo rexistros de Loki

En Badoo, monitorizamos constantemente as novas tecnoloxías e avaliamos se paga a pena usalas no noso sistema. Gustaríanos compartir un destes estudos coa comunidade. Está dedicado a Loki, un sistema de agregación de rexistros.

Loki é unha solución para almacenar e ver rexistros, e esta pila tamén ofrece un sistema flexible para analizalos e enviar datos a Prometheus. En maio lanzouse outra actualización, promovida activamente polos creadores. Interesábanos o que pode facer Loki, que capacidades ofrece e ata que punto pode actuar como alternativa a ELK, a pila que usamos agora.

Que é Loki

Grafana Loki é un conxunto de compoñentes para un sistema completo para traballar con rexistros. A diferenza doutros sistemas similares, Loki baséase na idea de indexar só metadatos de rexistro: etiquetas (o mesmo que en Prometheus) e comprimir os propios rexistros en anacos separados.

Páxina de inicio, GitHub

Antes de entrar no que podes facer con Loki, quero aclarar o que queremos dicir con "a idea de indexar só metadatos". Comparemos o enfoque de Loki e o enfoque da indexación en solucións tradicionais como Elasticsearch, usando o exemplo dunha liña do rexistro nginx:

172.19.0.4 - - [01/Jun/2020:12:05:03 +0000] "GET /purchase?user_id=75146478&item_id=34234 HTTP/1.1" 500 8102 "-" "Stub_Bot/3.0" "0.001"

Os sistemas tradicionais analizan toda a fila, incluíndo campos cun gran número de valores únicos user_id e item_id, e almacenan todo en índices grandes. A vantaxe deste enfoque é que pode executar consultas complexas rapidamente, xa que case todos os datos están no índice. Pero isto ten un custo en que o índice faise grande, o que se traduce en requisitos de memoria. Como resultado, o índice de rexistro de texto completo é comparable en tamaño aos propios rexistros. Para buscar rapidamente a través del, o índice debe cargarse na memoria. E cantos máis rexistros, máis rápido crece o índice e máis memoria consume.

O enfoque de Loki require que só se extraian os datos necesarios dunha cadea, cuxo número de valores é pequeno. Deste xeito obtemos un pequeno índice e podemos buscar os datos filtrándoos por tempo e por campos indexados, e despois escaneando o resto con expresións regulares ou busca de subcadeas. O proceso non parece o máis rápido, pero Loki divide a solicitude en varias partes e execútaas en paralelo, procesando unha gran cantidade de datos en pouco tempo. O número de fragmentos e solicitudes paralelas neles é configurable; así, a cantidade de datos que se poden procesar por unidade de tempo depende linealmente da cantidade de recursos proporcionados.

Esta compensación entre un índice grande e rápido e un pequeno índice paralelo de forza bruta permite a Loki controlar o custo do sistema. Pódese configurar e ampliar de forma flexible segundo as necesidades.

A pila de Loki consta de tres compoñentes: Promtail, Loki, Grafana. Promtail recolle rexistros, procesaos e envíaos a Loki. Loki gárdaos. E Grafana pode solicitar datos de Loki e mostralos. En xeral, Loki pode usarse non só para almacenar rexistros e buscar a través deles. Toda a pila ofrece grandes oportunidades para procesar e analizar os datos entrantes mediante o método Prometheus.
Pódese atopar unha descrición do proceso de instalación aquí.

Busca de rexistro

Podes buscar os rexistros nunha interface especial de Grafana - Explorer. As consultas usan a linguaxe LogQL, que é moi semellante ao PromQL usado en Prometheus. En principio, pódese pensar como un grep distribuído.

A interface de busca ten o seguinte aspecto:

Recollendo rexistros de Loki

A propia solicitude consta de dúas partes: selector e filtro. O selector é unha busca que utiliza metadatos indexados (etiquetas) que se asignan aos rexistros, e o filtro é unha cadea de busca ou expresión regular que filtra os rexistros definidos polo selector. No exemplo dado: Nas llaves hai un selector, todo despois é un filtro.

{image_name="nginx.promtail.test"} |= "index"

Debido ao funcionamento de Loki, non podes facer consultas sen un selector, pero as etiquetas pódense facer tan xerais como queiras.

Un selector é un valor clave-valor entre chaves. Pode combinar selectores e especificar diferentes condicións de busca usando os operadores =, != ou expresións regulares:

{instance=~"kafka-[23]",name!="kafka-dev"} 
// Найдёт логи с лейблом instance, имеющие значение kafka-2, kafka-3, и исключит dev 

Un filtro é texto ou expresión regular que filtrará todos os datos recibidos polo selector.

É posible obter gráficos ad-hoc en función dos datos recibidos en modo métrica. Por exemplo, podes saber con que frecuencia aparece unha entrada que contén o índice de cadea nos rexistros de nginx:

Recollendo rexistros de Loki

Na documentación pódese atopar unha descrición completa das capacidades LogQL.

Análise de rexistros

Hai varias formas de recoller rexistros:

  • Usando Promtail, un compoñente estándar da pila para recoller rexistros.
  • Directamente desde o contedor docker usando Controlador de rexistro de Loki Docker.
  • Usa Fluentd ou Fluent Bit, que pode enviar datos a Loki. A diferenza de Promtail, teñen analizadores preparados para case calquera tipo de rexistro e tamén poden xestionar rexistros multiliña.

Normalmente úsase Promtail para analizar. Fai tres cousas:

  • Busca fontes de datos.
  • Apúntalles etiquetas.
  • Envía datos a Loki.

Actualmente Promtail pode ler rexistros de ficheiros locais e do diario systemd. Debe instalarse en cada máquina da que se recollen os rexistros.

Hai integración con Kubernetes: Promtail automaticamente, a través da API REST de Kubernetes, recoñece o estado do clúster e recolle rexistros dun nodo, servizo ou pod, publicando inmediatamente etiquetas baseadas nos metadatos de Kubernetes (nome do pod, nome do ficheiro, etc.) .

Tamén podes colgar etiquetas baseadas nos datos do rexistro usando Pipeline. Pipeline Promtail pode constar de catro tipos de etapas. Máis detalles en documentación oficial, Vou observar inmediatamente algúns matices.

  1. Etapas de análise. Esta é a fase RegEx e JSON. Nesta fase, extraemos datos dos rexistros no chamado mapa extraído. Podemos extraer de JSON simplemente copiando os campos que necesitamos no mapa extraído ou mediante expresións regulares (RegEx), onde os grupos nomeados son "mapeados" no mapa extraído. O mapa extraído é un almacén de clave-valor, onde clave é o nome do campo e valor é o valor dos rexistros.
  2. Transformar etapas. Esta etapa ten dúas opcións: transformar, onde establecemos as regras de transformación, e fonte - a fonte de datos para a transformación do mapa extraído. Se non hai tal campo no mapa extraído, crearase. Deste xeito é posible crear etiquetas que non estean baseadas no mapa extraído. Nesta fase podemos manipular os datos do mapa extraído usando un dispositivo bastante potente Modelo Golang. Ademais, debemos lembrar que o mapa extraído cárgase totalmente durante a análise, o que permite, por exemplo, comprobar o valor nel: “{{se .tag}valor da etiqueta existe{end}}”. O modelo admite condicións, bucles e algunhas funcións de cadea, como Substituír e Recortar.
  3. Etapas de actuación. Neste punto podes facer algo co contido extraído:
    • Crea unha etiqueta a partir dos datos extraídos, que será indexado por Loki.
    • Cambia ou establece a hora do evento desde o rexistro.
    • Cambia os datos (texto de rexistro) que irán a Loki.
    • Crear métricas.
  4. Etapas de filtrado. A fase de coincidencia, na que podemos enviar entradas que non necesitamos /dev/null ou reenvialas para o seu procesamento posterior.

Usando un exemplo de procesamento de rexistros normais de nginx, mostrarei como pode analizar rexistros usando Promtail.

Para a proba, tomemos como nginx-proxy unha imaxe nginx modificada jwilder/nginx-proxy:alpine e un pequeno daemon que pode preguntarse por HTTP. O daemon ten varios puntos finais, aos que pode proporcionar respostas de diferentes tamaños, con diferentes estados HTTP e con diferentes atrasos.

Recolleremos rexistros dos contedores docker, que se poden atopar no camiño /var/lib/docker/containers/ / -json.log

En docker-compose.yml configuramos Promtail e especificamos o camiño á configuración:

promtail:
  image: grafana/promtail:1.4.1
 // ...
 volumes:
   - /var/lib/docker/containers:/var/lib/docker/containers:ro
   - promtail-data:/var/lib/promtail/positions
   - ${PWD}/promtail/docker.yml:/etc/promtail/promtail.yml
 command:
   - '-config.file=/etc/promtail/promtail.yml'
 // ...

Engade o camiño aos rexistros a promtail.yml (hai unha opción "docker" na configuración, que fai o mesmo nunha liña, pero non sería tan claro):

scrape_configs:
 - job_name: containers

   static_configs:
       labels:
         job: containerlogs
         __path__: /var/lib/docker/containers/*/*log  # for linux only

Cando se habilita esta configuración, os rexistros de todos os contedores enviaranse a Loki. Para evitar isto, cambiamos a configuración do nginx de proba en docker-compose.yml - engade un campo de etiqueta de rexistro:

proxy:
 image: nginx.test.v3
//…
 logging:
   driver: "json-file"
   options:
     tag: "{{.ImageName}}|{{.Name}}"

Editando promtail.yml e configurando Pipeline. A entrada inclúe rexistros do seguinte tipo:

{"log":"u001b[0;33;1mnginx.1    | u001b[0mnginx.test 172.28.0.3 - - [13/Jun/2020:23:25:50 +0000] "GET /api/index HTTP/1.1" 200 0 "-" "Stub_Bot/0.1" "0.096"n","stream":"stdout","attrs":{"tag":"nginx.promtail.test|proxy.prober"},"time":"2020-06-13T23:25:50.66740443Z"}
{"log":"u001b[0;33;1mnginx.1    | u001b[0mnginx.test 172.28.0.3 - - [13/Jun/2020:23:25:50 +0000] "GET /200 HTTP/1.1" 200 0 "-" "Stub_Bot/0.1" "0.000"n","stream":"stdout","attrs":{"tag":"nginx.promtail.test|proxy.prober"},"time":"2020-06-13T23:25:50.702925272Z"}

Etapa do gasoduto:

 - json:
     expressions:
       stream: stream
       attrs: attrs
       tag: attrs.tag

Extraemos os campos stream, attrs, attrs.tag (se os existen) do JSON entrante e poñémolos no mapa extraído.

 - regex:
     expression: ^(?P<image_name>([^|]+))|(?P<container_name>([^|]+))$
     source: "tag"

Se conseguimos poñer o campo da etiqueta no mapa extraído, entón mediante regexp extraemos os nomes da imaxe e do contedor.

 - labels:
     image_name:
     container_name:

Asignamos etiquetas. Se as claves image_name e container_name se atopan nos datos extraídos, os seus valores asignaranse ás etiquetas correspondentes.

 - match:
     selector: '{job="docker",container_name="",image_name=""}'
     action: drop

Descartamos todos os rexistros que non teñan instaladas as etiquetas nome_imaxe e nome_contedor.

  - match:
     selector: '{image_name="nginx.promtail.test"}'
     stages:
       - json:
           expressions:
             row: log

Para todos os rexistros cuxo nome_imagen é nginx.promtail.test, extrae o campo de rexistro do rexistro de orixe e colócao no mapa extraído coa clave de fila.

  - regex:
         # suppress forego colors
         expression: .+nginx.+|.+[0m(?P<virtual_host>[a-z_.-]+) +(?P<nginxlog>.+)
         source: logrow

Borramos a liña de entrada con expresións regulares e retiramos o host virtual nginx e a liña de rexistro de nginx.

     - regex:
         source: nginxlog
         expression: ^(?P<ip>[w.]+) - (?P<user>[^ ]*) [(?P<timestamp>[^ ]+).*] "(?P<method>[^ ]*) (?P<request_url>[^ ]*) (?P<request_http_protocol>[^ ]*)" (?P<status>[d]+) (?P<bytes_out>[d]+) "(?P<http_referer>[^"]*)" "(?P<user_agent>[^"]*)"( "(?P<response_time>[d.]+)")?

Analiza o rexistro de nginx usando expresións regulares.

    - regex:
           source: request_url
           expression: ^.+.(?P<static_type>jpg|jpeg|gif|png|ico|css|zip|tgz|gz|rar|bz2|pdf|txt|tar|wav|bmp|rtf|js|flv|swf|html|htm)$
     - regex:
           source: request_url
           expression: ^/photo/(?P<photo>[^/?.]+).*$
       - regex:
           source: request_url
           expression: ^/api/(?P<api_request>[^/?.]+).*$

Analizemos request_url. Usando regexp determinamos o propósito da solicitude: datos estáticos, fotos, API e establecemos a clave correspondente no mapa extraído.

       - template:
           source: request_type
           template: "{{if .photo}}photo{{else if .static_type}}static{{else if .api_request}}api{{else}}other{{end}}"

Usando operadores condicionais en Template, comprobamos os campos instalados no mapa extraído e establecemos os valores necesarios para o campo request_type: foto, estático, API. Asigne outro se falla. request_type agora contén o tipo de solicitude.

       - labels:
           api_request:
           virtual_host:
           request_type:
           status:

Establecemos as etiquetas api_request, virtual_host, request_type e status (estado HTTP) en función do que conseguimos poñer no mapa extraído.

       - output:
           source: nginx_log_row

Cambiar a saída. Agora o rexistro nginx limpo do mapa extraído vai a Loki.

Recollendo rexistros de Loki

Despois de executar a configuración anterior, podes ver que a cada entrada se lle asignan etiquetas en función dos datos do rexistro.

Unha cousa a ter en conta é que recuperar etiquetas cun gran número de valores (cardinalidade) pode ralentizar significativamente a Loki. É dicir, non debería poñer, por exemplo, user_id no índice. Lea máis sobre isto no artigo "Como as etiquetas en Loki poden facer que as consultas de rexistro sexan máis rápidas e sinxelas" Pero isto non significa que non poida buscar por user_id sen índices. Debes usar filtros cando buscas ("tomar" os datos) e o índice aquí actúa como un identificador de fluxo.

Visualización de rexistros

Recollendo rexistros de Loki

Loki pode actuar como fonte de datos para gráficos Grafana usando LogQL. Admítense as seguintes funcións:

  • rate — número de rexistros por segundo;
  • count over time — o número de rexistros no intervalo especificado.

Tamén hai funcións de agregación Sum, Avg e outras. Podes construír gráficos bastante complexos, por exemplo un gráfico do número de erros HTTP:

Recollendo rexistros de Loki

A fonte de datos estándar Loki ten unha funcionalidade algo reducida en comparación coa fonte de datos Prometheus (por exemplo, non pode cambiar a lenda), pero Loki pódese conectar como fonte co tipo Prometheus. Non estou seguro de se este é un comportamento documentado, pero a xulgar pola resposta dos desenvolvedores "Como configurar Loki como fonte de datos de Prometheus? · Número 1222 · grafana/loki”, por exemplo, é completamente legal e Loki é totalmente compatible con PromQL.

Engade Loki como fonte de datos co tipo Prometheus e engade o URL /loki:

Recollendo rexistros de Loki

E podemos facer gráficos, coma se traballásemos con métricas de Prometheus:

Recollendo rexistros de Loki

Creo que a discrepancia na funcionalidade é temporal e os desenvolvedores corrixirán isto no futuro.

Recollendo rexistros de Loki

Métricas

Loki ofrece a capacidade de extraer métricas numéricas dos rexistros e envialas a Prometheus. Por exemplo, o rexistro nginx contén o número de bytes por resposta, así como, cunha determinada modificación do formato de rexistro estándar, o tempo en segundos que tardou en responder. Estes datos pódense extraer e enviar a Prometheus.

Engade outra sección a promtail.yml:

- match:
   selector: '{request_type="api"}'
   stages:
     - metrics:
         http_nginx_response_time:
           type: Histogram
           description: "response time ms"
           source: response_time
           config:
             buckets: [0.010,0.050,0.100,0.200,0.500,1.0]
- match:
   selector: '{request_type=~"static|photo"}'
   stages:
     - metrics:
         http_nginx_response_bytes_sum:
           type: Counter
           description: "response bytes sum"
           source: bytes_out
           config:
             action: add
         http_nginx_response_bytes_count:
           type: Counter
           description: "response bytes count"
           source: bytes_out
           config:
             action: inc

A opción permítelle definir e actualizar métricas en función dos datos do mapa extraído. Estas métricas non se envían a Loki; aparecen no punto final de Promtail /metrics. Prometheus debe estar configurado para recibir os datos recibidos nesta fase. No exemplo anterior, para request_type=“api” recollemos unha métrica de histograma. Con este tipo de métricas é conveniente obter percentiles. Para estática e foto, recollemos a suma de bytes e o número de filas nas que recibimos bytes para calcular a media.

Ler máis sobre as métricas aquí.

Abre o porto en Promtail:

promtail:
     image: grafana/promtail:1.4.1
     container_name: monitoring.promtail
     expose:
       - 9080
     ports:
       - "9080:9080"

Asegúrate de que aparecen as métricas co prefixo promtail_custom:

Recollendo rexistros de Loki

Configurando Prometheus. Engadir un promtail de traballo:

- job_name: 'promtail'
 scrape_interval: 10s
 static_configs:
   - targets: ['promtail:9080']

E debuxamos un gráfico:

Recollendo rexistros de Loki

Deste xeito podes coñecer, por exemplo, as catro consultas máis lentas. Tamén pode configurar a supervisión destas métricas.

Escalado

Loki pode estar en modo binario único ou en modo fragmentado (modo escalable horizontalmente). No segundo caso, pode gardar datos na nube, e os anacos e o índice almacénanse por separado. A versión 1.5 introduce a posibilidade de almacenar nun só lugar, pero aínda non se recomenda usalo na produción.

Recollendo rexistros de Loki

Os fragmentos pódense almacenar en almacenamento compatible con S3 e pódense usar bases de datos escalables horizontalmente para almacenar índices: Cassandra, BigTable ou DynamoDB. Outras partes de Loki - Distribuidores (para escribir) e Querier (para consultas) - son sen estado e tamén escalan horizontalmente.

Na conferencia DevOpsDays Vancouver 2019, un dos participantes Callum Styan anunciou que con Loki o seu proxecto ten petabytes de rexistros cun índice inferior ao 1% do tamaño total: "Como Loki correlaciona métricas e rexistros e aforra cartos".

Comparación de Loki e ELK

Tamaño do índice

Para probar o tamaño do índice resultante, tomei rexistros do contenedor nginx para o que se configurou o pipeline anterior. O ficheiro de rexistro contiña 406 liñas cun volume total de 624 MB. Os rexistros xeráronse nunha hora, aproximadamente 109 entradas por segundo.

Exemplo de dúas liñas do rexistro:

Recollendo rexistros de Loki

Cando o indexou ELK, deu un tamaño de índice de 30,3 MB:

Recollendo rexistros de Loki

No caso de Loki, isto deu lugar a aproximadamente 128 KB de índice e aproximadamente 3,8 MB de datos en anacos. Cabe destacar que o rexistro foi xerado artificialmente e non tiña unha gran variedade de datos. Un simple gzip no rexistro orixinal de Docker JSON con datos deu unha compresión do 95,4% e, tendo en conta o feito de que só se enviou o rexistro nginx limpo ao propio Loki, é comprensible a compresión de ata 4 MB. O número total de valores únicos para as etiquetas de Loki foi de 35, o que explica o pequeno tamaño do índice. Para ELK tamén se borra o rexistro. Así, Loki comprimiu os datos orixinais nun 96% e ELK nun 70%.

Consumo de memoria

Recollendo rexistros de Loki

Se comparamos toda a pila de Prometheus e ELK, entón Loki "come" varias veces menos. Está claro que un servizo Go consome menos que un servizo Java, e comparar o tamaño do JVM Heap Elasticsearch e a memoria asignada para Loki é incorrecto, pero, con todo, cabe destacar que Loki usa moita menos memoria. A súa vantaxe de CPU non é tan obvia, pero tamén está presente.

Acelerar

Loki "devora" os rexistros máis rápido. A velocidade depende de moitos factores: que tipo de rexistros son, o sofisticado que somos para analizalos, rede, disco, etc., pero definitivamente é maior que ELK (na miña proba, aproximadamente o dobre). Isto explícase polo feito de que Loki pon moitos menos datos no índice e, en consecuencia, dedica menos tempo á indexación. Coa velocidade de busca, a situación é a contraria: Loki diminúe notablemente nos datos máis grandes que varios gigabytes, mentres que a velocidade de busca de ELK non depende do tamaño dos datos.

Busca de rexistro

Loki é significativamente inferior a ELK en termos de capacidades de busca de rexistros. Grep con expresións regulares é poderoso, pero é inferior a unha base de datos madura. A falta de consultas de rango, a agregación só por etiquetas, a incapacidade de buscar sen etiquetas - todo isto nos limita a buscar información de interese en Loki. Isto non significa que non se poida atopar nada usando Loki, pero define o fluxo de traballo cos rexistros cando se atopa un problema nas gráficas de Prometheus, e despois usa estas etiquetas para buscar o que pasou nos rexistros.

interface

En primeiro lugar, é fermoso (perdón, non me puiden resistir). Grafana ten unha interface agradable, pero Kibana é moito máis rica en funcións.

Pros e contras de Loki

Unha das vantaxes é que Loki se integra con Prometheus, polo que saímos de métricas e alertas. É conveniente para recoller rexistros e almacenalos desde Kubernetes Pods, xa que ten o servizo de descubrimento herdado de Prometheus e adxunta etiquetas automaticamente.

A desvantaxe é a documentación débil. Algunhas cousas, por exemplo, as características e capacidades de Promtail, descubrín só no proceso de estudo do código, afortunadamente é de código aberto. Outra desvantaxe son as débiles capacidades de análise. Por exemplo, Loki non pode analizar rexistros multiliña. Outra desvantaxe é que Loki é unha tecnoloxía relativamente nova (a versión 1.0 foi en novembro de 2019).

Conclusión

Loki é unha tecnoloxía 100% interesante que é axeitada para proxectos de pequeno e mediano tamaño, o que lle permite resolver moitos problemas de agregación de rexistros, busca de rexistros, seguimento e análise de rexistros.

Non usamos Loki en Badoo porque temos unha pila ELK que nos convén e que foi superada con varias solucións personalizadas ao longo dos anos. Para nós, o escollo é buscar nos rexistros. Con case 100 GB de rexistros ao día, é importante para nós poder atopalo todo e un pouco máis e facelo rapidamente. Para a elaboración de gráficos e o seguimento, utilizamos outras solucións adaptadas ás nosas necesidades e integradas entre si. A pila de Loki ten beneficios tanxibles, pero non nos dará máis do que xa temos, e os seus beneficios certamente non superarán o custo da migración.

E aínda que despois da investigación quedou claro que non podemos usar Loki, esperamos que esta publicación che axude na túa elección.

Localízase o repositorio co código utilizado no artigo aquí.

Fonte: www.habr.com

Engadir un comentario