Load balancer ne yapar
Bir load balancer, istemcilerle hepsi aynı isteğe cevap verebilen bir sunucu grubu arasında durur ve her isteğin hangi sunucuya gideceğine karar verir. İstemciler tek bir adrese bağlanır; load balancer sağlıklı bir backend seçer, isteği iletir ve cevabı geri taşır.
Üç iş yapar. Yükü dağıtır: üç sunucu, kabaca tek sunucunun üç katı trafik taşıyabilir. Arızalı sunucuları çıkarır: sağlık kontrolleri cevap vermeyi bırakan bir backend'i fark eder ve o düzelene kadar kimseyi ona göndermez. Ve değişiklikleri görünmez kılar: bir sunucuyu çıkarabilir, yamalayabilir, deploy edebilir ve geri koyabilirsiniz; ziyaretçiler hata görmez.
Çoğu zaman en önemlisi ikinci iştir: bir load balancer arkasındaki iki sunucu kapasiteden çok, birinin ölmesine izin verebilmekle ilgilidir. Layer 7 load balancer, işi özdeş backend'ler arasında seçim yapmak olan bir tür reverse proxy'dir; HAProxy ve nginx ikisini de yapar.
Layer 4 ve layer 7 yük dengeleme
Katman, load balancer'ın karar vermeden önce trafiğin ne kadarını okuduğudur.
Layer 4 load balancer TCP bağlantıları ve UDP paketleriyle çalışır. Kaynak ve hedef adresleri ve portları görür, bağlantı açılırken bir backend seçer ve baytları iki yönde kopyalar. URL'i, başlığı ya da çerezi göremez; bu yüzden o bağlantıdaki her istek aynı sunucuya gider. Karşılığında hızlıdır, protokolden bağımsızdır (veritabanları, MQTT, SMTP, oyun sunucuları) ve TLS'i olduğu gibi geçirebilir; sertifika backend'lerde kalır. HAProxy'nin mode tcp'si ve nginx'in stream {} bloğu burada çalışır.
Layer 7 load balancer HTTP konuşur. Bağlantıyı sonlandırır, her isteği okur ve host, yol, başlık ya da çereze göre yönlendirebilir: /api bir havuza, / başka bir havuza, bir canary çerezi yeni sürüme. Başarısız bir isteği başka bir sunucuda yeniden deneyebilir ve tek bir HTTP/2 bağlantısının isteklerini birkaç backend'e dağıtabilir (HAProxy mode http, nginx http {}).
HTTP'yi okumak için layer 7 dengeleyicinin onu çözmesi gerekir; yani TLS sonlandırma işin doğasında vardır. Sertifika load balancer'da durur, backend'ler özel ağ üzerinden düz HTTP alır ya da ağa güvenilmiyorsa ikinci bir TLS bağlantısı. Bedeli, backend'in artık istemciyi değil load balancer'ı görmesidir. Layer 7 dengeleyiciler bunu X-Forwarded-For ve X-Forwarded-Proto ile çözer; layer 4 dengeleyiciler ise backend'in kabul edecek şekilde yapılandırılması gereken PROXY protokolünü kullanır.
Dengeleme algoritmaları: round robin, least connections, hashing, ağırlıklar
Round robin istekleri sırayla her sunucuya verir. Neredeyse her yerde varsayılandır ve isteklerin maliyeti yaklaşık aynıysa ve sunucular özdeşse doğrudur.
Ağırlıklı round robin büyük sunucuya büyük pay verir: 2, 2 ve 1 ağırlıklarıyla üçüncü sunucu trafiğin beşte birini alır. Canary de ağırlıklarla yapılır: yeni sürümü havuza küçük bir ağırlıkla koyun, hata oranını izleyin, sonra ağırlığı artırın.
Least connections sıradaki isteği en az aktif bağlantısı olan sunucuya gönderir. İstek maliyeti çok değişkense (sayfa görüntülemelerinin yanında raporlar, API çağrılarının yanında yüklemeler) ya da WebSocket'lerde olduğu gibi bağlantılar uzun ömürlüyse kullanın.
IP hash (HAProxy balance source, nginx ip_hash) her istemci adresini her seferinde aynı sunucuya eşler. Çerez olmadan bağlılık (affinity) sağlar, ama dağılım istemci kitlesini izler: ortak bir adresin arkasındaki tek bir ofis ya da tek bir mobil operatör binlerce kullanıcıyı tek bir backend'e yığabilir.
Bir anahtar (URI, kullanıcı ID'si) üzerinde consistent hashing aynı anahtarı aynı sunucuda tutar; bir sunucu eklendiğinde ya da çıkarıldığında anahtarların yalnızca küçük bir kısmı yer değiştirir. Önbelleklerin önünde istenen budur: URI'ye göre hash'leyin, her nesne hepsinde değil tek bir önbellekte yaşasın. nginx bunu hash $request_uri consistent; olarak, HAProxy hash-type consistent ile birlikte balance uri olarak yazar.
Hangisini seçerseniz seçin, algoritma doğruyu söyleyen sağlık kontrollerinden daha az önemlidir.
Sağlık kontrolleri ve failover
Bir load balancer, hangi sunucuların ayakta olduğuna dair bilgisi kadar iyidir.
Aktif sağlık kontrolleri her backend'i bir zamanlayıcıyla yoklar: bir TCP bağlantısı açar ya da /healthz gibi bir URL ister ve 200 bekler. Arka arkaya fall kadar başarısızlıktan sonra sunucu down işaretlenir ve hiçbir şey almaz; rise kadar başarıdan sonra geri gelir. HAProxy bunu açık kaynak sürümde yapar. Açık kaynak nginx yapmaz: max_fails ve fail_timeout pasif kontrollerdir; başarısız gerçek istekleri sayar ve sunucuyu bir süre dinlendirir. nginx'te aktif kontroller ticari NGINX Plus'ın parçasıdır. Zamanlama bir ödünleşimdir: fall 3 ile iki saniyede bir yapılan kontrol ölü bir sunucuyu yaklaşık altı saniyede çıkarır; 30 saniyede bir yapılan kontrol bir buçuk dakikalık hata demektir.
Failover bundan sonra olandır. Trafik kalan sunuculara kayar; bu yüzden onlarda yer olmalıdır: her biri %80'de çalışan üç sunucu birinin kaybını kaldıramaz. Bir backup sunucusu yalnızca bütün birincil sunucular down olduğunda trafik alır; soğuk yedek ya da bakım sayfası için uygundur.
Sağlık uç noktası, load balancer'ın sorduğu soruya cevap versin: *bu sunucu şu anda trafik almalı mı?* Bu, sürecin kendisine ait olanı kontrol etmek (başladı mı, kendi bağımlılıklarına ulaşabiliyor mu, kapanma sürecinde mi) ve hızlı dönmek demektir. Deploy sırasında önce kontrolü başarısız yapın, yoldaki isteklerin bitmesine izin verin (connection draining), sonra süreci durdurun.
Oturum kalıcılığı (sticky session)
Bir uygulama giriş yapmış kullanıcının oturumunu tek bir sunucunun belleğinde tutuyorsa, sonraki isteğin o sunucuya ulaşması gerekir; yoksa kullanıcının oturumu kapanır. Oturum kalıcılığı (session persistence) load balancer'ın kimin nereye gittiğini hatırlamasını sağlar.
Olağan yöntemler şunlardır: eklenen çerez (load balancer sunucuyu adlandıran bir çerez ekler; HAProxy bunu cookie SRV insert indirect nocache ve her server satırındaki bir cookie değeriyle yapar), öğrendiği bir uygulama çerezi (PHPSESSID, JSESSIONID) ya da kaynak IP hash'i. Açık kaynak nginx'te yalnızca hashing yöntemleri vardır (ip_hash ya da hash $cookie_name); sticky direktifi NGINX Plus'a aittir.
Yapışkanlığın bir bedeli var. Yük dengeli olmaktan çıkar, çünkü birkaç ağır kullanıcı bir sunucuya çakılı kalır. Bir sunucuyu boşaltmak en uzun oturumu kadar sürer ve bir sunucu öldüğünde kullanıcıları oturumlarını yine kaybeder.
Kalıcı çözüm sunucuları birbirinin yerine geçebilir yapmaktır: oturumları Redis ya da veritabanı gibi ortak bir depoda veya imzalı bir çerezde tutun ve her isteğe herhangi bir sunucu cevap versin. O zaman arızalanan bir sunucu kimsenin oturumuna mal olmaz.
Çalışan bir HAProxy yapılandırması
Bir HAProxy yapılandırması yukarıdan aşağı okunur: süreç için global, her bölümün miras aldığı defaults, bağlantıları kabul eden bir frontend ve havuzu tutan bir backend.
Örnek TLS'i 443'te sonlandırır (.pem dosyası sertifika zincirini ve özel anahtarı birlikte tutar), düz HTTP'yi yönlendirir, iletim başlıklarını ekler ve least connections ile dengeler. Sağlık kontrolü Host başlıklı gerçek bir HTTP isteğidir, çünkü pek çok uygulama bu başlık olmadan gelen isteğe 404 ya da yönlendirmeyle cevap verir ve 200 bekleyen bir kontrol sağlıklı bir sunucuda başarısız olur. http-check send HAProxy 2.2 ya da daha yenisini gerektirir; eski sürümler isteği option httpchk satırına yazar.
default-server kontrol zamanlamasını bir kez ayarlar: iki saniyede bir yoklama, sunucuyu down işaretlemek için üç başarısızlık, geri getirmek için iki başarı. app3 daha küçük bir makinedir ve diğerlerinin yarısı kadar pay alır; spare yalnızca üçü de down olduğunda trafik alır. Oturumları yapışkan yapmak için backend'e cookie SRV insert indirect nocache, her server satırına da cookie app1 (ve benzerlerini) ekleyin.
Her reload'dan önce dosyayı haproxy -c -f /etc/haproxy/haproxy.cfg ile kontrol edin.
/etc/haproxy/haproxy.cfg: TLS sonlandırma, least connections, HTTP sağlık kontrolleri
global
log /dev/log local0
maxconn 20000
defaults
mode http
log global
option httplog
timeout connect 5s
timeout client 60s
timeout server 60s
frontend fe_web
bind :80
bind :443 ssl crt /etc/haproxy/certs/example.com.pem alpn h2,http/1.1
http-request redirect scheme https unless { ssl_fc }
option forwardfor
http-request set-header X-Forwarded-Proto https
default_backend be_app
backend be_app
balance leastconn
option httpchk
http-check send meth GET uri /healthz ver HTTP/1.1 hdr Host app.example.com
http-check expect status 200
default-server inter 2s fall 3 rise 2
server app1 10.0.0.11:8080 check weight 100
server app2 10.0.0.12:8080 check weight 100
server app3 10.0.0.13:8080 check weight 50
server spare 10.0.0.20:8080 check backup
nginx upstream ile yük dengeleme
nginx'te havuz bir upstream bloğudur ve proxy_pass ona adıyla işaret eder. Algoritma satırı yoksa nginx ağırlıklı round robin kullanır; least_conn, ip_hash, hash … consistent ve random bunu değiştirir.
Örnekteki birkaç ayrıntı kolayca gözden kaçar. keepalive 32 backend'lere giden boştaki bağlantıları yeniden kullanmak için açık tutar, ama yalnızca proxy_http_version 1.1 ve boş bir Connection başlığıyla birlikte çalışır; bu iki satır olmadan nginx her istek için yeni bir bağlantı açar. max_fails=3 fail_timeout=10s, on saniye içinde üç başarısız istekten sonra bir sunucuyu on saniyeliğine çıkarır: nginx'in pasif sağlık kontrolü budur. backup yalnızca round robin, ağırlıklı ve least_conn yöntemleriyle çalışır, hashing yöntemleriyle çalışmaz.
proxy_next_upstream, başarısız bir isteğin ne zaman sıradaki sunucuda yeniden deneneceğine karar verir. nginx varsayılan olarak bağlantı hatalarında ve timeout'larda yeniden dener; 1.9.13'ten beri non_idempotent eklemediğiniz sürece bir POST, LOCK ya da PATCH'i asla yeniden denemez. Böyle bırakın: ilk sunucu kartı çektikten sonra timeout'a düştü diye bir ödemeyi yeniden denemek, bir hatadan daha kötüdür. proxy_next_upstream_tries 2, tek bir yavaş isteğin bütün havuzu dolaşmasını engeller. Başlıklar her reverse proxy'nin ihtiyaç duyduklarıdır; gerisi nginx rehberinde.
nginx: ağırlıklar, pasif kontroller, bir yedek ve keep-alive içeren bir upstream havuzu
upstream app {
least_conn;
server 10.0.0.11:8080 weight=2 max_fails=3 fail_timeout=10s;
server 10.0.0.12:8080 weight=2 max_fails=3 fail_timeout=10s;
server 10.0.0.13:8080 max_fails=3 fail_timeout=10s;
server 10.0.0.20:8080 backup;
keepalive 32;
}
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /etc/ssl/example.com/fullchain.pem;
ssl_certificate_key /etc/ssl/example.com/privkey.pem;
location / {
proxy_pass http://app;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_next_upstream error timeout http_502 http_503;
proxy_next_upstream_tries 2;
proxy_connect_timeout 3s;
}
}
Global ve yerel: DNS, GeoDNS, anycast ve bulut load balancer'ları
Yukarıdaki her şey yerel yük dengelemedir: tek bir site, tek bir havuz, istek yolunda bir proxy. Global yük dengeleme ise bir ziyaretçinin en başta hangi siteye, bölgeye ya da veri merkezine ulaşacağına karar verir ve genellikle herhangi bir proxy isteği görmeden önce yapılır.
DNS round robin en eski biçimdir: birkaç A kaydı yayımlarsınız, resolver'lar bunları dönen bir sırayla dağıtır. Maliyeti yoktur ve trafiği kabaca dağıtır, ama DNS sağlık durumundan habersizdir. Ölü bir sunucunun adresi biri onu kaldırana kadar dağıtılmaya devam eder; resolver'lar ve tarayıcılar eski cevabı kaydın TTL'i boyunca, bazen daha uzun süre önbellekte tutar.
GeoDNS sorgunun nereden geldiğine göre farklı cevap verir; böylece bir ülkedeki ziyaretçiler yakındaki sitenin adresini alır. DNS tarafındaki sağlık kontrolleriyle birleşince DNS failover olur: kontrollerini geçemeyen bölge cevaplardan çekilir. Sınırları aynı TTL önbelleklemesi ve konumun ziyaretçinin değil resolver'ın konumu olmasıdır; resolver istemcinin adresinin bir kısmını (EDNS Client Subnet) iletiyorsa bu değişir. TTL'ler ve resolver'lar DNS rehberinde ayrıntılı olarak anlatılıyor.
Anycast aynı IP adresini BGP üzerinden birçok konumdan duyurur ve internetin yönlendirmesi her paketi en yakınına teslim eder. Beklenecek bir TTL yoktur: bir konum duyurmayı bıraktığında rotalar saniyeler ile dakikalar içinde kayar. Büyük DNS servisleri ve CDN'ler düğümlerine böyle ulaşır. Katmanlar üst üste biner: DNS ya da anycast konumu seçer, içindeki bir proxy sunucuyu seçer.
Bulut load balancer'ları aynı fikirleri yönetilen bir servis olarak paketler. AWS'te Application Load Balancer (layer 7) ve Network Load Balancer (layer 4) vardır; Google Cloud Load Balancing global ve bölgesel, uygulama ve ağ varyantları sunar; Azure, Azure Load Balancer'ı (layer 4) Application Gateway'den (layer 7) ayırır. Kubernetes'te bir Service bağlantıları pod'lara dağıtır, bir Ingress controller da layer 7 yönlendirmeyi yapar. Bu rehberdeki algoritmalar, sağlık kontrolleri ve tuzaklar onlar için de aynen geçerlidir.
Kesintiye yol açan yaygın hatalar
Load balancer tek hata noktasıdır. Tek bir HAProxy kutusunun arkasında üç uygulama sunucusu, sizi artık o kutunun düşüreceği anlamına gelir. İki tane çalıştırın ve aralarında VRRP ile kayan bir IP taşıyın (olağan araç keepalived'dır) ya da öne yönetilen ya da DNS seviyesinde bir katman koyun. Sonra aktif olanı kapatarak test edin.
Sağlık kontrolü yalan söyler, iki yönde de. Her zaman 200 dönen bir /healthz, veritabanı bağlantı havuzu tükenmiş bir sunucuyu rotasyonda tutar; panel her şeyi yeşil gösterirken isteklerin bir kısmı başarısız olur. Tersi de aynı derecede kötüdür: ortak veritabanını sorgulayan bir kontrol, beş saniyelik bir veritabanı aksamasında *bütün* sunucuları down işaretler ve kısa bir yavaşlama tam bir kesintiye dönüşür. Sunucuyu kontrol edin, bütün sunucuların paylaştığı sistemleri değil.
Uyumsuz timeout'lar. Backend boştaki keep-alive bağlantıları load balancer'ın beklediğinden önce kapatıyorsa, dengeleyici ara sıra backend'in az önce kapattığı bir bağlantıdan istek gönderir ve istemci rastgele bir 502 alır. Backend'in keep-alive timeout'unu load balancer'ın idle timeout'undan uzun tutun. Diğer nedenler 502 Bad Gateway rehberinde.
İstemcinin adresi kaybolur. X-Forwarded-For (layer 7) ya da PROXY protokolü (layer 4) olmadan uygulama load balancer'ın IP'sini loglar, ona rate limit uygular ve onun konumunu belirler.
Özdeş olmayan sunucular. Farklı build'ler, farklı yapılandırma ya da varsayılan ETag'i değiştiren farklı dosya zaman damgaları; böylece diğer sunucu cevap verdiğinde tarayıcının yeniden doğrulaması 304 yerine tam bir 200 döner. Her yere tek bir artefact deploy edin.
Test etmek basittir. Test sırasında backend'in adını bir yanıt başlığında gösterin ve istekleri döngüyle gönderin; dağılımı izleyin, sonra bir backend'i durdurup isteklerin kaymasını izleyin.
# which backend answered? (expose the server name in a debug header first)
for i in $(seq 1 10); do
curl -s -o /dev/null -D - https://example.com/ | grep -i '^x-served-by'
done
# HAProxy: live state of every server through the runtime socket
# (needs "stats socket /run/haproxy/admin.sock mode 660 level admin" in global)
echo "show servers state be_app" | socat stdio /run/haproxy/admin.sock
# take one server out gracefully before a deploy, then put it back
echo "set server be_app/app1 state drain" | socat stdio /run/haproxy/admin.sock
echo "set server be_app/app1 state ready" | socat stdio /run/haproxy/admin.sock
CDN'in yeri ve CDN.com.tr'nin yaptığı
Bir CDN, sizin kurmanız gerekmeyen global yük dengelemedir. Ziyaretçiler yakınlarındaki bir edge sunucusuna yönlendirilir, edge'ler yapabildikleri her şeye önbellekten cevap verir ve yalnızca önbellekte olmayanlar origin'inize gider. CDN.com.tr, Türkiye'de ve yurt dışında edge sunucuları çalıştırır ve ziyaretçileri bunlara dağıtır; yani ziyaretçileri konumlara dağıtma işi, istek sizin işlettiğiniz herhangi bir şeye ulaşmadan önce zaten yapılmış olur.
Sizde kalan origin'dir. Pull CDN sitesinde edge, panelde ayarladığınız Kaynak Adresi'nden (bir IP ya da alan adı) içerik çeker. Birkaç origin sunucusu çalıştırıyorsanız, aralarında seçim yapan load balancer o adresin arkasında durur ve bu rehberdeki her şey ona uygulanır. Onu yalnızca edge'den gelen bağlantıları kabul edecek şekilde kilitleyin: panelin kendi önerisi, tek yol edge'den geçsin diye origin'in adresini herkese açık DNS'te tutmamaktır.
Edge, origin arızalarını da yumuşatır. Edge önbelleğindeki içerik, origin kapalıyken TTL'i boyunca sunulmaya devam eder ve Trafik kalitesi raporu, origin yavaş ya da arızalıyken sunulan önbellek kopyasını STALE olarak işaretler. Aynı rapor origin sağlığını gösterir: origin'e giden istekler, yeniden deneme payı, bağlantı hataları (502/504) ve origin'in ortalama ilk bayt süresi. Önbellekte olmayan ve süresi dolmuş nesneler yine çalışan bir origin gerektirir; dinamik sayfalar için origin'in kendi yedekliğine yine ihtiyaç duymasının nedeni budur.
O katmanı hiç işletmek istemiyorsanız, CDN.com.tr'deki container uygulamaları her uygulama için bir replika sayısı ve bir sağlık kontrolü (HTTP yolu, TCP ya da yok) alır. Trafik edge'den girer; birkaç replika varken bir container yeniden başlıyorsa ya da sağlıksızsa diğerleri sunmaya devam eder ve rollout'lar sağlık kontrolüne bağlıdır; yeni bir sürümü deploy etmek uygulamanın erişilemez olduğu bir pencere açmaz. Oturumlar yönetilen Redis'e gider; böylece sonraki isteği hangi replika sunarsa sunsun kullanıcının oturumu açık kalır: yukarıda önerilen durumsuz tasarım, sticky çerez olmadan.
Load balancer hakkında sık sorulanlar
Yük dengeleme için HAProxy mi, nginx mi?
İkisi de hızlı ve güvenilir. HAProxy ücretsiz sürümde aktif sağlık kontrollerine, çerezle yapışkanlığa, sunucuları boşaltmak için bir runtime API'ye ve çok ayrıntılı istatistiklere sahiptir; bu da onu daha eksiksiz bir saf load balancer yapar. Aynı makine dosya da sunuyor, önbellekliyor ya da birkaç sitenin yönlendirmesini yapıyorsa nginx daha uygundur, ama ücretsiz sürümünde yalnızca pasif sağlık kontrolleri vardır.
Layer 4 ile layer 7 yük dengeleme arasındaki fark nedir?
Layer 4 her TCP bağlantısı ya da UDP akışı için bir sunucu seçer ve içeriği hiç okumaz; bu yüzden her protokolle çalışır ve TLS'i dokunmadan geçirebilir. Layer 7 her HTTP isteğini okur; bu yüzden URL, başlık ya da çereze göre yönlendirebilir, başka bir sunucuda yeniden deneyebilir ve iletim başlıkları ekleyebilir, bedeli TLS'i sonlandırmaktır.
Hangi yük dengeleme algoritmasını kullanmalıyım?
Özdeş sunucular ve benzer istekler için round robin ile başlayın. İstek süreleri çok değişiyorsa ya da WebSocket'lerde olduğu gibi bağlantılar açık kalıyorsa least connections'a geçin. Aynı anahtarın aynı sunucuya ulaşması gerekiyorsa, örneğin bir önbellek katmanının önündeki URL'ler için, consistent hashing kullanın.
DNS round robin gerçek bir load balancer mı?
Trafiği dağıtır, ama sağlık kontrolü yapmaz ve cevapları kaydın TTL'i boyunca önbellekte tutulur; istemciler ölü bir adresi denemeye devam eder. Her birinin kendi load balancer'ı olan siteler arasında kaba dağıtım için uygundur, tek failover mekanizması olarak zayıftır.
CDN kullanıyorsam load balancer'a ihtiyacım var mı?
CDN ziyaretçileri kendi edge sunucularına dağıtır ve trafiğin çoğunu önbellekten karşılar. Origin'iniz tek bir sunucuysa kapasite için dengeleyiciye ihtiyacınız yoktur, ama önbellekte olmayan her şey için origin yine tek hata noktasıdır. İki ya da daha fazla origin sunucusu çalıştırdığınızda, CDN'in içerik çektiği origin adresinin arkasına bir load balancer koyun.