Mikroservislerde Loglama & Log Streaming ve Log Consodilation
Mikroservis Mimarisinlerinde Loglama Yaklaşımları
Mikroservislerin monolitik doğası gereği farklı yaklaşımlara ihtiyaç duyduğu bir diğer konu da loglamadır. Monolitik sistemlerde gayet kolay çözümlerle halledebileceğimiz loglama konusu özellikle mikroservis mimarimiz büyüdükçe ve çeşitlendikçe sorun haline gelmeye başlayabilir.
Mikroservislerde Düzgün Loglama Neden Önemli?
Aslında loglamanın önemli olmadığı herhangi bir sistem yok. Her durumda hayat kurtarıcı bir nitelik taşıyor. O zaman neden özellikle böyle bir başlık attığım konusuna gelince mikroservisler doğası gereği dağıtık bir sistemdir. Yani en basit tabiriyle bambaşka fiziksel makinalardaki bambaşka servisler birbirleriyle iletişim kurar ve client’a dağıtık olduklarını hiç hissettirmeden tek bir sistemmiş gibi resultlarını sunarlar. Fiziksel makinalardaki uygulamaların her biri direkt client için anlamlı uygulamalar olmaz ve sadece ana sistemin belli bir kısmı için işini icraa eder. Dolayısıyla client uygulamaya bir istekte bulunduğunda transaction başlar. Uygulama bir servisi call eder , o servis başka bir servisi call eder belki o servis paralellde birden fazla başka servisi kontrol eder ve hepsi işlerini yaparlar.
Servis servisi çağırdı o başka bir servisi o da başka bir servisi, tam da orda servis fail verdi yani patladı. Client da bir şekilde hatayla karşılaştı. Uygulamanın sorumlusu olarak bu hata da size geldi. Trace etmeniz gerekiyor. En kötü düşünmek bile istemediğimiz senaryo loglama sisteminin hiç olmayışıdır. Eyvahlar olsun 🙂 . Client gibi uygulamayı ayağa kaldırırsınız. Client gibi request atarsınız o servisten o servise atlaya atlaya hatayı ararsınız. Neyse bunu hiç olmamış gibi geçiyorum.
Diğer senaryo bir loglama yapınız var her servis kendi loglamasını yapıyor ve bir yerlere logluyor. Fail veren servis de hata verdikten sonra loglamasını yaptı. Loglara baktınız hatayı buldunuz. Ancak client oraya hangi servis call zincirini takip ederek geldi bilmiyorsunuz. Odak noktaya inemiyorsunuz? Traceinizi yapamıyorsunuz.
Monolotik yapılarda loglama yaparken direkt uygulamanın içerisine kurduğumuz bir paket ile direkt olarak ihtiyaç dahilinde text dosyalarına veya database’e loglama yapabiliriz. İşimizi de görür hatta bir tık daha ileri gitmek istiyorsak bu loglamaları bir logger servis üzerinden de yapabiliriz. Herşey iyi. Peki ya mikroservis mimarisinde durum neden farklılaşıyor ve bu yaklaşımlar neden sorun çözmekten ziyade kısa vadeli sorun çözen ancak ilerisi için antipattern hal alıyor? biraz bu konular üzerinde bir şeyler karalamak istedim.
Öncelikle bu çözümler mikroservis mimarisinde işlemez mi? Yine her mikroservis kendi içerisinde bir log kütüphanesi üzerinden ya da bir logger servis üzerinden loglama yapsa sorun çözülmez mi? Mikroservis mimariniz az sayıda servisten oluşacaksa bu iki yaklaşım hızlıca implement edilebilir ve sorununuzu da çözer ancak sıkıntılar mikroservislerinizin sayısı ve birbiriyle iletişim ağı karmaşıklaştıkça sorunlar baş gösterir ve kimse de mikroservis sayınızın artmayacağının garantisini veremez. Veriyorsa yalan söylüyordur inanmayın 🙂
Tamam mikroservisleriniz artmaya başladı belli ki zaman geçtikçe daha büyük bir mikroservis mimariniz olacak ve loglama da olmazsa olmaz önem taşıyor. Neler yapmalı neler yapmamalı ?
Nasıl Loglama Yapmamalı?
İlk aklımıza gelen bir logger servis oluşturmak ve bütün servislerden bu servisi call ederek log işlemlerimizi yapmak. Başta kulağa güzel ve temiz bir yol olarak geliyor. Bizi kod tekrarından kurtaran ve log yapımızı standarta sokan bir servis. Ancak sadece errorları değil warningleri, infoları da loglayacağınız için mikroservis sayınız artıkça ortaya çıkan http trafiği baş ağrıtıcı olabilir ve darboğazlar oluşturabilir.Bunun dışında logları merkezi bir şekilde almak bazı durumlarda başka baş ağrıtıcı sıkıntılar da çıkarabilir. Örneğin, log servise bir parametre eklediğiniz zaman bunu gidip bütün mikroservislerde de değiştirmek istemeyeyiz.
Her mikroservisimize bir log kütüphanesi ile log yapısı kurup bu hepsinden istediğimiz yere loglama yapsak nasıl olur? Hepsinin log yapısı birbirinden bağımsız ve özgür :). Ancak yine mikroservisler doğası gereği olabildiğince saf ve dış bağımlılıklardan uzak olmalı ve bu sebeple mikroservislere kütüphane eklemekten olabildiğince kaçınmalıyız. Bu kütüphane bağımlılıkları her an değişebilir ya da yeni versiyon ve sürüm değişiklikleri bütün mikroservislere etki edebilir. Loglama ile ilgili herhangi bir değişiklik gerektiğinde bunu tüm servislere uygulamanız gerektiğini ve hepsini deploy etmeniz gerektiğini düşünün. Buna ilaveten farklı teknolojiler ile implemente ettiğiniz her bir mikroservis için bir logging yapısı kurmak veya farklı log kütüphanesi bulup kullanmak zorunda kalacaktınız.
Genel Standartlar
1. Logların Merkezileşmesi
Eğer ki dağıtık sistemler üzerinde çalışıyorsak, loglarımız da dağıtık yerlerde olabilir. Ancak logları da bir araya getirip tek elden izleyebilmemiz ve geri geldiğinde tek bir dashboarddan görüp analiz etmemiz gerekir. Dağıtık bir sistemde dağıtık tutulan loglarda bir hatanın kaynağına inmek saatler alabilir. Tüm bu logları tek elden izleyebileceğimiz merkezi bir yapıda sunulması gerekecektir.
2. Log Levelların Dinamikleştirilmesi
Atılan her bir log belki bir http trafiği belki bir alan belki başka birşey ama her halükarda bir kaynak tüketimidir. Bu bağlamda, servislerimiz ayaktayken, runtime’da log seviyemizi dinamik bir şekilde, değiştirmek isteyebiliriz. Tabi eğer kütüphane kullanıyorsak bu özelliği destekliyor olması gerekmekte. Örneğin kaynaklarımız tükenme seviyesindeyken sadece errorları loglamak, önlem alma durumlarında warningleri loglamak bazı durumlarda da tüm seviyelerde loglama yapmak isteyebiliriz. Bunu dinamik bir şekilde ayarlayabiliyor olmamız gerekir.
3. Asenkron Loglama
Loglama sizin için sonucu önemli olan bir işlem değildir olmamalıdır da. Dolayısıyla log işlemlerini asenkron yapmak performans açısından faydalı olabilir.
4. CorrelationId Kullanımı
CorrelationId konusu yazının başında da biraz bahsettiğim bir konu. Bir kullanıcı işlemini gerçekleştirmek için servislerin zincir şekilde birbirlerini çağırdığını, ortada biyerde de bir hata çıktığını düşünün. Biraz daha somut olması açısından client bir requestte bulunur. Uygulama A servisini, A sersivisi B servisini B de paralelde C ve D servisini çağırır. Hata da D servisinde meydana gelmiş olsun. Bu işleme ait logları görmek istediğimizde bu işlem bütününü yani zinciri ayırt edebileceğimiz zincir üyelerine özel bir ID olsa, zinciri bir grup şeklinde görsek nasıl olurdu? İşte bu correlationIddir. Bu gruplamayı yapmak için X-Correlation-Id http header’ını kullanabiliriz. Bu header’a işlem bazında unique bir değer set etmemiz gerekmekte. Ek olarak log tablomuza da CorrelationId adında bir alanda eklememiz gerekiyor tabi. Artık geriye, bu header’ı her servis request ve response’u için doğru şekilde set etmek olacaktır.
6. Loglarda Utc Time Kullanımı
Özellikle servislerin bulut tabanlı olduğu durumlarda, sunucuların veya yük oluştuğu anlarda devreye alınacak yeni sunucuların hangi zaman diliminde bulunacağıyla ilgilenmek istemezsiniz. Zamanla sıraladığınız logların işlem sırasına göre hatalı bir şekilde gelmesini istemezsiniz. Bu noktada, tavsiyemiz UTC zaman dilimini kullanmanızdır.
7. Instance Id
Eğer servislerinizi herhangi bir cloud provider üzerinde çalıştırıyorsanız ve otomatik ölçeklendirme özelliğini kullanıyorsanız, yukarıda belirttiğiniz gibi yük durumuna göre sürekli olarak yeni sunucular (instance) eklenip çıkarılacaktır. Logların hangi instance’dan kaydedildiği veya logu atan sunucunun hala etkin olup olmadığı gibi konular sizin için önemliyse, bu sorunu çözmek için loglarınıza InstanceId alanını ekleyebilirsiniz.
8. Log Alert Sistemi
Eğer canlı ortamda oluşan hatalarınız sizin için kritik durumdaysa bir alarm sistemini devreye almanız yerinde olacaktır
9. Anlamlı Loglar Tutma
Eğer loga baktığınızda hala üzerine uzun uzun düşünmeniz gerekiyorsa orada eksik birşeyler olabilir yani yazdığınız logun anlamlı olması gerekir. Yukarıdaki tüm saydığım maddeler logu daha anlamlı hale getirecektir ancak en temelde log kayıtlarının belli sorulara cevap verebiliyor olması lazım.
Ne zaman ? Tam Tarih Formatı ile hatanın ne zaman gerçekleştiği belirtilmeli.
Ne Oldu ? Tüm hatayı yani stack errorun logda bulunması şart.
Nerede ? Hata nerede gerçekleşti, makina ismi, servis ismi, class ismi, fonksiyon ismi
Kim ? client veya userın IP adressi var ise ya da hatanın kim tarafından gerçekleştirildiği. İletişime geçmeniz gerekebilir.
Peki o zaman?
LOG CONSODILATION
Loglama için farklı yaklaşımlar mevcut. Temelde yukarıdaki prensipleri & endişeleri göz önünde bulundurarak her mikroservisin loglarını standart output ile bir text dosyasına yazmasını ve bir log toplayıcının da bunları gelip toplayıp merkezileştirmesi yaklaşımını değerlendirebiliriz. Bu bize ne kazandırır ve ne gibi sorunlar ortaya çıkarır?
Öncelikle faydalarından başlayacak olursak,
- Logging paketi kullanmayacağımız için mikroservislerimiz olabildiğince saf ve dış bağımlılıklardan uzak kalmaya devam eder. Ayrıca farklı dille yazılmış mikroservislerde farklı logging paketler kullanma gibi zorunluluğumuz da ortadan kalkar.
- Single point of managementtan kurtuluruz. Yani tek bir log servisinde yapılacak değişikliklerin ya da bir log paketinde oluşacak versiyon kaynaklı değişikliğin tüm servislere etki etmesi ve tüm servislerde geliştirme yapılmasının önüne geçmiş oluruz.
- Mikroservis sayımız arttıkça oluşacak http trafiğinden kurtulmuş oluruz.
Sistemin implementasyonu sırasında ortaya çıkacak olan problemler ise
- Ilk olarak logları dosyalardan nasıl toplayacağımızı belirlememiz gerekir. Bunun için tahmin edebileceğiniz üzere farklı toollar bulunmakla birlikte en popüler olanı logstashtir. Ayrıca eğer sistemde önceden tasarlanmış farklı yerlerde toplanan loglar varsa da onları da logstah ile toplayabilirsiniz.
- Toplanan logların formatı ve patterni de ayrı bir konu olacaktır. Loglar formatı nerede başlıyor nerede bitiyor nasıl belirleyebiliriz?
- Diğer soru her logda loglara
header
cookie
vetimestamp
gibi alanları nasıl ekleyeceğimiz olacaktır. - Logları standardize etmek de bir diğer konu. Her mikro servis stout ile text dosyasına loglamasını yapacak ve zamanı gelince loglar oradan toplanacak. Peki her servis neyi text dosyasına yazacaklarına neye göre karar verecekler ve bu standartı nasıl sağlayacağız.
- Log Mesajını Structured Yapmak
LOG STREAMING
Tüm servislerin herhangi bir http requesti olmadan loglarını asenktron olarak bir kuyruğa gönderdiğini düşünelim. Burada message queue olarak apache kafka ya da rabbit mq kullanılabilir. Sonrasında logları logstash ile karşılayıp manipule edip arkasından da elasticsearche gönderebilirsiniz. Elasticsearch, bir arama motoru gibi çalışmak üzere optimize edilmiş, NoSQL document tipi veritabanıdır. ELK stack’inde logların fiziksel olarak saklandığı, indexlendiği ve sorgulandığı nokta burasıdır. Kibana, kullanışlı arayüzü, log sorgularken işimizi kolaylaştıran query syntax’ı, oluşturulan log raporlarını excel export edebilme gibi birçok özelliği nedeniyle tercih edilen bir tool.