Teknoloji

HAProxy ile L4/L7 Yük Dengeleme Nasıl Sıfır Kesinti Sunar? Health Check, Sticky Sessions ve TLS Passthrough’u Sade Sade Konuşalım

Bir Akşamüstü Trafik Fırtınası ve Aklıma Düşen HAProxy

Hiç başınıza geldi mi? Trafik sanki sözleşmiş gibi aynı anda yükselir, destek ekibi nefesini tutar, herkes monitöre yapışır. Benim için öyle bir akşamüstüydü. Siparişler artıyor, kullanıcılar koşturuyor, bir yandan yeni versiyon dağıtımı için dakikalar sayılıyor. Tam o sırada düşündüm: “Bu akışı kim sakince yönetiyor?” Cevap aslında bir süredir hayatımızdaydı: HAProxy ile L4/L7 yük dengeleme. Kibar bir trafik polisi gibi, hem sokaklar arası yönlendirme yapıyor, hem de kim nereye gidecek, kim yorulmuş, kim dinç anlıyor. Üstelik doğru kurarsak, tüm bu dans sıfır kesintiyle akıp gidiyor.

Bu yazıda, L4 ve L7 yük dengelemeyi bir hikaye gibi ele alacağız. Health check ile sunucuların nabzını nasıl tuttuğumuzu, sticky sessions ile ziyaretçiyi nasıl tanıyıp aynı uygulama sunucusuna “nazikçe” yönlendirdiğimizi konuşacağız. Sonra TLS termination ve TLS passthrough arasındaki farkı, günlük hayattan örneklerle açacağız. Son olarak da “sıfır kesinti” dağıtımın nasıl gerçek bir şey olduğunu, küçük püf noktalarıyla anlatacağım. Hazırsanız, birlikte o akşamüstü paniğini sakin bir akışa çevirelim.

L4 mü L7 mi? Yol Ağzında Durup Yön Soranların Hikayesi

Bir Katman Masalı: Kapıda mı, lobide mi karşılarsın?

Yük dengelemede L4 demek, biraz kapıdaki güvenlik görevlisi gibi. Kim geldi, hangi porta girmek istiyor, IP’si ne, o kadar. Hızlı karar verir, dosdoğru uygun kapıya yönlendirir. L7 ise lobideki danışma gibi; gelenin ne istediğini daha iyi anlar. URL nedir, başlıklar ne diyor, çerezi var mı, belki dil tercihi bile… L4 hızlı ve masrafsızdır, L7 ise daha “akıllı” yönlendirmeler yapar. Şimdi bunu günlük hayata taşıyalım. Mesela şöyle düşünün: Bir e-ticaret sitesinde bazı istekler dosya indirmeye gidiyor, bazıları sepete. Dosya indirmeyi L4 ile hızlıca bir node’a atmak isteyebilirsin, sepet ve ödeme gibi daha hassas akışları L7’nin ince ayarıyla ele alabilirsin.

Benim pratikte sevdiğim yaklaşım, dışarıda L4’ün gücünü kullanıp kritik uygulamalarda L7’nin zekasına başvurmak. Bu sayede hem hız hem esneklik dengesi kuruluyor. Aşağıda çok ufak bir tat:

# Basit L4 (TCP) terminasyonu olmadan geçiş - TLS passthrough örneği
frontend fe_tls
  bind :443
  mode tcp
  tcp-request inspect-delay 5s
  use_backend bk_api if { req.ssl_sni -i api.example.com }
  use_backend bk_www if { req.ssl_sni -i www.example.com }

backend bk_api
  mode tcp
  balance roundrobin
  server api1 10.0.0.11:443 check
  server api2 10.0.0.12:443 check

backend bk_www
  mode tcp
  balance roundrobin
  server www1 10.0.0.21:443 check
  server www2 10.0.0.22:443 check

Burada L4’teyiz; TLS’i çözmeden sadece SNI’ya bakıp trafiği ilgili arka uca yolluyoruz. Eğer L7 ile işleri daha renkli yapmak isterseniz, TLS’i burada sonlandırıp HTTP seviyesinde karar alabilirsiniz:

# L7 (HTTP) - TLS termination ve akıllı yönlendirme
frontend fe_https
  bind :443 ssl crt /etc/haproxy/certs/ alpn h2,http/1.1
  mode http
  option forwardfor
  http-request set-header X-Forwarded-Proto https
  use_backend bk_static if { path_beg /assets/ }
  default_backend bk_app

backend bk_static
  mode http
  balance roundrobin
  http-response set-header Cache-Control max-age=600
  server cdn1 10.0.0.31:80 check
  server cdn2 10.0.0.32:80 check

backend bk_app
  mode http
  balance leastconn
  server app1 10.0.0.41:8080 check
  server app2 10.0.0.42:8080 check

Gördüğünüz gibi, L7 ile daha ince kararlar alabiliyoruz. Statikleri bir yere, uygulamayı başka yere alırken, üstüne ufak önbellek başlıkları bile ekleyebiliyoruz. Düz konuşalım: İş ihtiyacına göre karışık kullanmak oldukça doğal ve işe yarıyor.

Health Check: Sunucunun Nabzını Parmak Ucunda Hissetmek

“İyi misin?” demenin otomatik yolu

Bir kullanıcı 404 görünce moral bozulur, 500 görünce güven bozulur. Sağlık kontrolü tam burada sahneye çıkar. HAProxy arka uçlardaki sunuculara düzenli “iyisin, değil mi?” sorusu sorar. Bu sadece ping atmak değildir; bazen belirli bir URL’den, belirli bir yanıt beklemek gerekir. Mesela uygulama sağlıklıysa “OK” yazan bir endpoint verir, HAProxy de onu arar. Eğer iki defa üst üste kötü cevap alırsa, o sunucuyu trafikten çıkarır; sonra düzelince tekrar oyuna alır.

Basit ve anlaşılır bir yapı için şöyle düşünebilirsiniz: Uygulamanızda /healthz gibi hafif bir kontrol noktası açın. Veritabanına, dış servislere gerek duymadan “ayaktayım” desin. HAProxy buna bakar, yükü güvende tutar. Küçük bir örnek:

backend bk_app
  mode http
  option httpchk GET /healthz
  http-check expect status 200
  default-server inter 2s fall 3 rise 2
  server app1 10.0.0.41:8080 check
  server app2 10.0.0.42:8080 check

“Fall 3, rise 2” gibi ayarlar, düşmeden önce kaç kez tökezledi, toparlanmak için kaç iyi cevap verdi, bunu anlatır. Bu sayede tek seferlik bir takılma yüzünden sunucular hemen oyundan düşmez. L4’teyseniz tcp-check kullanarak port seviyesinde nabız tutabilirsiniz. Daha karmaşık senaryolarda birden fazla kontrol yapmak, hatta kritik bağımlılıkları ayrı bir “/readyz” endpoint’i ile kontrol etmek iyi hissettirir.

Sticky Sessions: Ziyaretçiyi Tanıyan Kapıcı

“Bu ziyaretçi hep aynı masayı istiyor” durumu

Kimi uygulamalar, özellikle oturum tabanlı olanlar, kullanıcıyı hep aynı sunucuda görmek ister. Neden? Çünkü sunucu belleğinde oturum bilgisi duruyordur, paylaşımlı bir oturum deposu yoktur ya da henüz buna hazır değilsinizdir. Sticky sessions, bu ziyaretçiyi nazikçe aynı arka uca yönlendirir. Çerezle, IP ile, hatta özel kurallarla yapılabilir. En pratik olanı çerez yöntemidir.

Mesela şöyle düşünün: Giriş yapan kullanıcı sepetini dolduruyor. Sunucu 1’de oluşan sepet bellekte duruyor. Eğer kullanıcı akış içinde bir anda Sunucu 2’ye düşerse, sepet sanki kaybolmuş gibi olur. Bunu engellemek için HAProxy ile çerez ekleyip, bir kere bağlanan kullanıcıyı aynı sunucuya taşırız:

backend bk_app
  mode http
  balance roundrobin
  cookie SRV insert indirect nocache
  server app1 10.0.0.41:8080 cookie s1 check
  server app2 10.0.0.42:8080 cookie s2 check

Bu yapı, ziyaretçiye görünmez bir not veriyor gibi. “Bu arkadaş s1 masasında oturuyordu” diyor. Alternatif olarak IP tabanlı tutarlılık da kullanabilirsiniz. Bu daha kaba bir yöntemdir ama bazen iş görür:

backend bk_api
  mode http
  balance source
  hash-type consistent
  server api1 10.0.0.11:8080 check
  server api2 10.0.0.12:8080 check

Sticky sessions sihirli değnek değil. Eğer uygulama sunucusunu yeniden başlatırsanız, bellekteki oturumlar uçabilir. O yüzden orta vadede paylaşımlı bir oturum deposu ya da stateless tasarıma geçmek hep içi ferahlatır. Bu arada, veri katmanında tutarlılık ve dağıtım dendi mi, ben backend dünyasında ProxySQL ile read/write akışını tatlı tatlı ayırmanın uygulama deneyimini nasıl rahatlattığını çok gördüm; uygulama katmanı ile veri katmanını birlikte düşünmek güzel akış yaratır.

TLS Termination ve TLS Passthrough: Hangi Kapıda Paltomuzu Çıkaracağız?

Bazen kapıda çözersin, bazen içeri alırsın

TLS, trafiği şifreleyen paltomuz. Bu paltoyu HAProxy’de çıkarabilirsiniz (termination), ya da hiç dokunmadan arka uca iletebilirsiniz (passthrough). Termination olduğunda HAProxy içeriği görür; yönlendirme, WAF, başlık düzenleme gibi akıllı işler yapar. Passthrough’da ise HAProxy paltosuna karışmaz, sadece SNI’a bakarak nereye gideceğini söyler, şifre çözme işi arka uca kalır.

Hangi durumda neyi seçelim? Eğer L7 kararlarına ihtiyacınız varsa, TLS’i önde kırmak çok iş görüyor. Ancak regülasyon, güvenlik politikası ya da performans sebebiyle şifre çözmeyi uygulama sunucularına bırakmak istiyorsanız, passthrough doğru seçim. SNI yönlendirmesiyle birden fazla domain’i tek noktada taşıyabilirsiniz. Sertifika yönetimi tarafında ben genelde otomasyonu severim; örneğin Let’s Encrypt belgelerinde anlatılan ACME akışı ile sertifika yenilemeyi script’lerle rüzgâr gibi döndürmek mümkün.

Bir not: Arka uçta servisleriniz birbirini doğrulasın isterseniz, mTLS yani karşılıklı sertifika doğrulama akışı çok şık duruyor. Bu konuyu ayrı bir dünyada, mTLS ile mikroservisleri nasıl kale gibi sağlamlaştırabileceğinize dair rehberde detaylı konuşmuştuk. HAProxy önde TLS’i sonlandırırken, içeride servisler arası trafiği mTLS ile korumak içimizi rahatlatır.

Sıfır Kesinti: Dağıtımı Sahne Arkasında Değiştirirken Işıklar Hiç Sönmesin

“Hitless reload” ve yumuşak geçişler

Bir gerçek: Konfigürasyonlar değişir, sertifikalar yenilenir, yeni sürümler gelir. Önemli olan bunlar olurken kullanıcı akışının takılmaması. HAProxy’nin güzelliği, doğru kurduğunuzda yeniden yüklemeyi trafiği kesmeden yapabilmesi. Master-worker modeli ve “hitless reload” yaklaşımıyla, yeni süreç ayağa kalkarken eskisi işini bitirene kadar bekler. Ben pratikte şu yaklaşımı seviyorum: Control socket ile sunucuları nazikçe “drain” moduna alıp, yeni istek almalarını engellemek, var olan isteklerin bitmesini beklemek; sonra yeni sürümü koyup geri almak. Bu, bebek uyurken oda değiştirmek gibi sakince yapılmalı.

HAProxy tarafında bunu desteklemek için “stats socket” açmak iyi bir alışkanlık. Böylece komutla “şu sunucuyu geçici olarak devre dışı bırak, ağırlığını düşür” gibi hamleler yapabiliyorsunuz. Konfigürasyonu tekrar yüklerseniz de stick-table ve bağlantıların nazik aktarımı gibi detaylar işinizi kolaylaştırır. Bu konuda seamless reloads anlatımının yapıldığı rehber zahmetsiz geçişin mantığını çok güzel anlatır.

Dağıtımı daha da tatlandırmak isterseniz, mavi-yeşil gibi iki ortam kurup, trafiği yavaşça yenisine kaydırabilirsiniz. DNS tarafında da otomasyonla destek verince, iş iyice pürüzsüz olur. Biz bunu anlatırken, Terraform, Cloudflare ve sıfır kesinti dağıtımın nasıl birleştiğine dair yazıda DNS’i de oyuna dahil ederek anlatmıştık; HAProxy ile bu orkestrasyon çok uyumlu çalışıyor.

Gerçek Dünya Akışları: Web, API, WebSocket ve gRPC

“Mesela şöyle düşünün…”

Bir WordPress e-ticaret sitem var diyelim; kampanya sırasında trafik patlıyor. L7 ile statik dosyaları hafif sunuculara, PHP isteklerini ise daha güçlü düğümlere yönlendiriyorum. Health check ile yorulanı kenara çekiyorum. Sticky session ile sepet karışmıyor. Bir de önbellek başlığı takviyesiyle sayfa akışı hızlanıyor. Benzer bir durumda, kenarda küçük bir mikro önbellek bile hava açar; Nginx tarafında bu konuya merakınız varsa, mikro önbelleklemenin PHP uygulamalarını nasıl uçurduğunu anlattığım rehber hoşunuza gider.

API’lerde ise L7’nin yetenekleri parlıyor. Belirli endpoint’leri farklı havuzlara atmak, limit koymak, başlık düzenlemek katkı sağlar. Bazı ekipler WebSocket ya da gRPC kullanıyor; orada bağlantılar uzun süre açık kalır, kalp atışları önemlidir. Zaman aşımlarını özenle ayarlayın. Eğer uçta bir CDN veya proxy katmanınız da varsa, oradaki bekleme ayarlarının HAProxy ile uyumlu olduğundan emin olmak gerekir. Bu konuda, WebSocket ve gRPC akışını canlı tutmanın sırlarını paylaştığım yazıdaki ipuçları HAProxy’nin arkasında da işinize yarar.

İç servisler arası iletişimde güven tarafı ayrı bir keyifli konu. Bazı ekipler önde HAProxy ile L7 kararlarını alıp, içeride servisler arası konuşmayı mTLS ile güvene alıyor. Böylece içerideki trafik şifreli ve kimlik doğrulanmış oluyor. Üstelik sertifika yenileme ve dağıtım, küçük bir otomasyonla günlük işler arasına karışıyor. Yeri gelmişken, HAProxy yapılandırmasını öğrenirken HAProxy’nin resmi dokümantasyonundaki yapılandırma kılavuzu hayat kurtarır; ihtiyaç duyduğunuzda sayfayı bir işaretleyin, aradığınız direktif anında önünüze gelir.

Gözlem, Log ve Küçük İpuçları: Sorun Çıkmadan Önce Görmek

İzle, ölç, küçük düzeltmeler yap

Benim en sevdiğim anlardan biri, bir grafikte ufak bir diş görüp “Aha, burada bir şey var” demek. HAProxy loglarını anlaşılır tutmak, başlıklarla IP ve gerçek istemciyi not düşmek, yavaş istekleri işaretlemek büyük fark yaratır. Ardından uygulama tarafında ayrıntılı izler toplamak, uçtan uca safları sıklaştırır. Bu konuda, “ne nerede gecikiyor” sorusuna dair merakınızı OpenTelemetry ile izlenebilirlik rehberinde gerçeğe dönüştürecek örnek akışlar var; HAProxy önünde koşan uygulamalarda bu çok işe yarıyor.

Ufak ama önemli bir not: Zaman aşımı değerleri. İstemci, HAProxy ve arka uç arasında bu değerlerin uyumlu olması, gereksiz kopmaları önler. WebSocket/gRPC gibi uzun soluklu bağlantılarda bu ayarlar adeta emniyet kemeri. Sertifika yenileme tarafında da otomasyonu unutmayın; bir gece yarısı sertifikanın süresi bitmiş, tarayıcı bağırıyorsa can sıkıcı olur. Otomasyon için basit bir ACME istemcisi ve “hitless reload” çok iş görür.

Küçük Konfigürasyon Tarifleri: Tadında ve Yeterince

Bir tutam socket, bir çimdik drain

Bazen minimallik en iyisidir. Aşağıdaki örnekte, hem socket üzerinden kontrol, hem de yumuşak dağıtım pratikleri bir arada:

global
  log /dev/log local0
  stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners
  master-worker

defaults
  log global
  mode http
  option httplog
  timeout connect 5s
  timeout client  50s
  timeout server  50s

frontend fe_https
  bind :443 ssl crt /etc/haproxy/certs/ alpn h2,http/1.1
  default_backend bk_app

backend bk_app
  balance leastconn
  option httpchk GET /healthz
  default-server inter 2s fall 3 rise 2
  server app1 10.0.0.41:8080 check
  server app2 10.0.0.42:8080 check

Dağıtım öncesi, aşağıdaki gibi sunucuyu nazikçe sahneden çekebilirsiniz. Bu komutlar, aktif bağlantıları koparmadan “yeni misafir alma, eskileri uğurla” demek oluyor:

echo "set server bk_app/app2 state drain" | socat stdio /run/haproxy/admin.sock
# Yeni sürüm app2'ye, sağlık kontrolü yeşil, sonra geri al:
echo "set server bk_app/app2 state ready" | socat stdio /run/haproxy/admin.sock

Konfigürasyonu güncelledikten sonra “reload” verdiğinizde bağlantılar hit almadan geçiş yapar. Bu tekniğin ince noktalarını öğrenmek için yine hitless reload yaklaşımına dair açıklamaları okumak oldukça faydalı.

Kapanış: Trafiği Sakinleştirmek ve Günü Güzel Bitirmek

Yanınıza alacağınız küçük notlar

HAProxy ile L4/L7 yük dengeleme, doğru kurulduğunda trafiği sakinleştiriyor. Health check ile sunucuların nabzını tutuyor, sticky sessions ile kullanıcı deneyimini dengede tutuyor, TLS termination ya da passthrough ile güvenliği ve esnekliği yerli yerine koyuyoruz. Üstüne bir de sıfır kesinti dağıtım pratikleri eklenince, gece yarısı alarm kurma ihtiyacı azalıyor. Bu işin sırrı, küçük adımlar, tutarlı otomasyon ve düzenli gözlem. Bir de sade, anlaşılır konfigürasyonlar.

Pratik bir rota çizeyim: Önce basit bir health endpoint’i açın, HAProxy’de kontrol edin. Ardından sticky ihtiyacınızı çerezle mi, IP ile mi çözeceğinize karar verin. TLS’te nerede sonlandıracağınız hem güvenlik politikanıza hem de yönlendirme ihtiyaçlarınıza bağlı. Yenileme işini otomatikleştirin; isterseniz Let’s Encrypt dokümanlarından ilhamla başlayın. İş çoğalınca otomasyon ve orkestrasyon kaçınılmaz; burada Terraform ile DNS ve altyapıyı birlikte ele alan pratikleri gözden geçirin. İşi bütünsel görmek rahatlatır.

Umarım bu rehber, akşamüstü paniğini güneş batarkenki o tatlı dinginliğe çevirir. Bir gün yine yoğun bir dağıtım anında, HAProxy konfigürasyonunuzu güvenle “reload” ederken aklınıza bu satırlar gelsin. Sorularınız, merak ettikleriniz olursa paylaşın. Bir dahaki yazıda görüşmek üzere, trafiğiniz hep akıcı, kesintiniz sıfır olsun.

Sıkça Sorulan Sorular

Her zaman değil. Oturum bilgisini sunucuda tuttuğunuzda kullanıcıyı hep aynı düğüme göndermek gerekir. Paylaşımlı bir oturum deposu varsa veya uygulama durumsuzsa, sticky’e gerek kalmayabilir.

L7’de akıllı yönlendirme, başlık düzenleme, WAF gibi ihtiyaçlar varsa termination mantıklı. Güvenlik politikası gereği şifre çözmeyi uygulamada tutmak veya sadece SNI’a göre dağıtmak istiyorsanız passthrough seçin.

Sunucuları drain moduna alıp yeni istek kabul etmemek, var olanları bitirmesine izin vermek ve ardından hitless reload yapmaktır. Sağlık kontrolleri ve zaman aşımları doğruysa bu geçiş pürüzsüz olur.