OpenTelemetry’yi çalıştırmayı başardım, peki neden bu kadar karmaşık?
(iconsolutions.com)- Bir müşterinin OpenTelemetry desteği talebi, mevcut Prometheus·Jaeger·OpenTracing tabanlı gözlemlenebilirliğin OTel’in metrik·log·trace gereksinimleriyle tamamen aynı olmadığını ortaya koydu
- Loglar ve metrikler, appender ve yapılandırma eklenerek nispeten kolay taşındı; ancak dağıtık tracing, Spring ve Akka’nın aynı JVM içinde trace context’ini paylaşmasını gerektirdiği için çok daha zordu
- OTel ile Lightbend Telemetry/OpenTracing farklı Tracer API’leri ve SpanContext implementasyonları kullandığından, aynı transaction ayrı trace’lere bölündü
- Çözüm, OTel context’ini Java
Mapiçine inject edip Jaeger SpanContext olarak extract ettikten sonra Lightbend’inGlobalExtendedTracer.get().local().activateContext()yöntemiyle manuel olarak etkinleştirmekti - OTel, gözlemlenebilirlik standardizasyonuna yardımcı olur; ancak mevcut Akka·Jaeger tabanlı instrumentation ile birlikte kullanıldığında açık context bağlantısı gerekebilir
OTel’in birleştirmeye çalıştığı gözlemlenebilirlik sinyalleri
- OpenTelemetry(OTel), bir gözlemlenebilirlik framework’ü ve toolkit’i olarak, daha önce ayrışmış araç alanlarını metrikler, loglar ve trace’ler olmak üzere üç sinyalde standartlaştırmayı amaçlar
- Mevcut kombinasyon role göre ayrılmıştı
- Prometheus: metrikler
- Logstash ve Elasticsearch gibi merkezi toplayıcılar: loglar
- OpenTracing: dağıtık tracing
- OTel yalnızca spesifikasyonlar değil, temel bileşenler de sağlar
- OpenTelemetry Protocol(OTLP): uygulamaların telemetry verilerini raporladığı protokol
- OpenTelemetry Collector: telemetry verilerini vendor’dan bağımsız biçimde alan, işleyen ve dışa aktaran bileşen
- 10’dan fazla dil için SDK: OTLP ve telemetry export implementasyonları
- Uygulamalar otomatik instrumentation, API ve dil SDK’ları aracılığıyla OTel Collector’a sinyal gönderebilir; altyapı da Collector’a sinyal gönderebilir
IPF’in mevcut desteği ve müşteri talebi
- IPF’in hâlihazırda ELK Stack entegrasyonu, monitoring ve gözlemlenebilirlik dokümantasyonu vardı; müşteriler de bunları üretim ortamında kullanıyordu
- Framework’ün doğası gereği, kütüphaneyi kullanan uygulamaların hangi ortama dağıtılacağı bilinmediğinden belirli bir vendor’ı zorunlu kılmadan soyutlamalar ve üzerine yazılabilir varsayılanlar sağlamak gerekiyordu
- Temel öneri, logları Elasticsearch ve LogScale gibi toplayıcılara göndermek, metrikler için ise fiilî standart hâline gelen Prometheus’u etkinleştirip desteklemekti
- Müşteriler neredeyse aynı anda OTel tabanlı tracing için üç talepte bulundu; tracing’i OTel ile kullanmaya başlamışken loglar ve metrikleri de birlikte taşıma yönünde bir akış oluştu
- Loglar ve metrikler yeni appender ve yapılandırma eklenerek nispeten kolay ele alındı, ancak tracing ayrı bir sorun olarak kaldı
Dağıtık tracing’de context propagation neden gerekli?
- Dağıtık bir sistemde tek bir transaction’ı izlemek için sistemler arasında belirli çağrıları ve transaction’ı birbirine bağlayan bilgiyi aktarmak gerekir; buna context propagation denir
- Trace, span adı verilen birden fazla iş birimini kapsayan üst yapıdır; birden fazla span olabilir ve bunlar iç içe geçebilir
- Bir e-ticaret sitesinde “buy now” düğmesine basma akışı tek bir trace altında toplanabilir
- Frontend’in backend ile iletişim kurduğu span
- Backend’in ödeme, teslimat ve sipariş yönetimi servisleriyle iletişim kurduğu child span’ler
- Her downstream servisin başka sistemlerle yeniden iletişim kurduğu ek child span’ler
- OTel kullanıldığında her dağıtık servis, trace’in kendine ait bölümünü OTel Collector’a raporlar ve OTel tüm akışı tek bir trace ID altında oluşturur
Dört context propagation standardı ve OpenTracing’in izleri
- OTel’in mutlaka desteklemesi gereken dört context propagation implementasyonu vardır
- IPF’in Akka alanı, kullanımdan kaldırılması planlanan OpenTracing’i zaten destekliyordu ve Lightbend Telemetry’nin OpenTracing desteğini kullanıyordu
- Lightbend Telemetry 2.20.0, OpenTelemetry logs/events ve metrics desteği ekledi; ancak kritik tracing desteği eksikti
- Teoride OTel tracing, OpenTracing’in yeniden markalanmış hâline yakın görünüyordu ve Lightbend Telemetry’nin OpenTracing’i de dört propagation yöntemini desteklediği için çalışacak gibi duruyordu; fakat gerçek sonuç farklıydı
Spring ile Akka arasında kopan trace
- IPF, Spring ve Akka’yı birlikte kullanır
- Spring Boot ve Spring IoC: uygulama bootstrap’i, yapılandırma, dependency configuration
- Akka: event sourcing, scheduling, clustering, sharding, entegrasyon vb.
- Müşteri, ödeme akışını bir Spring REST controller veya
@KafkaListenereklenmiş bir metot üzerinden başlatabilir - OTel’den önce üç alan büyük bir çakışma olmadan çalışıyordu
- Metrics: Spring ve Akka farklı Prometheus endpoint’leri yayımlıyordu
- Logs: Her iki framework de SLF4J ve Logback kullanıyordu
- Traces: İkisi de OpenTracing kullanıyordu
- OTel kullanıldığında metrics ve logs için Spring ve Akka bağımsız çalışıp verileri normal şekilde gönderdi; ancak tracing’in JVM içinde aynı trace context’ini paylaşması gerekiyordu
- Uygulama içinde aynı tracing kavramını iki farklı API temsil ediyordu ve asıl sorun bu iki API’nin birbiriyle konuşmamasıydı
- Doğru davranış, Akka HTTP client’ın mevcut trace ID
123’ü yeniden kullanıp yalnızca yeni bir span ID oluşturarak bunu aynı trace’in yeni iş birimi olarak göstermesiydi; fakat pratikte ilişkisi olmayan iki trace üretildi
opentracing-shim ve implementasyon çakışması
- OTel tarafında, OTel
Tracer’ını OpenTracingTracergibi gösterenopentracing-shimvardır - Ancak Lightbend Telemetry özel bir Tracer implementasyonu kullanıyordu; bu nedenle hem shim hem de Jaeger başarısız oldu
- Loglarda şu hatalar çıktı
Expected to have an OpenTelemetry Span but got cinnamon.opentracing.TraceLocal$ContextOnlySpanExpected to have a JaegerSpanContext but got io.opentelemetry.opentracingshim.SpanContextShim
- Jaeger, OTel shim context’ini alıp başarısız oldu; OTel ise Lightbend context’ini alıp başarısız oldu
Java Agent ve Lightbend iç tracing’inin incelenmesi
- OTel ve Lightbend Telemetry instrumentation’larının ikisi de belirli sınıflardaki belirli metot çağrılarına hook takıp etkinliği tracer’a raporlamak için Java Agent kullanır
- Lightbend Telemetry instrumentation’ı açık kaynak olmadığından, davranışı anlamak için decompile edilmiş tracer kodunu incelemek gerekti
- Küçültülmüş bir reproduksiyon örneği oluşturularak Spring tarafındaki OTel trace context’inin Akka tarafına geçerken nerede koptuğu kontrol edildi
- Jaeger’ın başarısız olduğu nokta
JaegerTracer.javaiçindeki ilgili kod idi - Çağıran taraf, Lightbend Telemetry’nin tescilli instrumentation sınıfı
OpenTracingAkkaPersistenceActorInstrumentation.classidi - Decompile edilmiş kodda kritik koşul
var5’in durumuydu- OpenTracing
SpanContextolanvar5null değilse yeni span, mevcut aktif span’in child’ı olarak bağlanıyordu var5null ise mevcut trace ile ilişkisi olmayan bir span oluyorduvar5Jaeger SpanContext değilse Jaeger başarısız oluyordu
- OpenTracing
Manuel dönüşümle OTel ve OpenTracing’i bağlamak
SpanContext,this.traceLocal.currentContext()üzerinden alınıyordu vetraceLocal, Lightbend’inExtendedTracer’ı içinde initialize ediliyordu- Lightbend dokümantasyonunda
ExtendedTracer’a global olarak erişmenin bir yolu vardıGlobalExtendedTracer.get()
GlobalExtendedTracer.get()birExtendedTracerdöndürür;local()ise instrumentation kütüphanesinin kullandığıyla aynıTraceLocal’ı döndürür- Bu
TraceLocaliçinde OpenTracingSpanContextalan biractivateContextmetodu vardı - Çözüm akışı dört adımdan oluşuyordu
- OTel shim kullanılmaz
- Mevcut OpenTelemetry Context, Java
Mapiçine inject edilir - Bu
Mapdeğerleri kullanılarak Jaeger SpanContext extract edilir - Akka’ya girmeden önce Jaeger SpanContext, Lightbend
TraceLocaliçinde etkinleştirilir
- Kullanılan OTel propagators API işlemleri inject and extract operations idi
- Temel kod şu akışı izler
- Boş bir
HashMapoluşturulur GlobalOpenTelemetry.get().getPropagators().getTextMapPropagator().inject(...)ile OTel context map’e enjekte edilirnew TextMapCodec(false).extract(new TextMapAdapter(otelContext))ile JaegerSpanContext oluşturulurGlobalExtendedTracer.get().local().activateContext(openTracingContext)ile Akka’ya girmeden önce context etkinleştirilir
- Boş bir
Doğrulanan davranış ve operasyon önerisi
- Manuel bağlantıdan sonra trace beklendiği gibi devam etti
- Spring REST çağrısı
/submit’ten başlayarak tüm akış tek bir trace altında bağlandı - OTel API ile instrument edilmiş bölümler ile OpenTracing API ile instrument edilmiş bölümler birlikte kullanılabildi
- Trace, HTTP sınırını aşarak propagate edildi
- Spring REST çağrısı
- Örnekte iki instrumentation türünü ayırt etmek için farklı adlar kullanıldı; ancak gerçek müşteri ortamlarında dışarıdan tek bir uygulama gibi görünmesi için
otel.service.nameilecinnamon.applicationdeğerlerinin eşleştirilmesi önerilir
Neden karmaşıktı ve kalan endişeler
- Lightbend Telemetry’yi OTel API ile yeniden yazmak için hâlen Jaeger API’ye bağlı çok sayıda instrumentation’ın OTel API’ye port edilmesi gerekir; bu büyük bir iş olabilir
- OTel Collector, legacy Zipkin formatındaki trace’lerin toplanmasını desteklediği için Lightbend, legacy desteğe dayanarak tam OTel desteği sunabilir
- Karmaşıklığın doğrudan nedeni, farklı tracing kütüphaneleri kullanan iki instrumentation sistemini birleştirmeye çalışmaktı
- OTel projesi, semantic conventions gibi standartlaştırma girişimleriyle gözlemlenebilirlik alanını düzenlemeye çalışır; ilk başta anlaması biraz karmaşık olsa da faydalı bir FOSS projesi olarak değerlendiriliyor
- Akka’nın actor model içinde thread’ler arasında trace context’ini doğru şekilde aktarıp aktarmadığı konusunda hâlâ endişe var
- Küçük ölçekli yük testlerinde beklendiği gibi çalıştı
- İlgili ticket Akka tarafında açılmış durumda
1 yorum
Hacker News yorumları
Otel’i öğrenip port ederken kendimi sürekli Java dünyasına geri dönmüş gibi hissettim. Kodu satır satır izlediğim her seferinde EnterpriseFizzBuzz gibiydi; keşfedilebilirlik hiç yoktu ve kendi terminolojisi de sanki kafası bir şeylere güzel insanlar tarafından uydurulmuş gibiydi.
NodeJS’te StatsD’ye kıyasla CPU kullanımı yaklaşık 4 kattı; sonunda kullanımı düşürmek ve etiket patlamasını azaltmak için kendi agregasyonumu yazdım. StatsD’de birden fazla süreç aynı etiketleri raporlasa da sorun olmuyor, ama OTEL bunların üzerine yazıyor.
Tepe yükte bir CPU %60–80 kullanımda çalışıyordu ve bir şeyler değişmeden dikey ölçekleme bile yapamıyorduk. Çekirdek başına bir süreç kullanan diller için OTEL aktif biçimde düşmanca; şaka gibi. Sadece Prometheus kullanmak daha iyi; zaten fiilen başka rakip de yok.
.NET’te basit bir otel yapılandırması yapmaya çalışırken, kurumun seçtiği vendor’ın dokümanlarını saatlerce okuyup yine de anlayamayınca, bir iş arkadaşımın işlettiği Discord’a girdim. Oradaki iş modelinin bir kısmı “açık kaynak üründe düzgün otel kullanmak istiyorsan para öde” şeklindeydi; daha ilk anda, maliyeti ne olursa olsun buna değeceğini hissettim.
OTEL uygulaması yapmaktansa, önceden deneyimim olmadan kararlı bir event/pub-sub kütüphanesi daha yazmayı tercih ederim.
Minimum kurulum da gerçekten epey küçük; web UI, sunucu instance’ı ve zaten bildiğiniz bir DB yeterli: https://skywalking.apache.org/docs/main/latest/en/setup/back...
İzleme alanındaki Zabbix’e benzeyen yanları var. İkisi de kimseyi hayran bırakmaz ama pratiklikleri yeterli.
process.vpidgibi görece düşük kardinaliteli bir tamsayı özelliği oluşturdum. Uygulama yaşadığı sürece bu değerin benzersiz olmasını global nesne gibi bir şeyle koordine edebilirsiniz.Sonra bunları toplayıp ilgili özelliği kaldıran bir şey koymak yeterli. statsd/delta’da tek bir sinyal iletimini kaybederseniz tüm veri bozulur; kümülatif yöntemde ise yalnızca hassasiyet kaybedersiniz.
Benim kullanımım, scrape edilebilir uzun süre çalışan süreçler değil; “batch” araçlarından gelen push tabanlı metrikler.
Otel’in karmaşık görünmesinin nedeni, birçok gözlemlenebilirlik vendor’ının kendi özel SDK’ları, ajanları ve API’leri ile gözlemlenebilirlik uygulamasını çok kolay hale getirmiş olması. Otel bu sorunu çözmeye çalışıyor ve bunu yapanların harika iş çıkardığını düşünüyorum.
Grafana’nın OpenTelemetry’yi ekosistemin birinci sınıf bir bileşeni olarak benimsemesi de övgüye değer.
Yıllarca Datadog kullanımını savundum, ama orta ölçekli şirket ile büyük şirket arası ölçekte fiyatı karşılanması zor hale geldi. Zamanla OpenTelemetry API’leri ve SDK’ları olgunlaştıkça bunu uygulama gözlemlenebilirliği standardı olarak benimsedik.
Yine de dokümantasyon genel olarak daha iyi olabilir; onboarding dokümanlarının dile göre farklılık göstermesi de ideal değil.
Mevcut ekibimiz NodeJS/Typescript stack’i kullanıyor; OpenTelemetry’ye hızlı başlanabilmesi için bir paket seti ve örnek Grafana stack’i hazırladık: https://github.com/zonneplan/open-telemetry-js
Tüm APM vendor’larındaki en büyük sorun, sihirli ajanlar kernel hook’ları edinir edinmez geliştiricinin açıklayamadığı türlü türlü şeylerin yaşanmaya başlaması.
Eskiden başka bir şirkette Dynatrace devreye alınmıştı; neyse ki uygulamada zaten yeterince yerleşik metrik vardı, hatta baş SRE bunun enstrümantasyon açısından “örnek” sayılabileceğini düşünüyordu. Ama uygulama host’larına Dynatrace ajanı kurulur kurulmaz, düğüm yeniden başlatması gerektiren birkaç Heisenbug ortaya çıktı ve performans düşüşü de doğrudan ölçüldü.
İronik biçimde metrikler sayesinde acıdan kaçındık, ama bunu nasıl düzelteceğini kimse bilmiyordu. En kötüsü, MSSQL güncellemesi sırasında failover’ın ADO.NET bağlantı havuzunu tuhaf biçimde kirletmesiydi.
Yalnızca gerektiği kadar karmaşıklaşan bir yapı. Sihirli özelliklerden kaçınabilir, bağlamda en değerli ve anlaşılması en kolay olan alt kümeyi kullanmakla yetinebilirsiniz
Bizim ekipte durum çok basit. Yalnızca trace gönderen bir kütüphane kullanıyoruz; trace’ler uygulama gözlemlenebilirliğinde en büyük değeri sağlıyor ve başka veri türlerini de taşıyabiliyor. Temelde string ve float yerine hashmap kullanmak gibi
Otomatik enstrümantasyon yerine manuel enstrümantasyon kullanıyoruz; neyi gözlemleyeceğimizi bilinçli seçiyor ve hangi kodun span ürettiğini iyi anlıyoruz. Kod yapımıza uyan adlandırma kurallarımız da var
Backend olarak ucuz bir üçüncü taraf servis ile yerel geliştirme için all-in-one Jaeger kurulumunu birlikte kullanıyoruz. İkincisi sadece tek bir çalıştırılabilir dosya ya da tek bir Docker container çalıştırmayı gerektiriyor ve span’leri diske kaydetmiyor. Daha çok ekip üyelerini üçüncü taraf servisi taşırmayacakları konusunda rahatlatmak için kullanılıyor
Altyapı izleme için mevcut bir kurulumumuz var; bizim durumumuzda tüm altyapı loglarını ve metriklerini toplamakta büyük bir değer görmüyoruz. OTEL metrikleri ve loglarının hâlâ erken aşamada olduğunu düşünüyorum ama vendor’lar bunu böyle söylemiyor
Henüz scrape edemediğimiz altyapının bazı kısımlarından basit, tek seferlik metrikler gönderecek bir endpoint arıyorum
Otel yeni bir proje için uygun olabilir, ama zaten telemetrisi olan çalışan bir serviste bunu açmak, hareket hâlindeki bir arabanın lastiklerini değiştirmek gibiydi
Yalnızca otel kullanan ve otel dışı framework içermeyen greenfield proje için iyi olabileceğine inanıyorum. Ama ben henüz öyle bir dünyada yaşamıyorum
En büyük sorunlardan biri yerel geliştirme deneyimiydi. Yerelde log, trace ve metrikleri desteklemek istiyordum ama bunun için bir sürü Docker image ayağa kaldırmak istemiyordum. Dağıtımdan önce metriklerin, trace’lerin, baggage’in ve activity span’lerinin nasıl göründüğünü loglarda görmek istiyordum
Yakın zamanda .NET ekibi .NET Aspire’ı çıkardı ve gerçekten iyi. Yerel geliştirme stack’inde her şeyi tek yerde görselleştirmeyi kolaylaştırıyor ve kodla yazılmış bir orchestrator gibi çalışıyor
k8s’e dağıtırken OTEL endpoint’ini DataDog Agent’a yönlendirince her şey kendiliğinden çalışıyor. DataDog’un özel trace kütüphanesi ve SDK’lerinden kaçınıp yalnızca OTEL kullanıyoruz
Artık geliştirme deneyimi oldukça iyi hâle geldi
https://learn.microsoft.com/en-us/dotnet/aspire/fundamentals...
https://docs.datadoghq.com/opentelemetry/#overview
İlk kez yerel geliştirme makinesinde kurmak 5 dakika sürüyor; sonrasında ayrı bir terminal sekmesinde
/path/to/openobserveçalıştırmanız yeterli. Tek bir statik linklenmiş binary gibi devasa bir karmaşıklıktan kaçınmak istiyorsanız yerel/uzak çalıştırma için Docker image da sunuyorlar :PGüzel grafiklere sahip hepsi bir arada OpenTelemetry backend’i ve benim projelerimde şimdiye kadar fark edilebilir şekilde hiç başarısız olmadı
Aspire kullanınca uygulama seviyesine gereksiz karmaşıklık ekliyor ve dar bir ekosisteme bağlanıyorsunuz. Yerel geliştirme için docker compose gibi kanıtlanmış birçok alternatif var; Aspire da docker compose ve ortam değişkenlerinden özellikle çok daha kolay değil
Python’da otel yapacaksanız Logfire’ın istemcisini kullanmak iyi olur. Logfire servisini kullanmasanız bile böyle
Açık kaynak ve herhangi bir otel uyumlu endpoint’e gönderebiliyor. Üstelik pydantic ekibinin yaptığı istemci, resmî otel kütüphanesinden 10 kat daha iyi ve daha basit
Samuel Colvin’in buraya nasıl geldiğini anlattığı ilginç bir röportaj da var: https://www.bitecode.dev/p/samuel-colvin-on-logfire-mixing-p...
Bu yüzden OpenTelemetry’yi benimsemeyi tek satırlık bir komut kadar kolaylaştırmaya odaklanan açık kaynaklı bir proje başlattım: https://github.com/odigos-io/odigos
Günümüzde birçok web framework’ü enstrümantasyonun büyük kısmını sizin yerinize yapıyor. Örneğin opentelemetry-js kullanıp https://signoz.io gibi bir şeyi kendiniz barındırırsanız, bir saatten kısa sürede ayağa kaldırabilir ve özel kod yazmadan çokça veri elde edebilirsiniz.
OpenTelemetry trace’lerden doğup büyüdü, ancak metrikleri ve log’ları uzman çözümlere bırakmak çok daha iyi
Bu, “sızdıran soyutlama” ya da “sızdıran framework” sorununa benziyor. Her şeyi tek bir çatı altına koymak istiyorsanız SQL veritabanları da bunların hepsini aynı anda yapabilir. Ama bu, yapmaları gerektiği anlamına gelmez.
https://cra.mr/the-problem-with-otel/
Yine de OTel ile her uğraştığımda hâlâ sevmiyorum.
Sonuna kadar okursanız, acının büyük ölçüde kendi kendine yaratıldığını görürsünüz. Python’ın standart stack’inde (mysql, flask, redis, requests vb.) bu çok kolaydı. Servisin en üstüne birkaç import ekleyince otomatik olarak bağlanıyor ve fazla tantana çıkarmadan her şeyi izliyordu.
aiohttp’nun güncel sürümü 3.11.X; otomatik enstrümantasyon3.Xdesteklediğini söylüyor [0], ama gerçek sonuç kullandığınızaiohttp’nun ne kadar güncel olduğuna ve otomatik enstrümantasyon tarafının durumuna göre değişiyorHer şey denk gelirse sihir gibi çalışıyor, ama o iğne deliği epey dar
[0]: https://github.com/open-telemetry/opentelemetry-python-contr...
Trace’ler çalışıyor, spanmetrics exporter’ını da ayarladım; prometheus’a doğrudan sorgu atınca spanmetrics görünüyor. Ama ne yaptıysam jaeger’ın “monitor” sekmesinde görünmüyor
Buna 3 gün harcadıktan sonra patronum “Sadece elle enstrümante edip her şeyi SQL sunucusuna göndersek ve Grafana dashboard’u yapsak nasıl olur?” dedi, ama onu da yapmak istemiyorum
En basit kullanım senaryosu olmasına rağmen çalışmıyor. Üstüne bir de Grafana mı eklemem gerekiyor?
Karmaşık olmasının nedeni, bunu uygulayan mühendisler için değil, Otel uyumlu yazılım satan şirketler için tasarlanmış olması.
Aksine backend’lerin benimsemede oldukça yavaş kaldığını düşünüyorum.
Anlamlar değişir, sorun değil; ama artık spesifikasyona uygun yazılım yazma işi için kullanılan kelimenin ortadan kalkıp yalnızca “mevcut yazılımı sunucuya dağıtma işi”nin kalması ilginç