Últimas entradas

Implementando OpenTelemetry en Microservicios Distribuidos con Go

En este post, vamos a ver cómo instrumentar una arquitectura de microservicios desarrollada en Go utilizando OpenTelemetry para obtener trazas, métricas y logs. También veremos cómo enviar estos datos a sistemas de backend como Jaeger, Prometheus/Grafana y Seq Server, usando OpenTelemetry Collector como punto central de recolección y exportación.

1. Observabilidad en Arquitecturas Distribuidas

Cuando se trabaja con microservicios, la observabilidad es esencial para monitorear y analizar el rendimiento del sistema. Queremos tener acceso a:

  • Trazas: Para conocer el flujo de solicitudes a través de diferentes servicios.
  • Métricas: Para medir el rendimiento y el estado de los servicios.
  • Logs: Para rastrear errores y otros eventos importantes.

OpenTelemetry es una herramienta que permite recolectar estos tres tipos de datos y enviarlos a sistemas de backend como Jaeger (trazas), Prometheus (métricas) y Seq Server (logs), a través del Collector.

2. ¿Qué es OpenTelemetry?

OpenTelemetry es una plataforma estándar para recolectar y exportar trazas, métricas y logs en aplicaciones distribuidas. En nuestro entorno de microservicios, vamos a instrumentar las aplicaciones en Go para enviar estos datos al OpenTelemetry Collector, que se encargará de exportarlos a los backends de observabilidad.

3. Arquitectura de Observabilidad

En nuestro caso, cada microservicio desarrollado en Go enviará trazas, métricas y logs al OpenTelemetry Collector, que luego reenviará estos datos a los sistemas de backend. Veamos cómo configurar cada uno de estos elementos.

4. Configuración del OpenTelemetry Collector

Primero, necesitamos configurar el OpenTelemetry Collector para que reciba y exporte trazas, métricas y logs. Esta es la configuración básica que usaremos:

Configuración del Collector:

receivers:
  otlp:
    protocols:
      grpc:
        endpoint: "0.0.0.0:4317"  # Para recibir trazas, métricas y logs vía gRPC
      http:
        endpoint: "0.0.0.0:4318"  # Para recibir trazas, métricas y logs vía HTTP

  prometheus:
    config:
      scrape_configs:
        - job_name: 'otel-collector'
          scrape_interval: 5s
          static_configs:
            - targets: ['0.0.0.0:8888']  # Para recibir métricas Prometheus

  logs:
    protocols:
      grpc:
        endpoint: "0.0.0.0:4317"  # Para recibir logs vía gRPC
      http:
        endpoint: "0.0.0.0:4318"  # Para recibir logs vía HTTP

exporters:
  jaeger:
    endpoint: "jaeger-host:14250"
    tls:
      insecure: true

  prometheus:
    endpoint: "0.0.0.0:8888"  # Para exportar métricas a Prometheus

  otlp:
    endpoint: "seq-server-host:4317"  # Para exportar logs a Seq Server

service:
  pipelines:
    traces:
      receivers: [otlp]  # Recibe trazas
      exporters: [jaeger]  # Exporta trazas a Jaeger

    metrics:
      receivers: [otlp, prometheus]  # Recibe métricas
      exporters: [prometheus]  # Exporta métricas a Prometheus

    logs:
      receivers: [otlp, logs]  # Recibe logs
      exporters: [otlp]  # Exporta logs a Seq Server

5. Instrumentación en Microservicios con Go

Para que nuestros microservicios en Go envíen trazas, logs y métricas al OpenTelemetry Collector, necesitamos agregar código de instrumentación en cada microservicio.

Trazas y Métricas:

Instalamos las dependencias de OpenTelemetry en Go:

go get go.opentelemetry.io/otel
go get go.opentelemetry.io/otel/sdk
go get go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc
go get go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc

Luego, añadimos el código de configuración para el envío de trazas y métricas:

package main

import (
    "context"
    "log"
    "go.opentelemetry.io/otel"
    "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
    "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc"
    "go.opentelemetry.io/otel/sdk/trace"
    "go.opentelemetry.io/otel/sdk/metric"
    "go.opentelemetry.io/otel/sdk/resource"
    semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
    "google.golang.org/grpc"
)

func initTracer() *trace.TracerProvider {
    ctx := context.Background()
    conn, err := grpc.DialContext(ctx, "collector-host:4317", grpc.WithInsecure())
    if err != nil {
        log.Fatalf("Failed to create gRPC connection: %v", err)
    }

    exporter, err := otlptracegrpc.New(ctx, otlptracegrpc.WithGRPCConn(conn))
    if err != nil {
        log.Fatalf("Failed to create trace exporter: %v", err)
    }

    tp := trace.NewTracerProvider(
        trace.WithBatcher(exporter),
        trace.WithResource(resource.NewWithAttributes(
            semconv.SchemaURL,
            semconv.ServiceNameKey.String("MyGoService"),
        )),
    )
    otel.SetTracerProvider(tp)
    return tp
}

func initMeter() *metric.MeterProvider {
    ctx := context.Background()
    conn, err := grpc.DialContext(ctx, "collector-host:4317", grpc.WithInsecure())
    if err != nil {
        log.Fatalf("Failed to create gRPC connection: %v", err)
    }

    exporter, err := otlpmetricgrpc.New(ctx, otlpmetricgrpc.WithGRPCConn(conn))
    if err != nil {
        log.Fatalf("Failed to create metric exporter: %v", err)
    }

    mp := metric.NewMeterProvider(
        metric.WithReader(exporter),
        metric.WithResource(resource.NewWithAttributes(
            semconv.SchemaURL,
            semconv.ServiceNameKey.String("MyGoService"),
        )),
    )
    return mp
}

Logs:

Para los logs, necesitamos usar un paquete específico que envíe los registros al OTLP:

go get go.opentelemetry.io/otel/exporters/otlp/otlpgrpc
go get go.opentelemetry.io/otel/sdk/logs

Luego, añadimos la configuración para los logs:

package main

import (
    "context"
    "log"
    "go.opentelemetry.io/otel/sdk/logs"
    "go.opentelemetry.io/otel/exporters/otlp/otlpgrpc"
    "google.golang.org/grpc"
)

func initLogger() *logs.LoggerProvider {
    ctx := context.Background()
    conn, err := grpc.DialContext(ctx, "collector-host:4317", grpc.WithInsecure())
    if err != nil {
        log.Fatalf("Failed to create gRPC connection: %v", err)
    }

    exporter, err := otlpgrpc.New(ctx, otlpgrpc.WithGRPCConn(conn))
    if err != nil {
        log.Fatalf("Failed to create log exporter: %v", err)
    }

    lp := logs.NewLoggerProvider(
        logs.WithBatcher(exporter),
    )
    return lp
}

6. Mejores Prácticas

Aquí algunas recomendaciones a medida que escales tu arquitectura de microservicios:

  • Variables de Entorno: Utiliza variables de entorno para configurar dinámicamente los endpoints del Collector, el muestreo de trazas, etc. Esto facilita la gestión y configuración de la observabilidad sin tener que cambiar el código.
  • Instrumentación Automática: Siempre que sea posible, usa la instrumentación automática proporcionada por OpenTelemetry para evitar el trabajo manual de instrumentar cada servicio.
  • Collector Centralizado o Sidecar: Implementar el Collector como un servicio centralizado o como sidecar puede ayudarte a reducir la complejidad en la gestión de los datos de observabilidad.

En este post hemos visto cómo configurar la observabilidad para microservicios desarrollados en Go, con la instrumentación adecuada y el uso del Collector, podemos capturar y analizar trazas, logs y métricas de manera eficiente en un entorno distribuido.

Referencia: https://usuarioperu.com/2022/05/14/opentelemetry/

Agregue un comentario

Su dirección de correo no se hará público. Los campos requeridos están marcados *