Aktif-aktif Kubernetes mimarisi için gerçekte neler gerekir
Aktif-aktif, trafiği ikiye bölmek değil, iki bölgede aynı anda tutarlı yazmaktır ve asıl iş veri katmanında biter. Senkron replikasyonun fiyatı, iki bölgeyle quorum kurulamaması, çakışma çözümü, oturum ve önbellek kararları, hangi iş yüklerinin buna hiç uygun olmadığı ve kademeli bir yol haritası.
Bir ekip "aktif-aktif olalım" dediğinde çoğu zaman kastettiği şey şudur: bir bölge çöktüğünde kimse fark etmesin. Bu isteğin karşılığı aktif-aktif olmak zorunda değil. Aktif-aktif, iki bölgenin de aynı anda yazma trafiği kabul etmesi demektir ve bu, altyapı ikizleme işi değil, veri modeli işidir. Trafiği ikiye bölmek birkaç günlük iş. İki bölgede aynı anda tutarlı yazmak, uygulamanın yeniden tasarlanması.
Aktif-pasif ile aktif-aktif arasındaki gerçek fark
Aktif-pasif kurulumda ikinci bölge ayaktadır, veriyi almaya devam eder, ama yazma kabul etmez. Arıza anında birincil rol oraya geçer. Ölçtüğünüz iki sayı vardır: RTO (kesintiden ne kadar sonra ayakta olduğunuz) ve RPO (ne kadarlık veriyi kaybetmeyi göze aldığınız). İyi kurulmuş ve düzenli tatbikat yapılan bir aktif-pasif kurulumda RTO dakikalarla, RPO saniyelerle ölçülür.
Aktif-aktifte kağıt üzerinde ikisi de sıfıra yaklaşır. Bedeli şudur: artık tek bir "doğru" veri kopyanız yok. Aynı kaydı iki bölgede aynı anda değiştiren iki istek olabilir ve birinin kazanması gerekir. Bu kararı ya veritabanı verir ve gecikme ödersiniz, ya uygulama verir ve karmaşıklık ödersiniz, ya da kimse vermez ve sessizce veri kaybedersiniz. Üçüncüsü en yaygın olanıdır, çünkü hiçbir yerde hata üretmez.
Maliyet de iki katı değil. Altyapı iki katı, işletme yükü daha fazla: iki kümede sürüm uyumu, gizli anahtar rotasyonu, şema göçü, iki katı gözlemlenebilirlik ve en zoru, iki bölgeli bir arızayı teşhis edebilecek insan.
Trafik katmanı en kolay kısım
DNS tabanlı yönlendirme en ucuzudur ve en yavaş devredendir. TTL değerini 30 saniyeye çekmeniz devir süresini 30 saniyeye indirmez. Ara çözücülerin bir kısmı TTL'e uymaz, tarayıcılar kendi önbelleklerini tutar, kurumsal ağlarda dakikalarca eski kayıt servis edilir. DNS'i planlı devirlerde kullanın, arıza anında saniyelik tepki beklemeyin.
Anycast tek bir IP'yi birden çok yerden duyurur ve yönlendirme tabloları yakınsadığında trafik kendiliğinden ayakta olan bölgeye akar. Az konuşulan tarafı, yakınsama sırasında açık TCP bağlantılarının farklı bir düğüme düşüp sıfırlanmasıdır: kısa HTTP istekleri için sorunsuz, uzun ömürlü WebSocket bağlantıları için değil.
En iyi çalışan düzen, kenarda anycast ile karşılanan ve arkada sağlık durumuna göre bölge seçen bir yedi katman yönlendiricidir. En sık yapılan hata sağlık kontrolünün yüzeysel olmasıdır. Kontrol koşulsuz 200 dönen bir uç noktaysa, veritabanına yazamayan bir bölge sağlıklı görünür ve trafik almaya devam eder; kontrolün yazma yolunu da içermesi gerekir.
Kubernetes tarafında karar nettir: bölge başına ayrı küme. Tek kümeyi iki bölgeye yaymak etcd'yi bölgeler arası bir Raft grubuna dönüştürür. etcd'nin kendi ayar rehberi heartbeat aralığının üyeler arası ortalama gidiş dönüş süresine yakın olmasını (kabaca 0,5 ila 1,5 katı), seçim zaman aşımının ise heartbeat'in en az 5 ila 10 katı olmasını söyler. Rehberdeki 50 saniyelik seçim zaman aşımı üst sınırı doğrudan küresel dağıtık kümeler için tanımlıdır. Yani yapılabilir, ama lider seçiminiz onlarca saniyeye çıkar ve her ağ dalgalanması yeni bir seçim tetikler. Ayrı kümeler ve üstte bir yönlendirme katmanı çok daha az sürpriz üretir.
Küme içinde bölgesel yakınlık için Kubernetes 1.33'te genel kullanıma geçen trafficDistribution alanı işe yarar. 1.34 ile PreferClose için PreferSameZone adında daha açık bir eşanlamlı geldi:
apiVersion: v1
kind: Service
metadata:
name: api
spec:
trafficDistribution: PreferClose
selector:
app: api
ports:
- port: 80
targetPort: 8080
Bu ayar aynı bölge (zone) içindir, bölgeler arası (region) bir çözüm değil; aynı prensibin küçük ölçeği olduğu için iyi bir ilk adımdır.
Asıl iş veri katmanında
Fiziği es geçemezsiniz. Işık fiberde kabaca saniyede 200.000 km yol alır, yani her 100 km yaklaşık 1 ms gidiş dönüş demektir. Bin km arayla iki bölge arasında en iyi ihtimalle 10 ms, gerçek yönlendirmeyle 15 ila 40 ms görürsünüz. Senkron replikasyonda bu süre her commit'e eklenir.
synchronous_standby_names = 'ANY 1 (replica_a, replica_b)'
synchronous_commit = remote_apply
remote_apply, işlemin karşı taraftaki veritabanına uygulandığı onayı gelene kadar bekler. remote_write yalnızca karşı tarafın kaydı yazdığı onayını bekler; daha hızlıdır ama replikanın işletim sistemi çökerse veri kaybı ihtimali doğar. Bölgeler arası uzaklıkta remote_apply seçerseniz, saniyede 200 küçük yazma yapan bir işi saniyede 25 yazmaya düşürmüş olabilirsiniz. Bu bir hata değil, ödediğiniz fiyattır. Sorun, bu fiyatın canlıya çıkana kadar hiç ölçülmemesidir.
Quorum meselesi: iki bölge çoğunluk kuramaz. İkiye bölünen bir sistemde her iki taraf da diğerinin öldüğü sonucuna varır. Bu yüzden quorum tabanlı her bileşenin, etcd dahil, üçüncü bir hata alanına ihtiyacı vardır; tam kopya olması gerekmez, oy veren küçük bir tanık düğüm yeter. Üçüncü hata alanınız yoksa otomatik devir açmayın, elle onaya bağlayın. İki bölge ve otomatik devir, split-brain üretmenin en kestirme yoludur.
Çakışma çözümünde üç seçenek var ve üçü de tatlı değil:
- Son yazan kazanır. Uygulaması kolay, sessizce veri kaybettirir. Saat kayması varsa "son" yazan gerçekten son yazan olmayabilir.
- CRDT. Sayaçlar, kümeler, ortak metin düzenleme gibi dar bir alanda gerçekten çözer. Sipariş, ödeme, stok gibi iş kurallarına genellemez.
- Kayıt sahipliği. Her kayıt bir bölgeye ait olur, o kaydın yazmaları oraya yönlenir, diğer bölge yalnızca okur. Karmaşıklığı en dürüst dağıtan yöntem budur ve çoğu ekibin uygulayabildiği tek yöntemdir. Sahiplik anahtarı genellikle müşteri ya da kiracı kimliğidir.
Kayıt sahipliğine geçerken karşınıza çıkacak ilk somut sorun otomatik artan birincil anahtarlardır: iki bölge de birden saymaya başlar ve birleştirme anında çakışırlar. Çözüm ya çakışmayan kimlikler, ya da bölge başına farklı başlangıç ve adım değeri verilmiş diziler. İkincisi kolaydır ama üçüncü bölgede yeniden düşünmek gerekir.
Oturum ve önbellek
Yapışkan oturumla aktif-aktif birbirini yer. Oturumu bir bölgenin belleğinde tutuyorsanız kullanıcının diğer bölgeye düşmesi oturumun kaybolması demektir; senkron tutmaya kalkarsanız her istek bölgeler arası bir tur atar. Pratik çözüm oturumu taşınabilir yapmaktır: doğrulaması her bölgede yerel yapılabilen, imzalı ve kısa ömürlü bir jeton. Bunun bedeli iptal listesidir; ama saniyede bir yayılan küçük bir liste, her istekte bölgeler arası sorgudan çok daha ucuzdur.
Önbellekte kural basit: her bölgenin kendi önbelleği olur, bölgeler arasında ortak bir önbellek kümesi kurulmaz. Geçersiz kılma bildirimleri bölgeler arasında en az bir kez ve sırasız gelir. Bu yüzden anahtarları sürümleyin, yani silmek yerine yeni sürüm anahtarı üretin. Sıraya bağlı geçersiz kılma iki bölgede farklı sonuç verir ve teşhisi berbattır.
Gecikme uygulama tasarımını değiştirir
Bölge içinde yarım milisaniye süren bir sorgu, bölgeler arasında 40 ms sürer. Bir istekte yüz küçük sorgu yapan klasik bir N+1 uç noktası tek bölgede 50 ms iken, veri diğer bölgedeyken 4 saniyeye çıkar. Ekiplerin ilk şoku genellikle budur ve mimari bir sorun değildir; yıllardır tolere edilen bir kod deseninin faturası aniden kesilmiştir.
İkinci şok, kendi yazdığını okuma garantisinin kaybolmasıdır. Kullanıcı bir bölgeye yazar, sonraki isteği diğer bölgeye düşer, yazdığını göremez ve tekrar dener. Ya oturum boyunca bölge yapışkanlığı verirsiniz, ki bu aktif-aktifin bir kısmından vazgeçmektir, ya da yazma sonrası kısa bir süre okumaları da yazma bölgesine yönlendirirsiniz.
Üçüncüsü tekrarlardır. Zaman aşımına uğrayan bir istek diğer bölgede yeniden denendiğinde, ilk denemenin başarılı olmuş olma ihtimali vardır. Aktif-aktifte idempotency anahtarı isteğe bağlı bir incelik değil, bütün yazma uçlarında zorunluluktur.
Neyin aktif-aktif olmaması gerekir
- Güçlü tutarlılık isteyen sayaçlar: stok rezervasyonu, kota, bakiye, koltuk satışı. Tek yazma sahibinde kalsınlar.
- Zamanlanmış işler. İki bölgede duran aynı cron iki kez çalışır; uzlaştırma, fatura üretimi ve toplu bildirim bu sınıfa girer. Bunlara bir kiralama mekanizması verin ya da tek bölgede bırakın.
- Şema göçleri. İki farklı şema sürümünün aynı anda aynı veriye yazması geri alınamayan hatalar üretir.
- Tek yazarlı veri depoları ve çoklu birincil için tasarlanmamış ürünler. Belgelerinde bu kelime geçmiyorsa öyle davranmayın.
Kademeli yol haritası
- Tek bölgeyi tekrarlanabilir yapın. Küme kurulumu, ağ, gizli anahtarlar ve sertifikalar kod olarak tanımlı olsun. Yedeği boş bir ortama geri yükleyip çalıştığını gördüğünüz gün gerçek başlangıç noktanızdır.
- Aktif-pasif kurun ve tatbikat yapın. Sürekli replikasyon, devir otomasyonu, ölçülmüş RTO. Tatbikat yapılmayan bir yedek bölge yalnızca pahalı bir umuttur. Buraya kadarki kısım çoğu ekibin gerçek ihtiyacının tamamıdır.
- Okuma trafiğini açın. Okuma replikaları ve bayat okumaya tolerans gösteren uç noktaların işaretlenmesi. Kazanç çift taraflı: gecikme düşer ve ikinci bölge sürekli test edilmiş olur.
- Sınırlı yazma açın. Kayıt sahipliği modeliyle ve önce riski düşük bir alanda: kullanıcı tercihleri, profil, telemetri.
- Genel yazmayı ancak veri deposu bunun için tasarlanmışsa açın.
Üçüncü adımdan sonrasına hiç geçmemek çok makul bir karardır. Nerede durduğunu bilerek durmak, yarım kalmış bir aktif-aktif kurulumdan çok daha iyidir.
Asıl soru
Hangi arıza sizi gerçekten vuruyor? Bölge çapında kesintiler nadirdir. Hatalı sürüm, bozuk göç, dolan disk, süresi geçmiş sertifika ve yanlış yapılandırma çok daha sık kesinti üretir. Aktif-aktif bunların hiçbirine çare değil, çoğuna karşı durumu kötüleştirir, çünkü hatalı sürüm iki bölgeye birden gider. Aktif-aktifin çözdüğü tek şey bölgenin tamamının kaybıdır.
Yatırım kararını da bu soruyla verin. Son bir yıldaki kesintilerinizi listeleyin ve her birinin yanına "aktif-aktif olsaydı engellenir miydi" diye yazın. O sütun çoğu ekipte boş çıkar, ve boş bir sütun, ikinci bölgenin parasını gözlemlenebilirliğe, göç güvenliğine ve tatbikatı yapılmış bir aktif-pasif kuruluma harcamanız gerektiğini söyler.