Kubernetes Üzerinde Jaeger ile Dağıtık İzleme (Distributed Tracing) Nasıl Uygulanır?
Modern mikroservis mimarileri, uygulamaların geliştirilme ve dağıtılma şekillerinde devrim yaratmıştır. Ancak bu mimariler, sistemlerin karmaşıklığını artırarak hata ayıklama ve performans sorunlarını tespit etme süreçlerini zorlaştırmaktadır. Tek bir kullanıcı isteğinin birden fazla servise yayıldığı, asenkron iletişim modellerinin ve mesaj kuyruklarının kullanıldığı ortamlarda, bir isteğin yaşam döngüsünü baştan sona takip etmek geleneksel günlük (logging) mekanizmalarıyla neredeyse imkansız hale gelir. İşte tam bu noktada dağıtık izleme (distributed tracing) devreye girer.
Dağıtık izleme, bir isteğin sistemdeki tüm servisler arasında nasıl hareket ettiğini görselleştirmemizi sağlayan güçlü bir tekniktir. Bu makalede, açık kaynaklı ve popüler bir dağıtık izleme aracı olan Jaeger’ı Kubernetes ortamında nasıl uygulayacağımızı ve yöneteceğimizi ayrıntılı bir şekilde inceleyeceğiz. Kubernetes’in sağladığı orkestrasyon yetenekleriyle Jaeger’ı birleştirerek, dinamik ve ölçeklenebilir mikroservis uygulamalarınızın derinlemesine görünürlüğünü nasıl elde edeceğinizi adım adım açıklayacağız.
Dağıtık İzleme Nedir ve Neden Gereklidir?
Dağıtık izleme, bir uygulamanın veya sistemin farklı bileşenleri arasında yayılan tek bir işlemin (örneğin, bir API çağrısı veya bir kullanıcı isteği) uçtan uca yolculuğunu takip etme yöntemidir. Her bir işlem, “izleme (trace)” adı verilen bir dizi “yayılım (span)” olarak temsil edilir. Her yayılım, bir serviste veya bileşende gerçekleşen belirli bir işlemi (örneğin, bir veritabanı sorgusu, bir HTTP isteği) temsil eder ve kendi başlangıç zamanı, süresi, etiketleri ve günlükleri gibi bilgilere sahiptir. Yayılımlar birbiriyle ilişkili olduğunda, bir isteğin sistem içindeki tüm yolculuğunu gösteren bir ağaç yapısı oluştururlar.
Monolitik ve Mikroservis Mimarilerinde İzleme Zorlukları
Monolitik uygulamalarda, tüm kod tabanı tek bir süreç içinde çalıştığından, bir hata veya performans sorunu genellikle aynı günlük dosyası veya hata ayıklayıcı içinde kolayca tespit edilebilir. Ancak mikroservis mimarilerinde durum tamamen farklıdır:
* İstek Akışı Karmaşıklığı: Bir kullanıcı isteği, birden fazla servisi, veritabanını, mesaj kuyruğunu ve önbelleği içerebilir. Her servis kendi günlüklerini tutar ve bu günlükleri birleştirmek zordur.
* Gecikme Tespiti: Hangi servisin veya işlemin genel isteğin yavaşlamasına neden olduğunu belirlemek zordur.
* Hata Ayıklama: Bir hata oluştuğunda, hatanın kaynağını (hangi serviste, hangi çağrıda) bulmak, izleme olmadan bir “iğne aramak” gibidir.
* Bağımlılık Analizi: Servisler arası bağımlılıkları ve bunların performans üzerindeki etkilerini anlamak için kapsamlı bir görünürlük gerekir.
Dağıtık izleme, bu zorlukları aşmak için tasarlanmıştır. Her isteğe benzersiz bir izleme kimliği (trace ID) atanır ve bu kimlik, isteğin geçtiği tüm servisler arasında yayılır (context propagation). Her servis, bu kimliği kullanarak kendi yayılımlarını oluşturur ve bunları bir izleme sistemine gönderir. İzleme sistemi bu yayılımları birleştirerek isteğin tam yolculuğunu ve her adımda ne kadar zaman harcandığını görselleştirir. Bu sayede geliştiriciler ve operasyon ekipleri, performans darboğazlarını, hata kaynaklarını ve servisler arası etkileşimleri hızlıca anlayabilirler.
Jaeger’ın Temel Bileşenleri
Jaeger, Uber tarafından geliştirilen ve daha sonra Cloud Native Computing Foundation (CNCF) bünyesine alınan açık kaynaklı bir dağıtık izleme sistemidir. Temel bileşenleri şunlardır:
Jaeger Client Libraries (İstemci Kütüphaneleri)
Uygulamalarınıza izleme yeteneği kazandırmak için kullanılan dile özel kütüphanelerdir (örneğin, Java için jaeger-client-java, Python için jaeger-client-python). Bu kütüphaneler, OpenTracing veya OpenTelemetry API’lerini kullanarak yayılımlar oluşturur, bağlamı yayar ve bu yayılımları Jaeger Agent’a gönderir. Geliştiricilerin kodlarına manuel olarak veya otomatik enstrümantasyon araçları (örneğin, OpenTelemetry Auto-Instrumentation) aracılığıyla entegre edilirler.
Jaeger Agent
İstemci uygulamalarından UDP üzerinden yayılımları alan bir ağ demonudur. Agent’ın temel görevleri şunlardır:
* Yayılım Toplama: Uygulamalardan yayılımları alır.
* Yayılım Toplulaştırma ve Batching: Birden fazla yayılımı bir araya getirerek ağ trafiğini optimize eder.
* Yayılım Yönlendirme: Topladığı yayılımları Jaeger Collector’a gönderir.
* Örnekleme (Sampling) Yapılandırması: İstemci kütüphanelerine hangi yayılımların gönderileceğini belirleyen örnekleme stratejilerini dinamik olarak sağlayabilir.
Kubernetes ortamında Jaeger Agent genellikle her uygulama pod’unun yanında bir sidecar kapsayıcı olarak veya her düğümde bir DaemonSet olarak çalıştırılır. Sidecar yaklaşımı, ağ gecikmesini azaltır ve uygulama ile ajan arasındaki iletişimi basitleştirir.
Jaeger Collector
Jaeger Agent’lardan veya doğrudan istemci kütüphanelerinden (HTTP/Thrift) yayılımları alır. Collector’ın görevleri şunlardır:
* Yayılım Doğrulama: Gelen yayılımların geçerliliğini kontrol eder.
* Yayılım İşleme: Yayılımları işler ve gerekirse dönüştürür.
* Depolama Katmanına Yazma: İşlenen yayılımları kalıcı depolama birimine (örneğin, Elasticsearch, Cassandra) yazar.
Collector, genellikle birden fazla örneği çalıştırılarak yatay olarak ölçeklenebilir.
Storage Backend (Depolama Katmanı)
Jaeger Collector’ın topladığı tüm izleme verilerinin kalıcı olarak saklandığı yerdir. Jaeger çeşitli depolama arka uçlarını destekler:
* Elasticsearch: En popüler seçenektir. Güçlü arama ve indeksleme yetenekleri sayesinde izleme verilerinin kolayca sorgulanmasını sağlar.
* Cassandra: Yüksek yazma performansı ve ölçeklenebilirlik sunar.
* Kafka: Bir arabellek olarak kullanılabilir. Collector’lar yayılımları önce Kafka’ya yazabilir, ardından başka bir bileşen (Kafka-to-Storage) bu verileri kalıcı depolamaya aktarabilir. Bu, Collector’ların yükünü azaltır ve veri kaybı riskini düşürür.
* In-Memory: Geliştirme ve test ortamları için uygundur, ancak verileri kalıcı olarak saklamaz.
Jaeger Query
Depolama katmanında saklanan izleme verilerini sorgulamak için bir API sağlar. Jaeger UI bu API’yi kullanarak izleme verilerini alır ve görselleştirir.
Jaeger UI (Kullanıcı Arayüzü)
İzleme verilerini görselleştirmek, aramak ve analiz etmek için kullanılan web tabanlı bir arayüzdür. Geliştiricilerin belirli bir hizmeti, işlemi veya izleme kimliğini kullanarak izlemeleri bulmasına, bir isteğin uçtan uca yolculuğunu bir şelale grafiği olarak görmesine ve performans darboğazlarını veya hataları kolayca tespit etmesine olanak tanır.
Kubernetes Üzerinde Jaeger Kurulumu
Kubernetes üzerinde Jaeger kurmanın en yaygın ve önerilen yolu Helm kullanmaktır. Helm, Kubernetes uygulamalarını yönetmek için bir paket yöneticisidir.
Helm ile Kurulum
Öncelikle Helm’in sisteminizde kurulu olduğundan emin olun. Ardından Jaeger Helm deposunu ekleyin:
helm repo add jaegertracing https://jaegertracing.github.io/helm-charts
helm repo update
Şimdi Jaeger’ı kurabiliriz. İki ana kurulum stratejisi vardır: “all-in-one” (hepsi bir arada) ve üretim ortamı için bileşenlerin ayrı ayrı dağıtıldığı daha ölçeklenebilir bir yapı.
Geliştirme/Test Ortamı İçin: All-in-One Kurulum
Bu kurulum, tüm Jaeger bileşenlerini (Agent, Collector, Query, UI ve in-memory depolama) tek bir pod içinde çalıştırır. Geliştirme ve test ortamları için hızlı ve kolay bir başlangıç sağlar, ancak üretim ortamları için önerilmez.
helm install jaeger jaegertracing/jaeger \
--namespace tracing \
--create-namespace \
--set allInOne.enabled=true \
--set agent.enabled=false \
--set collector.enabled=false \
--set query.enabled=false \
--set storage.type=memory
Bu komut, tracing ad alanına jaeger adıyla bir Jaeger all-in-one örneği kurar. agent.enabled=false, collector.enabled=false, query.enabled=false ayarları, all-in-one pod’unun kendi içindeki bu bileşenleri kullanmasını sağlar ve ayrı bileşenlerin dağıtılmasını engeller.
Üretim Ortamı İçin: Ölçeklenebilir Kurulum (Elasticsearch ile)
Üretim ortamlarında, Jaeger bileşenlerinin ayrı ayrı dağıtılması ve kalıcı bir depolama arka ucu (genellikle Elasticsearch) kullanılması önerilir. Bu, her bileşenin bağımsız olarak ölçeklenmesine ve daha yüksek performans ile güvenilirlik elde edilmesine olanak tanır.
Öncelikle bir Elasticsearch kümesi kurmanız gerekecektir. Bunu da Helm ile yapabilirsiniz (örneğin, Elastic’in kendi Helm grafikleri veya Bitnami gibi üçüncü taraf grafikler). Basit bir örnek olarak:
# Elasticsearch Helm deposunu ekle
helm repo add elastic https://helm.elastic.co
helm repo update
Elasticsearch'i kur (küçük bir test kurulumu için)
helm install elasticsearch elastic/elasticsearch \
--namespace tracing \
--create-namespace \
--set replicas=1 \
--set minimumMasterNodes=1 \
--set clusterName=jaeger-elasticsearch \
--set esJavaOpts="-Xmx512m -Xms512m" \
--set volumeClaimTemplate.storageClassName=standard \
--set volumeClaimTemplate.resources.requests.storage=10Gi
Elasticsearch kurulduktan sonra, Jaeger’ı bu Elasticsearch kümesini kullanacak şekilde yapılandırabiliriz.
helm install jaeger jaegertracing/jaeger \
--namespace tracing \
--create-namespace \
--set strategy=production \
--set agent.strategy=DaemonSet \
--set collector.replicas=2 \
--set query.replicas=1 \
--set storage.type=elasticsearch \
--set storage.elasticsearch.host=elasticsearch-master.tracing.svc.cluster.local \
--set storage.elasticsearch.port=9200 \
--set storage.elasticsearch.username="" \
--set storage.elasticsearch.password="" \
--set storage.elasticsearch.indexPrefix=jaeger \
--set storage.elasticsearch.tagKeysAsFields=true \
--set storage.elasticsearch.maxSpanAge=72h # 3 gün sonra sil
Bu komutta:
* strategy=production: Jaeger’ın üretim ortamı için optimize edilmiş bir dağıtım stratejisi kullanmasını sağlar.
* agent.strategy=DaemonSet: Her Kubernetes düğümünde bir Jaeger Agent pod’u çalıştırır. Bu, uygulamaların Agent’a ulaşmasını kolaylaştırır. Alternatif olarak, agent.sidecar=true ile her uygulama pod’una sidecar olarak da eklenebilir.
* collector.replicas=2: İki adet Jaeger Collector pod’u çalıştırır. Bu, gelen yayılım yükünü dengelemek için ölçeklenebilirliği artırır.
* storage.type=elasticsearch: Depolama arka ucu olarak Elasticsearch’i kullanır.
* storage.elasticsearch.host ve port: Elasticsearch servisinin adresini belirtir. elasticsearch-master.tracing.svc.cluster.local varsayılan Helm kurulumuna göre Elasticsearch servis adıdır.
* storage.elasticsearch.maxSpanAge: İzleme verilerinin ne kadar süreyle saklanacağını belirler (örneğin, 72 saat = 3 gün).
Kurulumdan sonra, Jaeger UI’ya erişmek için bir port-forward yapabilirsiniz:
kubectl port-forward svc/jaeger-query 16686:16686 -n tracing
Şimdi tarayıcınızdan http://localhost:16686 adresine giderek Jaeger UI’ya erişebilirsiniz.
Uygulamaların İzleme Entegrasyonu
Jaeger’ı Kubernetes’e kurduktan sonra, uygulamalarınızın izleme verilerini göndermesini sağlamanız gerekir. Bu süreç, uygulamanızın koduna izleme istemci kütüphanelerini entegre etmeyi içerir. Günümüzde bu entegrasyon için en yaygın yaklaşımlar OpenTracing ve onun halefi olan OpenTelemetry’dir.
Tracing Client Kütüphaneleri ve OpenTelemetry
OpenTracing, dağıtık izleme için satıcıdan bağımsız bir API ve enstrümantasyon standardı sağlayan bir CNCF projesiydi. OpenTelemetry ise OpenTracing ve OpenCensus projelerinin birleşimiyle ortaya çıkan, izleme, metrikler ve günlükler için birleşik bir telemetri toplama standardıdır. Yeni projeler için OpenTelemetry kullanılması şiddetle tavsiye edilir, çünkü geleceğe dönük ve daha kapsamlı bir çözümdür. Jaeger, OpenTelemetry tarafından üretilen izleme verilerini doğrudan kabul edebilir.
Uygulamanıza izleme entegrasyonu için genel adımlar şunlardır:
1. OpenTelemetry SDK ve Jaeger Exporter Ekleme: Uygulamanızın kullandığı programlama diline uygun OpenTelemetry SDK’sını ve Jaeger exporter’ını bağımlılık olarak ekleyin.
* Python Örneği:
pip install opentelemetry-api opentelemetry-sdk opentelemetry-exporter-jaeger
* Java Örneği (Maven):
io.opentelemetry
opentelemetry-api
1.x.x
io.opentelemetry
opentelemetry-sdk
1.x.x
io.opentelemetry
opentelemetry-exporter-jaeger
1.x.x
2. Tracer’ı Başlatma ve Yapılandırma: Uygulamanızın başlangıcında bir Tracer örneği oluşturun ve onu Jaeger Agent’a veya Collector’a gönderecek şekilde yapılandırın. Genellikle, Jaeger Agent’ın varsayılan UDP portu olan 6831‘e gönderim yapılır.
* Python Örneği (Basit):
from opentelemetry import trace
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import ConsoleSpanExporter, SimpleSpanProcessor
from opentelemetry.exporter.jaeger.proto.thrift import JaegerExporter
# Uygulama adını belirtin
resource = Resource.create({"service.name": "my-python-service"})
# TracerProvider'ı oluştur
trace.set_tracer_provider(TracerProvider(resource=resource))
# Jaeger Exporter'ı yapılandır (Agent'a göndermek için)
jaeger_exporter = JaegerExporter(
agent_host_name="jaeger-agent.tracing.svc.cluster.local", # Kubernetes servis adı
agent_port=6831,
)
# Yayılımları işlemek için SimpleSpanProcessor kullan
trace.get_tracer_provider().add_span_processor(
SimpleSpanProcessor(jaeger_exporter)
)
# Tracer'ı al
tracer = trace.get_tracer(__name__)
# Örnek bir işlem izleme
with tracer.start_as_current_span("my-operation"):
print("Doing some work...")
with tracer.start_as_current_span("sub-operation"):
print("Doing sub-work...")
* Java Örneği (Basit):
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.sdk.trace.SdkTracerProvider;
import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor;
import io.opentelemetry.exporter.jaeger.JaegerGrpcSpanExporter;
import static io.opentelemetry.semconv.resource.attributes.ResourceAttributes.SERVICE_NAME;
public class MyApp {
private static final String SERVICE_NAME_VALUE = "my-java-service";
public static void main(String[] args) {
// Kaynakları tanımla (servis adı)
Resource serviceNameResource = Resource.getDefault().toBuilder()
.put(SERVICE_NAME, SERVICE_NAME_VALUE)
.build();
// Jaeger Exporter'ı yapılandır (Collector'a göndermek için gRPC kullanabilir)
// Eğer Agent'a gönderecekseniz Thrift over UDP kullanmanız gerekir.
JaegerGrpcSpanExporter jaegerExporter = JaegerGrpcSpanExporter.builder()
.setEndpoint("http://jaeger-collector.tracing.svc.cluster.local:14250") // GRPC port
.build();
// TracerProvider'ı oluştur
SdkTracerProvider tracerProvider = SdkTracerProvider.builder()
.setResource(serviceNameResource)
.addSpanProcessor(SimpleSpanProcessor.create(jaegerExporter))
.build();
OpenTelemetry openTelemetry = OpenTelemetrySdk.builder()
.setTracerProvider(tracerProvider)
.buildAndRegisterGlobal();
Tracer tracer = openTelemetry.getTracer(MyApp.class.getName());
// Örnek bir işlem izleme
Span parentSpan = tracer.spanBuilder("my-parent-operation").startSpan();
try (io.opentelemetry.api.trace.Scope scope = parentSpan.makeCurrent()) {
System.out.println("Doing some parent work...");
Span childSpan = tracer.spanBuilder("my-child-operation").startSpan();
try (io.opentelemetry.api.trace.Scope childScope = childSpan.makeCurrent()) {
System.out.println("Doing some child work...");
} finally {
childSpan.end();
}
} finally {
parentSpan.end();
}
// Uygulama kapanırken exporter'ı kapat
tracerProvider.close();
}
}
3. İstekler Arası Bağlam Yayılımı (Context Propagation): Mikroservisler arasında bir istek geçerken, izleme bağlamının (trace ID, span ID) HTTP başlıkları, gRPC meta verileri veya mesaj kuyruğu özellikleri aracılığıyla yayılması çok önemlidir. OpenTelemetry kütüphaneleri genellikle popüler web çerçeveleri ve RPC kütüphaneleri için otomatik enstrümantasyon sağlar. Bu, manuel kod yazmadan bağlam yayılımını otomatik olarak halleder. Eğer otomatik enstrümantasyon mümkün değilse, W3C Trace Context veya B3 başlıklarını manuel olarak eklemeniz gerekebilir.
Sidecar Enjeksiyonu ile Jaeger Agent
Kubernetes’te Jaeger Agent’ı uygulamalarınızla birlikte çalıştırmanın en iyi yolu, her uygulama pod’una bir sidecar kapsayıcı olarak eklemektir. Bu, uygulamanın Agent’a localhost üzerinden ulaşmasını sağlar ve ağ yapılandırmasını basitleştirir.
Bunu manuel olarak yapabileceğiniz gibi, Jaeger Operator veya bir mutating admission webhook kullanarak otomatik olarak da yapabilirsiniz.
Manuel Sidecar Ekleme Örneği (Deployment YAML):
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-service
namespace: default
spec:
selector:
matchLabels:
app: my-service
template:
metadata:
labels:
app: my-service
spec:
containers:
- name: my-service-app
image: my-docker-repo/my-service:latest
ports:
- containerPort: 8080
env:
- name: JAEGER_AGENT_HOST
valueFrom:
fieldRef:
fieldPath: status.hostIP # DaemonSet kullanılıyorsa düğüm IP'sini al
- name: JAEGER_AGENT_PORT
value: "6831"
# ... diğer uygulama yapılandırmaları ...
- name: jaeger-agent
image: jaegertracing/jaeger-agent:latest
command: ["/usr/bin/agent", "--collector.host-port=jaeger-collector.tracing.svc.cluster.local:14268"]
ports:
- containerPort: 6831 # UDP Thrift
protocol: UDP
- containerPort: 6832 # UDP Binary
protocol: UDP
- containerPort: 5778 # HTTP Configuration
resources:
limits:
memory: "128Mi"
cpu: "100m"
requests:
memory: "64Mi"
cpu: "50m"
Not: Eğer DaemonSet olarak kurduğunuz Jaeger Agent’ı kullanacaksanız, JAEGER_AGENT_HOST ortam değişkenini pod’un çalıştığı düğümün IP’sine ayarlamanız gerekir. Ancak sidecar olarak Agent’ı doğrudan pod’un içine eklemek, uygulamanın localhost üzerinden Agent’a ulaşmasını sağlayacağı için JAEGER_AGENT_HOST‘u localhost olarak ayarlamak daha basit ve güvenilirdir.
# Sidecar olarak Jaeger Agent
...
- name: my-service-app
image: my-docker-repo/my-service:latest
ports:
- containerPort: 8080
env:
- name: JAEGER_AGENT_HOST
value: "localhost" # Sidecar Agent'a localhost üzerinden erişim
- name: JAEGER_AGENT_PORT
value: "6831"
# ...
- name: jaeger-agent
image: jaegertracing/jaeger-agent:latest
# Sidecar Agent'lar genellikle Collector'a doğrudan gönderecek şekilde yapılandırılır.
command: ["/usr/bin/agent", "--collector.host-port=jaeger-collector.tracing.svc.cluster.local:14268"]
ports:
- containerPort: 6831 # UDP Thrift
protocol: UDP
# ...
Bu yapılandırma, uygulamanızın izleme verilerini localhost:6831 adresindeki Jaeger Agent sidecar’ına göndermesini sağlar. Sidecar Agent da bu verileri Kubernetes kümesindeki Jaeger Collector’a iletir.
İzleme Verilerinin Görselleştirilmesi ve Analizi
Uygulamalarınız izleme verilerini göndermeye başladıktan sonra, Jaeger UI’yı kullanarak bu verileri görselleştirebilir ve analiz edebilirsiniz.
Jaeger UI Kullanımı
Jaeger UI’ya eriştiğinizde (genellikle http://localhost:16686 üzerinden port-forward ile), sol üstte bir “Service” seçici ve çeşitli filtre seçenekleri (operation, tags, min/max duration, limit) bulunur.
1. Servis Seçimi: İzlemek istediğiniz servisi “Service” açılır menüsünden seçin.
2. Operasyon Seçimi: İsteğe bağlı olarak, belirli bir operasyonu (örneğin, /users/{id} GET isteği) seçebilirsiniz.
3. Filtreler: Süre, etiketler (örneğin, http.status_code=500 veya error=true) gibi ek filtreler uygulayarak aramanızı daraltabilirsiniz.
4. İzlemeleri Bul: “Find Traces” düğmesine tıklayın.
Arama sonuçları, her bir izlemenin özetini (hizmet adı, operasyon, süre, başlangıç zamanı) listeleyecektir. Bir izlemeye tıkladığınızda, o izlemenin detaylı görünümüne yönlendirilirsiniz.
Trace Waterfall View (Şelale Görünümü): Bu görünüm, bir isteğin sisteminizdeki tüm servisler ve işlemler arasında nasıl yayıldığını zaman çizelgesi üzerinde görselleştirir. Her bir çubuk bir yayılımı temsil eder ve rengi hizmeti belirtir. Bu sayede:
* Performans Darboğazları: Hangi yayılımın veya servisin en uzun sürdüğünü kolayca görebilirsiniz. Uzun süren bir veritabanı sorgusu veya harici bir API çağrısı hemen dikkat çekecektir.
* Hata Tespiti: Hatalı yayılımlar genellikle kırmızı renkle veya bir hata etiketiyle işaretlenir. Hatanın tam olarak nerede meydana geldiğini ve hangi servisleri etkilediğini hızla belirleyebilirsiniz.
* Servis Bağımlılıkları: Hangi servislerin birbirini çağırdığını ve bu çağrıların sırasını net bir şekilde anlayabilirsiniz.
Her yayılıma tıklayarak o yayılımın detaylarını (etiketler, günlükler, süreç bilgileri) görüntüleyebilirsiniz. Bu bilgiler, hata ayıklama sırasında çok değerli olabilir.
Dependency Graph (Bağımlılık Grafiği): Jaeger UI ayrıca servisler arası bağımlılıkları gösteren bir grafik de sunar. Bu grafik, uygulamanızın mimarisini anlamak ve potansiyel tek hata noktalarını veya aşırı bağımlılıkları tespit etmek için faydalıdır.
Gelişmiş Konular ve En İyi Uygulamalar
Dağıtık izlemeyi üretim ortamında etkin bir şekilde kullanmak için bazı gelişmiş konuları ve en iyi uygulamaları göz önünde bulundurmak önemlidir.
Örnekleme Stratejileri (Sampling Strategies)
Yüksek trafikli sistemlerde her isteği izlemek, hem performans hem de depolama maliyetleri açısından sürdürülemez olabilir. Bu nedenle örnekleme (sampling) stratejileri kullanılır. Jaeger, çeşitli örnekleme stratejilerini destekler:
* Probabilistic Sampling (Olasılıksal Örnekleme): Belirli bir olasılıkla (örneğin, %0.1) izlemelerin bir kısmını yakalar. Yüksek hacimli trafik için iyi bir denge sağlar.
* Constant Sampling (Sabit Örnekleme): Her zaman tüm izlemeleri yakalar veya hiçbirini yakalamaz. Genellikle geliştirme/test ortamlarında veya kritik servisler için kullanılır.
* Rate Limiting Sampling (Oran Sınırlamalı Örnekleme): Belirli bir zaman diliminde maksimum sayıda izlemeyi yakalar. Bu, sistemin aşırı yüklenmesini önlerken yeterli veri toplamanıza olanak tanır.
Örnekleme stratejileri genellikle Jaeger Agent veya Collector üzerinden yapılandırılır ve istemci kütüphanelerine iletilir. Üretim ortamlarında, genellikle olasılıksal örnekleme ile başlanır ve kritik hata durumları veya belirli kullanıcılar için daha yüksek örnekleme oranları uygulanır.
Bağlam Yayılımı (Context Propagation)
Doğru bağlam yayılımı, dağıtık izlemenin kalbidir. Bir istek bir servisten diğerine geçerken, izleme kimliği (trace ID) ve yayılım kimliği (span ID) gibi bilgiler HTTP başlıkları (örneğin, traceparent, tracestate, x-b3-traceid, x-b3-spanid) veya gRPC meta verileri aracılığıyla taşınmalıdır. OpenTelemetry kütüphaneleri, yaygın protokoller için bu yayılımı otomatik olarak yönetir. Ancak manuel olarak yazdığınız veya özel protokoller kullanan kodlarda, bu başlıkları kendiniz eklemeniz ve ayrıştırmanız gerekebilir. Tutarlı bir bağlam yayılımı olmadan, izlemeler parçalı görünür ve uçtan uca görünürlük sağlanamaz.
Performans ve Ölçeklenebilirlik
Kubernetes üzerinde Jaeger’ı ölçeklendirirken dikkat edilmesi gerekenler:
* Jaeger Collector: Gelen yayılım trafiğini işlemek için birden fazla Collector pod’u çalıştırın. Bir yük dengeleyici (Load Balancer) veya Kubernetes Service, trafiği bu pod’lar arasında dağıtabilir.
* Storage Backend: Elasticsearch veya Cassandra gibi depolama arka uçlarının da kendi başına ölçeklenebilir ve performanslı olması gerekir. Disk IOPS, CPU ve bellek kaynakları izleme verilerinin hacmine göre ayarlanmalıdır.
* Kaynak Ataması: Jaeger bileşenleri için Kubernetes kaynak istekleri (requests) ve limitleri (limits) doğru ayarlanmalıdır. Özellikle Collector ve Query bileşenleri, yüksek CPU ve bellek tüketimine sahip olabilir.
* Ağ Yapılandırması: Uygulamalar ve Jaeger Agent’lar arasındaki ağ gecikmesini en aza indirmek için sidecar yaklaşımı tercih edilmelidir. Agent’lar ve Collector’lar arasındaki ağ trafiği de optimize edilmelidir.
Güvenlik
Üretim ortamında Jaeger’ı güvence altına almak önemlidir:
* Erişim Kontrolü: Jaeger UI’ya erişimi kısıtlayın. Bir Ingress Controller ve kimlik doğrulama/yetkilendirme (örneğin, OAuth2 Proxy) kullanarak UI’yı koruyun.
* Ağ Politikaları: Kubernetes ağ politikaları (Network Policies) kullanarak Jaeger bileşenleri arasındaki iletişimi kısıtlayın. Yalnızca gerekli portların ve kaynakların erişimine izin verin.
* TLS/SSL: İstemci uygulamaları ile Collector’lar ve Collector’lar ile depolama arka uçları arasındaki iletişimi TLS/SSL ile şifreleyin.
* Kimlik Bilgileri: Depolama arka uçlarına erişmek için kullanılan kimlik bilgilerini (örneğin, Elasticsearch kullanıcı adı/parolası) Kubernetes Secret’larda saklayın ve bunları Collector pod’larına güvenli bir şekilde enjekte edin.
OpenTelemetry ile Gelecek
OpenTelemetry, izleme, metrikler ve günlükler için birleşik bir standart sunarak telemetri alanında geleceğin çözümüdür. Jaeger, OpenTelemetry Collector ile birlikte çalışabilir ve OpenTelemetry protokolünü (OTLP) destekler. Uygulamalarınızı OpenTelemetry ile enstrümante etmek, gelecekte farklı izleme arka uçlarına (örneğin, Prometheus, Grafana Tempo, Azure Application Insights) geçiş yapma esnekliği sağlar. OpenTelemetry Collector, yayılımları alıp işleyerek bunları Jaeger’a veya diğer arka uçlara gönderebilir. Bu, uygulama kodunuzu belirli bir satıcıya kilitlemekten kaçınmanızı sağlar.
Sorun Giderme ve Yaygın Hatalar
Dağıtık izleme uygularken karşılaşabileceğiniz bazı yaygın sorunlar ve bunların çözümleri:
* İzlemeler Görünmüyor:
* Uygulama Enstrümantasyonu: Uygulamanızın izleme kütüphanelerini doğru şekilde entegre ettiğinden ve tracer’ı başlattığından emin olun.
* Agent/Collector Bağlantısı: Uygulamanın Jaeger Agent’a (veya doğrudan Collector’a) ulaşabildiğinden emin olun. Ağ politikalarını ve portları kontrol edin.
* Örnekleme Oranı: Örnekleme oranı çok düşük olabilir. Geliştirme/test ortamlarında örneklemeyi kapatın veya %100’e ayarlayın.
* Kubernetes Logları: Jaeger Agent, Collector ve Query pod’larının günlüklerini kontrol edin. Bağlantı hataları veya yapılandırma sorunları orada görünebilir.
* Eksik veya Parçalı İzlemeler:
* Bağlam Yayılımı: Servisler arasında izleme bağlamının (trace ID, span ID) doğru şekilde yayıldığından emin olun. Özellikle HTTP başlıklarının veya gRPC meta verilerinin tüm servisler arasında iletildiğini doğrulayın.
* Asenkron İşlemler: Mesaj kuyrukları veya asenkron görevler kullanılıyorsa, izleme bağlamının bu asenkron işlemlere de doğru şekilde aktarıldığından emin olun.
* Performans Sorunları (Jaeger’ın Kendisi):
* Depolama Performansı: Elasticsearch veya Cassandra gibi depolama arka ucunuzun performansını kontrol edin. Disk IOPS, CPU kullanımı gibi metrikleri izleyin.
* Collector Yükü: Collector pod’larının CPU ve bellek kullanımını izleyin. Yüksekse, daha fazla Collector pod’u eklemeyi veya kaynak limitlerini artırmayı düşünün.
* Örnekleme: Yüksek hacimli izleme verilerini işliyorsanız, örnekleme oranlarını ayarlayarak gelen trafiği azaltın.
* Jaeger UI’ya Erişim Sorunları:
* Port Forwarding/Ingress: Jaeger Query servisine doğru şekilde eriştiğinizden emin olun (port-forward veya Ingress yapılandırması).
* Kubernetes Servisleri: Jaeger Query servisinin çalışır durumda olduğunu ve doğru portta dinlediğini doğrulayın.
Sonuç
Kubernetes üzerinde Jaeger ile dağıtık izleme uygulamak, mikroservis mimarilerinizin karmaşıklığını yönetmek ve derinlemesine görünürlük elde etmek için vazgeçilmez bir araçtır. Bu makalede, Jaeger’ın temel bileşenlerini, Kubernetes üzerinde Helm ile nasıl kurulacağını, uygulamaların OpenTelemetry kullanarak izleme verilerini nasıl entegre edeceğini ve bu verilerin Jaeger UI aracılığıyla nasıl görselleştirilip analiz edileceğini detaylı bir şekilde ele aldık.
Dağıtık izleme, performans darboğazlarını hızlıca tespit etmenize, hata ayıklama süreçlerini hızlandırmanıza ve servisler arası bağımlılıkları daha iyi anlamanıza olanak tanır. Örnekleme stratejileri, bağlam yayılımı ve güvenlik gibi en iyi uygulamaları takip ederek, üretim ortamlarınızda sağlam ve ölçeklenebilir bir izleme çözümü oluşturabilirsiniz. OpenTelemetry’nin benimsenmesiyle, gelecekteki telemetri ihtiyaçlarınız için esnek ve standartlara uygun bir temel atmış olursunuz. Mikroservislerinizin sağlığını ve performansını güvence altına almak için dağıtık izlemeyi sisteminizin ayrılmaz bir parçası haline getirmek, modern bulut tabanlı uygulamaların başarısı için kritik öneme sahiptir.
