TechForge'un yeni rehberi, birçok yeni başlayan mikroservis projesinin, herhangi bir ölçeklendirme avantajı sağlamadan ağ çağrılarının gecikmesini (latency) getiren "dağıtık monolitlere" (distributed monoliths) dönüştüğü konusunda uyarıyor. Yazı, mühendislik ekiplerini sağlam bir monolit ile başlamaya ve ancak net ölçeklendirme veya sahiplik ihtiyaçları ortaya çıktığında yapıyı parçalamaya teşvik ediyor.
Ekipler neden mikroservislere acele ediyor
Mikroservislerin cazibesi ortadadır: bağımsız servisler, ayrı dağıtımlar (deployments) ve bir uygulamanın her bir parçasını kendi şartlarına göre ölçeklendirme vaadi. Start-up kültürü ve son dönemdeki başarı hikayeleri, bu deseni modern mühendisliğin bir nişanı haline getirdi. Ancak bir monoliti çok erken bölmek, genellikle yeni bir monolit türü —onlarca ağa bağlı bileşen— yaratır. Bedeli nedir? Orijinal avantajlar ulaşılamaz haldeyken; daha yüksek gecikme süresi, daha zor hata ayıklama (debugging) ve daha fazla operasyonel yük.
İlk hata: Sadece isimde bir monolit ile başlamak
Ekipler, tek bir kod tabanı ve paylaşılan bir veritabanı kullanmaya devam ederken sistemleri genellikle "mikroservis tabanlı" olarak etiketliyor. Sonuç, hala birbirleriyle HTTP veya RPC üzerinden konuşan, birbirine sıkı sıkıya bağlı (tightly coupled) bir dizi modüldür. Rehber bunu "dağıtık monolit" olarak adlandırıyor. Yaşanan sorunlar, geleneksel bir monolitin sorunlarıyla —sıkı bağlılık ve bir parçayı diğerlerini etkilemeden değiştirme zorluğu— aynıdır; buna ek olarak ağ sıçramalarından (network hops) kaynaklanan gecikme süresi de eklenir.
Bunun yerine ne yapılmalı: Önce temiz bir monolit inşa edin. Net modül sınırları tanımlayın, veri katmanını birleşik tutun ve uygulamanın tek bir birim olarak test edilebildiğinden ve dağıtılabildiğinden emin olun. Bir modülü ancak bağımsız ölçeklendirmeye veya ayrı bir ekip sahipliğine ihtiyaç duyduğunda kendi servisine dönüştürün.
Teknik katmana göre bölme ile iş yeteneğine göre bölme arasındaki fark
Bir diğer sık yapılan hata, servisleri teknik kaygılara —UI, iş mantığı (business logic) veya veri erişimi— göre bölmektir. Bu durum, tek bir işlem için bir isteğin bir servis zinciri boyunca seyahat etmesine zorlar, yanıt sürelerini şişirir ve kırılgan bir bağımlılık grafiği oluşturur.
Daha iyi bir yaklaşım: Servisleri "siparişler", "ödemeler" veya "envanter" gibi iş yetenekleri (business capabilities) etrafında organize edin. Her yeteneğin kendi verisine ve kendi API'sine sahip olmasına izin vererek, bir isteğin katmanlar arasında sıçrama yapması ihtiyacını ortadan kaldırın.
Veri sahipliği önemlidir
İki servis aynı veritabanı tablosuna yazıyorsa, artık bağımsız değillerdir. Rehber, bir servisin asla başka bir servisin tablolarını doğrudan sorgulamaması gerektiğini; her zaman o servisin genel (public) API'si üzerinden gitmesi gerektiğini vurguluyor. Bir veritabanını paylaşmak servisleri birbirine bağlar, izolasyonu bozar ve şema değişikliklerini bir koordinasyon kabusuna dönüştürür.
Senkron HTTP evrensel bir çözüm değildir
Her etkileşim için senkron HTTP'ye güvenmek, tüm sistemi tek bir yavaş servise karşı savunmasız hale getirir. Eğer Servis A, istemciye dönmeden önce Servis B'nin yanıt vermesini bekliyorsa, B'deki herhangi bir yavaşlama A'ya ve nihayetinde kullanıcıya yansır.
Alternatif desenler: Anında yanıt gerektirmeyen görevler için asenkron mesajlaşma kullanın. Mesaj kuyrukları veya arka plan işleri (background jobs), servislerin işi devretmesine ve işlemeye devam etmesine olanak tanıyarak genel sistemin daha dirençli kalmasını sağlar.
Nihai tutarlılığı (eventual consistency) kabul etmek
Geleneksel ilişkisel veritabanları size ACID işlemlerini —Atomicity, Consistency, Isolation, Durability— sunar. Servis sınırları aşıldığında bu garantiler ortadan kalkar. İki aşamalı commit'leri (two-phase commits; dağıtık işlemleri yerel işlemler gibi davranmaya zorlamaya çalışan bir protokol) uygulamaya çalışmak, karmaşıklığa ve istikrarsızlığa yol açar.
Rehber, sagaları (bir dizi telafi edici eylem) veya outbox desenini (bir servisin olayları daha sonra yayınlanmak üzere yerel bir tabloya yazdığı yöntem) önermektedir. Bu yaklaşımlar, verilerin geçici olarak senkronizasyon dışında olabileceğini kabul eder ve iş mantığını bu boşlukları yönetecek şekilde tasarlar.
İlk günden başarısızlığa hazır inşa edin
Bir servisteki hata tüm sistemi çökertmemelidir. Sonsuza kadar beklemeyi önlemek için zaman aşımları (timeouts), geçici hataları yönetmek için geri çekilmeli yeniden denemeler (retries with back-off) ve arızalı bir servise yapılan çağrıları servis düzelene kadar durduran devre kesiciler (circuit breakers) uygulayın. Bu güvenlik önlemlerini bir üretim kesintisi (production outage) sonrasında eklemek için çok geçtir; bunlar en baştaki tasarıma dahil edilmelidir.
Gözlemlenebilirlik (Observability) tartışmaya kapalıdır
Logların birçok konteyner arasında dağıldığı dağıtık bir sistemde hata ayıklamak neredeyse imkansızdır. Merkezi günlükleme (centralized logging), birleştirilmiş metrikler ve istek düzeyinde korelasyon kimlikleri (correlation IDs), mühendislerin tek bir kullanıcı isteğini birden fazla servis arasında hareket ederken izlemesine olanak tanır. İzleme (tracing) araçları çağrı grafiğini görselleştirerek performans darboğazlarının ve hataların yerini tespit etmeyi kolaylaştırır.
Altyapıyı başlangıçta hafif tutun
Kubernetes, güçlü olsa da, dik bir öğrenme eğrisi ve operasyonel yük getirir. Bir avuç servis için Docker Compose, tüm yığını yerel olarak ayağa kaldırmak için yeterli orkestrasyonu sağlar. Daha karmaşık bir platform ancak trafik modelleri, dağıtım sıklığı veya ekip büyüklüğü gerektirdiğinde devreye alınmalıdır.
Servisleri ekip sahipliğiyle uyumlu hale getirin
Mikroservisler, kısmen küçük ve özerk ekiplerin bir servisin tüm yaşam döngüsüne sahip olabilmesi için icat edilmiştir. Eğer tek bir ekip on servisten sorumluysa, koordinasyon maliyetleri çarpıcı bir şekilde artar ve hedeflenen faydaları aşındırır. Rehber, on kişiden az üyeye sahip ekiplerin, modüler geliştirmeye izin verirken basitliği koruyan bir monolit ile daha iyi hizmet alabileceğini öne sürüyor.
Karşı argüman: Mikroservislerin parladığı anlar
Rehber, mikroservislerin doğası gereği kötü olduğunu iddia etmiyor. Bir uygulamanın farklı bölümlerinin birbirinden çok farklı ölçeklendirme gereksinimlerine sahip olduğu veya yasal kısıtlamaların katı veri izolasyonu gerektirdiği ortamlarda, bu desen gerçek bir değer sağlayabilir. Birden fazla ürün hattına sahip büyük organizasyonlar, bağımsız servislerin ekipler arası sürtünmeyi azalttığını ve daha hızlı sürüm döngüleri sağladığını sıklıkla görürler.
Anahtar nokta niyetdir. Eğer bir ekip, belirli bir özellik için saniyede milyonlerce isteği yönetmeleri gerektiği için veya yeni bir ürün hattının ayrı bir iş birimi tarafından sahiplenilmesi gerektiği için mikroservisleri benimsiyorsa, eklenen karmaşıklık haklıdır. Rehberdeki uyarılar, kararın somut gereksinimlerden ziyade popülerlik (hype) tarafından yönlendirildiği durumları hedef almaktadır.
Sırada neye dikkat edilmeli
Daha fazla şirket bulut tabanlı (cloud-native) yığınları benimsedikçe; service mesh, dağıtık izleme (distributed tracing) ve otomatik canary dağıtımları etrafındaki araçlar olgunlaşmaya devam ediyor. Bu ilerlemeler operasyonel engelleri düşürse de rehberde vurgulanan temel tasarım seçimlerini ortadan kaldırmıyor. Ekipler, gözlemlenebilirlik (observability) platformlarının ve asenkron mesajlaşma çerçevelerinin gelişimini takip etmeli, ancak yine de ayağa kaldırdıkları her servis için net bir gerekçeyle yola çıkmalıdır.
Özet
Mikroservisler bir amaca ulaşmak için araçtır, amaçların kendisi değildir. İyi yapılandırılmış bir monolit ile başlayın, her servise kendi verisinin gerçek sahipliğini verin, mümkün olan yerlerde asenkron iletişim kullanın ve dayanıklılık (resilience) ile gözlemlenebilirliği (observability) ilk kod satırından itibaren sisteme dahil edin. İş gerekçesi netleştiğinde servisleri bilinçli bir şekilde ayırın; aksi takdirde mimariyi problemin gerektirdiği kadar basit tutun.
