L
Observability & Monitoring • Literally Web ID

Enterprise Observability: Distributed Tracing dengan OpenTelemetry dan Prometheus

⏱️ 18 menit baca • 📅 19 Oct 2026 • 👁️ 3 views
Enterprise Observability: Distributed Tracing dengan OpenTelemetry dan Prometheus

Enterprise Observability: Distributed Tracing dengan OpenTelemetry dan Prometheus

1. Konteks Industri & Analisis Akar Masalah

Dalam lanskap arsitektur perangkat lunak modern, transisi dari monolitik tradisional ke arsitektur mikroservis terdistribusi telah mendefinisikan ulang cara kita membangun dan menskalakan aplikasi web skala enterprise. Hari ini, sebuah permintaan HTTP tunggal yang masuk melalui edge proxy (seperti Nginx atau Cloudflare) tidak lagi sekadar memicu eksekusi kode tunggal pada satu server database. Permintaan tersebut dapat melompat melewati beberapa lapisan API Gateway, mengotentikasi diri melalui layanan OAuth terpusat, memanggil layanan berbasis Go untuk pemrosesan bisnis inti, berinteraksi dengan layanan Node.js untuk agregasi data real-time, meminta aset dari penyimpanan S3/MinIO, membaca dan menulis ke kluster Redis dan MySQL/MariaDB, hingga memicu event asinkron melalui sistem antrean berbasis RabbitMQ atau Kafka yang dikelola oleh Supervisor.

Kompleksitas ini mendatangkan tantangan operasional yang masif. Ketika terjadi latensi lonjakan (latency spike) atau kegagalan sistem (cascade failure) di lingkungan produksi, metode debugging konvensional seperti membaca file log teks mentah (tail -f /var/log/nginx/error.log) menjadi tidak lagi efektif. Engineer SRE (Site Reliability Engineering) dan platform teams sering kali terjebak dalam masalah "needle in a haystack" di mana korelasi log antar layanan terputus karena ketiadaan identifier unik yang konsisten.

Sebelum adopsi enterprise observability yang komprehensif, kondisi operasional tipikal di perusahaan ditandai oleh:

  • Blind Spots yang Luas: Tim hanya mengetahui layanan mana yang mati (lewat alert CPU/Memory), tetapi tidak tahu mengapa rantai panggilan antar servis mengalami penundaan 3 detik.
  • Silo Monitoring: Metrik (Prometheus) terpisah dari log (ELK Stack), yang terpisah pula dari error tracking (Sentry), tanpa jembatan analitik yang menyatukan konteks waktu dan transaksi.
  • Mean Time to Resolution (MTTR) yang Tinggi: Waktu investigasi insiden memakan waktu berjam-jam karena harus mencocokkan timestamp secara manual di berbagai platform.

Setelah menerapkan arsitektur Distributed Tracing menggunakan OpenTelemetry yang dikombinasikan dengan metrik performa Prometheus, paradigma berubah total:

  • Setiap request mendapatkan Trace ID dan Span ID unik yang disuntikkan secara otomatis pada perbatasan jaringan.
  • Metrik infrastruktur dan trace aplikasi dihubungkan dalam satu dashboard Grafana yang kohesif.
  • Akar masalah dari degradasi performa dapat diidentifikasi dalam hitungan detik, baik itu query SQL yang lambat, bottleneck pada koneksi Redis, maupun penundaan respons pada API pihak ketiga.

2. Konsep Inti & Deep Dive Arsitektur

Untuk memahami observability enterprise secara utuh, kita harus membedah tiga pilar utama observability: Metrics, Logs, dan Traces. OpenTelemetry (OTel) hadir sebagai standar industri tunggal yang dinstandarisasi oleh Cloud Native Computing Foundation (CNCF) untuk mengumpulkan ketiganya tanpa vendor lock-in.

Alur Data dan Eksekusi (Data Flow Architecture)

Perhatikan diagram aliran data di bawah ini yang menggambarkan perjalanan sebuah request dari client hingga penyimpanan telemetri:

+-----------------------------------------------------------------------------------------+
|                                 Client / Browser Request                                |
+-----------------------------------------------------------------------------------------+
                                             |
                                             v
+-----------------------------------------------------------------------------------------+
|                             Nginx Edge Proxy / WAF / CDN                                |
+-----------------------------------------------------------------------------------------+
                                             |
                                             | (W3C Trace Context Injection: traceparent)
                                             v
+-----------------------------------------------------------------------------------------+
|                       Go Microservice (API Gateway / Core Logic)                        |
|                   [OpenTelemetry SDK] ---> OTLP Exporter (gRPC :4317)                   |
+-----------------------------------------------------------------------------------------+
                                             |
                                             | (Propagated Trace Context)
                                             v
+-----------------------------------------------------------------------------------------+
|                     Node.js Microservice (Real-time / Aggregator)                       |
|                   [OpenTelemetry SDK] ---> OTLP Exporter (gRPC :4317)                   |
+-----------------------------------------------------------------------------------------+
                                             |
                                             v
+-----------------------------------------------------------------------------------------+
|                                OpenTelemetry Collector                                  |----+
|              (Receives OTLP, Batching, Filtering, Attribute Processing)                 |    |
+-----------------------------------------------------------------------------------------+    |
                                             |                                                 |
                  +--------------------------+--------------------------+                      |
                  | (Exports Traces)                                    | (Scrapes Metrics)    |
                  v                                                     v                      v
+-----------------------------------+                 +-----------------------------------+    |
|             Jaeger                |                 |            Prometheus             |    |
|       (Trace Storage & UI)        |                 |      (Metrics Time-Series DB)     |    |
+-----------------------------------+                 +-----------------------------------+    |
                  ^                                                     ^                      |
                  |                                                     |                      |
                  +--------------------------+--------------------------+----------------------+
                                             |
                                             v
+-----------------------------------------------------------------------------------------+
|                                  Grafana Dashboard UI                                   |
+-----------------------------------------------------------------------------------------+

Komponen Kunci Arsitektur:

  1. Trace: Representasi dari seluruh perjalanan sebuah request melintasi sistem terdistribusi. Terdiri dari pohon (tree) dari struktur yang lebih kecil bernama Span.
  2. Span: Unit kerja tunggal dalam sebuah trace. Menyimpan informasi nama operasi, timestamp mulai dan selesai, atribut key-value, event (logs), serta status error.
  3. Context Propagation: Mekanisme pembawa metadata (seperti header HTTP traceparent) antar batas proses dan jaringan sehingga relasi parent-child antar span tetap terjaga.
  4. OpenTelemetry Collector: Daemon atau layanan terpusat yang bertindak sebagai agen penerima, pemroses, dan pengirim data telemetri. Collector mengurangi beban pada aplikasi dengan melakukan batching, kompresi, dan sanitasi data sebelum dikirim ke backend.

3. Perbandingan Komparatif / Analisis Pendekatan

Sebelum OpenTelemetry, dunia observability dikuasai oleh agen proprietari dan SDK terfragmentasi (misalnya Jaeger Client vs Zipkin Client). Berikut adalah analisis komparatif pendekatan pengumpulan metrik dan trace di lingkungan enterprise:

Parameter Komparasi OpenTelemetry + Prometheus Legacy APM Agents (Datadog/Dynatrace Proprietary) Custom StatsD + Log Analytics
Vendor Lock-in Sangat Rendah (Standar Terbuka CNCF) Sangat Tinggi (Agen khusus vendor) Rendah (Namun butuh banyak kustomisasi)
Overhead CPU & RAM Rendah (Optimalisasi gRPC, batching efisien) Sedang hingga Tinggi (Bergantung pada fitur auto-instrumentation) Rendah (Tetapi minim konteks trace)
Kompleksitas Setup Sedang (Memerlukan Collector & konfigurasi pipeline) Sangat Rendah (Plug and play satu baris kode) Tinggi (Harus membangun agregator sendiri)
Dukungan Bahasa Pemrograman Komprehensif (Go, Node.js, Python, Java, PHP, C++, dll) Luas (Namun fitur terkadang timpang antar bahasa) Manual (Bergantung pada klien perpustakaan pihak ketiga)
Biaya Lisensi Gratis / Open Source (Biaya infrastruktur mandiri) Sangat Mahal (Berdasarkan volume ingest data / host) Gratis (Namun biaya rekayasa pemeliharaan tinggi)
Granularitas Data Kustomisasi Penuh (Sampling dinamis, tail-based sampling) Terkunci pada kebijakan vendor Terbatas pada metrik angka dasar

[!TIP] Untuk enterprise dengan regulasi data ketat (seperti perbankan atau kesehatan), kombinasi OpenTelemetry, Prometheus, dan Grafana yang di-host secara mandiri (self-hosted) adalah pilihan terbaik untuk memastikan kepatuhan kedaulatan data (data sovereignty).


4. Prasyarat Sistem & Persiapan Lingkungan

Sebelum memulai implementasi teknis, pastikan infrastruktur target memenuhi spesifikasi dan prasyarat berikut:

Spesifikasi Minimum Hardware & Software:

  • OS: Ubuntu 22.04 LTS / Debian 12 / RHEL 9 (Kernel Linux >= 5.4)
  • Memori RAM: Minimum 4GB (Direkomendasikan 8GB+ untuk menjalankan Collector, Prometheus, dan Jaeger secara bersamaan di server dev/staging)
  • Go Runtime: Versi 1.21 atau lebih baru
  • Node.js Runtime: Versi 18.x LTS atau 20.x LTS
  • Docker & Docker Compose: Versi terbaru untuk orkestrasi lokal

Verifikasi Port dan Firewall:

Pastikan port-port berikut diizinkan pada firewall internal (ufw atau iptables):

  • 4317: OTLP gRPC Receiver (OpenTelemetry Collector)
  • 4318: OTLP HTTP Receiver (OpenTelemetry Collector)
  • 9090: Prometheus Time-Series Database
  • 3000: Grafana Dashboard UI
  • 14268: Jaeger Collector (HTTP)

5. Panduan Implementasi Langkah demi Langkah

Kita akan membangun sistem monitoring lengkap menggunakan Docker Compose yang mencakup:

  1. OpenTelemetry Collector (Menerima data OTLP dari aplikasi Go dan Node.js)
  2. Prometheus (Mengikis metrik dari Collector)
  3. Jaeger (Menyimpan dan menampilkan distributed traces)
  4. Grafana (Visualisasi terpadu)
  5. Go Microservice & Node.js Microservice (Aplikasi sampel yang saling berkomunikasi)

Langkah 1: Konfigurasi Infrastruktur (Docker Compose)

Buat direktori proyek dan buat file docker-compose.yml:

version: '3.8'

services:
  otel-collector:
    image: otel/opentelemetry-collector-contrib:0.95.0
    container_name: otel-collector
    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
      - "8889:8889"   # Prometheus exporter metrics
    networks:
      - observability

  prometheus:
    image: prom/prometheus:v2.50.0
    container_name: prometheus
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
    ports:
      - "9090:9090"
    networks:
      - observability

  jaeger:
    image: jaegertracing/all-in-one:1.54
    container_name: jaeger
    environment:
      - COLLECTOR_OTLP_ENABLED=true
    ports:
      - "16686:16686"
      - "14268:14268"
    networks:
      - observability

  grafana:
    image: grafana/grafana:10.3.3
    container_name: grafana
    ports:
      - "3000:3000"
    environment:
      - GF_SECURITY_ADMIN_USER=admin
      - GF_SECURITY_ADMIN_PASSWORD=securepassword123
    networks:
      - observability

networks:
  observability:
    driver: bridge

Langkah 2: Konfigurasi OpenTelemetry Collector

Buat file otel-collector-config.yaml di direktori yang sama:

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

processors:
  batch:
    timeout: 1s
    send_batch_size: 512

exporters:
  otlp:
    endpoint: jaeger:4317
    tls:
      insecure: true
  prometheus:
    endpoint: 0.0.0.0:8889
    namespace: "enterprise"

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [batch]
      exporters: [otlp]
    metrics:
      receivers: [otlp]
      processors: [batch]
      exporters: [prometheus]

Langkah 3: Konfigurasi Prometheus

Buat file prometheus.yml:

global:
  scrape_interval: 10s

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

Langkah 4: Instrumentasi Aplikasi Go (Backend Utama)

Inisialisasi modul Go dan buat file main.go:

package main

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

	"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"
	trace "go.opentelemetry.io/otel/trace"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
)

func initTracer(ctx context.Context) (*sdktrace.TracerProvider, error) {
	res, err := resource.New(ctx,
		resource.WithAttributes(
			semconv.ServiceNameKey.String("go-backend-service"),
			attribute.String("environment", "production"),
		),
	)
	if err != nil {
		return nil, fmt.Errorf("failed to create resource: %w", err)
	}

	conn, err := grpc.DialContext(ctx, "otel-collector:4317", grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithBlock())
	if err != nil {
		return nil, fmt.Errorf("failed to connect to collector: %w", err)
	}

	exporter, err := otlptracegrpc.New(ctx, otlptracegrpc.WithGRPCConn(conn))
	if err != nil {
		return nil, fmt.Errorf("failed to create trace exporter: %w", err)
	}

	tp := sdktrace.NewTracerProvider(
		sdktrace.WithSampler(sdktrace.AlwaysSample()),
		sdktrace.WithResource(res),
		sdktrace.WithBatcher(exporter),
	)

	otel.SetTracerProvider(tp)
	otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{}))

	return tp, nil
}

func handleTask(w http.ResponseWriter, req *http.Request) {
	ctx := otel.GetTextMapPropagator().Extract(req.Context(), propagation.HeaderCarrier(req.Header))
	tr := otel.Tracer("go-backend")
	ctx, span := tr.Start(ctx, "ProcessBusinessLogic", trace.WithSpanKind(trace.SpanKindServer))
	defer span.End()

	span.SetAttributes(attribute.String("task.type", "compute"))

	// Simulasi operasi berat
	time.Sleep(150 * time.Millisecond)

	// Memanggil layanan Node.js downstream
	outReq, err := http.NewRequestWithContext(ctx, "GET", "http://nodejs-service:4000/aggregate", nil)
	if err == nil {
		otel.GetTextMapPropagator().Inject(ctx, propagation.HeaderCarrier(outReq.Header))
		client := &http.Client{}
		resp, err := client.Do(outReq)
		if err == nil {
			resp.Body.Close()
		}
	}

	w.WriteHeader(http.StatusOK)
	w.Write([]byte("Go Microservice Handled Request Successfully\n"))
}

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 TracerProvider: %v", err)
		}
	}()

	http.HandleFunc("/process", handleTask)
	log.Println("Go backend service listening on :8080...")
	if err := http.ListenAndServe(":8080", nil); err != nil {
		log.Fatal(err)
	}
}

Langkah 5: Instrumentasi Aplikasi Node.js (Downstream Service)

Buat file package.json dan server.js untuk layanan Node.js:

'use strict';

const { NodeSDK } = require('@opentelemetry/sdk-node');
const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-grpc');
const { Resource } = require('@opentelemetry/resources');
const { SEMRESATTRS_SERVICE_NAME } = require('@opentelemetry/semantic-conventions');
const express = require('express');

const sdk = new NodeSDK({
  resource: new Resource({
    [SEMRESATTRS_SERVICE_NAME]: 'nodejs-aggregator-service',
  }),
  traceExporter: new OTLPTraceExporter({
    url: 'http://otel-collector:4317',
  }),
});

sdk.start();

const app = express();

app.get('/aggregate', (req, res) => {
  setTimeout(() => {
    res.send('Node.js Aggregation Complete\n');
  }, 80);
});

app.listen(4000, () => {
  console.log('Node.js aggregator listening on port 4000');
});

6. Bedah Kode & Penjelasan Detail Baris Kunci

Mari kita bedah beberapa komponen penting dari kode implementasi di atas:

  1. Context Extraction & Injection di Go (handleTask):

    ctx := otel.GetTextMapPropagator().Extract(req.Context(), propagation.HeaderCarrier(req.Header))
    

    Baris ini krusial. Ketika request HTTP masuk dari Nginx atau layanan hulu (upstream), header HTTP membawa metadata traceparent. Fungsi Extract membaca header tersebut dan membangun kembali konteks trace sehingga span baru di Go terhubung secara akurat sebagai child span dari request asal.

  2. Resource Definition (semconv.ServiceNameKey):

    resource.WithAttributes(semconv.ServiceNameKey.String("go-backend-service"))
    

    Atribut resource mengidentifikasi entitas yang menghasilkan data telemetri. Penamaan service yang konsisten (go-backend-service dan nodejs-aggregator-service) sangat menentukan bagaimana Grafana dan Jaeger mengelompokkan metrik dan trace per layanan di production.

  3. Batch Span Processor: Di konfigurasi Go (sdktrace.WithBatcher(exporter)), kita menggunakan batching alih-alih mengirim setiap span secara sinkron (simple processor). Batching mengurangi overhead jaringan dan penggunaan CPU secara drastis pada beban traffic tinggi.


7. Skenario Produksi & Studi Kasus Dunia Nyata

Di lingkungan produksi berskala tinggi (misalnya menangani 50.000 req/sec pada platform e-commerce saat flash sale), pengiriman 100% trace akan membebani jaringan dan penyimpanan backend (Jaeger/Elasticsearch) secara berlebihan.

Strategi Tail-Based Sampling di OpenTelemetry Collector:

Alih-alih Head-Based Sampling (memutuskan apakah sebuah trace direkam di awal aplikasi), arsitektur enterprise menggunakan Tail-Based Sampling di Collector. Artinya, Collector mengumpulkan seluruh trace sementara di memori, lalu memutuskan apakah trace tersebut layak disimpan berdasarkan kondisi akhir:

  • Simpan 100% trace yang menghasilkan error HTTP 5xx atau latensi di atas 1000ms.
  • Simpan hanya 1% trace dengan status HTTP 200 OK (sampling rate 1%) untuk menghemat biaya penyimpanan telemetri.

Konfigurasi Collector untuk Tail Sampling:

processors:
  tail_sampling:
    decision_wait: 10s
    num_traces: 50000
    expected_new_traces_per_sec: 2000
    policies:
      - name: drop-successful-requests
        type: probabilistic
        probabilistic:
          sampling_percentage: 1.0
      - name: keep-errors
        type: status_code
        status_code:
          status_codes: [ERROR]
      - name: keep-slow-traces
        type: latency
        latency:
          threshold_ms: 1000

8. Common Pitfalls, Anti-Patterns & Solusi

[!WARNING] Kesalahan konfigurasi pada sistem observability dapat berdampak buruk pada performa aplikasi inti Anda.

  1. Anti-Pattern: Memblokir Event Loop atau Goroutine dengan Ekspor Sinkron

    • Masalah: Mengirim data trace menggunakan protokol sinkron secara langsung di thread utama request handler.
    • Solusi: Selalu gunakan asynchronous exporter dengan batching buffer yang memadai dan tetapkan timeout koneksi gRPC yang ketat (maksimal 2-3 detik).
  2. Pitfall: Kardinalitas Tinggi pada Atribut Span (High Cardinality)

    • Masalah: Menyimpan nilai unik seperti ID pengguna, email, atau UUID lengkap sebagai dimension key dalam metrik Prometheus.
    • Dampak: Menyebabkan ledakan deret waktu (time-series explosion), membuat RAM Prometheus habis, dan mengakibatkan Out of Memory (OOM) Crash.
    • Solusi: Simpan ID pengguna sebagai atribut di dalam Trace (yang diindeks untuk pencarian), bukan sebagai metrik Prometheus counters/gauges.

9. Benchmark, Metrik Kinerja & Tuning Performa

Untuk menguji ketahanan infrastruktur observability yang telah dibangun, kita dapat melakukan uji beban menggunakan tool k6. Buat file skrip load-test.js:

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

export const options = {
  stages: [
    { duration: '30s', target: 50 },  // Ramp up ke 50 virtual users
    { duration: '1m', target: 100 },  // Pertahankan beban 100 VU
    { duration: '10s', target: 0 },   // Ramp down
  ],
};

export default function () {
  const res = http.get('http://localhost:8080/process');
  check(res, {
    'status is 200': (r) => r.status === 200,
  });
  sleep(0.1);
}

Jalankan benchmark dengan perintah terminal:

k6 run load-test.js

Parameter Tuning Lanjutan di Linux Kernel (sysctl.conf):

Untuk menangani trafik jaringan berkecepatan tinggi pada OpenTelemetry Collector, pastikan parameter kernel Linux dioptimalkan:

net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 16384
net.ipv4.ip_local_port_range = 1024 65535

Terapkan perubahan dengan:

sysctl -p

10. Checklist Penerapan Produksi & Kesimpulan

Sebelum meluncurkan sistem Distributed Tracing ke lingkungan produksi enterprise, verifikasi seluruh item pada checklist berikut:

  • Kerahasiaan Data: Pastikan informasi sensitif seperti password, token JWT, nomor kartu kredit, dan PII (Personally Identifiable Information) disaring (redacted) menggunakan Attribute Processors di OpenTelemetry Collector sebelum dikirim ke penyimpanan.
  • Ketersediaan Kolektor (High Availability): Sebarkan minimal dua instance OpenTelemetry Collector di belakang load balancer internal untuk mencegah titik kegagalan tunggal (single point of failure).
  • Manajemen Retensi Penyimpanan: Konfigurasi kebijakan TTL (Time-To-Live) pada penyimpanan data Jaeger/Elasticsearch agar disk server tidak penuh.
  • Pengujian Alerting: Pastikan aturan alert Prometheus terhubung dengan sistem notifikasi seperti PagerDuty, Slack, atau Webhook internal untuk merespons insiden secara cepat.
  • Resource Limits Kubernetes/Docker: Tentukan batasan alokasi CPU dan Memory yang tegas (limits dan requests) untuk kontainer Collector dan Jaeger agar tidak mengganggu layanan inti aplikasi.

Dengan mengimplementasikan panduan komprehensif ini, Anda tidak hanya memiliki sistem pemantauan yang mumpuni, tetapi juga membangun budaya engineering yang berorientasi pada keandalan (reliability), visibilitas penuh (full-stack observability), dan ketahanan operasional jangka panjang di tingkat enterprise.

🏷️ Tags: #Sre #Opentelemetry #Prometheus #Grafana #Distributed Tracing