RousselTMLEARNING

TP · OpenTelemetry: del descubrimiento a la experiencia

Práctica: Perfilar una aplicación y vincular perfiles y trazas con corrección

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

Lo que vas a aprender en este TP
  • Enviar perfiles continuos
  • Leer un flame graph
  • Vincular spans y perfiles

Introducción #

Lo que vas a aprender en esta sección
  • Enviar perfiles continuos
  • Leer un flame graph
  • Vincular spans y perfiles

Contexto. Un servicio de cálculo de descuentos es lento y consume mucha CPU, pero sus trazas solo muestran un span largo. Añades la cuarta señal para encontrar la línea responsable.

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: 45 min

Un cuello de botella identificado en el código gracias al vínculo span-perfil, corregido y medido.

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. Un servicio lento

    ¿Por qué? El código contiene un defecto clásico: una expresión regular recompilada y una concatenación costosa en cada llamada.

    Instala el SDK de Pyroscope y su integración con OpenTelemetry y escribe remises.py: trazas hacia Tempo, perfiles hacia Pyroscope con el mismo nombre de servicio y el PyroscopeSpanProcessor.
    Ver la corrección
    pip install opentelemetry-sdk opentelemetry-exporter-otlp-proto-http pyroscope-io pyroscope-otel
    cat > remises.py <<'EOF'
    import random, re, time
    import pyroscope
    from pyroscope.otel import PyroscopeSpanProcessor
    from opentelemetry import trace
    from opentelemetry.sdk.resources import Resource
    from opentelemetry.sdk.trace import TracerProvider
    from opentelemetry.sdk.trace.export import BatchSpanProcessor
    from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
    
    pyroscope.configure(application_name="remises", server_address="http://localhost:4040", tags={"env": "tp"})
    provider = TracerProvider(resource=Resource.create({"service.name": "remises"}))
    provider.add_span_processor(PyroscopeSpanProcessor())
    provider.add_span_processor(BatchSpanProcessor(OTLPSpanExporter()))
    trace.set_tracer_provider(provider)
    tracer = trace.get_tracer("tp.remises")
    
    def code_valide(code):
        motif = re.compile(r"^(PROMO|VIP)-[0-9]{4}" + "(-X)?" * random.randint(20, 40) + "$")   # recompilé à chaque appel
        return bool(motif.match(code))
    
    def calculer_remise(panier):
        texte = ""
        for article in panier:
            texte += f"{article};"                       # concaténation quadratique
        return sum(1 for a in panier if code_valide(a)) * 5
    
    while True:
        with tracer.start_as_current_span("calculer remise") as span:
            panier = [f"PROMO-{random.randint(1000, 9999)}" for _ in range(random.randint(200, 800))]
            span.set_attribute("shop.cart.items", len(panier))
            calculer_remise(panier)
        time.sleep(0.05)
    EOF
    python remises.py &
  3. 3. Leer el flame graph

    ¿Por qué? El flame graph muestra dónde se consume realmente la CPU, sin añadir ni un span.

    En Explore > Pyroscope (o Drilldown > Profiles), selecciona la aplicación remises y el tipo CPU. Identifica las mesetas más anchas y la parte de code_valide y re.compile.
    Ver la corrección
    # requête Pyroscope
    process_cpu:cpu:nanoseconds:cpu:nanoseconds{service_name="remises"}
    # attendu : calculer_remise > code_valide > re.compile / sre_compile très larges
  4. 4. Del span al perfil

    ¿Por qué? El span processor etiqueta las muestras con el span_id: se obtiene el perfil de una petición concreta.

    En Tempo, abre una traza calculer remise larga y usa Profiles for this span. Si el enlace no aparece, configura Trace to profiles en la fuente de datos de Tempo (fuente Pyroscope, tipo CPU).
    Ver la corrección
    { resource.service.name = "remises" && duration > 300ms }
    # span sélectionné > Profiles for this span : flame graph limité à ce span
    # vérifier l'attribut pyroscope.profile.id ajouté au span par le PyroscopeSpanProcessor
  5. 5. Corregir y comparar

    ¿Por qué? El perfil demuestra el efecto de una optimización en lugar de suponerlo.

    Compila la expresión una sola vez a nivel de módulo y sustituye la concatenación por ';'.join(). Vuelve a lanzarlo y compara los dos periodos con la vista Diff de Pyroscope y la duración de los spans en Tempo.
    Ver la corrección
    kill %1
    python - <<'EOF'
    s = open("remises.py").read()
    s = s.replace('def code_valide(code):\n    motif = re.compile(r"^(PROMO|VIP)-[0-9]{4}" + "(-X)?" * random.randint(20, 40) + "$")   # recompilé à chaque appel\n    return bool(motif.match(code))',
                  'MOTIF = re.compile(r"^(PROMO|VIP)-[0-9]{4}(-X)*$")\n\ndef code_valide(code):\n    return bool(MOTIF.match(code))')
    s = s.replace('    texte = ""\n    for article in panier:\n        texte += f"{article};"                       # concaténation quadratique\n', '    texte = ";".join(panier)\n')
    open("remises.py", "w").write(s)
    EOF
    python remises.py &
    # Tempo : avg_over_time des durées { name = "calculer remise" } ; Pyroscope : Diff avant / après
  6. 6. Situar los perfiles de OpenTelemetry

    ¿Por qué? El SDK de Pyroscope es una solución probada; la señal OTLP Profiles busca unificar la recogida, pero aún no es estable.

    Consulta la página de estado de la señal profiles en opentelemetry.io y la documentación del perfilador eBPF de OpenTelemetry. Redacta en tres líneas qué haría falta para sustituir el SDK por el perfilador eBPF y un Collector, y detén la pila.
    Ver la corrección
    kill %1 ; docker rm -f lgtm
    # Pistes : profileur eBPF en DaemonSet (privilégié, noyau récent), Collector avec feature gate profiles,
    # exportateur OTLP vers Pyroscope, mêmes attributs de ressource que les traces

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

Mi seguimiento

  • ⏱ Duración estimada : ≈ 45 min

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

✉️ Hacer una pregunta

Artículos recomendados

Les profils, le quatrième signal d'OpenTelemetry : profilage continu et corrélation avec les traces

Comprendre le nouveau signal profile d'OpenTelemetry : profilage continu, flame graphs, modèle de...

Grafana Alloy: Perfilado Continuo con Pyroscope

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