L
Observability & Monitoring • Literally Web ID

Observabilitas Sistem Distribusi dengan OpenTelemetry dan Prometheus

⏱️ 22 menit baca • 📅 20 Oct 2026 • 👁️ 2 views
Observabilitas Sistem Distribusi dengan OpenTelemetry dan Prometheus

1. Konteks Industri & Analisis Akar Masalah

Dalam lanskap arsitektur sistem terdistribusi modern, migrasi dari monolit ke mikroservis (microservices) telah membawa skalabilitas yang luar biasa, namun di sisi lain menimbulkan kompleksitas operasional yang masif. Permintaan HTTP tunggal dari klien kini dapat melintasi puluhan atau bahkan ratusan layanan independen yang berjalan di atas kontainer Docker atau klaster Kubernetes. Ketika terjadi latensi tinggi atau kegagalan sistem (failure), mengisolasi akar masalah (root cause) menggunakan metode traditional logging (seperti grep pada file log teks datar atau melihat log terisolasi di masing-masing server) adalah tindakan yang sia-sia, tidak efisien, dan membuang waktu.

Bottleneck performa yang umum terjadi di dunia nyata meliputi:

  • Cascading Failures: Satu layanan backend (misalnya, basis data MySQL/MariaDB yang mengalami lock atau antrean Redis yang penuh) melambat, menyebabkan thread pool di layanan Node.js kehabisan kapasitas, yang akhirnya memicu timeout masif pada API Gateway.
  • Network Jitter & DNS Latency: Kegagalan resolusi DNS internal antar kontainer atau latensi tinggi pada komunikasi gRPC/REST antar mikroservis.
  • Resource Starvation: Kebocoran memori (memory leak) pada proses Go routines atau skrip PHP-FPM yang tidak terkontrol, membebani kernel Linux dan memicu Out-Of-Memory (OOM) Killer.

Sebelum Penerapan Observabilitas Modern: Tim SRE dan Backend Engineer menghabiskan waktu berjam-jam melakukan finger-pointing antar tim. Developer backend mengklaim kode aman, tim infrastruktur menyalahkan jaringan, dan database administrator menyalahkan query lambat. Waktu penyelesaian insiden (Mean Time To Resolution / MTTR) sangat tinggi.

Sesudah Penerapan OpenTelemetry dan Prometheus: Setiap request diberi Trace ID unik sejak masuk melalui edge proxy (seperti Cloudflare atau Nginx), diteruskan ke API Gateway, backend Go/Node.js, query database, hingga operasi caching Redis. Metrik performa, log, dan traces dikorelasikan secara real-time. Metrik dikikis oleh Prometheus dan divisualisasikan dalam dashboard Grafana yang memberikan visibilitas instan terhadap RED Method (Rate, Errors, Duration) dan USE Method (Utilization, Saturation, Errors).

[!IMPORTANT] Observabilitas bukan sekadar alat monitoring (monitoring tells you if a system works, observability tells you why it doesn't). Tanpa tracing terdistribusi yang dikumpulkan secara terstandarisasi melalui OpenTelemetry, Anda terbang buta di atas sistem produksi berskala besar.


2. Konsep Inti & Deep Dive Arsitektur

OpenTelemetry (OTel) adalah standar industri open-source yang dibentuk dari penggabungan OpenTracing dan OpenCensus. OTel menyediakan spesifikasi dan perangkat lintas bahasa untuk mengumpulkan Telemetry Data yang terdiri dari tiga pilar utama:

  1. Metrics: Nilai numerik yang diagregasi dari waktu ke waktu (counter, gauge, histogram).
  2. Traces: Representasi jalur perjalanan sebuah request melalui berbagai komponen sistem.
  3. Logs: Catatan peristiwa bertekanan waktu yang terstruktur.

Alur data operasional (data flow execution) di balik layar bekerja melalui arsitektur berikut:

+-------------------------------------------------------------------------+
|                         Distributed Architecture                        |
|                                                                         |
|  +-------------------+       gRPC/HTTP      +------------------------+  |
|  |   Client Request  | -------------------> | Nginx Reverse Proxy    |  |
|  +-------------------+                      +------------------------+  |
|                                                          |              |
|                                                   W3C Trace Context     |
|                                                          v              |
|  +-------------------+                      +------------------------+  |
|  | Node.js / Go Svc  | <================--> | OpenTelemetry Collector|  |
|  +-------------------+ (OTLP Exporter)      +------------------------+  |
|     |           |                                    |                  |
|  MySQL/MariaDB  Redis                                PromQL / Scrape    |
|                                                      v                  |
|                                             +------------------------+  |
|                                             |       Prometheus       |  |
|                                             +------------------------+  |
|                                                      |                  |
|                                                      v                  |
|                                             +------------------------+  |
|                                             |    Grafana Dashboard   |  |
|                                             +------------------------+  |
+-------------------------------------------------------------------------+

Alur Kontekstual Tracing (W3C Trace Context):

  • Ketika request masuk ke Nginx atau API Gateway, header HTTP khusus disuntikkan: traceparent (berisi version, trace-id, parent-id, dan trace-flags) serta tracestate.
  • Layanan backend hilir (downstream) membaca header ini, melanjutkan trace-id yang sama, dan membuat span baru sebagai anak (child span) dari parent span sebelumnya.
  • OTel SDK di dalam aplikasi mengumpulkan span ini dan mengirimkannya secara asinkron menggunakan protokol OTLP (OpenTelemetry Protocol) ke OpenTelemetry Collector atau langsung ke backend pemantauan.

3. Perbandingan Komparatif / Analisis Pendekatan

Untuk sistem skala enterprise, pemilihan metode pengumpulan dan penyimpanan metrik serta tracing sangat menentukan performa infrastruktur secara keseluruhan.

Parameter / Aspek OpenTelemetry + Prometheus APM Komersial (Datadog / New Relic) Custom Logging & StatsD
Kepemilikan Data Penuh (Self-Hosted, GDPR Compliant) Vendor Lock-in (SaaS Cloud) Penuh
Overhead CPU / RAM Rendah - Menengah (Tergantung konfigurasi eksportir) Menengah - Tinggi (Agen agent sering rakus memori) Sangat Rendah
Standarisasi Standar CNCF universal (Vendor-neutral) Proprietary format Ad-hoc / Tidak ada standar
Biaya Operasional Biaya infrastruktur server & penyimpanan Biaya lisensi per host / per GB ingest data Biaya pemeliharaan kode custom yang masif
Skalabilitas Tracing Sangat tinggi dengan OTel Collector + Jaeger/Tempo Sangat tinggi (dikelola vendor) Sangat buruk untuk skala besar

[!TIP] Gunakan OpenTelemetry untuk instrumentasi aplikasi guna menghindari vendor lock-in. Jika di kemudian hari Anda ingin beralih dari Prometheus ke sistem penyimpanan lain (seperti Thanos, VictoriaMetrics, atau ClickHouse), Anda tidak perlu menulis ulang kode instrumentasi aplikasi Anda.


4. Prasyarat Sistem & Persiapan Lingkungan

Sebelum melakukan implementasi teknis, pastikan lingkungan infrastruktur Anda memenuhi spesifikasi berikut:

  • Sistem Operasi: Ubuntu 22.04 LTS / Debian 12 / RHEL 9 (Kernel Linux >= 5.15 untuk performa eBPF jika diperlukan).
  • Runtime: Node.js v20.x LTS atau Go v1.22+
  • Web Server & Backend: Nginx v1.24+, PHP-FPM 8.3 (jika ada layanan hybrid PHP), Redis 7.2+, MySQL 8.0 / MariaDB 11.2+
  • Tooling: Docker Engine v24+ & Docker Compose v2.20+
  • Network / Firewall: Port 4317 (OTLP gRPC), 4318 (OTLP HTTP), 9090 (Prometheus), 3000 (Grafana) harus diizinkan secara internal di jaringan privat klaster.

Verifikasi awal ketersediaan port dan sumber daya sistem:

sysctl vm.max_map_count
ulimit -n 65535

5. Panduan Implementasi Langkah demi Langkah

Langkah 1: Konfigurasi Docker Compose untuk Infrastruktur Observabilitas

Buat file docker-compose.yml untuk menjalankan Prometheus, OpenTelemetry Collector, dan Grafana.

version: '3.8'

services:
  prometheus:
    image: prom/prometheus:v2.51.0
    container_name: prometheus
    restart: unless-stopped
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--storage.tsdb.path=/prometheus'
      - '--web.console.libraries=/usr/share/prometheus/console_libraries'
      - '--web.console.templates=/usr/share/prometheus/consoles'
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
      - prometheus_data:/prometheus

  otel-collector:
    image: otel/opentelemetry-collector-contrib:0.96.0
    container_name: otel-collector
    restart: unless-stopped
    command: ["--config=/etc/otel-collector-config.yaml"]
    ports:
      - "4317:4317" # OTLP gRPC receiver
      - "4318:4318" # OTLP HTTP receiver
      - "8889:8889" # Prometheus exporter metrics
    volumes:
      - ./otel-collector-config.yaml:/etc/otel-collector-config.yaml:ro

  grafana:
    image: grafana/grafana:10.4.0
    container_name: grafana
    restart: unless-stopped
    ports:
      - "3000:3000"
    environment:
      - GF_SECURITY_ADMIN_USER=admin
      - GF_SECURITY_ADMIN_PASSWORD=securepassword123
    volumes:
      - grafana_data:/var/lib/grafana

volumes:
  prometheus_data:
  grafana_data:

Langkah 2: Konfigurasi Prometheus (prometheus.yml)

global:
  scrape_interval: 15s
  evaluation_interval: 15s

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

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

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

exporters:
  prometheus:
    endpoint: 0.0.0.0:8889
    namespace: "app"

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

Langkah 4: Instrumentasi Kode Aplikasi Go (Backend API)

Buat file main.go yang mengintegrasikan OpenTelemetry SDK.

package main

import (
	"context"
	"log"
	"net/http"
	"os"
	"os/signal"
	"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(ctx context.Context) (*sdktrace.TracerProvider, error) {
	res, err := resource.New(ctx,
		resource.WithAttributes(
			semconv.ServiceNameKey.String("golang-backend-service"),
			semconv.ServiceVersionKey.String("1.0.0"),
		),
	)
	if err != nil {
		return nil, err
	}

	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
	}

	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 main() {
	ctx := context.Background() آتے
	tp, err := initTracer(ctx)
	if err != nil {
		log.Fatalf("Gagal inisialisasi tracer: %v", err)
	}
	defer func() {
		if err := tp.Shutdown(ctx); err != nil {
			log.Printf("Error saat mematikan TracerProvider: %v", err)
		}
	}()

	http.HandleFunc("/api/v1/data", func(w http.ResponseWriter, r *http.Request) {
		tr := otel.Tracer("golang-backend")
		ctx, span := tr.Start(r.Context(), "HandleGetUserData", trace.WithSpanKind(trace.SpanKindServer))
		defer span.End()

		// Simulasi latensi database / bisnis logika
		time.Sleep(50 * time.Millisecond)

		// Sub-span untuk operasi cache redis tiruan
		_, subSpan := tr.Start(ctx, "RedisCacheLookup")
		time.Sleep(10 * time.Millisecond)
		subSpan.End()

		w.WriteHeader(http.StatusOK)
		w.Write([]byte(`{"status":"success","data":"Enterprise Observability Active"}`)) 
	})

	log.Println("Server Go berjalan pada port 8080...")
	if err := http.ListenAndServe(":8080", nil); err != nil {
		log.Fatal(err)
	}
}

Langkah 5: Instrumentasi Kode Aplikasi Node.js (Backend Service)

Buat file instrumentation.js dan jalankan sebelum aplikasi utama diinisialisasi.

'use strict';

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

const traceExporter = new OTLPTraceExporter({
  url: 'http://localhost:4317',
});

const sdk = new NodeSDK({
  resource: new Resource({
    [SEMRESATTRS_SERVICE_NAME]: 'nodejs-backend-service',
  }),
  traceExporter,
  instrumentations: [getNodeAutoInstrumentations()],
});

sdk.start();

process.on('SIGTERM', () => {
  sdk.shutdown()
    .then(() => console.log('Tracing dihentikan dengan aman'))
    .catch((error) => console.log('Error menghentikan tracing', error))
    .finally(() => process.exit(0));
});

6. Bedah Kode & Penjelasan Detail Baris Kunci

  1. resource.New(...) (Go & Node.js): Mendefinisikan metadata global layanan (seperti service.name dan service.version). Atribut ini sangat krusial agar Prometheus dan Grafana dapat membedakan metrik dari berbagai layanan mikroservis yang berbeda dalam satu klaster.
  2. sdktrace.WithSampler(sdktrace.AlwaysSample()): Menentukan kebijakan sampling. AlwaysSample berarti setiap request akan dicatat dalam trace. Di lingkungan produksi dengan beban traffic jutaan requests per second (RPS), konfigurasi ini dapat membebani penyimpanan; ubah menggunakan ParentBased(TraceIDRatioBased(0.05)) untuk mengambil sampel sebesar 5% saja.
  3. propagation.NewCompositeTextMapPropagator(...): Memastikan kompatibilitas propagasi konteks lintas bahasa (misalnya dari Go ke Node.js atau layanan Python/PHP) menggunakan standar W3C Trace Context.
  4. otel-collector-config.yaml (Batch Processor): Penggunaan processors: [batch] mengelompokkan span sebelum dikirimkan ke eksportir. Hal ini mengurangi overhead jaringan (network socket churn) dan penggunaan CPU secara drastis.

7. Skenario Produksi & Studi Kasus Dunia Nyata

Studi Kasus: Sistem E-Commerce skala besar menghadapi lonjakan Flash Sale dengan 50.000 RPS. Layanan pemesanan (Order Service) mengalami lonjakan latensi secara misterius.

Analisis dengan OpenTelemetry:

  1. SRE membuka Grafana, melihat lonjakan Duration pada endpoint /api/v1/checkout di RED metrics Prometheus.
  2. Melalui Trace Explorer (Jaeger/Tempo yang terhubung via OTel Collector), SRE menelusuri Trace ID yang mengalami latensi > 2 detik.
  3. Visualisasi span memperlihatkan bahwa waktu eksekusi terbesar dihabiskan pada child span MySQL_Execute_Inventory_Update.
  4. Investigasi lebih lanjut menunjukkan kurangnya indeks pada tabel stok gudang MySQL, menyebabkan table scan penuh di bawah beban tinggi.
  5. Setelah indeks ditambahkan dan query dioptimalkan, latensi rata-rata turun dari 2.30adian menjadi 45 milidetik.

8. Common Pitfalls, Anti-Patterns & Solusi

  • Anti-Pattern 1: High Cardinality pada Atribut Span/Metrik. Kesalahan: Menjadikan UUID pengguna atau alamat IP sebagai label atau attribute di Prometheus/OpenTelemetry. Dampak: Menyebabkan ledakan kardinalitas (cardinality explosion), membuat memori Prometheus habis (OOM), dan melumpuhkan sistem monitoring. Solusi: Simpan data unik tersebut di dalam payload Log atau Span Event, bukan sebagai atribut metrik dimensi Prometheus.

  • Anti-Pattern 2: Sinkronisasi Ekspor Span yang Memblokir Thread Utama. Kesalahan: Menggunakan eksportir sinkron pada aplikasi berkinerja tinggi. Solusi: Selalu gunakan Batch Span Processor agar pengiriman data telemetri berjalan secara asinkron di latar belakang.

  • Anti-Pattern 3: Mengabaikan Time Synchronization (NTP). Kesalahan: Server mikroservis tidak melakukan sinkronisasi waktu clock NTP. Dampak: Urutan span pada distributed tracing menjadi kacau dan tidak masuk akal secara kronologis. Solusi: Pastikan daemon chrony atau ntp aktif di seluruh node infrastruktur.


9. Benchmark, Metrik Kinerja & Tuning Performa

Lakukan pengujian beban (load testing) menggunakan k6 untuk mengukur dampak instrumentasi OpenTelemetry terhadap throughput aplikasi.

Buat file script k6 load-test.js:

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

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

export default function () {
  const res = http.get('http://localhost:8080/api/v1/data');
  check(res, {
    'status adalah 200': (r) => r.status === 200,
    'latensi < 100ms': (r) => r.timings.duration < 100,
  });
  sleep(0.1);
}

Jalankan benchmark:

k6 run load-test.js

Tuning Parameter Lanjutan pada Collector: Sesuaikan memori limit pada kontainer OpenTelemetry Collector di docker-compose.yml untuk mencegah OOM:

    deploy:
      resources:!
        limits:
          memory: 1G
        reservations:
          memory: 512M

10. Checklist Penerapan Produksi & Kesimpulan

Gunakan checklist berikut sebelum meluncurkan arsitektur observabilitas ke lingkungan produksi:

  • Apakah enkripsi TLS diaktifkan untuk komunikasi OTLP antara aplikasi dan Collector?
  • Apakah kebijakan sampling trace sudah disesuaikan dengan kapasitas volume traffic (misalnya 1%-10% untuk high-traffic)?
  • Apakah label metrik Prometheus bebas dari high cardinality (menghindari user ID, email, atau timestamp mentah)?
  • Apakah retention period data Prometheus dan penyimpanan trace sudah diatur sesuai kapasitas disk server?
  • Apakah alert manager sudah dikonfigurasi untuk mendeteksi lonjakan error rate (HTTP 5xx) secara real-time?

Dengan menerapkan arsitektur OpenTelemetry dan Prometheus secara disiplin, tim SRE dan Backend Engineer memiliki kekuatan penuh untuk mengintip isi perut sistem terdistribusi, mendiagnosis anomali dalam hitungan detik, serta menjamin ketersediaan tinggi (high availability) bagi seluruh ekosistem digital enterprise.

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