Explorando o OpenTelemetry Collector

O OpenTelemetry Collector ocupa o centro da arquitetura do OpenTelemetry, mas não está relacionado ao W3C Trace Context. No meu demo de rastreamento, utilizo o Jaeger em vez do Collector. No entanto, é onipresente, como em cada post relacionado ao OpenTelemetry. Eu queria explorá-lo mais a fundo.

Neste post, explorarei os diferentes aspectos do Collector:

  • O tipo de dados: logs, métricas e rastreamentos
  • Modelos de push e pull
  • Operações: leituras, transformações e escritas

Primeiros Passos

A long time ago, observability as we know it didn’t exist; what we had instead was monitoring. Back then, monitoring was a bunch of people looking at screens displaying dashboards. Dashboards themselves consisted of metrics and only system metrics, mainly CPU, memory, and disk usage. For this reason, we will start with metrics.

Prometheus é uma das principais soluções de monitoramento. Funciona com um modelo baseado em pull: o Prometheus raspa pontos finais compatíveis de sua aplicação(ões) e armazena-os internamente.

Utilizaremos o OTEL Collector para raspar um ponto final compatível com o Prometheus e imprimir o resultado no console. O Grafana Labs oferece um projeto que gera métricas aleatórias para brincar. Por simplicidade, vou usar o Docker Compose; a configuração é a seguinte:

YAML

 

version: "3"

services:
  fake-metrics:
    build: ./fake-metrics-generator                                         #1
  collector:
    image: otel/opentelemetry-collector:0.87.0                              #2
    environment:                                                            #3
      - METRICS_HOST=fake-metrics
      - METRICS_PORT=5000
    volumes:
      - ./config/collector/config.yml:/etc/otelcol/config.yaml:ro           #4

  1. Não há imagem Docker disponível para o projeto de métricas falsas; portanto, precisamos construí-la
  2. Versão mais recente do OTEL Collector na época da redação deste texto
  3. Parametrizar o seguinte arquivo de configuração
  4. Tudo acontece aqui

Como mencionei acima, o OTEL Collector pode fazer muito. Portanto, a configuração é tudo.

YAML

 

receivers:                                                                  #1
  prometheus:                                                               #2
    config:
      scrape_configs:                                                       #3
        - job_name: fake-metrics                                            #4
          scrape_interval: 3s
          static_configs:
            - targets: [ "${env.METRICS_HOST}:${env.METRICS_PORT}" ]
            
exporters:                                                                  #5
  logging:                                                                  #6
    loglevel: debug
    
service:
  pipelines:                                                                #7
    metrics:                                                                #8
      receivers: [ "prometheus" ]                                           #9
      exporters: [ "logging" ]                                              #10

  1. Lista de receptores. Um receptor lê dados; pode ser baseado em push ou pull.
  2. Utilizamos o receptor pré-definido prometheus
  3. Definir tarefas de pull
  4. Configuração do trabalho
  5. Lista de exportadores. Em contraste com os receptores, um exportador escreve dados.
  6. O exportador mais simples é escrever dados no standard out
  7. Pipelines montam receptores e exportadores
  8. Definir um pipeline relacionado a métricas
  9. O pipeline obtém dados do receptor prometheus previamente definido e envia-os para o exportador logging, ou seja, imprime-os

Aqui está um exemplo do resultado:

2023-11-11 08:28:54 otel-collector-collector-1     | StartTimestamp: 1970-01-01 00:00:00 +0000 UTC
2023-11-11 08:28:54 otel-collector-collector-1     | Timestamp: 2023-11-11 07:28:54.14 +0000 UTC
2023-11-11 08:28:54 otel-collector-collector-1     | Value: 83.090000
2023-11-11 08:28:54 otel-collector-collector-1     | NumberDataPoints #1
2023-11-11 08:28:54 otel-collector-collector-1     | Data point attributes:
2023-11-11 08:28:54 otel-collector-collector-1     |      -> fake__embrace_world_class_systems: Str(concept)
2023-11-11 08:28:54 otel-collector-collector-1     |      -> fake__exploit_magnetic_applications: Str(concept)
2023-11-11 08:28:54 otel-collector-collector-1     |      -> fake__facilitate_wireless_architectures: Str(extranet)
2023-11-11 08:28:54 otel-collector-collector-1     |      -> fake__grow_magnetic_communities: Str(challenge)
2023-11-11 08:28:54 otel-collector-collector-1     |      -> fake__reinvent_revolutionary_applications: Str(support)
2023-11-11 08:28:54 otel-collector-collector-1     |      -> fake__strategize_strategic_initiatives: Str(internet_solution)
2023-11-11 08:28:54 otel-collector-collector-1     |      -> fake__target_customized_eyeballs: Str(concept)
2023-11-11 08:28:54 otel-collector-collector-1     |      -> fake__transform_turn_key_technologies: Str(framework)
2023-11-11 08:28:54 otel-collector-collector-1     |      -> fake__whiteboard_innovative_partnerships: Str(matrices)
2023-11-11 08:28:54 otel-collector-collector-1     | StartTimestamp: 1970-01-01 00:00:00 +0000 UTC
2023-11-11 08:28:54 otel-collector-collector-1     | Timestamp: 2023-11-11 07:28:54.14 +0000 UTC
2023-11-11 08:28:54 otel-collector-collector-1     | Value: 53.090000
2023-11-11 08:28:54 otel-collector-collector-1     | NumberDataPoints #2
2023-11-11 08:28:54 otel-collector-collector-1     | Data point attributes:
2023-11-11 08:28:54 otel-collector-collector-1     |      -> fake__expedite_distributed_partnerships: Str(approach)
2023-11-11 08:28:54 otel-collector-collector-1     |      -> fake__facilitate_wireless_architectures: Str(graphical_user_interface)
2023-11-11 08:28:54 otel-collector-collector-1     |      -> fake__grow_magnetic_communities: Str(policy)
2023-11-11 08:28:54 otel-collector-collector-1     |      -> fake__reinvent_revolutionary_applications: Str(algorithm)
2023-11-11 08:28:54 otel-collector-collector-1     |      -> fake__transform_turn_key_technologies: Str(framework)
2023-11-11 08:28:54 otel-collector-collector-1     | StartTimestamp: 1970-01-01 00:00:00 +0000 UTC
2023-11-11 08:28:54 otel-collector-collector-1     | Timestamp: 2023-11-11 07:28:54.14 +0000 UTC
2023-11-11 08:28:54 otel-collector-collector-1     | Value: 16.440000
2023-11-11 08:28:54 otel-collector-collector-1     | NumberDataPoints #3
2023-11-11 08:28:54 otel-collector-collector-1     | Data point attributes:
2023-11-11 08:28:54 otel-collector-collector-1     |      -> fake__exploit_magnetic_applications: Str(concept)
2023-11-11 08:28:54 otel-collector-collector-1     |      -> fake__grow_magnetic_communities: Str(graphical_user_interface)
2023-11-11 08:28:54 otel-collector-collector-1     |      -> fake__target_customized_eyeballs: Str(extranet)

Além da Impressão

O que foi mencionado é um excelente primeiro passo, mas há mais do que apenas imprimir no console. Vamos expor as métricas para serem raspadas por uma instância regular de Prometheus; podemos adicionar um painel do Grafana para visualizá-las. Embora possa parecer sem sentido, tenha paciência, pois é apenas um passo intermediário.

Para alcançar o que foi mencionado, basta alterar a configuração do OTEL Collector:

YAML

 

exporters:
  prometheus:                                                               #1
    endpoint: ":${env:PROMETHEUS_PORT}"                                     #2

service:
  pipelines:
    metrics:
      receivers: [ "prometheus" ]
      exporters: [ "prometheus" ]                                           #3

  1. Adicionar um exportador prometheus
  2. Expor um ponto final compatível com o Prometheus
  3. Substituir impressão por exposição

É isso. O OTEL Collector é muito flexível.

Note que o Collector é multi-entrada, multi-saída. Para imprimir dados e expor via ponto de extremidade, adicionamos-os ao pipeline:

YAML

 

exporters:
  prometheus:                                                               #1
    endpoint: ":${env:PROMETHEUS_PORT}"
  logging:                                                                  #2
    loglevel: debug

service:
  pipelines:
    metrics:
      receivers: [ "prometheus" ]
      exporters: [ "prometheus", "logging" ]                                #3

  1. Expor dados
  2. Imprimir dados
  3. O pipeline irá imprimir dados e expor eles

Com o exportador do Prometheus configurado, podemos visualizar métricas no Grafana.

Note que os receptores e exportadores especificam seu tipo e cada um deles deve ser único. Para cumprir com o último requisito, podemos adicionar um qualificador para distinguir entre eles, por exemplo, prometheus/foo e prometheus/bar.

Processamento de Dados Intermediário

A valid question would be why the OTEL Collector is set between the source and Prometheus, as it makes the overall design more fragile. At this stage, we can leverage the true power of the OTEL Collector: data processing. So far, we have ingested raw metrics, but the source format may not be adapted to how we want to visualize data. For example, in our setup, metrics come from our fake generator, “business,” and the underlying NodeJS platform, “technical.” It is reflected in the metrics’ name. We could add a dedicated source label and remove the unnecessary prefix to filter more efficiently.

Você declara processadores de dados na seção processors do arquivo de configuração. O coletor os executa na ordem em que são declarados. Vamos implementar a transformação acima.

O primeiro passo para alcançar nosso objetivo é entender que o coletor possui duas versões: uma “básica” e uma contrib que se baseia nela. Os processadores incluídos na primeira são limitados, tanto em número quanto em capacidades; portanto, precisamos mudar para a versão contrib.

YAML

 

collector:
  image: otel/opentelemetry-collector-contrib:0.87.0                        #1
  environment:
    - METRICS_HOST=fake-metrics
    - METRICS_PORT=5000
    - PROMETHEUS_PORT=8889
  volumes:
    - ./config/collector/config.yml:/etc/otelcol-contrib/config.yaml:ro     #2

  1. Use o sabor contrib
  2. Para adicionar diversão, o arquivo de configuração está em outro caminho

Neste ponto, podemos adicionar o próprio processador:

YAML

 

processors:
  metricstransform:                                                         #1
    transforms:                                                             #2
      - include: ^fake_(.*)$                                                #3
        match_type: regexp                                                  #3
        action: update
        operations:                                                         #4
          - action: add_label                                               #5
            new_label: origin
            new_value: fake
      - include: ^fake_(.*)$
        match_type: regexp
        action: update                                                      #6
        new_name: $${1}                                                     #6-7
# Faça o mesmo com métricas geradas por NodeJS

  1. Invoque o processador de transformação de métricas
  2. Lista de transformações aplicadas na ordem
  3. Corresponde a todas as métricas com o regexp definido
  4. Lista de operações aplicadas na ordem
  5. Adicione a etiqueta
  6. Renomeie a métrica removendo o prefixo do grupo de regexp
  7. Coisas divertidas: a sintaxe é $${x}

Finalmente, adicionamos o processador definido ao pipeline:

YAML

 

service:
  pipelines:
    metrics:
      receivers: [ "prometheus" ]
      processors: [ "metricstransform" ]
      exporters: [ "prometheus" ]

Aqui estão os resultados:

Conectando Receptores e Exportadores

A connector is both a receiver and an exporter and connects two pipelines. The example from the documentation receives the number of spans (tracing) and exports the count, which has a metric. I tried to achieve the same with 500 errors — spoiler: it doesn’t work as intended.

Primeiro, vamos adicionar um receptor de logs:

YAML

 

receivers:
  filelog:
    include: [ "/var/logs/generated.log" ]

Em seguida, adicionamos um conector:

YAML

 

connectors:
  count:
    requests.errors:
      description: Number of 500 errors
      condition: [ "status == 500 " ]

Por último, conectamos o receptor de logs e o exportador de métricas:

YAML

 

service:
   pipelines:
     logs:
       receivers: [ "filelog" ]
       exporters: [ "count" ]
     metrics:
       receivers: [ "prometheus", "count" ]

A métrica é nomeada como log_record_count_total, mas seu valor permanece em 1.

Manipulação de Logs

Processadores permitem a manipulação de dados; operadores são processadores especializados que trabalham com logs. Se você está familiarizado com a pilha ELK, eles são equivalentes ao Logstash.

Até agora, o timestamp do log é o timestamp de ingestão. Vamos mudá-lo para o timestamp de criação.

YAML

 

receivers:
  filelog:
    include: [ "/var/logs/generated.log" ]
    operators:
      - type: json_parser                                                   #1
        timestamp:                                                          #2
          parse_from: attributes.datetime                                   #3
          layout: "%d/%b/%Y:%H:%M:%S %z"                                    #4
        severity:                                                           #2
          parse_from: attributes.status                                     #3
          mapping:                                                          #5
            error: 5xx                                                      #6
            warn: 4xx
            info: 3xx
            debug: 2xx
      - id: remove_body                                                     #7
        type: remove
        field: body
      - id: remove_datetime                                                 #7
        type: remove
        field: attributes.datetime
      - id: remove_status                                                   #7
        type: remove
        field: attributes.status

  1. O log está em formato JSON; podemos usar o fornecido analisador JSON
  2. Atributos de metadados para definir
  3. Campos para ler
  4. Padrão de análise
  5. Tabela de mapeamento
  6. Aceitar uma faixa, por exemplo, 501-599. O operador tem um valor interpretado especial 5xx (e similares) para status HTTP.
  7. Remover dados duplicados

Logs

Neste ponto, podemos enviar os logs para qualquer componente de agregação de logs. Vamos permanecer no ambiente Grafana Labs e usar o Loki.

YAML

 

exporters:
  loki:
    endpoint: "http://loki:3100/loki/api/v1/push"

Também podemos usar logs do próprio coletor:

YAML

 

service:
  telemetry:
    logs:

Finalmente, vamos adicionar outra pipeline:

YAML

 

service:
  pipelines:
    logs:
      receivers: [ "filelog" ]
      exporters: [ "loki" ]

O Grafana também pode visualizar os logs. Escolha o Loki como fonte de dados.

Conclusão

Neste post, exploramos o coletor OpenTelemetry. Embora não seja parte obrigatória da arquitetura do OTEL, é uma faca suíça útil para todos os seus processamentos de dados. Caso você não esteja preso a uma pilha específica ou não queira estar, isso é de grande ajuda.

O código-fonte completo deste post pode ser encontrado no GitHub.

Para ir mais longe

Source:
https://dzone.com/articles/exploring-the-opentelemetry-collector