Bir uygulamayı mikroservislere bölmek; sistem tek bir ekibin yönetemeyeceği kadar büyüdüğünde, bölümleri çok farklı hızlarda değiştiğinde ya da ayrı ayrı ölçeklenmesi gerektiğinde mantıklıdır. Küçük ve orta ölçekli ekipler için ise iyi modüllere ayrılmış tek bir uygulama, yani modüler monolit, çoğu zaman daha doğru seçimdir. Bölmek gerektiğinde de sistem bir kerede yeniden yazılmaz; çalışmaya devam ederken parça parça taşınır.

Mikroservis çoğu zaman ulaşılması gereken bir hedef gibi konuşulur. Oysa her mimari bir maliyetle gelir. Bu yazıda üç yaklaşımı karşılaştırıyor, mikroservislerin gerçek maliyetini, bölme zamanının geldiğini gösteren işaretleri ve güvenli bir geçişin nasıl yapılacağını anlatıyoruz.

Monolit, modüler monolit ve mikroservis: farkı ne?

Monolit, tüm işlevlerin tek bir kod tabanında toplandığı ve tek parça olarak yayına alındığı uygulamadır. Shopify mühendislik ekibinin 2019 tarihli yazısı avantajlarını şöyle sıralıyor: kurulması en basit mimaridir ve ürünü erken yayına almayı sağlar; tek depo, tek test ve dağıtım hattı, tek veritabanı vardır; bileşenler birbirini ağ üzerinden değil doğrudan çağırır.

Sorun, iç sınırlar olmadığında başlar. Aynı yazıda Shopify, kargo ücretini hesaplayan kodun ödeme koduyla yan yana durduğunu ve ikisinin birbirini çağırmasını engelleyen neredeyse hiçbir şey olmadığını anlatıyor. Zamanla her şey her şeye bağlanır.

Modüler monolit, Shopify’ın tanımıyla, kodun tek bir uygulamayı çalıştırdığı ama alanlar (domain) arasında sıkı biçimde uygulanan sınırların bulunduğu yapıdır. Hâlâ tek parça yayına alınır; fark, sipariş, ödeme ve stok gibi modüllerin birbirinin iç yapısına değil, yalnızca tanımlı arayüzlerine erişmesidir. Shopify, var olan en büyük Ruby on Rails kod tabanlarından biri olarak tanımladığı sistemini mikroservislere bölmek yerine modüler monolite dönüştürmeyi seçti.

Mikroservis, James Lewis ve Martin Fowler’ın 2014 tarihli tanımıyla, uygulamayı bağımsız olarak yayına alınabilen servisler kümesi olarak tasarlamaktır. Servisler iş yeteneklerine (sipariş, faturalama gibi) göre ayrılır, birbirleriyle ağ üzerinden konuşur ve genellikle her biri kendi veritabanını yönetir.

KonuMonolitModüler monolitMikroservis
Yayına almaTek parçaTek parçaHer servis ayrı
VeriOrtak veritabanıOrtak veritabanı, modül bazında sahiplikHer servisin kendi verisi
Modüller arası çağrıDoğrudan, sınırsızDoğrudan, tanımlı arayüz üzerindenAğ üzerinden
İşletme yüküDüşükDüşükYüksek
Uygun olduğu durumKüçük ekip, erken aşamadaki ürünBüyüyen ürün, bir ya da birkaç ekipBirçok bağımsız ekip, farklı ölçek ihtiyaçları

Mikroservislerin gerçek maliyeti

Fowler, mikroservislerin getirdiği ek yükü “mikroservis primi” olarak adlandırıyor: Otomatik dağıtım, izleme, hata yönetimi ve nihai tutarlılık (eventual consistency) ek emek ister ve geliştirmeyi belirgin biçimde yavaşlatabilir. Bu maliyeti dört başlıkta toplayabiliriz.

İşletme

Tek bir uygulama yerine çok sayıda servisin ayrı ayrı yayına alınması, sürümlerinin takip edilmesi ve ölçeklenmesi gerekir. Fowler’ın mikroservis ödünleşimleri yazısı, çok sayıda servisi yönetmek için olgun bir operasyon ekibine ve DevOps kültürüne ihtiyaç olduğunu vurguluyor.

Dağıtık veri

Monolitte tek bir veritabanı işlemiyle (transaction) yapılan “siparişi kaydet, stoktan düş” işi, iki servise bölündüğünde zorlaşır. Chris Richardson’ın anlattığı servis başına veritabanı deseninde servisler arası işlemler Saga gibi yöntemlerle, servisler arası sorgular ise API birleştirme ya da CQRS ile çözülür. Hepsi ek tasarım ve kod demektir. Üstelik ağ çağrıları yavaştır ve başarısız olabilir; güçlü tutarlılığı korumak dağıtık bir sistemde çok daha zordur.

Gözlemlenebilirlik

Bir hata artık tek bir log dosyasında değil, isteğin geçtiği birkaç servise dağılmış durumdadır. Merkezi log, metrik ve dağıtık izleme (distributed tracing) olmadan sorunun nerede olduğunu bulmak zorlaşır. OpenTelemetry’nin tanımıyla bir iz (trace), bir isteğin farklı servisler ve sunucular arasındaki yolunu uçtan uca gösterir; bunu kurmak ve işletmek de ayrı bir iştir.

Ekip yapısı ve Conway yasası

Melvin Conway’in 1968’de dile getirdiği Conway yasası, bir sistemin tasarımının onu tasarlayan organizasyonun iletişim yapısını kopyaladığını söyler. Mikroservisler, bir iş yeteneği için gereken tüm becerilere sahip, küçük ve kalıcı ekiplerle iyi çalışır. Altı kişilik tek bir ekibin yirmi servisi yönetmesi ise organizasyonla uyuşmayan bir mimaridir: Herkes her servise dokunur, ama her servis ayrı ayrı işletilir.

Bölme zamanının geldiğini gösteren işaretler

Fowler’ın önerisi açık: Sistem monolit olarak yönetilemeyecek kadar karmaşık hale gelmedikçe mikroservisleri düşünmeyin. Karmaşıklığın kaynakları olarak büyük ekipleri, çok kiracılı (multi-tenant) yapıyı, bağımsız gelişmesi gereken iş işlevlerini ve ölçeklemeyi sayıyor. Pratikte şu işaretlere bakıyoruz:

  • Ekipler birbirini bekliyor; bir ekibin değişikliği diğerinin yayınını engelliyor.
  • Bir bölümün yük profili diğerlerinden çok farklı (yoğun rapor üretimi ya da görüntü işleme gibi) ve onu ayrı ölçeklemek ölçülebilir bir kazanç sağlıyor.
  • İş alanları çok farklı hızlarda değişiyor; biri haftada birkaç kez, diğeri yılda birkaç kez yayına çıkıyor.
  • Bir bölümün güvenlik ya da uyum gereksinimleri diğerlerinden belirgin biçimde ayrışıyor.
  • Yayına alma o kadar uzun ve riskli ki ekip yayın yapmaktan kaçınıyor.

Yanıltıcı işaretler de var. “Kod çok dağınık” tek başına bölme nedeni değildir: Sınırları belirsiz bir monolit bölündüğünde aynı karmaşa, bu kez ağ üzerinden konuşan servislere taşınır. “Ölçeklenemiyoruz” demeden önce de darboğazın nerede olduğu ölçülmelidir; bazen bir veritabanı indeksi ya da önbellek ayarı yeterli olur.

Önkoşullar: CI/CD, otomatik testler ve izleme

Fowler’ın mikroservis önkoşulları yazısı üç temel sayıyor: yeni bir sunucuyu saatler içinde ayağa kaldırabilmek, ciddi sorunları hızla fark eden temel bir izleme düzeni ve servisleri test ve canlı ortama birkaç saat içinde taşıyabilen bir dağıtım hattı. Bunlara geliştirme ve operasyon ekiplerinin yakın çalıştığı bir kültür eşlik etmeli.

Biz bu listeye otomatik testleri de ekliyoruz. Bir servisi tek başına yayına alabilmek için, değişikliğin diğer servislerle olan sözleşmeyi (API) bozmadığını otomatik olarak doğrulayabilmeniz gerekir. Bu temeller yoksa mikroservise geçmek, mevcut sorunları daha çok parçaya bölmek anlamına gelir. Sıra da önemli: Önce CI/CD hattı, testler ve izleme kurulur, sonra ilk servis ayrılır.

Strangler fig ile adım adım geçiş

Sistemi sıfırdan yeniden yazıp bir gecede geçmek cazip görünür, ancak Fowler bu basit görünen planın çoğu zaman başarısız olduğunu gördüklerini yazıyor. Önerdiği alternatif, 2001’de Avustralya’nın Queensland yağmur ormanlarında gördüğü, bir ağacın üzerinde filizlenip zamanla onu saran incir türünden adını alan strangler fig yaklaşımıdır: Yeni sistem eskisinin etrafında büyür, işlevler parça parça taşınır, eski sistem zamanla devreden çıkar.

Strangler fig deseni: önce tüm trafik monolite gider, sonra yönlendirme katmanı ilgili istekleri yeni servise taşır, sonunda monolit küçülür ya da kapanır.
İşlevler yönlendirme katmanının arkasında parça parça taşınır; sorun çıkarsa trafik eski koda geri alınabilir.
  1. Önce monoliti modüllere ayırın. Kod içindeki sınırları netleştirin, modüllerin birbirinin tablolarına doğrudan erişmesini engelleyin. Fowler’ın Monolith First yazısına göre hem API hem veri katmanında sınırları net olan bir monolit, gerektiğinde çok daha kolay bölünür.
  2. İlk parçayı seçin. Bağımlılığı az, sınırı net ve ayrılmasından somut kazanç beklenen bir modülle başlayın. Küçük parçalar daha az risk taşır.
  3. Önüne bir yönlendirme katmanı koyun. Bir API ağ geçidi ya da ters vekil sunucu (reverse proxy), ilgili istekleri kademeli olarak yeni servise yönlendirir; sorun çıkarsa trafik eski koda geri alınabilir.
  4. Veriyi taşıyın. Yeni servis kendi verisinin sahibi olur. Geçiş süresince iki tarafın veriyi senkron tutması gibi geçici çözümler gerekebilir; Fowler bunlara “geçiş mimarisi” (transitional architecture) diyor.
  5. Eski kodu kaldırın ve ölçün. Taşınan işlevin eski kodunu silin, beklenen kazancın gelip gelmediğine bakın, sonra sıradaki parçaya geçin.

Veri sahipliği

Geçişin en çok hafife alınan kısmı veridir. Servis başına veritabanı deseninde her servisin verisi yalnızca ona aittir; diğer servisler bu veriye yalnızca onun API’si üzerinden ulaşır. Aynı tabloları doğrudan okuyan iki servis gerçekte ayrılmış sayılmaz, çünkü birinin şema değişikliği diğerini bozar. Hangi verinin hangi servise ait olduğunu geçiş planının en başında yazılı olarak belirleyin.

Sık sorulan sorular

Yeni bir projeye mikroservisle başlamalı mıyız?

Çoğu durumda hayır. Fowler, başarılı mikroservis örneklerinin neredeyse tamamının fazla büyüyüp bölünen bir monolitle başladığını, baştan mikroservis olarak kurulan sistemlerin ise sıkça ciddi sorunlar yaşadığını gözlemliyor. Servis sınırlarını en baştan doğru çizmek, alanı iyi bilen deneyimli ekipler için bile zordur.

Kaç kişilik bir ekip mikroservis için yeterli?

Kesin bir sayı yok; Fowler da gerçek sistemlerde servis sayısının ve ekip büyüklüğünün çok farklılık gösterdiğini belirtiyor. Kullanışlı bir ölçüt şu: Her servisin onu yayına alan, izleyen ve sorun çıktığında müdahale eden belirli bir sahibi olmalı. Servis sayısı bu sahipliği taşıyabilecek ekip sayısını aşıyorsa, muhtemelen erken bölmüşsünüzdür.

Sonuç

Mikroservisler, bağımsız çalışan birçok ekibin ve farklı ölçek ihtiyaçlarının olduğu sistemlerde değerlidir; ama işletme, veri, izleme ve ekip yapısı açısından belirgin bir maliyet getirir. Küçük ve orta ölçekli ekipler için iyi modüllere ayrılmış bir monolit çoğu zaman daha doğru seçimdir. Bölmek gerektiğinde önce CI/CD, test ve izlemeyi kurmak, ardından strangler fig yaklaşımıyla parça parça ilerlemek riski düşürür.

Mevcut sisteminizin bölünmeye ihtiyacı olup olmadığından emin değilseniz yazılım mimarisi ve DevOps sayfamıza göz atabilir ya da bize yazabilirsiniz. Kararı ekibinizin büyüklüğüne ve sisteminizin yüküne bakarak birlikte verelim.