Blog'a Dön
DevOpsHAProxyPostgreSQLHigh AvailabilityInfrastructure

Yüksek Erişilebilirlikli Altyapı: HAProxy, Patroni ve %99,9 Çalışma Süresi

Umut Korkmaz2025-02-0110 min read

Hosting platformunuz 500 ile 1.000 arasında ücretli müşteriye hizmet veriyor ve yılda yaklaşık yarım milyon dolar gelir işliyorsa, kesinti bir sıkıntı değildir; doğrudan gelire ve güvene vurulan bir darbedir. Makdos hosting platformunu kurup işlettiğim dönemde, %99,9 çalışma süresine ulaşan, yüksek erişilebilirlikli bir altyapı yığını tasarlayıp hayata geçirdim. İşte mimarinin, kullanılan araçların ve bunların ardındaki zorlukla edinilmiş derslerin ayrıntılı bir incelemesi.

Yüksek Erişilebilirlik Neden Tartışmaya Kapalıydı

Makdos bir yan proje değildi. Altyapımız üzerinde gerçek işler yürüten gerçek müşterileri olan, ticari bir hosting platformuydu. Birkaç dakikalık veritabanı kesintisi; kaybolan işlemler, bozulan müşteri panelleri ve çözülmesi günler süren destek talepleri anlamına gelebiliyordu. Düğüm arızalarını, ağ bölünmelerini ve bakım pencerelerini müşteriye yansımadan atlatabilecek bir altyapıya ihtiyacımız vardı.

Hedef, yılda yaklaşık 8,7 saat izin verilen kesintiye karşılık gelen %99,9 çalışma süresiydi. Planlı bakımı, beklenmedik donanım arızalarını ve ara sıra yanlış yapılandırılmış dağıtımları hesaba katana kadar bu cömert gibi gelir. Yığındaki her bileşen, yedeklilik göz önünde tutularak tasarlanmak zorundaydı.

Yük Dengeleme Katmanı: HAProxy

HAProxy bizim giriş kapımızdı. Her HTTP ve HTTPS isteği, herhangi bir uygulama sunucusuna ulaşmadan önce HAProxy'ye düşüyordu. HAProxy'yi, sanal bir IP adresini (VIP) yöneten keepalived ile aktif-pasif bir yapılandırmada çalıştırdık. Birincil HAProxy düğümü çökerse keepalived, VIP'yi saniyeler içinde yedek düğüme taşıyordu.

HAProxy yapılandırması, kendimize özgü trafik desenlerimize göre ince ayarlanmıştı. Yoğun saatlerde aynı anda yaklaşık 200 kullanıcı ağırlıyorduk; bu, küresel ölçeklere göre devasa değildi ama dikkatli bir bağlantı yönetimi gerektiriyordu. Web uygulaması, API ve yönetim paneli için her birinin kendi sağlık kontrolü uç noktaları ve yük dengeleme algoritmalarıyla ayrı arka uçlar yapılandırdım.

Web ve API arka uçları için sunucu ağırlıkları ile dönüşümlü (round-robin) yöntem kullandım. Yeni dağıtılmış sunucular, ısınma süresi boyunca daha düşük ağırlık alıyordu; bu da soğuk önbelleklerin gecikme sıçramalarına yol açmasını önlüyordu. Sağlık kontrolleri iki saniyede bir çalışıyor ve bir sunucu, art arda üç başarısızlıktan sonra devre dışı olarak işaretleniyordu.

SSL sonlandırma, Certbot tarafından otomatik yenilemeyle yönetilen Let's Encrypt sertifikaları kullanılarak HAProxy katmanında gerçekleşiyordu. Bu, uygulama sunucularını sade tutuyordu; yalnızca düz HTTP trafiğini ele almaları yetiyordu.

Veritabanı Yüksek Erişilebilirliği: Patroni ve PostgreSQL

İşlerin ilginçleştiği yer veritabanı katmanıydı. PostgreSQL birincil veritabanımızdı ve onu kaybetmek her şeyi kaybetmek demekti. Otomatik devralma (failover) ile PostgreSQL yüksek erişilebilirliğini yönetmek için Patroni'yi hayata geçirdim.

Patroni, yüksek erişilebilirlikli bir PostgreSQL kümesi kurmak için bir şablondur. Lider seçimini ve küme durumunu yönetmek için dağıtık bir konsensüs deposu kullanır (biz etcd'yi seçtik). Kurulumumuz üç PostgreSQL düğümünden oluşuyordu: bir birincil ve iki eşzamanlı yedek.

etcd kümesi üç ayrı düğümde çalışarak kendi hataya dayanıklı konsensüs grubunu oluşturuyordu. Patroni, PostgreSQL birincilinin sağlığını izliyor ve bir arıza tespit ederse yedeklerden birini otomatik olarak birincil seviyesine yükseltiyordu. Yükseltme süreci tipik olarak 10 saniyenin altında tamamlanıyordu ve eşzamanlı çoğaltma kullandığımız için devralma sırasında sıfır veri kaybı garanti altındaydı.

Patroni'yi kurmak kolay değildi. Dokümantasyon makul, ancak uç durumlar çok sayıda. Arıza senaryolarını test etmek için epeyce zaman harcadım: birincil düğümü öldürmek, PostgreSQL ile etcd arasında ağ bölünmelerini taklit etmek ve etcd'nin kendisinin çoğunluğu (quorum) kaybetmesi durumunda ne olduğunu test etmek. Her senaryo, kümenin dayanıklılığını iyileştiren yapılandırma ince ayarlarını ortaya çıkardı.

Bağlantı Havuzlama: PgBouncer

Uygulama sunucuları ile PostgreSQL kümesi arasında, bağlantı havuzlayıcımız PgBouncer yer alıyordu. PostgreSQL her bağlantı için yeni bir süreç oluşturur ve aynı anda 200 kullanıcı ile arka plan iş parçacıkları bir araya geldiğinde, havuzlama olmadan sunucunun kaynaklarını çabucak tüketirdik.

PgBouncer, işlem havuzlama (transaction pooling) modunda çalışıyordu; bu da bağlantıların tüm oturum boyunca tutulmak yerine her işlemin ardından havuza geri verildiği anlamına geliyordu. Bu, gerçek PostgreSQL bağlantılarının sayısını çarpıcı biçimde azalttı: 300'ün üzerinde bağlantıya ihtiyaç duymaktan, rahatça 50 bağlantıyla çalışmaya geçtik.

PgBouncer ile Patroni arasındaki ince noktalardan biri devralmayı yönetmektir. Patroni yeni bir birincil seviyesine yükseltme yaptığında, PgBouncer'ın trafiği yeni düğüme göndermeye başlaması gerekir. Patroni'nin rol değişikliklerinde çalıştırdığı bir geri çağırma (callback) betiği yazdım; bu betik PgBouncer'ın yapılandırmasını güncelleyip bir yeniden yükleme tetikliyordu. Bu da devralmayı uygulamanın bakış açısından kesintisiz tutuyordu.

Dağıtık Konsensüs: etcd

etcd, küme koordinasyonumuzun bel kemiğiydi. Patroni'nin lider seçimine hizmet etmenin ötesinde, tüm düğümler arasında tutarlı olması gereken yapılandırma verilerini saklamak için de etcd kullandık: özellik bayrakları, hız sınırlama kuralları ve servis keşfi bilgileri.

etcd kümesi dikkatlice boyutlandırılıp ince ayarlandı. etcd'nin ileri yazma günlüğü (write-ahead log) için ayrılmış SSD'ler tahsis ettik; bu, performans açısından kritiktir. Sinyal (heartbeat) aralığı 500 ms, seçim zaman aşımı ise 2500 ms olarak ayarlandı; bu da arıza tespit hızı ile yanlış pozitiflerden kaçınma arasında iyi bir denge sağladı.

etcd'yi izlemek temel önemdeydi. Lider seçimlerini, öneri başarısızlıklarını ve disk fsync gecikmelerini takip eden Prometheus metrik toplama ile Grafana panoları kurdum. Bu metriklerden herhangi birindeki bir sıçrama, altyapı sorunlarının erken uyarı işaretiydi.

Keepalived ve Sanal IP'ler

Keepalived, HAProxy düğümlerinde ve bir çift PgBouncer düğümünde çalışarak her servis için sanal IP adreslerini yönetiyordu. VRRP protokolü, ağ katmanı için saniyenin altında devralma sağlıyordu; bu da düğüm arızaları sırasında kullanıcı deneyimini sürdürmek açısından kritikti.

Keepalived'i basit port kontrollerine güvenmek yerine özel sağlık kontrolü betikleriyle yapılandırdım. HAProxy sağlık kontrolü betiği, HAProxy'nin yalnızca çalışıyor olmadığını, aynı zamanda en az bir sağlıklı arka uç sunucusuna sahip olduğunu da doğruluyordu. PgBouncer sağlık kontrolü ise havuzlayıcının PostgreSQL birincile gerçekten ulaşabildiğini doğruluyordu.

Hepsini Bir Araya Getirmek

Tam istek akışı şöyle görünüyordu: istemci trafiği, aktif HAProxy düğümünü işaret eden keepalived VIP'sine düşüyordu. HAProxy SSL'i sonlandırıyor, isteği inceliyor ve onu uygun arka uç uygulama sunucusuna yönlendiriyordu. Uygulama sunucusu isteği işliyor ve veriye ihtiyaç duyarsa, başka bir keepalived VIP'si üzerinden PgBouncer'a bağlanıyordu. PgBouncer sorguyu PostgreSQL birincile (ya da salt okunur sorgular için bir yedeğe) yönlendiriyor ve sonuç aynı yol üzerinden geri akıyordu.

Bu zincirdeki her bileşenin yedekliliği vardı. Her devralma otomatikti. Ve her geçiş izleniyordu.

İzleme ve Uyarılar

İzlemesi olmayan bir altyapı, sessizce arızalanmayı bekleyen donanımdan ibarettir. Metrik toplama için Prometheus, görselleştirme için Grafana ve bildirimler için Alertmanager kullanarak kapsamlı bir izleme yığını kurdum.

Takip ettiğim temel metrikler arasında HAProxy arka uç yanıt süreleri, PgBouncer havuz kullanımı, PostgreSQL çoğaltma gecikmesi, etcd küme sağlığı ve CPU, bellek, disk G/Ç gibi sistem düzeyindeki metrikler vardı. Uyarılar önem düzeyleriyle yapılandırıldı: 100 ms'nin üzerindeki çoğaltma gecikmesi için bir uyarı, bir saniyenin üzerindeki gecikme için kritik bir alarm.

Cepheden Dersler

En büyük ders, yüksek erişilebilirliğin sonradan eklediğiniz bir özellik değil, en baştan tasarladığınız bir mimari olduğuydu. Tek sunuculu bir kuruluma yüksek erişilebilirliği sonradan eklemek, bunu ilk günden inşa etmekten katbekat daha zordur.

Devralma senaryolarını düzenli olarak test etmek tartışmaya kapalıdır. Düğümleri kasıtlı olarak öldürüp kurtarma sürelerini ölçtüğümüz aylık "kaos" tatbikatları yaptık. Her tatbikat yeni bir şeyi ortaya çıkardı: eksik bir geri çağırma, zamanla kaymış bir yapılandırma ya da bir izleme boşluğu.

Dokümantasyon bir başka kritik yatırımdı. Her çalıştırma kılavuzu (runbook), her kurtarma prosedürü ve her mimari karar belgelendi. Gecenin 3'ünde bir olay yaşandığında, sıfırdan hata ayıklamak yerine bir kontrol listesini izlemem gerekiyordu.

Son olarak, sadelik kazanır. Yüksek erişilebilirlik yığınındaki her ek bileşen, potansiyel bir başka arıza noktasıdır. Daha fazla araç ekleme dürtüsüne direndim ve bunun yerine mevcut bileşenleri sapasağlam kılmaya odaklandım. Burada anlattığım yığın (HAProxy, Patroni, PgBouncer, etcd, keepalived) iyi anlaşılmış, savaşta sınanmış ve bakımı yapılabilir bir yığındır. Bu, en yeni teknolojiye sahip olmaktan çok daha önemlidir.