RousselTMLEARNING

TP · OpenTelemetry: del descubrimiento a la experiencia

Práctica: Instrumentos, vistas y exemplars con el SDK de métricas con corrección

La corrección está integrada en el TP (Ver los recuadros verdes)

Lo que vas a aprender en este TP
  • Elegir el instrumento adecuado
  • Configurar una vista
  • Usar los exemplars

Introducción #

Lo que vas a aprender en esta sección
  • Elegir el instrumento adecuado
  • Configurar una vista
  • Usar los exemplars

Contexto. La dirección quiere seguir los pedidos, los carritos en curso, el stock y la latencia de la API. Produces estas métricas con OpenTelemetry y compruebas que se pueden explotar en Prometheus.

Requisitos previos: Docker y Python 3.10 o superior; la pila grafana/otel-lgtm (Collector, Tempo, Loki, Prometheus, Pyroscope y Grafana en un solo contenedor)  ·  Duración estimada: 50 min

Métricas bien tipadas, histogramas correctos y una métrica vinculada a una traza.

Puesta en práctica #

Pasos guiados, con su corrección.

  1. 1. Arrancar la pila de observabilidad

    ¿Por qué? Una sola imagen proporciona un Collector OTLP y todos los backends: nos centramos en la señal.

    Lanza grafana/otel-lgtm, crea un entorno Python y apunta los exportadores al puerto OTLP/HTTP 4318.
    Ver la corrección
    docker run -d --name lgtm -p 3000:3000 -p 4317:4317 -p 4318:4318 -p 4040:4040 grafana/otel-lgtm
    python3 -m venv .venv && . .venv/bin/activate
    export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
    # Grafana : http://localhost:3000 (admin / admin)
  2. 2. Producir métricas con las cuatro familias de instrumentos

    ¿Por qué? El tipo de instrumento determina la agregación y, por tanto, la exactitud de los gráficos.

    Escribe boutique.py que, en bucle, incremente un Counter de pedidos, haga variar un UpDownCounter de carritos, registre la duración de las peticiones en un Histogram y exponga el stock mediante un ObservableGauge. Exportación cada 5 segundos.
    Ver la corrección
    pip install opentelemetry-sdk opentelemetry-exporter-otlp-proto-http
    cat > boutique.py <<'EOF'
    import random, time
    from opentelemetry import metrics, trace
    from opentelemetry.metrics import Observation
    from opentelemetry.sdk.resources import Resource
    from opentelemetry.sdk.metrics import MeterProvider
    from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader
    from opentelemetry.sdk.metrics.view import View, ExplicitBucketHistogramAggregation
    from opentelemetry.sdk.trace import TracerProvider
    from opentelemetry.sdk.trace.export import BatchSpanProcessor
    from opentelemetry.exporter.otlp.proto.http.metric_exporter import OTLPMetricExporter
    from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
    
    resource = Resource.create({"service.name": "boutique", "service.version": "1.0.0"})
    VUES = []      # étape 4 : ajouter une vue ici
    reader = PeriodicExportingMetricReader(OTLPMetricExporter(), export_interval_millis=5000)
    metrics.set_meter_provider(MeterProvider(resource=resource, metric_readers=[reader], views=VUES))
    tp = TracerProvider(resource=resource); tp.add_span_processor(BatchSpanProcessor(OTLPSpanExporter()))
    trace.set_tracer_provider(tp)
    
    meter = metrics.get_meter("tp.boutique", "1.0.0")
    tracer = trace.get_tracer("tp.boutique")
    commandes = meter.create_counter("shop.orders", unit="{order}", description="Commandes validées")
    paniers = meter.create_up_down_counter("shop.carts.active", unit="{cart}", description="Paniers ouverts")
    duree = meter.create_histogram("http.server.request.duration", unit="s", description="Durée des requêtes")
    
    def lire_stock(options):
        for entrepot in ("lyon", "lille"):
            yield Observation(random.randint(50, 500), {"shop.warehouse": entrepot})
    meter.create_observable_gauge("shop.stock.level", callbacks=[lire_stock], unit="{item}")
    
    while True:
        with tracer.start_as_current_span("POST /orders"):
            d = random.choice([random.uniform(0.02, 0.2)] * 9 + [random.uniform(1, 3)])   # 10 % de requêtes lentes
            time.sleep(d / 10)
            duree.record(d, {"http.request.method": "POST", "http.route": "/orders", "http.response.status_code": 201})
        commandes.add(1, {"shop.payment.method": random.choice(["card", "paypal", "transfer"])})
        paniers.add(random.choice([1, 1, -1]))
    EOF
    python boutique.py &      # laisser tourner
  3. 3. Encontrar las métricas en Prometheus

    ¿Por qué? La conversión de OTLP a Prometheus renombra las métricas: hay que saber encontrarlas.

    En Explore > Prometheus, encuentra cada métrica con su nombre Prometheus y escribe las consultas: ritmo de pedidos por medio de pago, carritos abiertos, stock por almacén, latencia p95.
    Ver la corrección
    sum by (shop_payment_method) (rate(shop_orders_total[1m]))
    shop_carts_active
    shop_stock_level
    histogram_quantile(0.95, sum by (le) (rate(http_server_request_duration_seconds_bucket[1m])))
  4. 4. Corregir los límites con una vista

    ¿Por qué? Los límites por defecto (0, 5, 10, 25… 10000) están pensados para milisegundos: en segundos casi todo cae en el primer intervalo y el p95 es falso.

    Observa los buckets actuales, detén el script, añade una vista con límites en segundos, vuelve a lanzarlo y compara el p95.
    Ver la corrección
    # avant : sum by (le) (rate(http_server_request_duration_seconds_bucket[1m]))  -> tout dans le=5
    kill %1
    sed -i 's/^VUES = \[\].*/VUES = [View(instrument_name="http.server.request.duration", aggregation=ExplicitBucketHistogramAggregation([0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5]))]/' boutique.py
    python boutique.py &
    # après quelques minutes : le p95 reflète enfin les 10 % de requêtes lentes
  5. 5. Medir el efecto de la cardinalidad

    ¿Por qué? Cada valor de atributo distinto crea una serie: un identificador en una métrica dispara el almacenamiento.

    Cuenta las series de shop_orders_total, añade temporalmente un atributo user.id aleatorio, vuelve a contar y quítalo.
    Ver la corrección
    count(shop_orders_total)                                   # 3 séries
    # dans boutique.py : commandes.add(1, {..., "user.id": str(random.randint(1, 5000))})
    count(shop_orders_total)                                   # des centaines, puis des milliers
    # conclusion : identifiants sur les spans et les logs, jamais sur les métriques
  6. 6. Vincular una latencia con su traza

    ¿Por qué? Las mediciones hechas dentro de un span muestreado llevan un exemplar con el trace_id: se pasa del gráfico a la petición exacta.

    Muestra el p95 en un panel de Grafana con la opción Exemplars activada, haz clic en un punto alto y abre la traza correspondiente en Tempo. Si no aparece ningún exemplar, comprueba la versión del SDK y OTEL_METRICS_EXEMPLAR_FILTER.
    Ver la corrección
    pip show opentelemetry-sdk | grep Version          # exemplars : SDK Python récent
    export OTEL_METRICS_EXEMPLAR_FILTER=trace_based
    kill %1 ; python boutique.py &

Nivel de dificultad : ●●●○○ (3/5)

Mi seguimiento

  • ⏱ Duración estimada : ≈ 50 min

Inicia sesión para seguir tu progreso y tomar notas. Iniciar sesión

✉️ Hacer una pregunta

Artículos recomendados

Les métriques OpenTelemetry : instruments, temporalité, histogrammes et exemplars

Maîtriser le signal métrique : MeterProvider, instruments synchrones et asynchrones (Counter, UpD...

Grafana Alloy: Perfilado Continuo con Pyroscope

Descubra cómo configurar el perfilado continuo (Continuous Profiling) en sus entornos utilizando ...