Kurumlar monolitik mimarilerden mikroservislere geçiş yaptıkça, hata ayıklama ve izleme karmaşıklığı katlanarak artar. Mikroservis ortamında tek bir kullanıcı isteği, onlarca servisten, veritabanından ve mesaj kuyruğundan geçebilir. Bu etkileşimlere dair görünürlük olmadan, performans darboğazlarını veya arızaların kök nedenlerini belirlemek, bir samanlığında iğne bulmak gibi zor bir hal alır. İşte burada dağıtık izleme ve gözlemlenebilirlik devreye girer. Bu rehber, OpenTelemetry ve Jaeger gibi endüstri standartları araçlarını kullanarak Go gRPC servislerinde sağlam bir gözlemlenebilirlik nasıl uygulanacağını keşfeder.
Neden Standart Loglama Yeterli Değil
Geleneksel loglama, bir servis içindeki bireysel olayların bir anlık görüntüsünü sağlar. Ancak loglar izole edilir. Bir istek birden fazla serviste başarısız olduğunda, tam işlem yolunu yeniden oluşturmak için log girdilerini ilişkilendirmek son derece zordur. Dağıtık izleme, bu sorunu her gelen istek için benzersiz bir trace_id atayarak ve bunu servis sınırları boyunca yayarak çözer. Bu mühendislere bir isteğin tüm yaşam döngüsünü görselleştirme, her adımdan gelen gecikme katkısını anlama ve sorunların tam olarak nerede ortaya çıktığını belirleme imkanı tanır.
Araç Zincirinin Kurulumu
Bu uygulama için gözlemlenebilirlik için standart satıcıdan bağımsız çerçeve olan OpenTelemetry (OTel)'i ve izlemeleri toplamak ve görselleştirmek için arka uç olarak Jaeger'i kullanacağız. İlk olarak, Jaeger'in ortamınızda çalıştığından emin olun; bu genellikle Docker aracılığıyla yapılır:
docker run -d --name jaeger \
-e COLLECTOR_ZIPKIN_HTTP_PORT=9411 \
-p 5775:5775/udp \
-p 6831:6831/udp \
-p 6832:6832/udp \
-p 5778:5778 \
-p 16686:16686 \
-p 14268:14268 \
-p 14250:14250 \
-p 9411:9411 \
jaegertracing/all-in-one:latest
gRPC Sunucusunun Enstrümantasyonu
Go gRPC servislerinde izleme uygulamak, gRPC kesme zincirine ara katman (middleware) enjekte etmeyi gerektirir. Amaç, her RPC çağrısı için izleme bağlamını, span bilgilerini ve meta verileri otomatik olarak yakalamaktır. Bu işin çoğunu üstlenen go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc paketini kullanacağız.
Aşağıda, OpenTelemetry enstrümantasyonu ile bir gRPC sunucusunun nasıl yapılandırılacağına dair pratik bir örnek bulunmaktadır:
package main
import (
"context"
"log"
"net"
pb "your-project/proto" // Oluşturulmuş gRPC kodu
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/jaeger"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.7.0"
"google.golang.org/grpc"
)
func initTracer(ctx context.Context) (*sdktrace.TracerProvider, error) {
// Jaeger dışa aktarıcısını oluştur
exporter, err := jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint("http://localhost:14268/api/traces")))
if err != nil {
return nil, err
}
tp := sdktrace.NewTracerProvider(
sdktrace.WithBatcher(exporter),
sdktrace.WithResource(resource.NewWithAttributes(
semconv.ServiceNameKey.String("my-grpc-server"),
)),
)
otel.SetTracerProvider(tp)
return tp, nil
}
func main() {
ctx := context.Background()
tp, err := initTracer(ctx)
if err != nil {
log.Fatalf("İzleyici başlatılamadı: %v", err)
}
defer tp.Shutdown(ctx)
// otelgrpc enstrümantasyon ara katmanı ile gRPC sunucusu oluştur
opts := []grpc.ServerOption{
grpc.StatsHandler(otelgrpc.NewServerHandler()),
}
s := grpc.NewServer(opts...)
pb.RegisterMyServiceServer(s, &myServer{})
listen, err := net.Listen("tcp", ":50051")
if err != nil {
log.Fatalf("dinlenemedi: %v", err)
}
log.Printf("sunucu %v adresinde dinliyor", listen.Addr())
if err := s.Serve(listen); err != nil {
log.Fatalf("servis verilemedi: %v", err)
}
}
Buradaki kritik satır grpc.StatsHandler(otelgrpc.NewServerHandler())'dur. Bu, gelen ve giden RPC'ler için otomatik olarak span oluşturur, izleme bağlamını meta verilere ekler ve gecikme metriklerini kaydeder.
gRPC İstemcisinin Enstrümantasyonu
İstemci tarafında kurulum simetriktir. İzleme bağlamının, zincirdeki sonraki servise yayıldığından emin olmalısınız. Aynı otelgrpc kütüphanesini kullanarak istemci bağlayıcısını yapılandırıyoruz:
func newClient(ctx context.Context) (pb.MyServiceClient, *grpc.ClientConn, error) {
conn, err := grpc.DialContext(ctx, "localhost:50051",
grpc.WithStatsHandler(otelgrpc.NewClientHandler()),
grpc.WithTransportCredentials(insecure.NewCredentials()),
)
if err != nil {
return nil, nil, err
}
client := pb.NewMyServiceClient(conn)
return client, conn, nil
}
İstemci istatistik işleyicisini enjekte ederek, gRPC kütüphanesi otomatik olarak geleneksel istemci başlıklarını (trace parent headers) giden isteklere ekler. Bu, sunucu çağrıyı aldığında, yeni bir izleme başlatmak yerine mevcut izlemeye devam edebilmesini sağlar.
Gelişmiş: Özel Nitelikler ve Span'lar
Otomatik enstrümantasyon temel ihtiyaçları karşılasa da, gelişmiş gözlemlenebilirlik genellikle işe özgü bağlam eklemeyi gerektirir. Bunu manuel span'lar oluşturarak yapabilirsiniz. Örneğin, gRPC işleyiciniz bir veritabanı sorgusu gerçekleştiriyorsa, bu belirli işlemi izlemek için bir alt span eklemek isteyebilirsiniz:
tracer := otel.Tracer("my-service")
ctx, span := tracer.Start(ctx, "database_query")
defer span.End()
// Burada DB işlemi gerçekleştir
// Span başlangıç/bitiş zamanını ve durumu otomatik olarak kaydeder
Ayrıca, kullanıcı kimlikleri veya hata mesajları gibi meta verileri yakalamak için span'lere nitelikler ayarlayabilir, bu da Jaeger'de filtreleme ve arama yapmayı çok daha güçlü hale getirir.
Sonuç
Go gRPC mikroservislerinde dağıtık izleme uygulamak sadece bir en iyi uygulama değil; ölçeklenebilirlikte sistem güvenilirliğini korumak için bir zorunluluktur. OpenTelemetry ve Jaeger'den yararlanarak geliştiriciler sistem davranışı hakkında derin içgörüler elde edebilir, olaylar için ortalama çözüm süresini (MTTR) azaltabilir ve performansı optimize edebilir. Sağlanan kod parçacıkları, modern kütüphanelerle giriş bariyerinin düşük olduğunu göstermektedir. Hizmetlerinizi bugün enstrümante etmeye başlayın ve mikroservis mimarinizin hak ettiği gözlemlenebilirlik temelini oluşturun.