L
Observability & Monitoring • Literally Web ID

Strategi Observability dan Distributed Tracing dengan OpenTelemetry di Kubernetes

⏱️ 15 menit baca • 📅 16 Oct 2026 • 👁️ 1 views
Strategi Observability dan Distributed Tracing dengan OpenTelemetry di Kubernetes

1. Konteks Industri & Analisis Akar Masalah

Di dalam ekosistem arsitektur web modern yang berbasis cloud-native, pergeseran dari monolit ke arsitektur layanan mikro (microservices) telah membawa kompleksitas operasional yang masif. Sebuah peramban (browser) mengirimkan permintaan HTTP, melewati lapisan Cloudflare Edge CDN, menembus Web Application Firewall (WAF), melintasi Ingress Controller Kubernetes (seperti Nginx atau Traefik), sebelum akhirnya diterima oleh layanan backend yang ditulis dalam berbagai bahasa seperti Go, Node.js, atau PHP-FPM.

Dalam lintasan tersebut, satu permintaan pengguna (user request) dapat memicu puluhan komunikasi antar-layanan (service-to-service calls), kueri ke basis data MySQL/MariaDB, operasi pembacaan/penulisan cache Redis, pengambilan aset dari penyimpanan objek S3/MinIO, hingga memicu tugas asinkron melalui antrean pesan (message queue) yang dikelola oleh Worker. Ketika terjadi lonjakan latensi atau kegagalan sistem (500 Internal Server Error), tim SRE (Site Reliability Engineer) dan DevOps sering kali terjebak dalam proses debugging yang melelahkan akibat kurangnya visibilitas ujung-ke-ujung (end-to-end).

Sebelum vs Sesudah Penerapan OpenTelemetry

Aspek Kondisi Sebelum (Tradisional / Logging Terisolasi) Kondisi Sesudah (OpenTelemetry & Distributed Tracing)
Troubleshooting Latensi Menebak-nebak layanan mana yang lambat menggunakan grep pada log file mentah secara manual di setiap pod Kubernetes. Menemukan titik botol leher (bottleneck) secara instan melalui visualisasi waterfall Jaeger atau Grafana Tempo hingga tingkat kueri SQL tertentu.
Korelasi Data Log, metric, dan trace terpisah tanpa pengenal unik, menyulitkan penyatuan konteks kejadian lintas bahasa pemrograman. Konteks tunggal (Trace ID dan Span ID) disuntikkan secara otomatis ke setiap log, header HTTP, dan metric Prometheus.
Standarisasi Vendor Bergantung pada agen pemantauan berpemilik (proprietary APM agents) yang mahal dan memicu vendor lock-in. Menggunakan standar terbuka CNCF (Cloud Native Computing Foundation) yang dapat dikirim ke backend apa pun (Grafana, Jaeger, OTel Collector).
Dampak Performa Agen APM lama sering kali membebani heap memory JVM atau runtime Go/Node.js karena overhead internal yang tinggi. OpenTelemetry SDK dirancang sangat ringan (zero-allocation design) dengan tingkat sampling yang dapat dikonfigurasi dinamis.

Kegagalan mempertahankan observabilitas yang baik pada lingkungan produksi berkapasitas tinggi tidak hanya berimplikasi pada kerugian finansial akibat downtime, tetapi juga melanggar SLA (Service Level Agreement) klien secara sistematis.

2. Konsep Inti & Deep Dive Arsitektur

OpenTelemetry (OTel) bukan sekadar alat, melainkan sebuah kerangka kerja observabilitas yang menyediakan sekumpulan API, SDK, dan alat (Collector) untuk membuat, mengumpulkan, dan mengekspor data telemetri (metrics, logs, and traces).

Alur Data di Bawah Hood (Under the Hood)

Mari kita bedah alur perjalanan sebuah trace dari aplikasi di dalam kluster Kubernetes hingga ke penyimpanan data:

+--------------------------------------------------------------------------+
| Kubernetes Cluster                                                       |
|                                                                          |
|  +--------------------+      gRPC/OTLP      +-------------------------+  |
|  | Application Pod    | ------------------> | OTel Collector DaemonSet|  |
|  | (Go/Node/PHP/Go)   |                     | (Node-level local agent)|  |
|  +--------------------+                     +-------------------------+  |
|                                                          |               |
|                                                          | OTLP gRPC     |
|                                                          v               |  |
|                                             +-------------------------+  |
|                                             | OTel Collector Deployment| |
|                                             | (Central Pipeline/Batch)|  |
|                                             +-------------------------+  |
+----------------------------------------------------------|---------------+
                                                           |
                                     +---------------------+---------------------+
                                     |                                           |
                                     v                                           v
                          +---------------------+                     +---------------------+
                          | Grafana Tempo       |                     | Prometheus          |
                          | (Distributed Traces)|                     | (Metrics Storage)   |
                          +---------------------+                     +---------------------+
  1. Trace & Span Creation: Ketika request masuk ke aplikasi, SDK OpenTelemetry membuat sebuah Trace yang merepresentasikan seluruh siklus hidup request tersebut. Trace ini terdiri dari beberapa Span (unit kerja individual, misal: pemanggilan HTTP, kueri database MySQL, eksekusi perintah Redis).
  2. Context Propagation: Melalui injeksi header HTTP (seperti standar W3C Trace Context traceparent dan tracestate), konteks Trace ID diteruskan dari Nginx Ingress ke backend Go, lalu diteruskan saat memanggil layanan lain atau kueri Redis.
  3. Exporting to Collector: Aplikasi mengirimkan data telemetri menggunakan protokol OTLP (OpenTelemetry Protocol) baik via gRPC (port 4317) atau HTTP (port 4318) ke OpenTelemetry Collector yang berjalan sebagai DaemonSet di setiap node Kubernetes.
  4. Processing & Routing: Collector lokal menerima data, melakukan batching, filtering, atau penambahan atribut (processors), lalu meneruskannya ke Central OTel Collector Deployment atau langsung ke backend penyimpanan seperti Grafana Tempo (untuk trace) dan Prometheus (untuk metrics).

3. Perbandingan Komparatif / Analisis Pendekatan

Dalam mendesain arsitektur pengumpulan data telemetri di Kubernetes, terdapat dua pola utama penempatan OpenTelemetry Collector: DaemonSet Deployment vs Deployment (Centralized Service).

Parameter Komparasi DaemonSet (Node-Local Agent) Deployment (Centralized Cluster)
Network Overhead Sangat rendah. Aplikasi mengirim data ke localhost atau IP node lokal (menghemat hop jaringan antar-pod). Tinggi. Setiap aplikasi harus menyeberangi layanan Kubernetes (cluster service IP) untuk mencapai collector.
Resource Utilization (CPU/RAM) Terdistribusi ke setiap node. Konsumsi memori per node relatif kecil dan dapat dibatasi dengan resource limits Kubernetes. Terpusat pada beberapa pod. Membutuhkan alokasi sumber daya yang jauh lebih besar per pod untuk menangani high throughput.
Reliability / Fault Tolerance Jika satu node bermasalah, hanya data dari node tersebut yang terdampak (isolasi kegagalan tingkat node). Titik kegagalan tunggal (Single Point of Failure / SPOF) jika tidak dikonfigurasi dengan replika (HPA).
Fitur Pemrosesan Terbatas pada pengayaan metadata node (karena akses ke Kubernetes API via Downward API atau node attributes). Ideal untuk pemrosesan berat seperti tail-based sampling, agregasi metrik tingkat lanjut, dan deduplikasi.

Rekomendasi Arsitek: Gunakan pendekatan Hibrida. Terapkan OTel Collector sebagai DaemonSet untuk menerima data dari aplikasi lokal (meminimalkan latensi jaringan dan overhead), lalu arahkan data dari DaemonSet tersebut ke Collector pusat berjenis Deployment yang bertugas melakukan tail-based sampling dan meneruskannya ke penyimpanan jangka panjang.

4. Prasyarat Sistem & Persiapan Lingkungan

Sebelum melangkah ke implementasi teknis, pastikan kluster Kubernetes dan infrastruktur pendukung Anda memenuhi spesifikasi berikut:

  • Kluster Kubernetes: Versi minimum v1.25+ dengan akses cluster-admin.
  • Package Manager: Helm v3 terpasang pada mesin manajemen.
  • CLI Tools: kubectl, kubectx, dan jq.
  • Infrastruktur Pendukung di Kluster:
    • Ingress Controller (misal: Nginx Ingress Controller yang mendukung W3C Trace Context).
    • Backend Observability: Grafana Tempo (untuk trace) dan Prometheus (untuk metrics).

Verifikasi akses kluster Anda dengan perintah berikut:

kubectl cluster-info
kubectl get nodes -o wide

Pastikan namespace khusus untuk observability telah dibuat:

kubectl create namespace observability

5. Panduan Implementasi Langkah demi Langkah

Langkah 1: Instalasi OpenTelemetry Operator & Collector

Kita akan menggunakan OpenTelemetry Operator berbasis Helm untuk mengelola siklus hidup Collector dan instrumentasi otomatis di dalam kluster.

Tambahkan repositori Helm OpenTelemetry:

helm repo add opentelemetry https://open-telemetry.github.io/opentelemetry-helm-charts
helm repo update

Install OpenTelemetry Operator di namespace observability:

helm install opentelemetry-operator opentelemetry/opentelemetry-operator \
  --namespace observability \
  --set manager.collectorImage.repository=otel/opentelemetry-collector-contrib

Langkah 2: Konfigurasi OpenTelemetry Collector (DaemonSet)

Buat file manifest otel-collector.yaml yang mendefinisikan bagaimana Collector menerima, memproses, dan mengekspor data telemetri.

apiVersion: opentelemetry.io/v1alpha1
kind: OpenTelemetryCollector
metadata:
  name: cluster-collector
  namespace: observability
spec:
  config: |
    receivers:
      otlp:
        protocols:
          grpc:
            endpoint: 0.0.0.0:4317
          http:
            endpoint: 0.0.0.0:4318
      prometheus:
        config:
          scrape_configs:
            - job_name: 'otel-collector'
              scrape_interval: 10s
              static_configs:
                - targets: ['0.0.0.0:8889']

    processors:
      memory_limiter:
        check_interval: 1s
        limit_percentage: 80
        spike_limit_percentage: 20
      batch:
        send_batch_size: 1024
        timeout: 5s
      resourcedetection:
        detectors: [env, kubernetes, system]
        timeout: 2s

    exporters:
      otlp/tempo:
        endpoint: "tempo-distributor.observability.svc.cluster.local:4317"
        tls:
          insecure: true
      prometheus:
        endpoint: "0.0.0.0:8889"

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

Terapkan manifest tersebut ke Kubernetes:

kubectl apply -f otel-collector.yaml -n observability

Langkah 3: Instrumentasi Aplikasi Backend (Go)

Berikut adalah contoh implementasi aplikasi Go backend yang menginisialisasi OpenTelemetry SDK, terhubung ke OTLP Collector lokal, serta melakukan instrumentasi manual pada kueri basis data MySQL dan cache Redis.

Buat file main.go:

package main

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

	_ "github.com/go-sql-driver/mysql"
	"github.com/redis/go-redis/v9"
	"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.21.0"
	trace "go.opentelemetry.io/otel/trace"
)

var tracer trace.Tracer

func initTracer(ctx context.Context) (*sdktrace.TracerProvider, error) {
	hostname, _ := os.Hostname()
	exporter, err := otlptracegrpc.New(ctx,
		otlptracegrpc.WithInsecure(),
		otlptracegrpc.WithEndpoint(os.Getenv("OTEL_EXPORTER_OTLP_ENDPOINT")),
	)
	if err != nil {
		return nil, fmt.Errorf("gagal membuat eksporter otlp: %w", err)
	}

	res, err := resource.New(ctx,
		resource.WithAttributes(
			semconv.ServiceNameKey.String("web-backend-service"),
			semconv.ServiceVersionKey.String("1.0.0"),
			semconv.HostNameKey.String(hostname),
		),
	)
	if err != nil {
		return nil, fmt.Errorf("gagal membuat resource otel: %w", err)
	}

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

	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 menginisialisasi tracer: %v", err)
	}
	defer func() {
		_ = tp.Shutdown(ctx)
	}()

	tracer = otel.GetTracerProvider().Tracer("web-backend-tracer")

	// Inisialisasi koneksi MySQL
	db, err := sql.Open("mysql", os.Getenv("MYSQL_DSN"))
	if err != nil {
		log.Fatalf("Gagal terhubung ke MySQL: %v", err)
	}
	defer db.Close()

	// Inisialisasi koneksi Redis
	rdb := redis.NewClient(&redis.Options{
		Addr: os.Getenv("REDIS_ADDR"),
	})
	defer rdb.Close()

	http.HandleFunc("/api/v1/resource", func(w http.ResponseWriter, r *http.Request) {
		// Ekstraksi konteks dari header HTTP yang dikirimkan oleh Ingress/upstream
		ctx := otel.GetTextMapPropagator().Extract(r.Context(), propagation.HeaderCarrier(r.Header))
		ctx, span := tracer.Start(ctx, "HTTP GET /api/v1/resource")
		defer span.End()

		// Simulasi operasi Redis Cache Check
		_, redisSpan := tracer.Start(ctx, "Redis GET session_cache")
		time.Sleep(15 * time.Millisecond)
		val, err := rdb.Get(ctx, "session_key").Result()
		redisSpan.End()

		if err != nil {
			// Simulasi kueri MySQL jika cache miss
			_, dbSpan := tracer.Start(ctx, "MySQL SELECT users")
			var name string
			_ = db.QueryRowContext(ctx, "SELECT name FROM users LIMIT 1").Scan(&name)
			dbSpan.End()
		}

		w.WriteHeader(http.StatusOK)
		_, _ = w.Write([]byte(fmt.Sprintf(`{"status":"success","data":"processed","cache":"%s\
🏷️ Tags: #Nginx #High Concurrency #Performance #Sysadmin #Web Server