L
Observability & Monitoring • Literally Web ID

Distributed Tracing dan Observability pada Arsitektur Microservices

⏱️ 15 menit baca • 📅 15 Oct 2026 • 👁️ 1 views
Distributed Tracing dan Observability pada Arsitektur Microservices

1. Konteks Industri & Analisis Akar Masalah

Dalam arsitektur monolitik tradisional, melacak eksekusi sebuah permintaan (request) HTTP relatif mudah. Log aplikasi yang dikumpulkan oleh sistem seperti ELK Stack atau Graylog biasanya cukup untuk menelusuri alur eksekusi dari masuknya permintaan melalui web server Nginx hingga query ke basis data MySQL. Namun, ketika organisasi bermigrasi ke arsitektur microservices—di mana satu panggilan API eksternal dapat memicu puluhan atau bahkan ratusan permintaan internal antar layanan yang ditulis dalam berbagai bahasa pemrograman seperti Go dan Node.js, berjalan di atas orkestrator Kubernetes, dan memanfaatkan sistem caching Redis—debugging menjadi mimpi buruk.

Bottleneck Performa dan Keamanan di Dunia Nyata

Tanpa sistem distributed tracing yang memadai, tim Site Reliability Engineering (SRE) dan DevOps sering kali menghadapi masalah berikut:

  • Blind Spots Lintas Layanan: Ketika sebuah transaksi gagal atau melambat, sulit menentukan layanan spesifik yang menjadi bottleneck (apakah gateway API, auth service, inventory service, atau database).
  • High Latency Masquerading: Latensi kumulatif sering kali terdistribusi secara asinkron di berbagai queue dan message broker (seperti RabbitMQ atau Kafka), membuat metrik rata-rata berbasis CPU/RAM tidak berguna.
  • Cascading Failures: Kegagalan pada satu layanan hilir (downstream) dapat memicu retry storm yang melumpuhkan layanan hulu (upstream) tanpa menyisakan jejak log yang jelas.

Perbandingan Kondisi: Sebelum vs Sesudah Penerapan

Parameter Sebelum Distributed Tracing Sesudah Implementasi OpenTelemetry + Tempo
Mean Time to Resolution (MTTR) Berjam-jam hingga hari; mengandalkan tebakan dan korelasi manual ID log. Hitungan menit; langsung menunjuk operasi kode dan baris query yang lambat.
Identifikasi Bottleneck Hanya tahu sistem lambat secara umum; tidak tahu layanan mana yang bermasalah. Visualisasi Flame Graph menunjukkan durasi presisi per span hingga tingkat mikrodetik.
Korelasi Log & Metrik Terpisah; sulit menghubungkan lonjakan CPU dengan transaksi spesifik. Terintegrasi penuh; Trace ID disisipkan otomatis ke dalam header log dan metrik.

2. Konsep Inti & Deep Dive Arsitektur

Distributed tracing melacak perjalanan sebuah permintaan saat melewati batas-batas jaringan, proses, dan mesin. Konsep fundamental dalam ekosistem OpenTelemetry terdiri dari:

  1. Trace: Representasi dari seluruh perjalanan sebuah permintaan (request). Sebuah trace dibentuk oleh sekumpulan spans yang terhubung dalam bentuk pohon (tree).
  2. Span: Unit kerja individual atau operasi tunggal dalam sebuah trace. Memiliki nama, stempel waktu mulai dan selesai, atribut (key-value pairs), peristiwa (events), dan konteks.
  3. Context Propagation: Mekanisme untuk meneruskan informasi trace (seperti trace_id dan span_id) lintas batas jaringan menggunakan HTTP headers (biasanya standar W3C Trace Context: traceparent dan tracestate).

Diagram Alur Arsitektur

+------------------+         HTTP Request (W3C Headers)         +-----------------------+
|                  | -----------------------------------------> |                       |
|  Client / Nginx  |                                            |   API Gateway (Go)    |
|                  | <----------------------------------------- |                       |
+------------------+           JSON Response                    +-----------------------+
                                                                            |
                                                             gRPC / HTTP    | (Propagated Trace Context)
                                                                            v
                                                                +-----------------------+
                                                                |                       |
                                                                |  User Service (Node)  |
                                                                |                       |
                                                                +-----------------------+
                                                                            |
                                                                            +---> [Redis Cache]
                                                                            |
                                                                            +---> [MySQL DB]

Alur data di atas menunjukkan bagaimana Trace ID yang dibangkitkan di pintu gerbang (API Gateway) diteruskan ke layanan hilir (User Service), dicatat oleh OpenTelemetry Collector, dan akhirnya dikirim ke penyimpanan penyimpanan backend seperti Grafana Tempo.


3. Perbandingan Komparatif / Analisis Pendekatan

Dalam memilih backend dan agen tracing, terdapat beberapa standar industri yang umum digunakan. Tabel berikut membandingkan OpenTelemetry dengan pendekatan alternatif lama.

Fitur / Metrik OpenTelemetry (OTel) + Grafana Tempo Jaeger (Legacy Standalone) Zipkin APM Komersial (Datadog / New Relic)
Standarisasi Standar industri universal (CNCF). Standar lama CNCF. Standar lama (Twitter). Proprietary (Vendor Lock-in).
Penggunaan Sumber Daya (RAM/CPU) Sangat efisien; collector dapat diskalakan terpisah. Sedang; membutuhkan memori besar untuk in-memory storage. Rendah hingga Sedang. Agen berjalan di setiap node, overhead bervariasi.
Biaya Lisensi Open Source (Apache 2.0). Open Source (Apache 2.0). Open Source (Apache 2.0). Berbayar berdasarkan volume ingest data.
Integrasi Ekosistem Sempurna dengan Prometheus, Grafana, Loki. Baik, namun mulai ditinggalkan demi OTel. Terbatas pada ekosistem Java/Spring. Komprehensif namun mahal.

[!TIP] Gunakan OpenTelemetry Collector sebagai perantara (proxy) antara aplikasi Anda dan penyimpanan backend (Grafana Tempo). Hal ini memungkinkan Anda mengubah tujuan backend (dari Tempo ke Jaeger atau Datadog) tanpa harus mengubah kode aplikasi sedikit pun.


4. Prasyarat Sistem & Persiapan Lingkungan

Sebelum melakukan implementasi, pastikan infrastruktur dan environment pengembangan Anda memenuhi spesifikasi minimum berikut:

  • Sistem Operasi: Ubuntu 22.04 LTS / Debian 12 / RHEL 9 (atau containerized environment Docker & Kubernetes).
  • Go Runtime: Versi 1.21 atau lebih tinggi.
  • Node.js Runtime: Versi 18.x LTS atau 20.x LTS.
  • Docker & Docker Compose: Versi 24+ untuk menjalankan infrastruktur observabilitas lokal (Grafana, Tempo, OTel Collector).
  • Port Jaringan yang Harus Dibuka:
    • 4317: OTLP gRPC receiver
    • 4318: OTLP HTTP receiver
    • 3000: Grafana Web UI
    • 3200: Grafana Tempo

Verifikasi ketersediaan port dengan perintah berikut:

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

5. Panduan Implementasi Langkah demi Langkah

Langkah 1: Konfigurasi Infrastruktur Observabilitas dengan Docker Compose

Buat file bernama docker-compose.yml untuk menjalankan OpenTelemetry Collector, Grafana Tempo, dan Grafana UI.

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
    networks:
      - observabilitas

  tempo:
    image: grafana/tempo:2.4.0
    container_name: tempo
    command: ["-config.file=/etc/tempo.yaml"]
    volumes:
      - ./tempo.yaml:/etc/tempo.yaml
      - tempo-data:/tmp/tempo
    ports:
      - "3200:3200" # Tempo HTTP
    networks:
      - observabilitas

  grafana:
    image: grafana/grafana:10.4.0
    container_name: grafana
    environment:
      - GF_AUTH_ANONYMOUS_ENABLED=true
      - GF_AUTH_ANONYMOUS_ORG_ROLE=Admin
    ports:
      - "3000:3000"
    volumes:
      - ./grafana-datasources.yaml:/etc/grafana/provisioning/datasources/datasources.yaml
    networks:
      - observabilitas

volumes:
  tempo-data:

networks:
  observabilitas:
    driver: bridge

Buat file konfigurasi OTel Collector (otel-collector-config.yaml):

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

exporters:
  otlp:
    endpoint: "tempo:4317"
    tls:
      insecure: true

service:
  pipelines:
    traces:
      receivers: [otlp]
      exporters: [otlp]

Buat file konfigurasi Tempo (tempo.yaml):

server:
  http_listen_port: 3200

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

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

Buat datasource provisioning untuk Grafana (grafana-datasources.yaml):

apiVersion: 1

datasources:
  - name: Tempo
    type: tempo
    access: proxy
    url: http://tempo:3200
    isDefault: true
    jsonData:
      httpMethod: GET

Jalankan stack observabilitas:

docker-compose up -d

Langkah 2: Instrumentasi Layanan Go (API Gateway)

Inisialisasi modul Go dan instal pustaka OpenTelemetry:

mkdir go-gateway && cd go-gateway
go mod init github.com/enterprise/gateway
go get go.opentelemetry.io/otel
go get go.opentelemetry.io/otel/sdk
go get go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc
go get google.golang.org/grpc

Buat file main.go:

package main

import (
	"context"
	"fmt"
	"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.4.0"
	"google.golang.org/grpc"
)

func initTracer(ctx context.Context) (*sdktrace.TracerProvider, error) {
	exporter, err := otlptracegrpc.New(ctx,
		otlptracegrpc.WithInsecure(),
		otlptracegrpc.WithEndpoint("localhost:4317"),
	)
	if err != nil {
		return nil, err
	}

	tp := sdktrace.NewTracerProvider(
		sdktrace.WithSampler(sdktrace.AlwaysSample()),
		sdktrace.WithBatcher(exporter),
		sdktrace.WithResource(resource.NewWithAttributes(
			semconv.SchemaURL,
			semconv.ServiceNameKey.String("go-api-gateway"),
		)),
	)
	otel.SetTracerProvider(tp)
	otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{}))
	return tp, nil
}

func handler(w http.ResponseWriter, r *http.Request) {
	ctx, span := otel.Tracer("gateway-tracer").Start(r.Context(), "HTTP GET /order")
	defer span.End()

	// Simulasi pemanggilan layanan hilir (Node.js User Service)
	time.Sleep(50 * time.Millisecond)

	w.WriteHeader(http.StatusOK)
	fmt.Fprintf(w, "Order processed successfully by Go Gateway!")
}

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

	http.HandleFunc("/order", handler)
	fmt.Println("Go API Gateway berjalan di port 8080...")
	log.Fatal(http.ListenAndServe(":8080", nil))
}

Langkah 3: Instrumentasi Layanan Node.js (User Service)

Inisialisasi proyek Node.js:

mkdir node-service && cd node-service
npm init -y
npm install @opentelemetry/api @opentelemetry/sdk-node @opentelemetry/auto-instrumentations-node @opentelemetry/exporter-trace-otlp-http

Buat file tracing setup tracing.js:

'use strict';

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

const collectorExporter = new OTLPTraceExporter({
  url: 'http://localhost:4318/v1/traces',
});

const sdk = new NodeSDK({
  resource: new Resource({
    [SemanticResourceAttributes.SERVICE_NAME]: 'node-user-service',
  }),
  traceExporter: collectorExporter,
  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));
});

Buat server HTTP sederhana di server.js:

require('./tracing');

const http = require('http');

const server = http.createServer((req, res) => {
  if (req.url === '/user' && req.method === 'GET') {
    setTimeout(() => {
      res.writeHead(200, { 'Content-Type': 'application/json' });
      res.end(JSON.stringify({ userId: 101, name: 'Enterprise Admin' }));
    }, 120);
  } else {
    res.writeHead(404);
    res.end();
  }
});

server.listen(3001, () => {
  console.log('Node.js User Service berjalan di port 3001...');
});

6. Bedah Kode & Penjelasan Detail Baris Kunci

Mari kita bedah beberapa konfigurasi kritis dari kode Go dan Node.js di atas:

  1. sdktrace.WithSampler(sdktrace.AlwaysSample()) (Go):

    • Fungsi: Mengatur sampling rate. AlwaysSample berarti setiap permintaan akan dilacak. Di lingkungan produksi dengan ribuan request per second (RPS), penggunaan AlwaysSample dapat membebani jaringan dan penyimpanan. Di produksi, gunakan WithSampler(sdktrace.ParentBased(sdktrace.TraceIDRatioBased(0.1))) untuk mengambil sampel 10% dari total traffic.
  2. propagation.NewCompositeTextMapPropagator (Go):

    • Fungsi: Menentukan standar injeksi dan ekstraksi header jaringan. Kombinasi TraceContext (standar W3C traceparent) memastikan kompatibilitas lintas bahasa pemrograman antara Go, Node.js, Python, dan PHP.
  3. getNodeAutoInstrumentations() (Node.js):

    • Fungsi: Fitur unggulan OpenTelemetry yang secara otomatis melakukan monkey-patching pada pustaka populer seperti http, express, pg, redis, dan mongodb tanpa mengharuskan developer menulis kode span manual di setiap modul.

7. Skenario Produksi & Studi Kasus Dunia Nyata

Dalam sistem e-commerce berskala besar yang menangani 50.000 traffic per detik selama flash sale, lonjakan latensi terjadi pada endpoint /checkout.

Langkah Investigasi Menggunakan Grafana Tempo:

  1. Buka Grafana UI (http://localhost:3000), lalu arahkan ke menu Explore. Pilih datasource Tempo.
  2. Lakukan pencarian berdasarkan kriteria latensi tinggi: duration > 500ms dan tag http.route = "/checkout".
  3. Pilih salah satu trace yang muncul. Anda akan melihat Flame Graph yang memperlihatkan alur:
    • go-api-gateway menerima permintaan (10ms).
    • Memanggil node-user-service untuk verifikasi saldo (45ms).
    • Memanggil java-payment-service untuk eksekusi kartu kredit (420ms — Inilah Bottleneck-nya).
  4. Dengan distributed tracing, tim tidak lagi menebak-nebak, melainkan langsung mengetahui bahwa integrasi gateway pembayaran eksternal mengalami kelambatan, sehingga dapat segera menerapkan circuit breaker atau fallback strategy.

8. Common Pitfalls, Anti-Patterns & Solusi

[!WARNING] Anti-Pattern Terbesar: Mengirim data tracing langsung dari aplikasi ke storage backend tanpa melalui OpenTelemetry Collector lokal. Hal ini berisiko menyebabkan aplikasi mengalami crash atau memory leak jika jaringan ke backend terputus, karena data span akan menumpuk di memori lokal aplikasi.

  • Kesalahan 1: Kardinalitas Atribut yang Terlalu Tinggi.
    • Masalah: Menyimpan User Email atau UUID transaksi sebagai attribute key pada setiap span. Hal ini akan meledakkan ukuran indeks backend (Tempo/Jaeger).
    • Solusi: Simpan data sensitif atau kardinalitas tinggi di dalam log message atau span events, bukan sebagai attribute keys utama.
  • Kesalahan 2: Lupa Meneruskan Konteks (Context Propagation).
    • Masalah: Membuat goroutine baru di Go atau panggilan fetch() di Node.js tanpa meneruskan objek context.Context atau headers HTTP, sehingga trace terputus (broken trace).
    • Solusi: Selalu teruskan ctx pada setiap fungsi asinkron dan pastikan HTTP client menyuntikkan propagator headers secara otomatis.

9. Benchmark, Metrik Kinerja & Tuning Performa

Untuk menguji dampak performa dari agen OpenTelemetry pada aplikasi Anda, jalankan load testing menggunakan alat seperti k6.

Buat skrip uji beban 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: 50 },   // Pertahankan beban
    { duration: '10s', target: 0 },   // Ramp-down
  ],
};

export default function () {
  const res = http.get('http://localhost:8080/order');
  check(res, {
    'status was 200': (r) => r.status === 200,
    'response time < 100ms': (r) => r.timings.duration < 100,
  });
  sleep(1);
}

Jalankan pengujian:

k6 run load-test.js

Hasil Tuning Lanjutan:

  • Dengan batching yang diaktifkan pada OTel Collector (mengatur send_batch_size: 1024 dan timeout: 1s), overhead CPU pada aplikasi akibat pengiriman telemetry data dapat ditekan hingga di bawah 2%.

10. Checklist Penerapan Produksi & Kesimpulan

Sebelum meluncurkan arsitektur distributed tracing ke lingkungan produksi (production release), pastikan checklist berikut terpenuhi:

  • OpenTelemetry Collector dijalankan sebagai sidecar di setiap cluster Kubernetes atau sebagai agen terpusat yang aman.
  • Sampling rate disesuaikan (misal: 5% - 10%) untuk menghemat biaya penyimpanan data tracing.
  • Rotasi dan retensi data Grafana Tempo dikonfigurasi (misal: retensi 7 hari) untuk mencegah kehabisan ruang disk (disk exhaustion).
  • Mekanisme authentication dan TLS encryption diaktifkan pada komunikasi antara aplikasi dan OTel Collector (otlptracegrpc.WithTLS(...)).
  • Integrasi lintas sinyal (logs, metrics, and traces) dihubungkan menggunakan Trace ID yang disisipkan ke dalam stdout log aplikasi.

Dengan menerapkan panduan komprehensif ini, Anda tidak hanya membangun sistem yang dapat diamati (observable), tetapi juga memastikan infrastruktur microservices Anda memiliki ketahanan operasional tingkat enterprise yang siap menghadapi lonjakan traffic ekstrem.

🏷️ Tags: #Nginx #High Concurrency #Performance #Sysadmin #Web Server