Nginx ile API Gateway Kurulumu ve Yapılandırması

Mikroservis mimarisine geçiş yapan ya da mevcut monolitik uygulamasını parçalara ayırmaya başlayan her sysadmin’in eninde sonunda karşısına çıkan bir soru var: “Tüm bu servislerin önüne ne koyacağım?” İşte tam bu noktada Nginx devreye giriyor. Nginx’i sadece bir web sunucusu olarak görmek büyük bir hata. Doğru konfigürasyon ile güçlü bir API Gateway’e dönüşebiliyor. Bu yazıda sıfırdan başlayarak gerçek dünya senaryolarıyla Nginx tabanlı bir API Gateway nasıl kurulur, adım adım inceliyoruz.

API Gateway Nedir ve Neden Nginx?

API Gateway, istemcilerden gelen tüm istekleri tek bir giriş noktasından karşılayan ve bu istekleri arka taraftaki ilgili servislere yönlendiren bir katmandır. Rate limiting, authentication, logging, SSL termination gibi cross-cutting concern’leri tek bir yerden yönetmeni sağlar. Her servise ayrı ayrı bu özellikleri eklemeye çalışmak hem zahmetli hem de tutarsızlığa davetiye çıkarır.

Nginx’i bu iş için tercih etmemizin birkaç sebebi var. Öncelikle neredeyse her Linux sunucusunda zaten kurulu ya da kolayca kurulabilir. Öğrenme eğrisi düşük, dokümantasyon zengin ve topluluk desteği güçlü. Kong, AWS API Gateway gibi çözümlere kıyasla çok daha az kaynak tüketiyor. Tabii ki enterprise düzeyde gelişmiş özellikler için özel API Gateway çözümlerine bakman gerekebilir ama birçok senaryoda Nginx fazlasıyla yeterli.

Ortam Hazırlığı

Senaryomuzda şunlar var: Üç adet mikroservis (kullanıcı servisi, ürün servisi, sipariş servisi), bunların hepsi farklı portlarda çalışıyor ve Nginx bunların önünde duruyor. Tüm dünyaya tek bir domain üzerinden hizmet verecek.

Önce Nginx’i kuralım. Ubuntu/Debian sistemlerde:

sudo apt update
sudo apt install nginx -y
sudo systemctl enable nginx
sudo systemctl start nginx

# Versiyon kontrolü
nginx -v

# Mevcut konfigürasyonu yedekle
sudo cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak

CentOS/RHEL sistemlerde:

sudo dnf install epel-release -y
sudo dnf install nginx -y
sudo systemctl enable --now nginx
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload

Şimdi test amaçlı mikroservislerimizi simüle edelim. Gerçek hayatta bunlar ayrı sunucularda ya da container’larda çalışıyor olacak ama development ortamı için basit Python HTTP sunucuları yeterli:

# Kullanici servisi - 8001 portunda
python3 -m http.server 8001 &

# Urun servisi - 8002 portunda
python3 -m http.server 8002 &

# Siparis servisi - 8003 portunda
python3 -m http.server 8003 &

# Servislerin ayakta olduğunu doğrula
curl -s http://localhost:8001
curl -s http://localhost:8002
curl -s http://localhost:8003

Temel Reverse Proxy Konfigürasyonu

Nginx konfigürasyon yapısını anlamak önemli. /etc/nginx/nginx.conf ana konfigürasyon dosyası, /etc/nginx/conf.d/ dizini ise site bazlı konfigürasyonlar için. Biz her şeyi düzenli tutmak adına ayrı bir dosya oluşturacağız.

sudo nano /etc/nginx/conf.d/api-gateway.conf

Temel yapıyı kuralım:

# Upstream blokları - backend servisleri tanımla
upstream user_service {
    server 127.0.0.1:8001;
    keepalive 32;
}

upstream product_service {
    server 127.0.0.1:8002;
    keepalive 32;
}

upstream order_service {
    server 127.0.0.1:8003;
    keepalive 32;
}

server {
    listen 80;
    server_name api.orneksite.com;

    # Genel timeout ayarları
    proxy_connect_timeout 10s;
    proxy_send_timeout 30s;
    proxy_read_timeout 30s;

    # Proxy header'ları
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;

    # Kullanici servisi route
    location /api/v1/users/ {
        proxy_pass http://user_service/;
    }

    # Urun servisi route
    location /api/v1/products/ {
        proxy_pass http://product_service/;
    }

    # Siparis servisi route
    location /api/v1/orders/ {
        proxy_pass http://order_service/;
    }

    # 404 handler
    location / {
        return 404 '{"error": "Endpoint bulunamadi"}';
        add_header Content-Type application/json;
    }
}

Konfigürasyonu test edip uygulayalım:

# Syntax kontrolü
sudo nginx -t

# Hata yoksa uygula
sudo systemctl reload nginx

# Test edelim
curl -s http://localhost/api/v1/users/
curl -s http://localhost/api/v1/products/

Rate Limiting

API Gateway’in en kritik özelliklerinden biri rate limiting. Bir istemci saniyede binlerce istek atıyorsa bunu sınırlamak hem güvenlik hem de stabilite açısından şart.

Nginx’in limit_req_zone direktifi bu iş için kullanılır. Ana nginx.conf dosyasının http bloğuna ya da konfigürasyon dosyamızın başına ekleriz:

# Rate limiting zone tanımları
# $binary_remote_addr: İstemci IP'si (binary format daha az yer kaplar)
# zone=api_limit: Zone ismi
# 10m: 10MB shared memory (~160.000 IP adresi tutabilir)
# rate=10r/s: Saniyede maksimum 10 istek

http {
    limit_req_zone $binary_remote_addr zone=api_general:10m rate=10r/s;
    limit_req_zone $binary_remote_addr zone=api_auth:10m rate=3r/s;
    limit_req_zone $binary_remote_addr zone=api_heavy:10m rate=1r/m;

    # Rate limit aşıldığında dönen HTTP kodu (varsayılan 503)
    limit_req_status 429;
    limit_conn_status 429;
}

Şimdi bu zone’ları location bloklarına uygulayalım:

server {
    listen 80;
    server_name api.orneksite.com;

    # Kullanici servisi - authentication endpoint hassas
    location /api/v1/users/login {
        limit_req zone=api_auth burst=5 nodelay;
        limit_req_log_level warn;
        proxy_pass http://user_service/login;
    }

    # Genel API endpoint'leri
    location /api/v1/users/ {
        limit_req zone=api_general burst=20 nodelay;
        proxy_pass http://user_service/;
    }

    # Raporlama gibi ağır endpoint'ler
    location /api/v1/orders/reports/ {
        limit_req zone=api_heavy burst=2;
        proxy_pass http://order_service/reports/;
    }

    # Rate limit aşıldığında özel hata mesajı
    error_page 429 = @rate_limit_exceeded;
    location @rate_limit_exceeded {
        default_type application/json;
        return 429 '{"error": "Cok fazla istek gonderdiniz. Lutfen bekleyiniz.", "code": "RATE_LIMIT_EXCEEDED"}';
    }
}

burst: Anlık aşımı tolere eder. burst=20 demek normalde 10r/s olan limite 20 istek üst üste gelirse bunları kuyruğa alır, hepsini reddetmez.

nodelay: Kuyruklanmış istekler beklemek yerine hemen işlenir ama limit yine de sayılır.

SSL/TLS Termination

Production ortamında mutlaka HTTPS kullanılmalı. SSL termination Nginx katmanında yapılır, backend servisler HTTP üzerinden konuşur.

Let’s Encrypt ile ücretsiz sertifika alalım:

# Certbot kurulumu
sudo apt install certbot python3-certbot-nginx -y

# Sertifika al
sudo certbot --nginx -d api.orneksite.com

# Otomatik yenileme kontrolü
sudo systemctl status certbot.timer
sudo certbot renew --dry-run

SSL konfigürasyonunu manuel olarak da yapabilirsiniz. Güvenli bir SSL konfigürasyonu şöyle görünür:

server {
    listen 80;
    server_name api.orneksite.com;
    # HTTP'yi HTTPS'e yönlendir
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name api.orneksite.com;

    # SSL sertifikaları
    ssl_certificate /etc/letsencrypt/live/api.orneksite.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/api.orneksite.com/privkey.pem;

    # Güvenli SSL ayarları
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
    ssl_prefer_server_ciphers off;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 1d;
    ssl_session_tickets off;

    # HSTS
    add_header Strict-Transport-Security "max-age=63072000" always;

    # OCSP Stapling
    ssl_stapling on;
    ssl_stapling_verify on;
    ssl_trusted_certificate /etc/letsencrypt/live/api.orneksite.com/chain.pem;
    resolver 8.8.8.8 8.8.4.4 valid=300s;
    resolver_timeout 5s;
}

Token Tabanlı Authentication

Nginx tek başına JWT doğrulayamaz ama bir auth servisine istek yönlendirerek authentication yapabilir. Bunun için auth_request modülünü kullanıyoruz.

Önce Nginx’in bu modülle derlenip derlenmediğini kontrol edelim:

nginx -V 2>&1 | grep -o with-http_auth_request_module
# Çıktı "with-http_auth_request_module" ise modül mevcut

Auth request konfigürasyonu:

server {
    listen 443 ssl http2;
    server_name api.orneksite.com;

    # Auth servisi tanımı (dahili, dışarıya kapalı)
    location = /auth/validate {
        internal;
        proxy_pass http://127.0.0.1:8080/validate;
        proxy_pass_request_body off;
        proxy_set_header Content-Length "";
        proxy_set_header X-Original-URI $request_uri;
        proxy_set_header X-Original-Method $request_method;
        proxy_set_header Authorization $http_authorization;
    }

    # Korunan endpoint örneği
    location /api/v1/orders/ {
        # Her istekte auth servisini çağır
        auth_request /auth/validate;

        # Auth servisinden dönen header'ları forward et
        auth_request_set $auth_user_id $upstream_http_x_user_id;
        auth_request_set $auth_user_role $upstream_http_x_user_role;

        # Bu header'ları backend'e ilet
        proxy_set_header X-User-ID $auth_user_id;
        proxy_set_header X-User-Role $auth_user_role;

        proxy_pass http://order_service/;
    }

    # Auth hatalarını yakala
    error_page 401 = @unauthorized;
    error_page 403 = @forbidden;

    location @unauthorized {
        default_type application/json;
        return 401 '{"error": "Kimlik dogrulama gerekli", "code": "UNAUTHORIZED"}';
    }

    location @forbidden {
        default_type application/json;
        return 403 '{"error": "Bu kaynaga erisim yetkiniz yok", "code": "FORBIDDEN"}';
    }
}

Load Balancing

Bir servisin birden fazla instance’ı varsa Nginx aralarında load balancing yapabilir. Upstream bloğunda bunu kolayca ayarlayabiliyoruz:

upstream order_service {
    # Round robin (varsayılan)
    server 10.0.0.1:8003 weight=3;
    server 10.0.0.2:8003 weight=2;
    server 10.0.0.3:8003 weight=1;

    # Sunucu devre dışıysa yedek kullan
    server 10.0.0.4:8003 backup;

    # Sağlık kontrolü - 3 başarısız istekten sonra 30 saniye dışla
    # (nginx plus özelliği, açık kaynak versiyonda passive health check)
    # server 10.0.0.1:8003 max_fails=3 fail_timeout=30s;

    keepalive 64;
}

upstream user_service {
    # IP hash - aynı istemci hep aynı sunucuya gider (session stickiness)
    ip_hash;
    server 10.0.0.10:8001;
    server 10.0.0.11:8001;
}

upstream product_service {
    # En az bağlantı sayısına sahip sunucuya gönder
    least_conn;
    server 10.0.0.20:8002;
    server 10.0.0.21:8002;
    server 10.0.0.22:8002;
}

Passive health check için basit bir konfigürasyon:

upstream order_service {
    server 10.0.0.1:8003 max_fails=3 fail_timeout=30s;
    server 10.0.0.2:8003 max_fails=3 fail_timeout=30s;
    server 10.0.0.3:8003 max_fails=3 fail_timeout=30s;
}

Loglama ve Monitoring

İyi bir API Gateway’de her şeyin loglanması gerekir. Özel bir log formatı tanımlayalım:

http {
    # API Gateway için özel log formatı
    log_format api_gateway escape=json
        '{'
            '"timestamp":"$time_iso8601",'
            '"request_id":"$request_id",'
            '"remote_addr":"$remote_addr",'
            '"method":"$request_method",'
            '"uri":"$request_uri",'
            '"status":$status,'
            '"body_bytes":$body_bytes_sent,'
            '"request_time":$request_time,'
            '"upstream_addr":"$upstream_addr",'
            '"upstream_response_time":"$upstream_response_time",'
            '"upstream_status":"$upstream_status",'
            '"http_user_agent":"$http_user_agent",'
            '"http_x_forwarded_for":"$http_x_forwarded_for"'
        '}';

    # Bu logu kullan
    access_log /var/log/nginx/api_gateway_access.log api_gateway;
    error_log /var/log/nginx/api_gateway_error.log warn;
}

Request ID eklemek, distributed tracing için çok değerli:

server {
    # Her istekte unique ID oluştur
    add_header X-Request-ID $request_id always;

    location /api/v1/ {
        proxy_set_header X-Request-ID $request_id;
        # ...
    }
}

Logları analiz etmek için birkaç kullanışlı komut:

# Son 100 hata logunu göster
tail -100 /var/log/nginx/api_gateway_access.log | python3 -m json.tool | grep '"status":5'

# En çok istek atan IP'leri bul
cat /var/log/nginx/api_gateway_access.log | python3 -c "
import sys, json
ips = {}
for line in sys.stdin:
    try:
        d = json.loads(line)
        ip = d['remote_addr']
        ips[ip] = ips.get(ip, 0) + 1
    except:
        pass
for ip, count in sorted(ips.items(), key=lambda x: x[1], reverse=True)[:10]:
    print(f'{count}t{ip}')
"

# Ortalama response time hesapla
cat /var/log/nginx/api_gateway_access.log | python3 -c "
import sys, json
times = []
for line in sys.stdin:
    try:
        d = json.loads(line)
        times.append(float(d['request_time']))
    except:
        pass
if times:
    print(f'Ortalama: {sum(times)/len(times):.3f}s')
    print(f'Maksimum: {max(times):.3f}s')
    print(f'Toplam istek: {len(times)}')
"

Güvenlik Başlıkları ve CORS

API Gateway’de güvenlik başlıkları tek yerden yönetilir:

server {
    listen 443 ssl http2;

    # Güvenlik başlıkları
    add_header X-Content-Type-Options nosniff always;
    add_header X-Frame-Options DENY always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;

    # CORS konfigürasyonu
    location /api/ {
        # Preflight isteklerini handle et
        if ($request_method = 'OPTIONS') {
            add_header Access-Control-Allow-Origin "https://app.orneksite.com";
            add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS";
            add_header Access-Control-Allow-Headers "Authorization, Content-Type, X-Request-ID";
            add_header Access-Control-Max-Age 3600;
            add_header Content-Type "text/plain; charset=utf-8";
            add_header Content-Length 0;
            return 204;
        }

        add_header Access-Control-Allow-Origin "https://app.orneksite.com" always;
        add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" always;
        add_header Access-Control-Allow-Headers "Authorization, Content-Type, X-Request-ID" always;
        add_header Access-Control-Expose-Headers "X-Request-ID, X-RateLimit-Remaining" always;

        proxy_pass http://upstream_service/;
    }

    # Hassas dosyalara erişimi engelle
    location ~ /. {
        deny all;
    }

    # Büyük body'leri sınırla (DDoS koruması)
    client_max_body_size 10m;
    client_body_timeout 10s;
    client_header_timeout 10s;
}

Konfigürasyon Yönetimi ve Reload

Production’da konfigürasyon değişikliklerini sıfır downtime ile uygulamak için:

# Konfigürasyon syntax kontrolü (MUTLAKA yapılmalı)
sudo nginx -t

# Çıktı bu olmalı:
# nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
# nginx: configuration file /etc/nginx/nginx.conf test is successful

# Graceful reload (mevcut bağlantıları kesmez)
sudo nginx -s reload
# veya
sudo systemctl reload nginx

# Konfigürasyon değişikliklerini takip etmek için git kullanalım
cd /etc/nginx
sudo git init
sudo git add .
sudo git commit -m "Ilk API Gateway konfigürasyonu"

# Değişiklik yapınca
sudo git add conf.d/api-gateway.conf
sudo git commit -m "Rate limiting eklendi"
sudo nginx -t && sudo nginx -s reload

Konfigürasyonu modüler hale getirmek büyük projelerde hayat kurtarır:

# Rate limiting ayarlarını ayrı dosyaya taşı
sudo nano /etc/nginx/snippets/rate-limiting.conf

# SSL ayarlarını ayrı dosyaya taşı
sudo nano /etc/nginx/snippets/ssl-params.conf

# Ana konfigürasyonda include kullan
# /etc/nginx/conf.d/api-gateway.conf içinde:
# include snippets/rate-limiting.conf;
# include snippets/ssl-params.conf;

Gerçek Dünya İpuçları

Birkaç yılın deneyiminden süzdüğüm pratik notlar:

  • Upstream keepalive kullan: Her istek için yeni TCP bağlantısı açmak pahalı. keepalive 32 ile bağlantıları yeniden kullan.
  • Proxy buffer’larını ayarla: Büyük header’lar olan API’larda proxy_buffer_size 128k; proxy_buffers 4 256k; eklemek timeout sorunlarını çözer.
  • Timeout değerlerini servis özelinde ayarla: Tüm servislere aynı timeout uygulamak yanlış. Raporlama servisi uzun sürebilir, login servisi hızlı yanıt vermeli.
  • Health endpoint’i ekle: Load balancer’lar için Nginx’in kendisinde basit bir health check endpoint tut: location /health { return 200 "ok"; add_header Content-Type text/plain; }
  • Logları ayır: Her servisin logunu ayrı dosyaya yaz, hata ayıklama çok kolaylaşır.
  • Configuration test’i otomatize et: CI/CD pipeline’ına nginx -t ekle, hatalı konfigürasyonun production’a gitmesini önle.

Sonuç

Nginx ile kurulan bir API Gateway, küçük ve orta ölçekli projeler için oldukça güçlü ve yeterli bir çözüm sunuyor. Rate limiting, SSL termination, authentication proxy, load balancing ve detaylı loglama ile gerçek anlamda production-ready bir gateway elde edebilirsiniz.

Tabii Nginx’in açık kaynak versiyonunun sınırları var. Aktif health check, JWT native doğrulama, görsel dashboard gibi özellikler için ya Nginx Plus’a ya da önüne bir Prometheus/Grafana stack’i kurman gerekiyor. Kong, Traefik gibi native API Gateway çözümlerini de göz ardı etmemek gerek, özellikle plugin ekosistemi önemli olduğunda.

Ama işin gerçeği şu: Çoğu startup ve orta ölçekli şirket için bu yazıda anlattıklarımız fazlasıyla yeterli. Nginx’i iyi öğrenmek, pahalı bir API Gateway çözümü satın almaktan çok daha değerli. Altyapını anlıyorsun, sorun çıktığında ne yapacağını biliyorsun ve gereksiz karmaşıklıktan kaçınmış oluyorsun.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir