Ukratko

Najvažnije iz članka

  • OpenTelemetry je de facto standard za generiranje, prikupljanje i izvoz telemetrijskih podataka (tracevi, metrike, logovi) u distribuiranim sustavima, rješavajući problem fragmentacije između vendorskih rješenja.
  • Distributed tracing omogućuje vizualizaciju putanje pojedinačnog zahtjeva kroz više mikrousluga, ključno za debugiranje i optimizaciju performansi složenih arhitektura.
  • Implementacija OpenTelemetryja uključuje konfiguraciju Collector servisa (npr. s Jaegerom) i instrumentaciju aplikacija pomoću OpenTelemetry SDK-ova, koristeći automatsku i/ili manualnu instrumentaciju.
  • Glavne prednosti OpenTelemetryja su standardizacija, vendor-agnostičnost, fleksibilnost i podrška za sva tri stupa observabilnosti, što dovodi do boljeg razumijevanja sustava i bržeg rješavanja problema.
Sadržaj članka
  1. Uvod u Observabilnost i Distributed Tracing
  2. Izazovi distribuiranog praćenja prije OpenTelemetryja
  3. Što je OpenTelemetry?
  4. Implementacija Distributed Tracinga s OpenTelemetryjem
  5. 1. Postavljanje infrastrukture
  6. 2. Instrumentacija Python mikrousluga
  7. 3. Pokretanje i testiranje
  8. Prednosti OpenTelemetryja
  9. Zaključak

Observabilnost (eng. observability) postala je ključna komponenta modernih softverskih sustava, pogotovo u arhitekturama temeljenim na mikrouslugama. Dok tradicionalno praćenje (monitoring) govori je li sustav ispravan, observabilnost nam omogućuje razumijevanje zašto sustav radi ili ne radi na određeni način. U svijetu gdje kompleksnost sustava raste eksponencijalno, sposobnost praćenja toka zahtjeva kroz desetke, pa čak i stotine, neovisnih servisa postaje neizbježna. Tu na scenu stupa distributed tracing, a OpenTelemetry se pozicionirao kao de facto standard za njegovu implementaciju.

Uvod u Observabilnost i Distributed Tracing

Observabilnost se obično sastoji od tri glavna stupa: logova (logs), metrika (metrics) i traceva (traces). Svaki stup pruža jedinstven uvid u ponašanje aplikacije:

  • Logovi: Zapisuju specifične događaje koji se dogode unutar aplikacije. Pružaju detaljan kontekst pojedinih operacija.
  • Metrike: Numeričke vrijednosti koje se mjere tijekom vremena. Odlične su za agregirane poglede na performanse sustava (npr. prosječno vrijeme odziva, iskorištenost CPU-a).
  • Tracevi: Prikazuju putanju pojedinačnog zahtjeva kroz više servisa u distribuiranom sustavu. Omogućuju precizno lociranje uskih grla i grešaka u složenim interakcijama.

Distributed tracing je posebno moćan jer omogućava vizualizaciju cijelog životnog ciklusa zahtjeva. Bez njega, debugiranje problema koji obuhvaćaju više mikrousluga često se svodi na mukotrpno pregledavanje logova iz različitih izvora, što je neučinkovito i sklono pogreškama. Svaki zahtjev dobiva jedinstveni identifikator (trace ID), a svaka operacija unutar tog zahtjeva dobiva svoj span ID. Spans su hijerarhijski organizirani, što nam omogućuje da vidimo tko je koga pozvao i koliko je vremena svaka operacija trajala.

Izazovi distribuiranog praćenja prije OpenTelemetryja

Prije OpenTelemetryja, ekosustav distribuiranog praćenja bio je fragmentiran. Postojali su razni vendorski specifični SDK-ovi (npr. Jaeger, Zipkin, AWS X-Ray, Google Cloud Trace) koji su bili nekompatibilni. To je značilo da bi promjena dobavljača ili korištenje više alata istovremeno bila izuzetno složena i zahtijevala bi značajno prepisivanje koda. Nedostatak standardizacije otežavao je interoperabilnost i usporavao usvajanje distributed tracinga.

Što je OpenTelemetry?

OpenTelemetry je CNCF (Cloud Native Computing Foundation) projekt stvoren spajanjem OpenTracinga i OpenCensus projekata. Njegov cilj je pružiti jedinstven, vendor-agnostičan set API-ja, SDK-ova i alata za generiranje, prikupljanje i izvoz telemetrijskih podataka (tracevi, metrike, logovi) iz vaše aplikacije. Ključne značajke OpenTelemetryja uključuju:

  • Standardizirani API-ji i SDK-ovi: Dostupni su za većinu popularnih programskih jezika. Omogućuju developerima da instrumentiraju svoje aplikacije bez vezivanja za specifične pozadinske sustave.
  • Kolektor (Collector): Robustan proxy koji prima telemetrijske podatke, obrađuje ih (filtrira, preoblikuje, obogaćuje) i izvozi ih u različite backende (Jaeger, Prometheus, Kafka, OTLP).
  • Automatska instrumentacija: Za mnoge popularne frameworke i biblioteke, OpenTelemetry nudi automatsku instrumentaciju koja minimalizira potrebu za ručnim kodiranjem.
  • Proširivost: OpenTelemetry je dizajniran da bude proširiv, omogućujući korisnicima da dodaju vlastite procesore, eksportore i instrumentacije.

OpenTelemetry ne pruža vlastiti backend za vizualizaciju. Umjesto toga, fokusira se na generiranje i izvoz telemetrijskih podataka, ostavljajući vizualizaciju postojećim, specijaliziranim alatima (Jaeger, Grafana, SigNoz, Lightstep, Datadog itd.). Ova odvojenost omogućuje fleksibilnost i izbjegava vendor lock-in.

Implementacija Distributed Tracinga s OpenTelemetryjem

Za demonstraciju implementacije koristit ćemo jednostavan sustav s tri mikrousluge pisane u Pythonu, a OpenTelemetry Collector će posredovati u slanju podataka u Jaeger kao backend za vizualizaciju. Pretpostavit ćemo da imate Docker instaliran.

1. Postavljanje infrastrukture

Prvo ćemo postaviti Jaeger i OpenTelemetry Collector koristeći Docker Compose. Kreirajte docker-compose.yaml datoteku:

version: '3.8'
services:
  jaeger:
    image: jaegertracing/all-in-one:latest
    ports:
      - "6831:6831/udp"
      - "16686:16686"
      - "14268:14268"
    environment:
      - COLLECTOR_OTLP_ENABLED=true

  otel-collector:
    image: otel/opentelemetry-collector:latest
    command: ["--config=/etc/otel-collector-config.yaml"]
    volumes:
      - ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
    ports:
      - "4317:4317" # OTLP gRPC receiver
      - "4318:4318" # OTLP HTTP receiver
      - "8888:8888" # Prometheus metrics
    depends_on:
      - jaeger

networks:
  default:
    driver: bridge

Zatim, kreirajte otel-collector-config.yaml za konfiguraciju kolektora. Ovdje ćemo konfigurirati OTLP prijemnik i Jaeger eksportor:

receivers:
  otlp:
    protocols:
      grpc:
      http:

exporters:
  jaeger:
    endpoint: jaeger:14250
    tls:
      insecure: true

  logging:
    loglevel: debug

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: []
      exporters: [jaeger, logging]
    metrics:
      receivers: [otlp]
      processors: []
      exporters: [logging]
    logs:
      receivers: [otlp]
      processors: []
      exporters: [logging]

Pokrenite infrastrukturu:

docker-compose up -d

Sada imate Jaeger UI dostupan na http://localhost:16686 i OpenTelemetry Collector spreman za primanje podataka.

2. Instrumentacija Python mikrousluga

Pretpostavimo da imamo tri servisa: frontend, auth i product_catalog. frontend poziva auth za autentifikaciju i product_catalog za dohvaćanje proizvoda.

Zajednička logika instrumentacije

Prvo, instalirajte potrebne OpenTelemetry Python pakete:

pip install opentelemetry-api opentelemetry-sdk opentelemetry-exporter-otlp opentelemetry-instrumentation-flask opentelemetry-instrumentation-requests

Kreirajmo instrumentation.py datoteku koja će sadržavati zajedničku logiku za inicijalizaciju OpenTelemetryja za svaki servis:

from opentelemetry import trace
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import SimpleSpanProcessor
from opentelemetry.instrumentation.flask import FlaskInstrumentor
from opentelemetry.instrumentation.requests import RequestsInstrumentor

def configure_opentelemetry(service_name):
    resource = Resource.create({"service.name": service_name})

    # Inicijalizacija Tracer Providera
    provider = TracerProvider(resource=resource)
    trace.set_tracer_provider(provider)

    # Konfiguracija OTLP exportera za slanje podataka Collectoru
    span_exporter = OTLPSpanExporter(endpoint="http://localhost:4317", insecure=True)
    span_processor = SimpleSpanProcessor(span_exporter)
    provider.add_span_processor(span_processor)

    # Automatska instrumentacija
    FlaskInstrumentor().instrument()
    RequestsInstrumentor().instrument()

    print(f"OpenTelemetry configured for service: {service_name}")

auth servis (auth_service.py)

Jednostavan Flask servis za provjeru autentičnosti.

from flask import Flask, jsonify, request
from instrumentation import configure_opentelemetry

app = Flask(__name__)
configure_opentelemetry("auth-service")

@app.route('/authenticate', methods=['POST'])
def authenticate():
    username = request.json.get('username')
    password = request.json.get('password')

    # Simulacija provjere autentifikacije
    if username == "user" and password == "pass":
        return jsonify({"status": "authenticated", "user_id": 123}), 200
    else:
        return jsonify({"status": "unauthorized"}), 401

if __name__ == '__main__':
    app.run(port=5001, debug=True)

product_catalog servis (product_catalog_service.py)

Jednostavan Flask servis za dohvaćanje proizvoda.

from flask import Flask, jsonify
from instrumentation import configure_opentelemetry

app = Flask(__name__)
configure_opentelemetry("product-catalog-service")

@app.route('/products', methods=['GET'])
def get_products():
    products = [
        {"id": 1, "name": "Laptop", "price": 1200},
        {"id": 2, "name": "Mouse", "price": 25},
        {"id": 3, "name": "Keyboard", "price": 75}
    ]
    return jsonify(products)

if __name__ == '__main__':
    app.run(port=5002, debug=True)

frontend servis (frontend_service.py)

Flask servis koji poziva auth i product_catalog.

from flask import Flask, jsonify, request
import requests # Automatski instrumentiran od strane OpenTelemetryja
from instrumentation import configure_opentelemetry
from opentelemetry import trace

app = Flask(__name__)
configure_opentelemetry("frontend-service")

tracer = trace.get_tracer(__name__)

@app.route('/dashboard', methods=['POST'])
def dashboard():
    username = request.json.get('username')
    password = request.json.get('password')

    with tracer.start_as_current_span("authenticate_user"):
        auth_response = requests.post('http://localhost:5001/authenticate', json={'username': username, 'password': password})
        if auth_response.status_code != 200:
            return jsonify({"error": "Authentication failed"}), auth_response.status_code

    with tracer.start_as_current_span("fetch_products"):
        products_response = requests.get('http://localhost:5002/products')
        if products_response.status_code != 200:
            return jsonify({"error": "Failed to fetch products"}), products_response.status_code

    return jsonify({
        "message": f"Welcome, {username}!",
        "user_info": auth_response.json(),
        "products": products_response.json()
    }), 200

if __name__ == '__main__':
    app.run(port=5000, debug=True)

3. Pokretanje i testiranje

Pokrenite sve servise u odvojenim terminalima (ili koristite Docker za svaki servis):

# Terminal 1
python auth_service.py

# Terminal 2
python product_catalog_service.py

# Terminal 3
python frontend_service.py

Sada pošaljite zahtjev na frontend servis koristeći curl ili Postman:

curl -X POST -H "Content-Type: application/json" -d '{"username": "user", "password": "pass"}' http://localhost:5000/dashboard

Otvorite Jaeger UI na http://localhost:16686. Odaberite frontend-service iz padajućeg izbornika "Service" i kliknite "Find Traces". Trebali biste vidjeti trace koji prikazuje cijeli tok zahtjeva kroz frontend-service, auth-service i product-catalog-service.

Primijetite kako OpenTelemetry automatski propagira kontekst praćenja (trace ID, span ID) kroz HTTP headere (npr. traceparent). Ručno smo dodali with tracer.start_as_current_span() blokove unutar frontend_service.py kako bismo stvorili custom spanove za logičke operacije (authenticate_user, fetch_products), što dodatno poboljšava granularnost praćenja. Ovo je primjer manualne instrumentacije, dok su pozivi Flask i Requests biblioteka automatski instrumentirani.

Prednosti OpenTelemetryja

  • Standardizacija: Rješava fragmentaciju u ekosustavu observabilnosti, omogućujući interoperabilnost između različitih alata i dobavljača.
  • Vendor-agnostičnost: Ne vezuje vas za određenog dobavljača. Možete promijeniti backend za vizualizaciju (npr. s Jaegera na SigNoz) bez promjene koda aplikacije.
  • Fleksibilnost: Podržava automatsku i manualnu instrumentaciju, omogućujući prilagodbu potrebama vašeg sustava.
  • Potpuna observabilnost: Nije ograničen samo na traceve; podržava i metrike i logove, pružajući holistički pogled na aplikaciju.
  • Aktivna zajednica: Kao CNCF projekt, ima snažnu podršku zajednice i kontinuirano se razvija.

Zaključak

OpenTelemetry je game-changer u području observabilnosti. Usvajanjem ovog standarda, development timovi mogu značajno poboljšati svoju sposobnost razumijevanja i otklanjanja problema u distribuiranim sustavima. Ne samo da olakšava debugiranje proizvodnih incidenata, već pruža i dublji uvid u performanse, pomažući u optimizaciji i donošenju informiranih odluka o arhitekturi. Integracija OpenTelemetryja u vaše mikrousluge danas je investicija koja će se višestruko isplatiti u budućnosti.

Daljnji koraci mogu uključivati integraciju OpenTelemetryja s prometrikama i logovima, kao i istraživanje naprednijih značajki kao što su sampliranje (sampling) i kontekstualni atributi.

Izvori i dodatno čitanje

  1. OpenTelemetry Documentation
  2. Jaeger Tracing
  3. CNCF OpenTelemetry Project
  4. OpenTelemetry Python Instrumentation
B
Uredništvo portala

BAJT

Službeni autorski profil redakcije portala BAJT. Sadržaj priprema i provjerava uredništvo portala.