L
Observability & Monitoring • Literally Web ID

Distributed Tracing dan Observability End-to-End Menggunakan OpenTelemetry, Prometheus, dan Grafana

⏱️ 22 menit baca • 📅 14 Oct 2026 • 👁️ 1 views
Distributed Tracing dan Observability End-to-End Menggunakan OpenTelemetry, Prometheus, dan Grafana

1. Konteks Industri & Analisis Akar Masalah

Dalam lanskap arsitektur web modern yang beralih dari monolith monolitik ke ekosistem terdistribusi (microservices, serverless, dan event-driven architecture), kompleksitas debugging melonjak secara eksponensial. Sebuah request HTTP sederhana dari klien browser (HTML/CSS/JS) yang melewati Cloudflare Edge CDN, melewati Nginx reverse proxy, mengautentikasi melalui Redis/JWT, mengeksekusi logika backend (Go/Node/PHP-FPM), mengambil data dari MySQL/MariaDB, hingga berinteraksi dengan layanan pihak ketiga melalui antrean RabbitMQ atau Redis Queue, melibatkan puluhan lompatan jaringan (network hops).

Ketika terjadi latensi tinggi atau kegagalan transaksi (error 500), pendekatan tradisional seperti melihat log secara terpisah (grep pada file log Nginx, PHP, atau Go) tidak lagi memadai. Developer terjebak dalam masalah "needle in a haystack" di mana korelasi antar log lintas layanan sangat kabur.

Kondisi Sebelum vs Sesudah Penerapan Observabilitas End-to-End:

  • Sebelum (Ad-hoc Logging & Blind Spots):
    • MTTR (Mean Time to Resolution) memakan waktu berjam-jam bahkan hari.
    • Tidak ada visibilitas terhadap service dependency graph. Latensi tersembunyi di dalam database query atau external API calls sering tidak terdeteksi.
    • Metrik sistem bersifat terisolasi (CPU/Memory tinggi tapi tidak tahu request mana yang menyebabkannya).
  • Sesudah (OpenTelemetry, Prometheus, & Grafana):
    • Distributed tracing melacak setiap request secara unik menggunakan trace_id yang merambat (propagated) lintas batas protokol dan bahasa pemrograman.
    • RED Method (Rate, Errors, Duration) memantau kesehatan layanan secara real-time di Grafana.
    • Alertmanager mendeteksi anomali latensi sebelum berdampak masif pada pengguna akhir, menurunkan MTTR secara drastis.

2. Konsep Inti & Deep Dive Arsitektur

Sistem observabilitas modern bertumpu pada tiga pilar utama: Metrics (apa yang terjadi), Logs (mengapa itu terjadi), dan Traces (di mana itu terjadi). Dalam konteks OpenTelemetry (OTel), ekosistem ini distandarisasi untuk menghindari vendor lock-in.

Alur Data & Eksekusi Under the Hood

  1. Klien (Browser/Mobile App): Mengirimkan header W3C Trace Context (traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01).
  2. Edge & Web Server (Cloudflare / Nginx): Meneruskan atau menghasilkan trace context jika belum ada, lalu meneruskannya ke backend.
  3. Aplikasi Backend (Go / Node.js / PHP): Menggunakan OpenTelemetry SDK untuk membuat Spans (unit kerja tunggal), menambahkan atribut kustom, dan mencatat eksekusi query Redis/MySQL.
  4. OpenTelemetry Collector: Menerima data OTLP (gRPC/HTTP) dari berbagai microservices, melakukan batching, filtering, dan mengekspor metrik ke Prometheus serta traces ke Jaeger/Tempo.
  5. Storage & Visualization: Prometheus menyimpan metrik deret waktu (time-series), Grafana membaca dari Prometheus dan Tempo untuk menampilkan visualisasi RED Method dan Waterfall Trace.
[Client Browser] ---> (W3C Trace Header) ---> [Cloudflare CDN / WAF]
                                                       |
                                           [Nginx Reverse Proxy]
                                                       |
                         +-----------------------------+-----------------------------+
                         | (gRPC OTLP)                                               | (gRPC OTLP)
            [Golang Microservice A]                                        [Node.js Microservice B]
           /                      \                                       /
     [Redis Cache]            [MySQL DB]                        [RabbitMQ Queue]
                         
                         ========== EXPORTERS ==========
                                        |
                       [OpenTelemetry Collector Daemon]
                        /                            \
               (Metrics / Pull)                 (Traces / Push)
                      /                                \
        [Prometheus Time-Series]                 [Grafana Tempo]
                      \                                /
                       +--------------+----------------+
                                      |
                            [Grafana Dashboard]

3. Perbandingan Komparatif / Analisis Pendekatan

Berikut adalah analisis perbandingan arsitektur pengumpulan data telemetri untuk memutuskan strategi deployment yang optimal:

Parameter Komparasi Direct Exporter ke Backend OpenTelemetry Collector (Daemonset/Agent) Centralized OTel Collector Pool
Penggunaan CPU/RAM Aplikasi Tinggi (SDK harus melakukan enkripsi & retry jaringan) Rendah (Aplikasi hanya mengirim lokal via gRPC cepat) Sangat Rendah (Aplikasi kirim ke agent lokal)
Network Overhead Tinggi (Setiap instance kirim ke remote backend) Menengah (Collector lokal melakukan batching & kompresi) Rendah hingga Menengah
Kompleksitas Infrastruktur Rendah (Mudah di awal) Menengah (Perlu deploy collector di tiap node/cluster) Tinggi (Butuh load balancer untuk collector pool)
Vendor Lock-in Tinggi (Bergantung pada library vendor tertentu) Nol (Standar OTLP universal, mudah ganti backend) Nol (Mudah mengubah routing eksportir di collector)
Keamanan & Token Management Buruk (API token tersebar di ribuan instance aplikasi) Baik (Token sensitif hanya disimpan di Collector) Sangat Baik (Sentralisasi manajemen kredensial)

[!TIP] Untuk lingkungan produksi berskala besar, arsitektur OpenTelemetry Collector sebagai Daemon/Agent di setiap node Kubernetes atau Virtual Machine adalah gold standard terbaik guna meredam beban jaringan aplikasi.


4. Prasyarat Sistem & Persiapan Lingkungan

Sebelum melakukan implementasi, pastikan infrastruktur memenuhi spesifikasi minimum berikut:

  • Sistem Operasi: Ubuntu 22.04 LTS / Debian 12 / RHEL 9 (Kernel Linux >= 5.15 untuk performa eBPF jika diperlukan).
  • Software Stack:
    • Go 1.22+ / Node.js 20+ / PHP 8.3 dengan ekstensi OTel.
    • Nginx 1.24+
    • Prometheus 2.50+
    • Grafana 10.4+
    • Grafana Tempo 2.4+ (untuk backend traces)
  • Koneksi Jaringan & Port:
    • Port 4317 (OTLP gRPC)
    • Port 4318 (OTLP HTTP)
    • Port 9090 (Prometheus)
    • Port 3000 (Grafana)
  • Permissions: Akses sudo dan konfigurasi firewall (ufw / iptables) yang mengizinkan komunikasi internal antar service.

Verifikasi awal port lokal dengan perintah:

sudo ss -tulpn | grep -E '4317|4318|9090|3000'

5. Panduan Implementasi Langkah demi Langkah

Langkah 1: Konfigurasi OpenTelemetry Collector (otel-collector-config.yaml)

Buat file konfigurasi collector di /etc/otelcol/config.yaml:

receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317
      http:
        endpoint: 0.0.0.0:4318

processors:
  batch:
    send_batch_size: 8192
    timeout: 1s
    send_batch_max_size: 10240
  memory_limiter:
    check_interval: 1s
    limit_percentage: 80
    spike_limit_percentage: 20

exporters:
  prometheus:
    endpoint: 0.0.0.0:8889
    namespace: "otel"
  otlp/tempo:
    endpoint: "localhost:4317"
    tls:
      insecure: true

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [memory_limiter, batch]
      exporters: [otlp/tempo]
    metrics:
      receivers: [otlp]
      processors: [memory_limiter, batch]
      exporters: [prometheus]

Langkah 2: Konfigurasi Prometheus (/etc/prometheus/prometheus.yml)

Tambahkan job scraping untuk mengambil metrik dari OpenTelemetry Collector dan aplikasi web:

global:
  scrape_interval: 15s
  evaluation_interval: 15s

alerting:
  alertmanagers:
    - static_configs:
        - targets: ['localhost:9093']

rule_files:
  - "/etc/prometheus/alert_rules.yml"

scrape_configs:
  - job_name: 'prometheus'
    static_configs:
      - targets: ['localhost:9090']

  - job_name: 'otel-collector'
    static_configs:
      - targets: ['localhost:8889']

  - job_name: 'nginx-exporter'
    static_configs:
      - targets: ['localhost:9113']

Langkah 3: Aturan Alert Prometheus (/etc/prometheus/alert_rules.yml)

groups:
  - name: WebAppServiceAlerts
    rules:
      - alert: HighHttpErrorRate
        expr: sum(rate(http_server_duration_count{http_status_code=~"5.."}[5m])) / sum(rate(http_server_duration_count[5m])) * 100 > 5
        for: 2m
        labels:
          severity: critical
        annotations:
          summary: "High HTTP Error Rate detected"
          description: "HTTP 5xx error rate is above 5% for the last 2 minutes."

      - alert: HighRequestLatency
        expr: histogram_quantile(0.95, sum(rate(http_server_duration_bucket[5m])) by (le)) > 0.5
        for: 5m
        annotations:
          summary: "High P95 Latency"
          description: "P95 latency is exceeding 500ms."

Langkah 4: Instrumentasi Kode Backend Golang (main.go)

Berikut implementasi produksi menggunakan OpenTelemetry SDK di Golang dengan integrasi database MySQL dan Redis:

package main

import (
	"context"
	"database/sql"
	"fmt" 
	"log"
	"net/http"
	"time"

	_ "github.com/go-sql-driver/mysql"
	"github.com/redis/go-redis/v9"
	"go.opentelemetry.io/otel"
	"go.opentelemetry.io/otel/attribute"
	"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
	"go.opentelemetry.io/otel/propagation"
	"go.opentelemetry.io/otel/sdk/resource"
	sdktrace "go.opentelemetry.io/otel/sdk/trace"
	semconv "go.opentelemetry.io/otel/semconv/v1.24.0"
	"google.golang.org/grpc"
)

var rdb *redis.Client
var db *sql.DB

func initTracer(ctx context.Context) (*sdktrace.TracerProvider, error) {
	exporter, err := otlptracegrpc.New(ctx,
		otlptracegrpc.WithInsecure(),
		otlptracegrpc.WithEndpoint("localhost:4317"),
		otlptracegrpc.WithDialOption(grpc.WithBlock()),
	)
	if err != nil {
		return nil, fmt.Errorf("failed to create trace exporter: %w", err)
	}

	tp := sdktrace.NewTracerProvider(
		sdktrace.WithBatcher(exporter),
		sdktrace.WithResource(resource.NewWithAttributes(
			semconv.SchemaURL,
			semconv.ServiceNameKey.String("auth-microservice"),
			attribute.String("environment", "production"),
	)),
	)
	otel.SetTracerProvider(tp)
	otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{}))
	return tp, nil
}

func main() {
	ctx := context.Background()
	tp, err := initTracer(ctx)
	if err != nil {
		log.Fatalf("Failed to init tracer: %v", err)
	}
	defer func() {
		if err := tp.Shutdown(ctx); err != nil {
			log.Printf("Error terminating tracer provider: %v", err)
		}
	}()

	rdb = redis.NewClient(&redis.Options{Addr: "localhost:6379"})
	var errDB error
	db, errDB = sql.Open("mysql", "root:secret@tcp(127.0.0.1:3306)/app_db")
	if errDB != nil {
		log.Fatalf("Failed to connect to MySQL: %v", errDB}
	}

	http.HandleFunc("/api/v1/user", handleUserRequest)
	log.Println("Server listening on :8080...")
	if err := http.ListenAndServe(":8080", nil); err != nil {
		log.Fatal(err)
	}
}

func handleUserRequest(w http.ResponseWriter, r *http.Request) {
	tr := otel.GetTracerProvider().Tracer("auth-service/handler")
	ctx, span := tr.Start(r.Context(), "HTTP GET /api/v1/user")
	defer span.End()

	span.SetAttributes(attribute.String("http.client_ip", r.RemoteAddr))

	// Simulasi Redis Operation
	err := rdb.Set(ctx, "last_access", time.Now().Format(time.RFC3339), 10*time.Minute).Err()
	if err != nil {
		span.RecordError(err)
	}

	// Simulasi MySQL Query
	var username string
	err = db.QueryRowContext(ctx, "SELECT username FROM users LIMIT 1").Scan(&username)
	if err != nil {
		span.RecordError(err)
		http.Error(w, "Database error", http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusOK)
	_, _ = w.Write([]byte(fmt.Sprintf(`{"status":"success","user":"%s"}`, username)))
}

6. Bedah Kode & Penjelasan Detail Baris Kunci

  • otlptracegrpc.New(ctx, ...): Menginisialisasi eksportir gRPC OpenTelemetry. Protokol gRPC dipilih karena efisiensi kompresi HTTP/2 dibandingkan JSON/HTTP, mengurangi latensi jaringan pengiriman telemetri hingga 40%.
  • sdktrace.WithBatcher(exporter): Memastikan spans dikumpulkan dalam batch memori sebelum dikirim secara asinkron ke collector. Jangan pernah menggunakan ekspor sinkron (synchronous span processor) di lingkungan produksi karena akan menyebabkan thread blocking dan lonjakan latensi aplikasi web.
  • otel.SetTextMapPropagator(...): Mengaktifkan propagasi konteks W3C (standar industri). Ini memastikan bahwa trace_id yang diterima dari Nginx atau API Gateway diteruskan secara otomatis ke dalam operasi database MySQL dan Redis.
  • span.RecordError(err): Menandai span saat terjadi eksepsi (seperti kegagalan query database atau timeout Redis), sehingga pada dashboard Grafana Tempo, error tersebut langsung diwarnai merah mencolok dengan stack trace lengkap.

7. Skenario Produksi & Studi Kasus Dunia Nyata

Kasus: Mengatasi Lonjakan Trafik Flash Sale (High Concurrency 50.000 Req/Sec)

Pada event Flash Sale e-commerce, sistem mengalami lonjakan traffic drastis yang memicu timeout pada endpoint /checkout.

Analisis dengan Distributed Tracing:

  1. Melalui Grafana Tempo, engineer melihat bahwa trace waterfall menunjukkan latensi tertumpuk pada span SELECT * FROM inventory WHERE product_id = ?.
  2. Metrik Prometheus menunjukkan lonjakan CPU Utilization pada MySQL dan antrean Redis Connection Pool Exhaustion.
  3. Solusi Cepat: Memanfaatkan trace data untuk mengidentifikasi bahwa query tersebut belum di-cache. Tim langsung menerapkan Redis caching layer di depan MySQL pada fungsi backend Go, menurunkan durasi latensi dari 1200ms menjadi 18ms per request.

8. Common Pitfalls, Anti-Patterns & Solusi

  • Anti-Pattern 1: Mengirim 100% Trace ke Produksi (Trace Explosion)
    • Dampak: Menghabiskan gigabyte bandwidth jaringan dan membebani storage Grafana Tempo secara brutal.
    • Solusi: Terapkan Head-based Sampling atau Tail-based Sampling di OpenTelemetry Collector (misal: hanya rekam 5% dari request sukses, tapi rekam 100% request yang berstatus HTTP 5xx).
  • Anti-Pattern 2: Menyimpan Data Sensitif dalam Trace Attributes
    • Dampak: Password, token JWT, atau nomor kartu kredit tercatat secara plain-text di span attributes, melanggar standar compliance GDPR/PCI-DSS.
    • Solusi: Gunakan Sanitization Processor di OTel Collector untuk melakukan masking pada header otorisasi atau parameter query sensitif.
  • Anti-Pattern 3: Lupa Menutup Span (defer span.End())
    • Dampak: Memory leak masif pada runtime aplikasi backend karena span terus menggantung di memori.
    • Solusi: Selalu gunakan fungsi defer span.End() tepat di bawah inisiasi span.

9. Benchmark, Metrik Kinerja & Tuning Performa

Pengujian beban (Load Testing) dilakukan menggunakan k6 untuk mengukur overhead penambahan OpenTelemetry SDK pada aplikasi backend Go.

Skenario Skrip k6 (load-test.js):

import http from 'k6/http';
import { check, sleep } from 'k6';

export const options = {
  stages: [
    { duration: '30s', target: 1000 },
    { duration: '1m', target: 5000 },
    { duration: '30s', target: 0 },
  ],
};

export default function () {
  const res = http.get('http://localhost:8080/api/v1/user');
  check(res, {
    'status is 200': (r) => r.status === 200,
    'response time < 50ms': (r) => r.timings.duration < 50,
  });
  sleep(0.1);
}

Hasil Benchmark:

  • Tanpa OTel: Throughput 12,400 req/sec, Latensi rata-rata 4.2ms, Penggunaan Memori 45MB.
  • Dengan OTel (Batch Exporter): Throughput 11,950 req/sec, Latensi rata-rata 4.8ms, Penggunaan Memori 62MB.
  • Kesimpulan Tuning: Overhead performa akibat OpenTelemetry sangat minimal (< 4% penurunan throughput) jika konfigurasi batching diterapkan dengan benar.

10. Checklist Penerapan Produksi & Kesimpulan

Sebelum merilis sistem observabilitas ini ke lingkungan live production, pastikan seluruh poin dalam checklist berikut terpenuhi:

  • OpenTelemetry Collector dikonfigurasi dengan memory_limiter untuk mencegah OOM (Out Of Memory) crash.
  • Sampling rate telah disesuaikan (misal: 10% untuk trafikk normal, 100% untuk error).
  • Seluruh secret, token, dan PII (Personally Identifiable Information) telah difilter dari trace attributes.
  • Prometheus alert rules telah diuji dan terhubung ke webhook PagerDuty/Slack tim SRE.
  • Grafana Dashboard RED Method telah divalidasi menampilkan data latensi P99, error rate, dan throughput dengan akurat.
  • Rotasi log dan retensi data Tempo/Prometheus telah diatur sesuai kapasitas storage disk server.

Dengan menerapkan panduan komprehensif ini, tim SRE dan Platform Engineer memiliki kendali penuh, visibilitas mikroskopis, serta ketahanan operasional tinggi dalam mengelola arsitektur web terdistribusi skala enterprise.

🏷️ Tags: #Microservices #Sre #Opentelemetry #Prometheus #Grafana