A

Blog Post

Zero-Code Observability di Kubernetes dengan OpenTelemetry eBPF dan Collector

Panduan hands-on untuk menambahkan tracing dan metrics dasar di Kubernetes tanpa ubah kode aplikasi, memakai eBPF instrumentation dan OpenTelemetry Collector.

5 min read
Zero-Code Observability di Kubernetes dengan OpenTelemetry eBPF dan Collector

TL;DR

  • Zero-code observability cocok untuk baseline visibility yang cepat, terutama di cluster polyglot yang sulit disentuh semua tim aplikasi.
  • eBPF instrumentation memberi trace dan network insight tanpa rebuild image, tetapi tidak menggantikan custom metrics bisnis.
  • OpenTelemetry Collector tetap menjadi komponen inti karena di situlah sampling, filtering, dan routing telemetry sebaiknya dikendalikan.

Pendahuluan

Salah satu hambatan terbesar dalam observability modern adalah koordinasi lintas tim. Tim platform ingin trace menyala cepat, tetapi tim aplikasi belum siap menambahkan SDK, mengubah image, atau merilis ulang semua service. Di sinilah pendekatan zero-code berbasis eBPF menjadi menarik. Ia tidak menyelesaikan seluruh problem observability, tetapi sangat efektif untuk baseline visibility di cluster yang kompleks dan heterogen.

Masalahnya, banyak implementasi zero-code berhenti pada “trace sudah muncul”. Itu belum cukup. Tanpa Collector yang jelas, pipeline observability cepat menjadi mahal, noisy, atau tidak stabil. Operator juga sering lupa bahwa eBPF punya prasyarat kernel, overhead, dan keterbatasan pada traffic terenkripsi atau metadata bisnis yang memang tidak terlihat dari kernel boundary.

Tutorial ini membangun pipeline minimal yang production-minded: instrumentasi eBPF untuk baseline trace dan RED metrics, lalu OpenTelemetry Collector sebagai gateway pemrosesan sebelum data dikirim ke backend observability. Contohnya menggunakan cluster Linux, karena eBPF memang bergantung pada kernel capability yang tepat.

Problem di Production

Banyak cluster Kubernetes punya blind spot besar justru karena terlalu banyak layanan lama, bahasa campuran, atau release cycle yang lambat. Saat incident datang, operator melihat CPU dan memory, tetapi tidak tahu request path mana yang lambat atau dependency mana yang paling sering error. Menunggu semua tim mengadopsi SDK bisa memakan kuartal, sedangkan kebutuhan visibility ada sekarang.

Problem lain adalah biaya. Telemetry yang dikirim mentah dari semua service ke vendor backend sering cepat membengkak. Tanpa pipeline yang menyaring dan merutekan data, proyek observability berubah menjadi tagihan besar yang sulit dipertahankan.

Prerequisites

  • Cluster Kubernetes 1.29+ berbasis node Linux.
  • Kernel host mendukung eBPF modern. Kernel 5.x lebih aman untuk baseline deployment.
  • helm dan kubectl.
  • Namespace khusus observability, misalnya observability.
  • Endpoint backend OTLP, misalnya Grafana Tempo, Jaeger, atau vendor APM yang menerima OTLP.
  • Akses untuk memasang DaemonSet di node aplikasi.

Architecture / Context

Arsitektur yang digunakan terdiri dari dua lapisan. Lapisan pertama adalah eBPF-based instrumentation yang berjalan dekat node atau workload dan menghasilkan telemetry tanpa modifikasi kode. Lapisan kedua adalah OpenTelemetry Collector gateway yang menerima data itu, memberi attribute enrichment, melakukan batching, sampling, atau filtering, lalu meneruskannya ke backend pilihan.

Model ini cocok untuk fase akselerasi observability. Tim mendapatkan coverage yang cukup cepat, sementara adopsi manual SDK bisa dilakukan kemudian hanya pada jalur yang butuh business metric atau custom span.

Concept diagram

Step-by-Step Implementation

Step 1: Buat namespace observability

kubectl create namespace observability

Step 2: Tambahkan repo Helm yang dibutuhkan

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

Collector akan dipasang dari chart OpenTelemetry. Untuk eBPF instrumentation, contoh ini menggunakan Beyla sebagai agent zero-code yang sudah mengekspor data dengan OpenTelemetry pipeline.

Step 3: Pasang OpenTelemetry Collector sebagai gateway

Buat file values:

mode: deployment

config:
  receivers:
    otlp:
      protocols:
        grpc:
        http:

  processors:
    memory_limiter:
      check_interval: 2s
      limit_mib: 1024
    batch:
      send_batch_size: 2048
      timeout: 5s
    resource:
      attributes:
        - key: k8s.cluster.name
          value: prod-cluster-01
          action: upsert

  exporters:
    otlp:
      endpoint: tempo-distributor.observability.svc.cluster.local:4317
      tls:
        insecure: true
    logging:
      verbosity: normal

  service:
    pipelines:
      traces:
        receivers: [otlp]
        processors: [memory_limiter, resource, batch]
        exporters: [otlp, logging]
      metrics:
        receivers: [otlp]
        processors: [memory_limiter, resource, batch]
        exporters: [otlp]

resources:
  limits:
    cpu: 1000m
    memory: 2Gi
  requests:
    cpu: 250m
    memory: 512Mi

Install:

helm upgrade --install otel-gateway open-telemetry/opentelemetry-collector \
  -n observability \
  -f otel-gateway-values.yaml

Collector gateway adalah titik kendali paling penting. Kalau tim butuh filter noisy endpoint, sampling, atau enrich cluster metadata, lakukan di sini.

Step 4: Pasang Beyla sebagai DaemonSet

apiVersion: v1
kind: ConfigMap
metadata:
  name: beyla-config
  namespace: observability
data:
  beyla-config.yml: |
    attributes:
      kubernetes:
        enable: true
    routes:
      unmatched: heuristic
    otel_metrics_export:
      endpoint: http://otel-gateway-opentelemetry-collector.observability.svc.cluster.local:4318
    otel_traces_export:
      endpoint: http://otel-gateway-opentelemetry-collector.observability.svc.cluster.local:4318
    discovery:
      services:
        - namespace: payments
        - namespace: checkout
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: beyla
  namespace: observability
spec:
  selector:
    matchLabels:
      app: beyla
  template:
    metadata:
      labels:
        app: beyla
    spec:
      serviceAccountName: default
      hostPID: true
      containers:
        - name: beyla
          image: grafana/beyla:2.4.0
          securityContext:
            privileged: true
            capabilities:
              add: ["BPF", "SYS_ADMIN", "NET_ADMIN", "PERFMON"]
          env:
            - name: BEYLA_CONFIG_PATH
              value: /config/beyla-config.yml
          volumeMounts:
            - name: config
              mountPath: /config
      volumes:
        - name: config
          configMap:
            name: beyla-config

Apply:

kubectl apply -f beyla.yaml

Privilege di sini adalah trade-off yang harus dipahami. eBPF observability butuh kemampuan kernel-level; karena itu deployment semacam ini layak berada di namespace platform yang dikontrol ketat.

Step 5: Label namespace yang ingin dipantau

kubectl label namespace payments observability=enabled --overwrite
kubectl label namespace checkout observability=enabled --overwrite

Jika tim ingin coverage lebih sempit dulu, mulai dari service yang paling sering incident.

Step 6: Tambahkan filter di Collector untuk noisy path

Jika health check dan metrics endpoint terlalu banyak, filter di Collector:

processors:
  filter:
    traces:
      span:
        - 'attributes["http.target"] == "/health"'
        - 'attributes["http.target"] == "/metrics"'

Lalu masukkan ke pipeline:

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [memory_limiter, filter, resource, batch]
      exporters: [otlp, logging]

Step 7: Tambahkan backend quick check jika belum ada

Untuk verifikasi lokal, misalnya pakai Jaeger all-in-one:

helm upgrade --install jaeger jaegertracing/jaeger \
  -n observability \
  --set provisionDataStore.cassandra=false \
  --set allInOne.enabled=true

Jika sudah punya Tempo atau vendor APM, arahkan OTLP exporter langsung ke sana.

Architecture flow

Verification

Cek pod observability:

kubectl -n observability get pods

Cek log Collector:

kubectl -n observability logs deploy/otel-gateway-opentelemetry-collector

Cek log Beyla:

kubectl -n observability logs ds/beyla

Uji traffic ke salah satu service yang dipantau, lalu pastikan trace masuk:

kubectl -n payments port-forward svc/payments-api 8080:80
curl http://127.0.0.1:8080/health
curl http://127.0.0.1:8080/api/checkout

Hasil yang sehat:

  • Collector menerima OTLP traffic tanpa drop besar
  • Beyla mendeteksi process atau workload target
  • trace dan metric dasar seperti request rate, error, dan latency terlihat di backend

Troubleshooting

Jika Beyla tidak jalan, cek capability kernel dan security context:

kubectl -n observability describe pod -l app=beyla
kubectl -n observability logs ds/beyla | tail -n 50

Jika Collector sehat tetapi trace kosong, cek endpoint exporter dan port OTLP:

kubectl -n observability get svc
kubectl -n observability exec deploy/otel-gateway-opentelemetry-collector -- netstat -lnt

Jika beberapa service tidak terdeteksi, kemungkinan process discovery tidak cocok, kernel terlalu lama, atau traffic terenkripsi di boundary yang tidak bisa di-inspect seperti yang dibayangkan. Dalam kasus ini, gabungkan dengan manual SDK untuk service kritis.

Production Notes

Zero-code observability sebaiknya dipakai sebagai baseline visibility, bukan pengganti total instrumentation. Untuk service dengan business-critical tracing, tambahkan manual span agar context bisnis tetap ada. Pertimbangkan sampling adaptif di Collector agar biaya tidak melonjak. Pantau overhead CPU pada node yang menjalankan eBPF agent, terutama di cluster dengan network churn tinggi.

Pisahkan ownership pipeline. Tim platform mengelola agent dan Collector, sedangkan tim aplikasi bertanggung jawab menambahkan telemetry kustom di jalur yang benar-benar membutuhkan detail domain.

Trade-offs

eBPF memberi time-to-value yang cepat, tetapi tidak selalu bisa melihat semua detail aplikasi. Collector gateway memberi fleksibilitas tinggi, tetapi menjadi dependency operasional baru. Menaruh instrumentation di kernel boundary menurunkan kebutuhan modifikasi kode, tetapi meningkatkan kebutuhan review pada node privilege dan compat matrix kernel.

Failure Modes

  • Agent eBPF tidak kompatibel dengan kernel atau security profile node.
  • Telemetry terlalu noisy sehingga Collector overload atau biaya backend naik drastis.
  • Tim mengira zero-code sudah cukup dan tidak pernah menambahkan business telemetry yang sebenarnya diperlukan.
  • Filter yang terlalu agresif membuang trace yang justru dibutuhkan saat incident.

Best Practices

  • Mulai dari coverage terbatas pada namespace paling kritis.
  • Gunakan Collector sebagai tempat sentral untuk filter, enrich, dan sampling.
  • Pantau overhead agent pada node, bukan hanya health pod.
  • Dokumentasikan service mana yang hanya memakai zero-code dan mana yang sudah diberi manual instrumentation.
  • Gunakan naming, resource attribute, dan cluster metadata yang konsisten.

Kesalahan Umum

  • memasang eBPF agent di semua node tanpa uji overhead
  • mengirim semua trace mentah ke backend tanpa filter
  • menganggap zero-code dapat menggantikan seluruh observability strategy
  • tidak memeriksa kecocokan kernel dan capability

Kesimpulan

OpenTelemetry eBPF instrumentation memberi jalur cepat untuk menutup blind spot Kubernetes tanpa menunggu semua tim aplikasi merilis ulang image mereka. Nilai sebenarnya baru terasa ketika data itu diarahkan melalui Collector yang disiplin, sehingga visibilitas meningkat tanpa mengorbankan kontrol biaya dan stabilitas pipeline observability.

Kalau artikel ini membantu, kamu bisa support eksperimen berikutnya.

Apresiasi di Trakteer

Keep Reading

Related posts