Apache mod_proxy ile Reverse Proxy ve Load Balancer Kurulumu

Modern web altyapılarında tek bir sunucuya güvenmek artık lüks değil, risk. Trafiğin artması, sunucu bakımları, beklenmedik çökmeler… Bunların hepsini yönetmenin en temiz yolu iyi kurulmuş bir reverse proxy ve load balancer mimarisinden geçiyor. Apache’nin mod_proxy modülü, bu ihtiyacı karşılamak için yıllardır savaşta test edilmiş, olgun ve güçlü bir çözüm sunuyor. Bu yazıda sıfırdan başlayıp production ortamına kadar götürebileceğiniz bir yapı kuracağız.

mod_proxy Nedir ve Neden Kullanmalısınız?

Apache HTTP Server, mod_proxy modülü sayesinde hem reverse proxy hem de load balancer olarak çalışabilir. Peki bu ikisi arasındaki fark ne?

Reverse proxy: İstemciden gelen istekleri alıp arka taraftaki sunuculara (backend) ileten yapıdır. İstemci, arka sunucuların varlığından habersizdir. Yani app.sirketim.com adresine gelen istek, aslında localhost:3000 üzerinde koşan bir Node.js uygulamasına yönlendiriliyor olabilir.

Load balancer: Reverse proxy’nin üstüne kurulu, gelen trafiği birden fazla backend sunucusuna dağıtan yapıdır. Bir sunucu çöktüğünde trafik otomatik olarak diğerlerine yönlenir.

mod_proxy‘yi tercih etmenizin birkaç somut sebebi var:

  • Apache zaten altyapınızda varsa ek bir yazılım kurmak zorunda kalmazsınız
  • SSL termination, header manipülasyonu ve erişim kontrolü aynı yerden yönetilir
  • Kapsamlı loglama ve izleme seçenekleri sunar
  • Nginx kadar hype almasa da production ortamlarında son derece kararlı çalışır

Gerekli Modüllerin Aktifleştirilmesi

Debian/Ubuntu tabanlı sistemlerde modülleri etkinleştirmek oldukça basit:

sudo a2enmod proxy
sudo a2enmod proxy_http
sudo a2enmod proxy_balancer
sudo a2enmod lbmethod_byrequests
sudo a2enmod lbmethod_bytraffic
sudo a2enmod lbmethod_bybusyness
sudo a2enmod headers
sudo a2enmod rewrite
sudo systemctl restart apache2

RHEL/CentOS/Rocky Linux tarafında modüller genellikle zaten yüklüdür, ama /etc/httpd/conf.modules.d/00-proxy.conf dosyasını kontrol etmeniz gerekir:

# Mevcut durumu kontrol et
cat /etc/httpd/conf.modules.d/00-proxy.conf

# Aktif modülleri görmek için
httpd -M | grep proxy

Çıktıda şunları görmelisiniz: proxy_module, proxy_http_module, proxy_balancer_module.

Temel Reverse Proxy Yapılandırması

En basit senaryo ile başlayalım: 80 portuna gelen tüm trafiği, lokalda 8080 portunda çalışan bir uygulamaya yönlendirmek.

# /etc/apache2/sites-available/reverse-proxy.conf

<VirtualHost *:80>
    ServerName app.sirketim.com
    ServerAlias www.app.sirketim.com

    # Proxy ayarları
    ProxyPreserveHost On
    ProxyRequests Off

    # Tüm istekleri backend'e yönlendir
    ProxyPass / http://localhost:8080/
    ProxyPassReverse / http://localhost:8080/

    # Hata logları
    ErrorLog ${APACHE_LOG_DIR}/app-error.log
    CustomLog ${APACHE_LOG_DIR}/app-access.log combined
</VirtualHost>

Burada iki kritik direktif dikkat çekiyor:

  • ProxyPreserveHost On: Orijinal Host header’ını backend’e iletir. Backend uygulamanız hostname’e göre davranış değiştiriyorsa bu şart
  • ProxyRequests Off: Forward proxy özelliğini kapatır. Açık bırakırsanız sunucunuz açık proxy olarak kötüye kullanılabilir, bunu kesinlikle kapatın

Yapılandırmayı aktifleştirip test edin:

sudo a2ensite reverse-proxy.conf
sudo apache2ctl configtest
sudo systemctl reload apache2

Belirli Path’leri Farklı Backend’lere Yönlendirmek

Gerçek dünyada çok daha sık karşılaştığınız senaryo şu: Monolitik bir uygulamayı microservice’lere bölüyorsunuz ve /api/ yolunu ayrı bir servise, /static/ yolunu başka bir yere yönlendirmeniz gerekiyor.

# /etc/apache2/sites-available/microservices.conf

<VirtualHost *:80>
    ServerName portal.sirketim.com

    ProxyPreserveHost On
    ProxyRequests Off

    # API isteklerini Node.js backend'e yönlendir
    ProxyPass /api/ http://localhost:3000/api/
    ProxyPassReverse /api/ http://localhost:3000/api/

    # Auth servisi ayrı çalışıyor
    ProxyPass /auth/ http://localhost:4000/
    ProxyPassReverse /auth/ http://localhost:4000/

    # Static dosyalar için ayrı bir Python servisi
    ProxyPass /static/ http://localhost:9000/static/
    ProxyPassReverse /static/ http://localhost:9000/static/

    # Geri kalan her şey ana PHP uygulamasına
    ProxyPass / http://localhost:8080/
    ProxyPassReverse / http://localhost:8080/

    ErrorLog ${APACHE_LOG_DIR}/portal-error.log
    CustomLog ${APACHE_LOG_DIR}/portal-access.log combined
</VirtualHost>

Önemli bir detay: ProxyPass direktifleri yukarıdan aşağıya okunur. Daha spesifik path’ler her zaman daha genel olanlardan önce gelmeli. / en sona yazılmalı.

Load Balancer Yapılandırması

Şimdi işin eğlenceli kısmına geliyoruz. Üç backend sunucusuna trafik dağıtan bir load balancer kuralım:

# /etc/apache2/sites-available/loadbalancer.conf

<VirtualHost *:80>
    ServerName lb.sirketim.com

    ProxyRequests Off
    ProxyPreserveHost On

    # Balancer grubunu tanımla
    <Proxy balancer://webcluster>
        BalancerMember http://192.168.1.101:8080 route=node1
        BalancerMember http://192.168.1.102:8080 route=node2
        BalancerMember http://192.168.1.103:8080 route=node3

        # Load balancing metodu
        ProxySet lbmethod=byrequests
    </Proxy>

    ProxyPass / balancer://webcluster/
    ProxyPassReverse / balancer://webcluster/

    ErrorLog ${APACHE_LOG_DIR}/lb-error.log
    CustomLog ${APACHE_LOG_DIR}/lb-access.log combined
</VirtualHost>

Load balancing metodları arasındaki farklar önemli:

  • byrequests: Her backend eşit sayıda istek alır. Varsayılan ve çoğu senaryo için idealdir
  • bytraffic: Byte cinsinden trafik miktarına göre dağıtır. Dosya indirme gibi büyük transfer senaryolarında kullanışlı
  • bybusyness: Aktif bağlantı sayısına göre dağıtır. Uzun süreli bağlantılarda tercih edilebilir

Ağırlıklı Load Balancing ve Yedek Sunucu Tanımlama

Tüm sunucularınız eşit kapasitede olmayabilir. Bir sunucu daha güçlüyse ona daha fazla trafik göndermek mantıklı. Ayrıca acil durumlar için yedek (hot standby) sunucu tanımlamak da production ortamlarında kritik önem taşır.

<Proxy balancer://appcluster>
    # Güçlü sunucu - ağırlık 3
    BalancerMember http://192.168.1.101:8080 loadfactor=3 route=node1

    # Orta sunucu - ağırlık 2
    BalancerMember http://192.168.1.102:8080 loadfactor=2 route=node2

    # Zayıf sunucu - ağırlık 1 (varsayılan)
    BalancerMember http://192.168.1.103:8080 loadfactor=1 route=node3

    # Yedek sunucu - sadece diğerleri çöktüğünde devreye girer
    BalancerMember http://192.168.1.104:8080 status=+H route=node4

    ProxySet lbmethod=byrequests
</Proxy>

status=+H ile işaretlenen sunucu hot standby olarak çalışır. Normal koşullarda trafik almaz, ancak diğer tüm sunucular devre dışı kaldığında otomatik olarak devreye girer. Bu özelliği kullanmak için mod_proxy_balancer modülünün aktif olması şart.

Sağlık Kontrolü (Health Check)

Arka sunuculardan biri çöktüğünde trafik ondan otomatik yönlendiriliyor mu? Bunu sağlamak için sağlık kontrolü mekanizmasını da yapılandırmanız gerekiyor.

# mod_proxy_hcheck modülünü aktifleştir
sudo a2enmod proxy_hcheck

# Yapılandırma
<Proxy balancer://healthcluster>
    BalancerMember http://192.168.1.101:8080 route=node1 hcmethod=GET hcuri=/health hcinterval=10 hcpasses=2 hcfails=3
    BalancerMember http://192.168.1.102:8080 route=node2 hcmethod=GET hcuri=/health hcinterval=10 hcpasses=2 hcfails=3
    BalancerMember http://192.168.1.103:8080 route=node3 hcmethod=GET hcuri=/health hcinterval=10 hcpasses=2 hcfails=3

    ProxySet lbmethod=byrequests
</Proxy>

Bu yapılandırmayla sistem her 10 saniyede bir her backend’in /health endpoint’ine GET isteği atar. Bir sunucu arka arkaya 3 kez başarısız olursa devre dışı bırakılır. 2 kez başarılı olduğunda yeniden aktife alınır.

Backend uygulamanızda basit bir health check endpoint’i olması bu yüzden önemli. Sadece 200 dönen bir route yeterli.

SSL Termination ile Güvenli Yapılandırma

Production ortamında mutlaka HTTPS kullanmalısınız. SSL termination reverse proxy katmanında yapılır, backend sunucular şifrelemeyle uğraşmak zorunda kalmaz.

# /etc/apache2/sites-available/secure-proxy.conf

<VirtualHost *:80>
    ServerName app.sirketim.com
    # HTTP'yi HTTPS'e yönlendir
    Redirect permanent / https://app.sirketim.com/
</VirtualHost>

<VirtualHost *:443>
    ServerName app.sirketim.com

    # SSL ayarları
    SSLEngine On
    SSLCertificateFile /etc/letsencrypt/live/app.sirketim.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/app.sirketim.com/privkey.pem

    # Modern SSL ayarları
    SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
    SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384
    SSLHonorCipherOrder off

    ProxyPreserveHost On
    ProxyRequests Off

    # Backend'e iletilecek header'lar
    RequestHeader set X-Forwarded-Proto "https"
    RequestHeader set X-Forwarded-Port "443"

    <Proxy balancer://securecluster>
        BalancerMember http://192.168.1.101:8080 route=node1
        BalancerMember http://192.168.1.102:8080 route=node2
        ProxySet lbmethod=byrequests
    </Proxy>

    ProxyPass / balancer://securecluster/
    ProxyPassReverse / balancer://securecluster/

    # Güvenlik header'ları
    Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains"
    Header always set X-Content-Type-Options "nosniff"
    Header always set X-Frame-Options "SAMEORIGIN"

    ErrorLog ${APACHE_LOG_DIR}/secure-app-error.log
    CustomLog ${APACHE_LOG_DIR}/secure-app-access.log combined
</VirtualHost>

X-Forwarded-Proto header’ı kritik. Backend uygulamanız bu header’ı okuyarak bağlantının HTTPS üzerinden geldiğini anlayabilir. Bunu set etmezseniz, backend uygulamanız kendisini HTTP üzerinden çalışıyor zanneder ve cookie’ler, redirect’ler gibi konularda sorun yaşarsınız.

Sticky Session (Oturum Yapışkanlığı)

Uygulamanız durum bilgisi tutuyorsa (stateful), yani kullanıcı oturumunu bellekte saklıyorsa, aynı kullanıcının her seferinde aynı backend’e gitmesi gerekir. Buna sticky session denir.

<VirtualHost *:443>
    ServerName crm.sirketim.com

    # SSL ayarları burada...

    <Proxy balancer://crmcluster>
        BalancerMember http://192.168.1.101:8080 route=node1
        BalancerMember http://192.168.1.102:8080 route=node2
        BalancerMember http://192.168.1.103:8080 route=node3

        # Sticky session için
        ProxySet stickysession=ROUTEID
    </Proxy>

    ProxyPass / balancer://crmcluster/
    ProxyPassReverse / balancer://crmcluster/

    # Route bilgisini cookie'ye yaz
    Header add Set-Cookie "ROUTEID=.%{BALANCER_WORKER_ROUTE}e; path=/" env=BALANCER_ROUTE_CHANGED
</VirtualHost>

Bu yapılandırma ile Apache, kullanıcının tarayıcısına ROUTEID adında bir cookie yazar. Sonraki isteklerde bu cookie’yi okuyarak kullanıcıyı aynı backend’e yönlendirir. O backend çöktüğünde ise başka birine geçer, yani işlevselliği bozulmaz.

Ancak şunu belirtmem gerek: Mümkünse uygulamanızı stateless yapın. Oturum verisini Redis veya Memcached gibi harici bir cache’e taşırsanız sticky session ihtiyacı ortadan kalkar ve load balancer çok daha verimli çalışır.

Balancer Manager ile Canlı İzleme

Apache’nin güzel özelliklerinden biri de web tabanlı balancer manager arayüzü. Canlı olarak backend’lerin durumunu görebilir, manuel olarak devre dışı bırakabilirsiniz.

# Balancer manager için güvenli erişim
<Location /balancer-manager>
    SetHandler balancer-manager

    # Sadece yönetici IP'lerinden erişim
    Require ip 192.168.1.0/24
    Require ip 10.0.0.1
</Location>

# Server status da ekleyelim
<Location /server-status>
    SetHandler server-status
    Require ip 192.168.1.0/24
</Location>

Bu arayüze https://app.sirketim.com/balancer-manager adresinden erişebilirsiniz. Acil durumda bir backend’i trafik dışı bırakmak için komut satırına koşmanıza gerek kalmaz.

Zaman Aşımı ve Bağlantı Optimizasyonu

Production ortamında muhakkak dikkat edilmesi gereken timeout ve bağlantı yönetimi ayarları:

# /etc/apache2/conf-available/proxy-tuning.conf

# Proxy zaman aşımı ayarları
ProxyTimeout 300

# Backend bağlantı havuzu
<Proxy balancer://tunedcluster>
    BalancerMember http://192.168.1.101:8080 
        route=node1 
        connectiontimeout=5 
        timeout=300 
        retry=30 
        max=100 
        ttl=120

    BalancerMember http://192.168.1.102:8080 
        route=node2 
        connectiontimeout=5 
        timeout=300 
        retry=30 
        max=100 
        ttl=120

    ProxySet lbmethod=bybusyness
</Proxy>

Bu parametreler ne anlama geliyor:

  • connectiontimeout=5: Backend’e bağlantı kurmak için en fazla 5 saniye bekle
  • timeout=300: Yanıt almak için en fazla 300 saniye bekle
  • retry=30: Bir backend hata verirse 30 saniye bekle, sonra tekrar dene
  • max=100: Bu backend’e aynı anda en fazla 100 bağlantı aç
  • ttl=120: Bağlantıları 120 saniye sonra kapat, yenisini aç

Gerçek Dünya Senaryosu: E-Ticaret Platformu

Şimdi her şeyi bir araya getirelim. Orta ölçekli bir e-ticaret platformu için örnek yapılandırma:

# /etc/apache2/sites-available/eticaret.conf

<VirtualHost *:80>
    ServerName www.magazam.com
    Redirect permanent / https://www.magazam.com/
</VirtualHost>

<VirtualHost *:443>
    ServerName www.magazam.com

    SSLEngine On
    SSLCertificateFile /etc/letsencrypt/live/www.magazam.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/www.magazam.com/privkey.pem
    SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1

    ProxyPreserveHost On
    ProxyRequests Off

    RequestHeader set X-Forwarded-Proto "https"
    RequestHeader set X-Real-IP %{REMOTE_ADDR}s

    # Ödeme API'si - tek sunucu, hassas sistem
    ProxyPass /api/payment/ http://payment-internal.magazam.local:9000/
    ProxyPassReverse /api/payment/ http://payment-internal.magazam.local:9000/

    # Genel API - 3 sunucu, yük dengeleme
    <Proxy balancer://apicluster>
        BalancerMember http://api1.magazam.local:8080 route=api1 loadfactor=2
        BalancerMember http://api2.magazam.local:8080 route=api2 loadfactor=2
        BalancerMember http://api3.magazam.local:8080 route=api3 loadfactor=1
        BalancerMember http://api-standby.magazam.local:8080 status=+H route=standby
        ProxySet lbmethod=byrequests
        ProxySet stickysession=SESSID
    </Proxy>

    ProxyPass /api/ balancer://apicluster/api/
    ProxyPassReverse /api/ balancer://apicluster/api/

    # Frontend - 2 sunucu
    <Proxy balancer://webcluster>
        BalancerMember http://web1.magazam.local:80 route=web1
        BalancerMember http://web2.magazam.local:80 route=web2
        ProxySet lbmethod=bybusyness
    </Proxy>

    ProxyPass / balancer://webcluster/
    ProxyPassReverse / balancer://webcluster/

    # Session cookie
    Header add Set-Cookie "SESSID=.%{BALANCER_WORKER_ROUTE}e; path=/; Secure; HttpOnly" env=BALANCER_ROUTE_CHANGED

    # Güvenlik header'ları
    Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
    Header always set X-Content-Type-Options "nosniff"
    Header always set X-Frame-Options "SAMEORIGIN"
    Header always set Referrer-Policy "strict-origin-when-cross-origin"

    # Balancer yönetimi - sadece iç ağdan
    <Location /balancer-manager>
        SetHandler balancer-manager
        Require ip 10.0.0.0/8
    </Location>

    ErrorLog ${APACHE_LOG_DIR}/magazam-error.log
    CustomLog ${APACHE_LOG_DIR}/magazam-access.log combined
</VirtualHost>

Sık Karşılaşılan Sorunlar ve Çözümleri

Sorun: Backend URL’de trailing slash sorunu

ProxyPass /app http://backend:8080 ile ProxyPass /app/ http://backend:8080/ arasındaki fark önemli. Trailing slash olmazsa Apache path’i doğru işleyemeyebilir. Her zaman her ikisinde de trailing slash kullanın ya da hiçbirinde kullanmayın, ama tutarlı olun.

Sorun: 502 Bad Gateway hataları

Backend hazır değilken veya çöktüğünde bu hatayı alırsınız. Error log’a bakın:

sudo tail -f /var/log/apache2/app-error.log | grep proxy

retry=0 ayarı hatalı backend’i hemen devre dışı bırakır. Veya retry=60 ile 60 saniye bekletebilirsiniz.

Sorun: Büyük dosya yüklemelerinde timeout

# Bu ayarı artırın
ProxyTimeout 600
LimitRequestBody 524288000

Sonuç

mod_proxy ile yapabileceğiniz şeyler burada anlattıklarımla sınırlı değil. WebSocket desteği için mod_proxy_wstunnel, FastCGI için mod_proxy_fcgi, AJP protokolü için mod_proxy_ajp gibi kardeş modüller de mevcut. Ancak yukarıdaki yapılandırmaları sağlam anlarsanız diğerlerine geçmek çok kolay olacak.

Production ortamına geçmeden önce şu kontrol listesini geçin: ProxyRequests Off mutlaka kapalı olsun, güvenlik header’ları eklenmiş olsun, balancer manager sadece iç ağdan erişilebilir olsun, health check aktif olsun ve loglama doğru yapılandırılmış olsun. Bunları sağladıktan sonra apache2ctl configtest ile sözdizimi kontrolü yapın, sonra yavaşça production’a alın.

En kritik öneri: Değişiklikleri önce staging ortamında test edin. Load balancer yapılandırması yanlış gittiğinde tüm siteniz çöker, tek tek sunucu sorunlarından çok daha kötü bir senaryo bu.

Yorum yapın