Com o rápido avanço dos sistemas de tecnologia, e o número diverso de micro serviços necessários para atender o funcionamento de um negócio, vem se tornando cada vez mais complexo a gestão e busca por qualidade no desenvolvimento e principalmente em produção. O OpenTelemetry surgiu como uma proposta de solução capaz de facilitar e acelerar a implementação de monitoramento de aplicações modernas.
O que é o OpenTelemetry?
O OpenTelemetry é um framework de observabilidade de código aberto que permite às equipes de desenvolvimento gerar, processar e transmitir dados de telemetria em um formato unificado, provendo um conjunto de APIs, SDKs e ferramentas padronizados e independentes de fornecedor para coletar e exportar esses dados de telemetria.
Desenvolvido pela Cloud Native Computing Foundation (CNCF) com o objetivo de padronizar a coleta e roteamento de métricas, logs e traces para plataformas de monitoramento diversas.
Ao utilizar o OpenTelemetry podemos desacoplar nossos sistemas dos backends de observabilidade, pois o mesmo provê capacidade de instrumentar o código para gerar dados padronizados de telemetria e enviá-los utilizando o protocolo OTLP.
Como funciona?
Junto com as ferramentas, APIs e SDKs o OpenTelemetry disponibiliza um agente (Collector) responsável por receber os dados das suas aplicações e enviar para os backends de observabilidade.
Arquitetura do Collector
Uma das boas práticas de uso do OpenTelemetry é fazer o deploy do Collector em um servidor separado da aplicação, e configurar a aplicação para enviar dados para o endpoint do mesmo.
Componentes do Collector:
Receptores (Receivers): Responsáveis por receber dados de telemetria de várias fontes. Eles podem lidar com diferentes formatos, como OTLP (OpenTelemetry Protocol), Jaeger, Prometheus e até ferramentas comerciais ou proprietárias. Um receptor pode receber traces de solicitações HTTP, métricas de um serviço específico ou logs de aplicativos.
Exportadores (Exporters): Responsáveis por enviar os dados de telemetria processados para os backends de observabilidade, como Jaeger, Zipkin, Prometheus ou qualquer outro destino. Entre os exportadores, os baseados no OpenTelemetry Protocol (OTLP) são especialmente relevantes, pois mantêm a integridade dos dados sem perda de informações.
Processadores (Processors): Atuam na transformação e filtragem dos dados de telemetria antes de serem exportados. Permitindo aplicar regras de negócios, agregar informações, adicionar campos personalizados ou até mesmo reduzir o volume de dados. Por exemplo, um processador pode enriquecer traces com informações contextuais ou remover spans irrelevantes
Também podemos instrumentar a aplicação e enviar os dados de telemetria diretamente para os backends de observabilidade sem utilizar o collector, desta forma tornando o uso mais simples sem a necessidade de mais uma peça para gerenciar, mas em contra partida gerando forte acoplamento entre o código do sistema e o backend de observabilidade.
Projeto de exemplo
Para exemplificar melhor o funcionamento do OpenTelemetry, vamos desenvolver alguns projetos .Net Core do tipo WebApi, simulando um sistema de pedidos. Também iremos usar o Docker e Docker Compose para subir as aplicações e alguns backends de observabilidade (Prometheus, Grafana, Jaeger).
Estrutura do projeto
Criando os projetos e fazendo a instrumentação.
Os 3 projetos é do tipo WebApi versão .net core 8, após criar o projetos vamos instalar os seguintes pacotes:
Na classe Program.cs
vamos fazer a instrumentação da aplicação:
- Definindo url do collector e adicionamento SDK do OpenTelemetry.
var tracingOtlpGrpcEndpoint = "http://otel-collector:4317";
var otel = builder.Services.AddOpenTelemetry();
- Configurando OpenTelemetry Resource e o nome da aplicação que será registrado nos dados de telemetria.
otel.ConfigureResource(resource => resource
.AddService(serviceName: builder.Environment.ApplicationName));
- Instrumentando a aplicação para enviar métricas.
otel.WithMetrics(metrics => metrics
// Metrics provider from OpenTelemetry
.AddHttpClientInstrumentation()
.AddAspNetCoreInstrumentation()
.AddMeter("PedidoApiMetrics")
.AddOtlpExporter(otlpOptions =>
{
otlpOptions.Endpoint = new Uri(tracingOtlpGrpcEndpoint);
})
);
- Instrumentando a aplicação para enviar tracing.
otel.WithTracing(tracing =>
{
tracing.AddAspNetCoreInstrumentation();
tracing.AddHttpClientInstrumentation();
tracing.AddSource("PedidoApi");
if (tracingOtlpGrpcEndpoint != null)
{
tracing.AddOtlpExporter(otlpOptions =>
{
otlpOptions.Endpoint = new Uri(tracingOtlpGrpcEndpoint);
});
}
else
{
tracing.AddConsoleExporter();
}
});
- Instrumentando a aplicação para enviar logs.
otel.WithLogging(logging =>
{
logging.AddOtlpExporter((otlpOptions, processorOptions) =>
{
otlpOptions.Endpoint = new Uri(tracingOtlpGrpcEndpoint);
processorOptions.BatchExportProcessorOptions.ScheduledDelayMilliseconds = 2000;
processorOptions.BatchExportProcessorOptions.MaxExportBatchSize = 512;
});
});
Configurando arquivo docker-compose.yml
name: webapiotlp-example
networks:
proxy:
driver: bridge
services:
grafana:
image: grafana/grafana-enterprise
container_name: grafana
restart: unless-stopped
ports:
- '3000:3000'
networks:
- proxy
environment:
- GF_AUTH_ANONYMOUS_ENABLED=true
- GF_AUTH_ANONYMOUS_ORG_ROLE=Admin
- GF_AUTH_DISABLE_LOGIN_FORM=true
- GF_FEATURE_TOGGLES_ENABLE=traceqlEditor
grafana-loki:
image: grafana/loki:3.1.0
container_name: grafana-loki
ports:
- '3100:3100'
volumes:
- ./grafana-loki-config.yaml:/etc/loki/local-config.yaml
command:
- -config.file=/etc/loki/local-config.yaml
- -print-config-stderr=true
networks:
- proxy
otel-collector:
image: otel/opentelemetry-collector-contrib:latest
command: ["--config=/etc/otel-collector-config.yaml"]
volumes:
# - ./logs:/etc/output:rw
- ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
ports:
- 1888:1888 # pprof extension
- 8888:8888 # Prometheus metrics exposed by the Collector
- 8889:8889 # Prometheus exporter metrics
- 13133:13133 # health_check extension
# - 4317:4317 # OTLP gRPC receiver
# - 4318:4318 # OTLP http receiver
# - 55679:55679 # zpages extension
networks:
- proxy
depends_on:
- grafana-loki
jaeger:
image: jaegertracing/all-in-one:1.48.0
restart: always
ports:
- "6831:6831/udp" # UDP port for Jaeger agent
- "16686:16686" # Web UI
# - "14268:14268" # HTTP port for spans
# - "4317:4317" # OTLP gRPC receiver for jaeger
# - "4318:4318" # OTLP HTTP receiver for jaeger
environment:
- LOG_LEVEL=debug
networks:
- proxy
prometheus:
image: prom/prometheus:latest
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
ports:
- "9090:9090"
networks:
- proxy
pedido-api:
image: pedido-api:dev
build:
context: .
dockerfile: ./Src/00-PedidoApi/Dockerfile
ports:
- "8080:8080"
environment:
- DELAY=2
- BASE_ADDRESS=http://estoque-api:8080/
- URL_POST=Estoque
networks:
- proxy
depends_on:
- otel-collector
- jaeger
estoque-api:
image: estoque-api:dev
build:
context: .
dockerfile: ./Src/01-EstoqueApi/Dockerfile
ports:
- "8081:8080"
environment:
- DELAY=1
- BASE_ADDRESS=http://notafiscal-api:8080/
- URL_POST=NotaFiscal
networks:
- proxy
depends_on:
- otel-collector
- jaeger
notafiscal-api:
image: notafiscal-api:dev
build:
context: .
dockerfile: ./Src/02-NotaFiscalApi/Dockerfile
ports:
- "8082:8080"
environment:
- DELAY=0
networks:
- proxy
depends_on:
- otel-collector
- jaeger
Configurando receptores e exportadores no arquivo otel-collector-config.yaml
receivers:
otlp:
protocols:
grpc:
endpoint: otel-collector:4317
http:
endpoint: otel-collector:4318
exporters:
debug:
verbosity: detailed
prometheus:
endpoint: "0.0.0.0:8889"
send_timestamps: true
metric_expiration: 180m
enable_open_metrics: true
logging:
otlp:
endpoint: jaeger:4317
tls:
insecure: true
otlphttp:
endpoint: http://grafana-loki:3100/otlp
# file:
# path: /etc/output/logs.json
service:
pipelines:
traces:
receivers: [otlp]
exporters: [debug, logging, otlp]
metrics:
receivers: [otlp]
exporters: [debug, logging, prometheus]
logs:
receivers: [otlp]
exporters: [debug, logging, otlphttp]
Configurando prometheus para coletar métricas do collector no arquivo prometheus.yml
global:
scrape_interval: 5s
scrape_configs:
- job_name: 'otel-collector'
static_configs:
- targets: ['otel-collector:8888', 'otel-collector:8889']
Vamos capturar os logs obtidos no collector pelo grafana-loki, para funcionar local é necessário configurar no arquivo grafana-loki-config.yaml
auth_enabled: false
limits_config:
allow_structured_metadata: true
server:
http_listen_port: 3100
common:
instance_addr: 127.0.0.1
path_prefix: /loki
storage:
filesystem:
chunks_directory: /loki/chunks
rules_directory: /loki/rules
replication_factor: 1
ring:
kvstore:
store: inmemory
schema_config:
configs:
- from: 2020-10-24
store: tsdb
object_store: filesystem
schema: v13
index:
prefix: index_
period: 24h
ruler:
alertmanager_url: http://localhost:9093
Executando o projeto e validando os dados coletados
- Vamos criar um novo pedido fazendo uma requisição na api de pedidos usando o Postman:
- Verificando no Jaeger os traces gerados:
- Verificando no Prometheus as métricas geradas:
- Verificando logs enviados para o grafana loki:
Quais são os benefícios ao utilizar o OpenTelemetry?
Como podemos ver, ganhamos muito ao utilizar o OpenTelemetry, com ele podemos implementar de uma forma simples e padronizada rastreabilidade de ponta a ponta em nossas aplicações, ajudando na identificação de gargalos e melhoria de desempenho. Também nos auxilia na detecção de erros, mostrando todo o fluxo de solicitações e logs. Não tenho dúvidas que é uma ferramenta essencial que irá ajudar o desenvolvedor a obter melhor qualidade em seus projetos.
Agradecimento
Obrigado por ler até aqui. Se você tiver alguma dúvida ou sugestão, não deixe de comentar.
Top comments (0)