SD #1: Scalability

Bir websitesi host ettigimizi dusunelim. Gidecegimiz yer bir hosting sirketi olacaktir. Bu hosting sirketi genellikle birden fazla musteriyi belki de ayni makine uzerinde host etmektedir. Birgun gelip de web sitemiz cok populer olursa, artik bu donanim ile web siteye erisim imkansiz ya da cok yavas bir hale gelebilir. Burada iki secenek var:

1- Vertical scaling: Yani daha fazla ram, cpu, disk. Elimizdeki donanimi artirmak. Ornek olarak fazladan bir cpu core'a sahip olmak demek, ayni anda birden fazla requeste gercekten paralel olarak cevap verebilmemize olanak taniyacaktir. Tek bir cpu core ile paralel islem yapmak olanaksizdir. Simdilerde bulmak zor ama tek core olan bilgisayarlarda bile ayni anda birden fazla is yapabiliyoruz. Ama aslinda isletim sistemi her bir uygulama icin cpu'dan sirayla hizmet aliyor ve biz bunu paralelmis gibi algiliyoruz.  

Ancak burada bir limit var. Bir bilgisayarin donanimini en fazla belirli bir olcude artirabilirsiniz. Bir noktadan sonra maliyerler de cok artart ve limite ulaslilir.

2- Horizontal scaling: Madem bize bir makine yetmiyor, ikinci bir makine koyalim yanina ve sitemizi bu iki makineden serv etmeye devam edelim. Bu sayede ziyaretci sayisi artikkca da yeni makineler ekleyebiliriz. Hem de bu makinelerin oyle ozel, yukse donanimli olmasina da gerek yok. Gayet ucuz, commodity hardware adi verilen `cikma` makineler olabilir. Sonucta burada sayimiza guveiyoruz. Yazinin bundan sonraki kisminin horizontal scaling ile ilgili olacagini soyleyebilirim.

Simdi ilk problemimiz su ki, madem birden fazla web serverimiz var, gelen requeste hangisi cevap verecek? Daha da oncesinde, birisi bizim sitenin adresini yazip entere basinca hangi IP adresine yonlecek? Burada birkac farkli strateji var ama genel adi Load Balancing, yani yuku, gelen requestleri eldeki web servarlara (mumkunse dengeli olarak) dagitma problemi. 

Load Balancing

1- Birisi xyz.com olarak sitemizin adresini yazip entere bastigi zaman, load balancer DNS gibi davranip web serverlardan bir tanesinin IP adresini dondurebilir. Bu sayede xyz.com'a giden kullanici direk olarak webservarlardan birtanesi ile muhatap olur. Siradaki ziyaretci de webserver #2'ye yonlendirilir mesela. Bu sayede tum webservarlara ziyaretci yonlendirildikten sonra tekrar #1'e donulebilir. Bu yaklasima round robin adi verilmektedir. 

Bu amacla, acik kaynakli bir DNS yazilimi olan BIND kullanilabilir. Kolayca konfigure edilerek gelen her requesti sirasiyle web serverlara yonlendirmesi saglanabilir. Mesle google.com her bir DNS lookup'ta farkli bir IP adresi dondurmektedir. Yani bizi farkli bir webservera yonlendirmektedir.  

Dezavantajlari: Round robin, bir webserverin suanda yuksek yuk altinda olup olmadigi ile ilgilenmez. Boyle bir bilgiye sahip degildir. Gelen requestleri sirayla dagitir. Bu durumda zaten agir yuk altinda olan bir webservar'a yeni requestler gondermis olabiliriz. Hatta cogu client, DNS lookup'i cache'te sakladigi icin, bir kere bir domain bir IP ile iliskilendirildiginde, cache expire olana kadar hep o IP adresine requestler gitmeye devam eder. Yani bir kullanici computationally heavy islemler yapiyorsa, surekli ayni webserver uzerinde calisiyor olacaktir. 

2- Load balancer kendisi de bir webserver gibi davranabilir. Kendine ait bir public IP adresi olur. xyz.com'a giden kullanicilar direk Load balancer ile muhattap olurlar. Load balancer da, gelen requeste hangi webserverin cevap vermesi gerektigine kendisi karar verebilir. Hatta bu stratejide webserverlarimizin public ip adresina sahip olmasina da gerek yoktur. (IPv4 adresleri git gide tukenmekte oldugu icin, her bir webserver icin public ip adresi almak maliyetli olabilir).

Dezavantajlari: Bir kullanici sitede oturum actigi zaman, yani session olusturuldugu zaman genellikle webserver'in hafizasinda tutulur. En kotu ihtimalle de o webserver'da /tmp klasorune serializer edilerek bu bilgiler yazilir. Eger load balancer, webserver#1'de oturum acmis bir kullaniciyi ikinci requestte webserver#2'ye yonlendirirse, session bilgisi kaybolacaktir. Ve kullaniciya tekrar login olmasi gerektigi soylenecektir. Buna session stickyness problemi adi verilmektedir.

Bu problemi cozmenin bir yolu, bir kullanici bir webserver'a oturum acmissa, bu kullanicidan gelen diger requestleri de ayni webserver'e yonlendirmektir. Yani kullaniciyi hangi webserver ile baslamissa ona bir sekilde baglamaktir. Bunu yapmanin da birkac yolu var:

- Load balancer kullanicinin IP adresi ile hangi webserver'a bagli oldugu bilgisini tutabilir.
- Load balancer ilk response'unu dondururken, client uzerinde cookie olusturur ve icerisine bir session_id kaydeder. Bu client tekrar requestte bulundugu zaman da, load balancer tarafindan bu session_id cookie'den okunur ve hangi webserver'a gitmesi gerektigi bulunur. 


Resime yukaridaki semada, kullanicilar webservarlara baglanmamis. Alttakinde ise yesil kullanici ornegin surekli ayni webserver ile muhattap oluyor, turuncu da ilk login oldugu webserver ile. Bu sayede session bilgisi korunmus oluyor. 

Ancak bu ozellik ile webserver'lari balans'ta tutmak zorlasmaktadir. Cunku bir kere bir webserver ile eslesmis olan kullanici hep o servera'a yonlendirilir. Bir webserver'in asiri yuklenmesi sozkonusu olabilir. 

Diger bir secenek ise, session bilgisini her webserver kendi uzerinde tutmak yerine, merkezi bir noktada tutabilir. Bu durumda kullanicilar ile webserverlari eslestirme karmasiklikligindan kurtulmus olabiliriz. Peki session bilgisinin tutuldugu makine cokerse ne olacak? Eger session bilgisi sadece tek bir makine uzerinde tutulur ise, bu durumda o makine Single Point of Failure olarak tabir edilebilir, yani o giderse tum sistem calisamaz hale gelir. 

Diyelim tum kullanicilarin session bilgileri tek bir makinede tutuluyor. Bu makinenin failure riskini azaltacak ne yapabiliriz? Disk icin RAID kullanilabilir. 

RAID: Redundant Array of Independent Disk anlamina gelen teknoloji olup birden fazla replika harddiski ayni anda calistirma esasina dayanir. Yani harddisklerden birisi bozulsa dahi sistem calismaya devam edecektir. Ve bu harddiskler downtime yasamadan kolay bir sekilde degistirilebilir. 

Bu detayi verdik ama RAID bile, session datasinin tutuldugu tek makinenin SPF olmasini engelleyemiyor. Hala makine cokebilir. Hala tum session datasi ulasilamaz hale gelebilir. Bu problemin cozumune ileride deginecegiz. 

Caching

Html, MySQL ve memcached seviyesinde cachleme yapilabilir. Bunlara gozatalim:

Html caching: Craigslist websitesine baktigimizda, bir post olusturuldugu zaman bir html sayfasi olusturuldugunu goruyoruz. Yani dinamik websitelerinde bir request geldigi zaman, requeste gore database sorgulanir, html templateler render edilir yani aslinda sayfa her requestte bastan render edilir. Ama craigslist, bir post eklendigi zaman direk olarak html outputu uretip bunu static bir content olarak serv etme yolunu secmis. 

Dezavantajlari: Eger bir milyon post varsa, bir milyon html tagi da diskte yazilmis demektir. Ama dinami olarak bu sayfalar olusturulmus olsa, tek bir html template olacaktir ve tek bir html tagi olacaktir. Ayrica, sitede goruluyor ki, bir post olusturduktan sonra onu edit edebiliyorsunuz. Yani aslinda html output olusturmasi yaninda bir de database'e kaydediyor. Yani ayni veri iki kere saklanmis oluyor. 

Ornegin bir noktada, post gorunumunu degistirmek isterlerse, eldeki tum html dosyalarini tekrar olusturmalari gerekecektir. 

Avantajlari: Hersey bir trade-off olduguna gore burada sectikleri tradeoff cok anlasilabilir. Reduntant data saklama tamam, degistirmesi zor tamam. Ama hersey static dosya seklinde ve read sayisi write sayisindan cok daha fazla olan bir site icin static content deliver etmek cok daha performansli olacaktir. 

Memcached: Tamamiyle bilgisayarin hafizasinda yasayan bir key-value store seklinde tanimlanabilir. Ornek olarak kullanici tablosunu sorgulamak maliyetli ise "select * from users where user_id=$id", bu sorgu sonucu gelen datayi memcached uzerinde hafizada tutabiliriz. Bu sayede erisim hizi cok yuksek olacaktir. 

Eger cache, bilgisayar hafizasina sigamayacak kadar buyurse, belirli bir stratejiye kullanarak bir kismini hafizadan silebiliriz. Least recently used anlamina gelen LRU cache'leme tam da boyle yapmaktadir. 

MySQL Query Caching: MySQL ozelikde bahsetsek de bu aslinda tum dbms'ler icin gecerli bir stratejidir. Burada verilen bir query icin, gelen sonucu DB seviyesinde cache'leyebilir ve performans artisi saglayabiliriz. Ayrica MySQL bircok farkli storage engine ile calisabilmektedir. Storage Engine, DB'den bagimsiz olarak verilerin nasil saklandigi ile ilgilenen kisimdir. Ornegin InnoDB, MyISAM, Memory veya Archive gibi storage engine'ler MySQL tarafindan desteklenmektedir. 

Burada memory storage engine secilerek, MySQL ile memcached tarzi bir in-memory table olusturulabilir. Ya da, Archive tipi ile log'lari saklamak icin tablolari compress eden bir engine secilebilir. Query'ler yavas olacaktir ama saklama alanindan kazanc saglanir. 

Replication

En bastaki tasarim problemine donecek olursak, eger session verilerini tek bir DB instance uzerinde saklar isek SPF yaratmis oluyorduk. Bunun onune gecebilmek icin replikasyondan yararlanabilirz. 

Bunun icin de 2 ana mimari bulunmaktadir. Master/slave ve master/master. 

Master/Slave replication
Bu mimaride bir master DB bulunur. Tum yazma islemleri bu master DB uzerine yapilir. Master uzerinde calistirilan sql query'leri aninda slave'lere de yonlendirilerek, master ile tamamen ayni olmalari saglanir. 

Eger master down olursa, slave'lerden bir tanesi master yapilir ve sistem calismaya devam edebilir. Eger yazma miktari okuma miktarindan fazla olan bir sistem sozkonusu ise, slave'lerden okuma yapilabilir. Slave'lerin onune bir load balancer konularak okuma isleri slave'lere dagitilabilir. Bu durumda okuma hizinda ciddi bir iyilesme olacaktir.

Dezavantajlari: Eger mater olurse, slave'lerden bir tanesi master yapilip da requestleri karsilamaya baslayincaya kadarki surede yazma islemi yapilamaz olacaktir. Bu acidan master, yazma islemi icin SPF konumundadir diyebiliriz. 

Master/master replication
Yazma islemleri icin birden fazla master barindiran DB replikasyon mimarisidir. Master/slave mimari, consistency saglanmasinda, yani datanin tum DB'lerde ayni olmasini saglama noktasinda daha kolay bir cozum sunar. Ancak master/master mimaride, birden fazla yazma noktasi oldugu icin, bu degisiklikler diger master'lar uzerine de propagate edilmeli yani yayilmalidir. Farkli masterlar uzerinde concurrent sekilde yapilan degisikler sonucu olusabilecek uyusmazliklarin cozulmesi gerekir. Bunun icin bir konsensus algoritmasi kullanirilir ve tum sistem nihayetinde tutarli hale getirilir. 

Load Balancing + Replication Ornek Mimari

Gunumude tasarlanan bu sistemlere multi-tiered architecture adi verilmektedir.  Asagida ornek bir mimari gorulebilir. Burada ilk sirayi kaplayan web-server katmani, front-end tier olarak adlandirilir. 


Ornek mimari ile ilgili birkac not:
- MySQL master hala bir SPF olarak goze carpiyor. Master/slave yerine master/master mimari ile degistirilebilir.
- Internetten gelen requestleri karsilayan load balancer'in kendisi de aslinda bir SPF durumundadir. 

Partitioning

DB scale etmek icin kullanilabilecek baska bir yontemdir. Datayi belirli bir mantiksal kurala gore farkli DB node'larinda tutmak icin kullanilan genel bir tabirdir.

Ornegin kullanicinin soyadinin ilk harfine gore, A'dan M'ye kadar olanlari bir DB'de; N'den Z'ye kadar olanlari da diger bir DB'de tutabiliriz. Bu durumda partitionlarin schema'lari tamamen ayni olacaktir. 

Horizontal partitioning: Biraz once bahsettigimiz ayni schema'ya sahip partitionlardan olusan, kullanicilari soyadinin ilk harfine gore partitionlara bolen yapiya horizontal partitioning ya da sharding adi verilir. DB'nin partitionlara ayrildigi key'e de sharding key adi verilir. Bu ornekte kullanicinin soyadinin ilk harfi sharding key'dir.

Vertical partitioning: Bazi column'lari baska node'da tutmak ise vertical partitioninge ornektir. Partitionlarin schema'lari  farkli olacaktir. 

HP, VP ve Sharding konusu biraz kafa karistirici olabiliyor. Burada guzel bir cevap bulabilirsiniz.

Horizontal partitioning'de, node'lardan birisi down olursa, sadece o kismi ilgilendiren datalarin yazma/okumasi ile ilgili sikinti olusur, diger taraf aynen calismaya devam eder. Hatta cogu replikasyon sisteminde, ayni partitiondan birden fazla olusturularak bu problemin de onune gecilir. 

Baska bir ornek de, Facook'un ilk yillarinda okullara gore database kurdugu soylenir. Yani mit.facebook.com ya da harvard.facebook.com gibi. Esas problem facebook'un iki okulun tum kullanicilarini ayni anda kaldiracak kapasitesi yoktu ve boyl bir yol izlediler. Bu yuzden bazi ozellikler sadece kullanicinin kendi networku icerisinde gerceklestirilebiliyordu. 

High Availability

Yukaridaki mimari orneginde de gorebilecegimiz gibi, replikasi olmayan, redundancy icermeyen her bilesen aslinda bir Single point of failure durumundadir. Load balancer da bunlardan bir tanesi. Yine yukaridaki mimaride, en tepedeki load balancer down olursa, tum mimari coker ve hicbir requeste cevap verilemez hale gelinir.

Bunu yine isin icine redundancy katarak cozecegiz ama ufak bir farkla. Bu sefer iki redundant yapinin uzerinde requestleri dagitan bir yapi olmayacak. Zaten boyle olursa bu sefer bu yapi SPF olur. Bunu yerine ayni mimariden iki adet olusturacagiz. Ancak load balancerlardan birtanesi sadece aktif durumda olacak ve digeri (ya da digerleri) pasif durumda bekleyecek. Pasif olan load balancerlar, aktif olandan surekli bir heartbeat sinyali alirlar. Aslinda cok basit bir TCP paketi. Ornegin her saniye. Eger bu kalp atis sinyali gelmez ise, aktif load balancerin down oldugu anlasilir ve pasif olan load balancer kendisini aktif load balancer yapar. 

Kaynak: Digital Ocean: High availability

Ancak burada da yine bir problem var. Internetten gelen trafigi onceden pasif olan load balancere aktarabilmek icin DNS kayitlarinda degisiklik yapilmasi gerekir. Yani bizim sitemizin domain adina karsilik gelen IP adresi eski aktif (sonradan down olan) load balancer'dan yeni aktive edilene degistirilmesi gerekir. DNS kaydindaki bu degisikligin tum internet uzerine dagilabilmesi ciddi zaman alabilir. Bu arada da ciddi bir downtime yasanabilir. 

Bu problemi daha once de bahsettigimiz Dns Round robin load balancing yontemi ile asabiliriz. Yani load balancerlarin onune, DNS gibi davranan bir load balancer daha koyup, bunun round robbin seklinde gelen trafigi load balancerlara yonlendirmesini saglayabiliriz. Bu durumda sisteme yapilan yeni requestler yeni load balancerin public IP adresine yonlendirilecektir. Ancak, daha onceden sisteme request atmis ve IP adresini cache'lemis olan clientlar icin problem cozulemez. Onlar sisteme ulasamamaya devam ederler. 

Bir baska cozum ise floating-ip yapisi kullanmaktir. Bu yapida, IP adresi sabit kalir, yani DNS kaydini guncellemeye gerke yoktur. Mevcut IP adresi, failure oldugu zaman, yeni load balancere verilir. Bu sayede IP adresini onceden cahce'lemis olan clientlar dahi problemden etkilenmez. Bunun nasil yapildigi yine digital ocean'in makalesinde gosterilmis:


Floating-ip yaklasimiyla, load balancerlarimiza bile redundancy kazandirip sistemi daha reliable hale getirsek bile, tum sistem hala tek bir data-center icerisinde bulunuyor. Zaman zaman AWS'nin dahi yasadigi gibi bazi datacenterlar tamamen down olabiliyor. Bu durumda ayni mimariden cografi olarak farkli yerlerde de bulunmasi gerekiyor. Ve tabi ki trafigin bunlar arasinda da dagitilmasi gerekli. 

Security

Yine bastaki ornek mimariye bakarsak, internetten gelen tum requestleri kabul etmemiz gerekir. Adim adim hangi katmanda neye izin verecegimizi belirlememiz gerekiyor.

Firewall: Internet ile data-center ya da networkumuz diyelim, arasinda bir firewall olmasi gerekiyor. 

Web trafigi http ve https icin, TCP : 80 , 443 izin verilmelidir.
Egere ssh baglantisi da istiyor isek, TCP : 22 de acilabilir. 

Load balancer'dan Webserver'lara: Bu noktadan sonra encription cok gerekli degildir. Eger ki, sistem bir sekilde compromise olursa, belki ic haberlesmenin de encrypted olmasi ise yarayabilir ama yine de elde edilecek olan performans artisi icin load balancer'dan sonrasinda tamemen http kullanilmasi yaygin bir pratiktir. Cunku http encryption ve de sertifika validasyonu icermedigi icin cok daha hizli iletisim saglamaktadir. O acidan load balancer'dan webservar'lara giden trafikte sadece TCP:80 acilmasi yeterli olacaktir. 

Webserver'dan database'e: Sql queryleri MySQL icin TCP:3306 uzerinde tasinmaktadir. Webserverlardan disariya bu portun acilmasi yeterli olacaktir. Bu sayede, bir sekilde webserver compromise olsa ve ele gecirilse dahi, diger webserverlara ve sistemin baska bilesenlerine ssh veya benzer sekillerde sizamayacaktir. Sadece database uzerinde islem yapabilecektir.

----

Buradaki notlar genel olarak Scalability Lecture'dan tutttugum notlardir. Bazi eklemeler yaptim. 

Diger bir postta gorusmek uzere. Stay hungry, stay foolish, stay healthy :)


Yorumlar

Bu blogdaki popüler yayınlar

Python'da Multithreading ve Multiprocessing

Threat Modeling 1