L
Observability & Monitoring • Literally Web ID

Observabilitas Sistem Terdistribusi: Implementasi OpenTelemetry, Prometheus, dan Grafana

⏱️ 15 menit baca • 📅 15 Oct 2026 • 👁️ 1 views
Observabilitas Sistem Terdistribusi: Implementasi OpenTelemetry, Prometheus, dan Grafana

1. Konteks Industri & Analisis Akar Masalah

Dalam lanskap arsitektur sistem terdistribusi modern, transisi dari monolitik menuju microservices membawa kompleksitas operasional yang eksponensial. Sebuah permintaan (request) HTTP tunggal dari pengguna akhir kini tidak lagi ditangani oleh satu proses aplikasi di balik satu web server Nginx atau Apache, melainkan melompati lusinan batas jaringan (network boundaries), melalui API Gateway, menyentuh load balancer, masuk ke service mesh, memanggil berbagai layanan backend berbasis Go, Node.js, atau PHP-FPM, berinteraksi dengan message broker seperti RabbitMQ atau Kafka, mengantre di background worker via Supervisor, hingga melakukan query ke klaster basis data MySQL/MariaDB serta memori cache Redis.

Bottleneck Performa & Keamanan di Dunia Nyata

Ketika terjadi degradasi performa atau lonjakan latensi (misalnya metrik P99 melonjak hingga melampaui 2000ms), tim Site Reliability Engineering (SRE) sering kali terjebak dalam masalah klasik: Blind Spot Operasional. Mengandalkan pengecekan log berbasis file teks mentah yang terdistribusi di berbagai kontainer Docker atau instance AWS EC2 adalah resep kegagalan. Log tidak memiliki korelasi kontekstual secara real-time, metrik sistem operasi (seperti utilisasi CPU dan RAM) berdiri sendiri tanpa kaitan langsung dengan trace transaksi bisnis, dan pelacakan error lintas jaringan menjadi teka-teki yang memakan waktu berjam-jam.

Perbandingan Kondisi: Sebelum vs Sesudah Observabilitas Menyeluruh

Aspek Evaluasi Sebelum (Traditional Monitoring & Logging) Sesudah (Implementasi OpenTelemetry + Prometheus + Grafana) Misi & Dampak Operasional
Deteksi Insiden Reaktif (menunggu komplain pengguna / alert SLA jebol) Proaktif (deteksi anomali metrik P99 dan error rate dalam hitungan detik) Mengurangi MTTR (Mean Time to Resolution) hingga 80%.
Root Cause Analysis Manual SSH ke berbagai server, grep log file satu per satu Terpusat menggunakan Distributed Tracing (Jaeger/Grafana Tempo) Mengeliminasi tebak-tebakan antar tim (blameless postmortem).
Korelasi Data Terkotak-kotak (Metrik terpisah dari Log, terpisah dari Trace) Tiga Pilar Terintegrasi (Log, Metric, Trace di satu dashboard Grafana) Visibilitas end-to-end dari Edge (Cloudflare/Nginx) hingga Database.
Kapasitas & Overhead Agen pemantauan kustom yang memakan memori tinggi OpenTelemetry Collector standar industri dengan overhead CPU < 2% Efisiensi infrastruktur pemantauan tanpa mengorbankan performa aplikasi.

[!WARNING] Mengabaikan implementasi distributed tracing pada arsitektur yang memiliki lebih dari 3 layanan mikro (microservices) akan mengakibatkan hilangnya visibilitas rantai ketergantungan, menyulitkan proses debugging saat terjadi kegagalan kaskade (cascading failures).


2. Konsep Inti & Deep Dive Arsitektur

Observabilitas dalam sistem terdistribusi bertumpu pada Tiga Pilar Utama (The Three Pillars of Observability):

  1. Metrics: Representasi numerik agregat dari keadaan sistem sepanjang waktu (contoh: penggunaan CPU, jumlah request per detik, latensi HTTP).
  2. Logs: Rekaman diskrit dari peristiwa yang terjadi dalam sistem, lengkap dengan stempel waktu dan konteks spesifik.
  3. Traces: Lintasan sebuah request saat melintasi berbagai layanan, direpresentasikan melalui hierarki spans yang dihubungkan oleh trace ID dan span ID yang unik.

Alur Kerja Under-the-Hood (Data Flow)

[Client Browser / App]
         │
         ▼ (HTTP Request via Cloudflare CDN / WAF)
[Nginx Reverse Proxy / Web Server]
         │
         ▼ (OpenTelemetry HTTP/gRPC Exporter)
[OpenTelemetry Collector Gateway] ──(Scrape/Receive)──> [Prometheus (Metrics)]
         │                                            └──> [Grafana Tempo (Traces)]
         ├────────────────────────────────────────────└──> [Loki / S3 (Logs)]
         ▼
[Backend Service (Go / Node.js / PHP-FPM)]
         │
         ├──────► [Redis Cache / Session Store]
         ├──────► [MySQL / MariaDB Primary Database]
         └──────► [RabbitMQ / Message Queue (Supervisor Worker)]
  1. Inisiasi Permintaan: Klien mengirimkan request yang melewati CDN Cloudflare, mengaktifkan aturan WAF (Web Application Firewall), dan diteruskan ke web server Nginx.
  2. Instrumentasi Kode: Backend (misalnya aplikasi Go atau Node.js) yang telah diinstrumentasi menggunakan OpenTelemetry SDK menghasilkan metrics, logs, dan traces secara asinkron.
  3. Pengumpulan Data (Collector): Data diekspor menggunakan protokol gRPC atau HTTP ke OpenTelemetry Collector. Collector bertindak sebagai agen pemrosesan terpusat yang melakukan batching, filtering, dan routing.
  4. Penyimpanan & Visualisasi: Metrik ditarik atau didorong ke Prometheus, traces dikirim ke backend penyimpanan kompatibel seperti Grafana Tempo, dan seluruhnya divisualisasikan secara terpadu melalui Grafana Dashboard.

3. Perbandingan Komparatif / Analisis Pendekatan

Dalam membangun infrastruktur observabilitas, pemilihan alat dan protokol komunikasi sangat menentukan efisiensi sumber daya sistem (CPU, RAM, dan Network Bandwidth).

Pendekatan / Komponen Kelebihan Utama Kekurangan / Risiko Pertimbangan Sumber Daya (CPU/RAM)
OpenTelemetry (OTel) Collector Standar industri universal, vendor-agnostic, mendukung gRPC/HTTP Konfigurasi awal kompleks, kurva belajar curam Konsumsi RAM moderat (~100MB-500MB tergantung beban batching).
Prometheus (Pull-based) Kuat untuk metrik time-series, penemuan target otomatis (service discovery) Kurang optimal untuk high-cardinality metrics dalam skala masif Membutuhkan RAM besar untuk menampung active time series di memori.
Grafana Tempo / Jaeger Penyimpanan traces sangat efisien, integrasi natif dengan Grafana Memerlukan infrastruktur penyimpanan terdistribusi (S3/MinIO) untuk skala besar Disk I/O tinggi pada penyimpanan objek backend.
StatsD / DogStatsD (Legacy) Ringan, implementasi kode sangat sederhana Tidak mendukung distributed tracing natif, format tidak terstandarisasi Konsumsi CPU sangat rendah, namun minim konteks analitis.

[!TIP] Gunakan arsitektur OpenTelemetry Collector dalam mode agent di setiap host/node Kubernetes, yang kemudian meneruskannya ke collector dalam mode gateway sebelum didistribusikan ke Prometheus dan Tempo untuk efisiensi jaringan yang optimal.


4. Prasyarat Sistem & Persiapan Lingkungan

Sebelum melangkah ke implementasi teknis, pastikan spesifikasi lingkungan pengembangan atau produksi memenuhi standar minimum berikut:

Spesifikasi Minimum Hardware & OS

  • OS: Ubuntu 22.04 LTS / Debian 12 / AlmaLinux 9 (Kernel Linux >= 5.10)
  • CPU: Minimal 2 Core vCPU
  • RAM: Minimal 4 GB (Disarankan 8 GB untuk menjalankan stack lengkap: App, OTel Collector, Prometheus, Grafana)
  • Storage: SSD NVMe dengan sisa ruang minimal 20 GB

Dependensi Perangkat Lunak

  • Docker & Docker Compose: Versi terbaru (Docker Engine >= 24.0, Compose >= 2.20)
  • Go Runtime: Versi 1.21+ (untuk contoh instrumentasi backend Go)
  • Curl & Git: Untuk pengunduhan repositori dan pengujian endpoint

Verifikasi Port & Firewall (UFW / Cloudflare / Security Group)

Pastikan port-port berikut terbuka atau dikonfigurasi dengan benar di balik reverse proxy / firewall:

  • 3000: Grafana Web UI
  • 9090: Prometheus Server UI
  • 4317: OpenTelemetry Collector gRPC Receiver
  • 4318: OpenTelemetry Collector HTTP Receiver

5. Panduan Implementasi Langkah demi Langkah

Berikut adalah konfigurasi end-to-end yang dapat dijalankan langsung di lingkungan produksi menggunakan Docker Compose.

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

Buat file konfigurasi untuk OpenTelemetry Collector yang menerima data dari aplikasi backend dan meneruskannya ke Prometheus dan Grafana Tempo.

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

processors:
  batch:
    timeout: 1s
    send_batch_size: 1024
  memory_limiter:
    check_interval: 1s
    limit_percentage: 80
    spike_limit_percentage: 20

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

service:
  pipelines:
    metrics:
      receivers: [otlp]
      processors: [memory_limiter, batch]
      exporters: [prometheus]
    traces:
      receivers: [otlp]
      processors: [memory_limiter, batch]
      exporters: [otlp/tempo]
  telemetry:
    logs:
      level: "info"

Langkah 2: Konfigurasi Prometheus (prometheus.yml)

Buat file konfigurasi agar Prometheus dapat melakukan scraping metrik dari OpenTelemetry Collector.

global:
  scrape_interval: 15s
  evaluation_interval: 15s

scrape_configs:
  - job_name: 'otel-collector'
    static_configs:
      - targets: ['otel-collector:8889']
  - job_name: 'backend-app'
    static_configs:
      - targets: ['backend-service:8080']

Langkah 3: Konfigurasi Grafana Tempo (tempo.yaml)

Konfigurasi backend penyimpanan traces.

server:
  http_listen_port: 3200

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

ingester:
  lifecycler:
    ring:
      replication_factor: 1
      kvstore:
        store: inmemory
    addr: 127.0.0.1
    join_after: 0s
    observe_period: 0s
    min_ready_duration: 0s
    final_sleep: 0s

storage:
  trace:
    backend: local
    local:
      path: /tmp/tempo/blocks
    wal:
      path: /tmp/tempo/wal

Langkah 4: Orkestrasi Infrastruktur (docker-compose.yml)

Gabungkan seluruh layanan ke dalam satu berkas orkestrasi.

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"
      - "4318:4318"
      - "8889:8889"
    networks:
      - observability-net

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

  tempo:
    image: grafana/tempo:2.3.1
    container_name: tempo
    command: ["-config.file=/etc/tempo.yaml"]
    volumes:
      - ./tempo.yaml:/etc/tempo.yaml
    ports:
      - "3200:3200"
      - "4317"
    networks:
      - observability-net

  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-net

networks:
  observability-net:
    driver: bridge

Langkah 5: Kode Backend Go yang Terinstrumentasi (main.go)

Contoh implementasi nyata pada aplikasi backend Go yang mengirimkan metrics dan traces ke OpenTelemetry Collector.

package main

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

	"go.opentelemetry.io/otel"
	"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() (*sdktrace.TracerProvider, error) {
	ctx := context.Background()

	// Hubungkan ke OpenTelemetry Collector via gRPC
	conn, err := grpc.DialContext(ctx, "otel-collector:4317", grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithBlock())
	if err != nil {
		return nil, err
	}

	exporter, err := otlptracegrpc.New(ctx, otlptracegrpc.WithGRPCConn(conn))
	if err != nil {
		return nil, err
	}

	rp := sdktrace.NewTracerProvider(
		calldtraced(exporter),
		sdktrace.WithResource(resource.NewWithAttributes(
			semconv.SchemaURL,
			semconv.ServiceNameKey.String("enterprise-backend-service"),
		)),
	)

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

	return rp, nil
}

func calldtraced(exp sdktrace.SpanExporter) sdktrace.TracerProviderOption {
	return sdktrace.WithBatcher(exp)
}

func main() {
	rp, err := initTracer()
	if err != nil {
		log.Fatalf("Gagal menginisialisasi tracer: %v", err)
	}
	defer func() {
		if err := rp.Shutdown(context.Background()); err != nil {
			log.Printf("Error saat mematikan tracer provider: %v", err)
		}
	}()

	tracer := otel.Tracer("enterprise-api")

	http.HandleFunc("/api/v1/process", func(w http.ResponseWriter, r *http.Request) {
		ctx, span := tracer.Start(r.Context(), "HTTP GET /api/v1/process", trace.WithSpanKind(trace.SpanKindServer))
		defer span.End()

		// Simulasi pemrosesan bisnis dan interaksi database / cache
		processBusinessLogic(ctx)

		w.WriteHeader(http.StatusOK)
		w.Write([]byte(`{"status":"success","message":"Observability active\
🏷️ Tags: #Nginx #High Concurrency #Performance #Sysadmin #Web Server