Nginx denince akla ilk gelen HTTP trafiği yönetimi olur, ama bu güçlü web sunucusunun yetenekleri HTTP ile sınırlı değil. Nginx’in stream modülü sayesinde TCP ve UDP trafiğini de proxy’leyebilir, yük dengeleyebilir ve yönetebilirsiniz. Veritabanı bağlantılarını load balance etmek, oyun sunucularına UDP trafiği yönlendirmek ya da mail servislerini proxy’lemek istiyorsanız, doğru yerdesiniz.
Bu yazıda Nginx stream proxy’yi sıfırdan kurarak gerçek dünya senaryolarıyla ele alacağız. MySQL cluster’ı önüne Nginx koymaktan tutun DNS load balancing’e kadar pek çok pratik örneği adım adım göreceğiz.
Nginx Stream Modülü Nedir?
Nginx’in ngx_stream_core_module modülü, OSI modelinin 4. katmanında (Transport Layer) çalışır. HTTP proxy’nin aksine, bu modül ham TCP ve UDP paketlerini işler. İçeriği parse etmez, sadece yönlendirir. Bu da onu son derece hızlı ve verimli kılar.
Stream modülü şu işler için biçilmiş kaftan:
- TCP Proxy: MySQL, PostgreSQL, Redis, SMTP, SSH gibi TCP tabanlı servisler
- UDP Proxy: DNS, Syslog, oyun sunucuları, VoIP gibi UDP tabanlı servisler
- TCP/UDP Load Balancing: Birden fazla backend sunucusu arasında trafik dağıtımı
- SSL/TLS Termination: TCP bağlantıları için SSL sonlandırma
- Health Checks: Backend sunucularının sağlık kontrolü
Kurulum Kontrolü
Nginx’in stream modülü ile derlenip derlenmediğini kontrol etmekle başlayalım. Her Nginx kurulumunda bu modül varsayılan olarak gelmeyebilir.
nginx -V 2>&1 | grep -o with-stream
Eğer çıktıda with-stream görüyorsanız hazırsınız demektir. Görmüyorsanız Nginx’i stream modülü ile yeniden derlemeniz ya da paket yöneticinizden uygun sürümü kurmanız gerekir.
Ubuntu/Debian sistemlerde nginx-full veya nginx-extras paketi stream modülünü içerir:
# Ubuntu/Debian
apt install nginx-full
# CentOS/RHEL
yum install nginx
# Stream modülünün yüklü olup olmadığını doğrula
nginx -V 2>&1 | tr ' ' 'n' | grep stream
Temel Konfigürasyon Yapısı
Stream konfigürasyonu, nginx.conf dosyasında http bloğuyla aynı seviyede tanımlanır. Bu önemli bir detay; stream bloğu http bloğunun içine girmez.
# /etc/nginx/nginx.conf temel yapısı
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
# HTTP trafiği için
http {
include /etc/nginx/mime.types;
include /etc/nginx/conf.d/*.conf;
}
# TCP/UDP trafiği için - http bloğunun DIŞINDA
stream {
include /etc/nginx/stream.d/*.conf;
}
Stream konfigürasyonlarını düzenli tutmak için ayrı bir dizin oluşturalım:
mkdir -p /etc/nginx/stream.d
Senaryo 1: MySQL Load Balancing
En yaygın kullanım senaryolarından biri veritabanı load balancing. Diyelim ki üç MySQL slave sunucunuz var ve okuma trafiğini bunlar arasında dağıtmak istiyorsunuz.
# /etc/nginx/stream.d/mysql.conf
upstream mysql_slaves {
least_conn; # En az bağlantılı backend'e yönlendir
server 192.168.1.101:3306 weight=1;
server 192.168.1.102:3306 weight=1;
server 192.168.1.103:3306 weight=2; # Bu sunucu daha güçlü
# Bağlantı tutma süresi
keepalive 32;
}
upstream mysql_master {
server 192.168.1.100:3306;
}
server {
listen 3306; # Okuma trafiği için
proxy_pass mysql_slaves;
proxy_connect_timeout 3s;
proxy_timeout 60s;
}
server {
listen 3307; # Yazma trafiği için master'a
proxy_pass mysql_master;
proxy_connect_timeout 3s;
proxy_timeout 60s;
}
Uygulamanızda okuma sorgularını localhost:3306‘ya, yazma sorgularını ise localhost:3307‘ye yönlendirirsiniz. Nginx gerisini halleder.
Load balancing metodlarını seçerken dikkatli olun:
- round_robin: Varsayılan, sırayla dağıtır
- least_conn: En az aktif bağlantısı olan backend’e gönderir
- ip_hash: Aynı IP her zaman aynı backend’e gider (session persistence)
- hash: Özel bir anahtara göre dağıtım yapılır
Senaryo 2: Redis Cluster Proxy
Redis Sentinel veya basit Redis replikasyonu kullanıyorsanız, Nginx ile önünde bir proxy katmanı oluşturabilirsiniz.
# /etc/nginx/stream.d/redis.conf
upstream redis_cluster {
server 192.168.1.201:6379 max_fails=3 fail_timeout=30s;
server 192.168.1.202:6379 max_fails=3 fail_timeout=30s;
server 192.168.1.203:6379 backup; # Sadece diğerleri düşünce devreye girer
}
server {
listen 6379;
proxy_pass redis_cluster;
proxy_connect_timeout 1s;
proxy_timeout 3s;
# Aktif health check için (Nginx Plus gerektirir, açık kaynak versiyonda yorum satırı)
# health_check interval=5 passes=2 fails=3;
}
max_fails ve fail_timeout parametrelerini doğru ayarlamak kritik. Bir backend belirli sayıda başarısız olursa belirtilen süre boyunca devre dışı kalır.
Senaryo 3: DNS Load Balancing (UDP)
UDP proxy’nin en güzel örneği DNS yük dengeleme. Birden fazla DNS sunucunuz varsa ve trafiği dağıtmak istiyorsanız:
# /etc/nginx/stream.d/dns.conf
upstream dns_servers {
server 8.8.8.8:53;
server 8.8.4.4:53;
server 1.1.1.1:53;
}
server {
listen 53 udp; # UDP dinle
listen 53; # TCP DNS için de (bazı sorgular TCP kullanır)
proxy_pass dns_servers;
proxy_timeout 2s;
proxy_responses 1; # UDP için kaç yanıt bekleneceği
}
proxy_responses direktifi UDP’ye özgü önemli bir parametredir. DNS için 1 kullanırsınız çünkü her sorguya tek bir yanıt gelir. Bazı protokollerde bu değer farklı olabilir.
Senaryo 4: SSH Jump Host
Bir güvenlik gereksinimi olarak, sadece belirli porttan SSH bağlantısı kabul etmek ve bunu iç ağdaki bir sunucuya yönlendirmek isteyebilirsiniz.
# /etc/nginx/stream.d/ssh.conf
# Birden fazla sunucuya SSH erişimi için port tabanlı yönlendirme
server {
listen 2201;
proxy_pass 10.0.0.11:22;
proxy_timeout 3600s; # SSH oturumları uzun sürebilir
proxy_connect_timeout 5s;
}
server {
listen 2202;
proxy_pass 10.0.0.12:22;
proxy_timeout 3600s;
proxy_connect_timeout 5s;
}
server {
listen 2203;
proxy_pass 10.0.0.13:22;
proxy_timeout 3600s;
proxy_connect_timeout 5s;
}
Bu yapı özellikle DMZ ortamlarında işe yarar. Dış dünyadan sadece belirli portlar açık, arka tarafta ise iç ağa erişim. Firewall kuralları da çok daha sade kalır.
Senaryo 5: SSL/TLS ile Güvenli TCP Proxy
Güvensiz bir ağ üzerinden geçen TCP trafiğini şifrelemek istiyorsanız Nginx stream modülüyle SSL termination yapabilirsiniz.
Önce sertifika oluşturalım (test için self-signed):
openssl req -x509 -nodes -days 365 -newkey rsa:2048
-keyout /etc/nginx/ssl/stream.key
-out /etc/nginx/ssl/stream.crt
-subj "/CN=stream.orneksite.com"
Şimdi SSL’li stream konfigürasyonu:
# /etc/nginx/stream.d/ssl_proxy.conf
upstream backend_servers {
server 10.0.0.10:5432; # PostgreSQL
server 10.0.0.11:5432;
}
server {
listen 5432 ssl;
ssl_certificate /etc/nginx/ssl/stream.crt;
ssl_certificate_key /etc/nginx/ssl/stream.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_handshake_timeout 10s;
proxy_pass backend_servers;
proxy_ssl off; # Backend'e giderken SSL kapalı (iç ağ güvenli)
}
Eğer backend sunuculara da şifreli bağlanmak istiyorsanız proxy_ssl on yapıp gerekli sertifika ayarlarını eklersiniz.
Senaryo 6: Syslog Trafiği (UDP)
Merkezi log yönetimi için UDP üzerinden gelen syslog trafiğini yük dengelemek oldukça yaygın:
# /etc/nginx/stream.d/syslog.conf
upstream syslog_servers {
server 10.0.1.10:514;
server 10.0.1.11:514;
server 10.0.1.12:514;
}
server {
listen 514 udp;
proxy_pass syslog_servers;
proxy_timeout 1s;
proxy_responses 0; # Syslog UDP için yanıt beklenmez
}
proxy_responses 0 dikkat edin. Syslog fire-and-forget bir protokol, sunucu yanıt dönmez. Bu yüzden sıfır yanıt bekliyoruz.
Loglama ve İzleme
Stream modülü için loglama HTTP modülünden biraz farklı çalışır. Log formatı stream’e özel değişkenlerle zenginleştirilmelidir.
# /etc/nginx/nginx.conf içinde stream bloğuna ekleyin
stream {
log_format stream_log '$remote_addr [$time_local] '
'$protocol $status $bytes_sent $bytes_received '
'$session_time "$upstream_addr" '
'"$upstream_bytes_sent" "$upstream_bytes_received" '
'$upstream_connect_time';
access_log /var/log/nginx/stream_access.log stream_log;
include /etc/nginx/stream.d/*.conf;
}
Stream’de kullanabileceğiniz önemli değişkenler:
- $remote_addr: İstemci IP adresi
- $protocol: TCP veya UDP
- $status: Bağlantı durumu (200, 400 gibi)
- $bytes_sent: Gönderilen byte miktarı
- $bytes_received: Alınan byte miktarı
- $session_time: Oturum süresi
- $upstream_addr: Seçilen backend adresi
- $upstream_connect_time: Backend’e bağlantı süresi
IP Tabanlı Erişim Kontrolü
Stream modülünde belirli IP adreslerine erişimi kısıtlayabilirsiniz:
# /etc/nginx/stream.d/mysql_secure.conf
geo $allowed_ips {
default 0;
10.0.0.0/8 1;
192.168.0.0/16 1;
172.16.0.0/12 1;
}
map $allowed_ips $blocked {
0 "BLOCKED";
1 "";
}
upstream mysql_backend {
server 192.168.1.101:3306;
}
server {
listen 3306;
# İzin verilmeyen IP'leri engelle
if ($blocked) {
return 403;
}
proxy_pass mysql_backend;
proxy_connect_timeout 3s;
proxy_timeout 60s;
}
Not: Stream modülünde if kullanımı HTTP modülüne göre çok daha kısıtlı. Erişim kontrolü için geo ve map kombinasyonu genellikle daha sağlıklı.
Gerçek Dünya: Çoklu Servis Proxy Mimarisi
Gerçek bir production ortamunda nasıl görünür? İşte eksiksiz bir örnek:
# /etc/nginx/stream.d/production.conf
# MySQL Master-Slave
upstream mysql_write {
server db-master.internal:3306 max_fails=2 fail_timeout=10s;
}
upstream mysql_read {
least_conn;
server db-slave1.internal:3306 max_fails=2 fail_timeout=10s;
server db-slave2.internal:3306 max_fails=2 fail_timeout=10s;
server db-slave3.internal:3306 max_fails=2 fail_timeout=10s;
}
# Redis
upstream redis_primary {
server redis01.internal:6379 max_fails=3 fail_timeout=15s;
server redis02.internal:6379 backup;
}
# RabbitMQ AMQP
upstream rabbitmq {
least_conn;
server rabbit01.internal:5672 max_fails=3 fail_timeout=10s;
server rabbit02.internal:5672 max_fails=3 fail_timeout=10s;
}
# MySQL yazma - port 3306
server {
listen 3306;
proxy_pass mysql_write;
proxy_connect_timeout 3s;
proxy_timeout 120s;
}
# MySQL okuma - port 3307
server {
listen 3307;
proxy_pass mysql_read;
proxy_connect_timeout 3s;
proxy_timeout 120s;
}
# Redis - port 6379
server {
listen 6379;
proxy_pass redis_primary;
proxy_connect_timeout 1s;
proxy_timeout 10s;
}
# RabbitMQ AMQP - port 5672
server {
listen 5672;
proxy_pass rabbitmq;
proxy_connect_timeout 5s;
proxy_timeout 300s; # AMQP bağlantıları uzun yaşar
}
Konfigürasyonu test edip yükleyin:
# Syntax kontrolü
nginx -t
# Hata yoksa reload
systemctl reload nginx
# Bağlantı kontrolü
ss -tlnp | grep nginx
# Stream log takibi
tail -f /var/log/nginx/stream_access.log
Performans Tuning
Stream proxy için birkaç önemli performans ayarı:
# /etc/nginx/nginx.conf
worker_processes auto;
worker_rlimit_nofile 65535;
events {
worker_connections 65535;
use epoll;
multi_accept on;
}
stream {
# Bağlantı havuzu
upstream_keepalive_connections 100;
upstream_keepalive_timeout 60s;
# Proxy buffer
proxy_buffer_size 16k;
include /etc/nginx/stream.d/*.conf;
}
proxy_buffer_size küçük tutarsanız ağ verimliliği düşer, çok büyük tutarsanız bellek israf edersiniz. 16k çoğu senaryo için makul başlangıç noktasıdır. Yüksek throughput gerektiren durumlarda 32k veya 64k deneyebilirsiniz.
Sık Karşılaşılan Sorunlar ve Çözümleri
Stream bloğu tanınmıyor hatası: Nginx’in stream modülü olmadan derlenmiş olması. nginx -V ile kontrol edin, gerekirse uygun paketi kurun.
Backend’e bağlanılamıyor: Firewall kurallarını kontrol edin. proxy_connect_timeout değeri çok düşük olabilir, 3-5 saniyeye çıkarın.
UDP trafiği çalışmıyor: listen direktifine udp yazmayı unutmak çok yaygın. listen 53 udp; şeklinde olmalı.
Çok fazla bağlantı hatası: worker_connections ve sistem ulimit değerlerini artırın:
# Sistem limiti
echo "nginx soft nofile 65535" >> /etc/security/limits.conf
echo "nginx hard nofile 65535" >> /etc/security/limits.conf
# Systemd service için
mkdir -p /etc/systemd/system/nginx.service.d/
echo "[Service]
LimitNOFILE=65535" > /etc/systemd/system/nginx.service.d/limits.conf
systemctl daemon-reload
systemctl restart nginx
Yüksek latency: proxy_connect_timeout ve proxy_timeout değerlerini uygulamanızın beklentisine göre ayarlayın. Timeout çok kısa olunca bağlantılar gereksiz yere kopabilir.
SSL handshake hataları: ssl_handshake_timeout değerini artırın, zayıf TLS protokollerini kaldırın, sertifika zincirinin eksiksiz olduğundan emin olun.
Sonuç
Nginx’in stream modülü, özellikle microservice mimarilerinde ve veritabanı katmanında son derece pratik bir araç. HTTP trafiğini zaten Nginx ile yönetiyorsanız, TCP/UDP proxy için ayrı bir araç kurmak yerine aynı Nginx’i kullanmak hem konfigürasyonu sadeleştirir hem de yönetim yükünü azaltır.
Özellikle dikkat etmeniz gereken noktaları özetleyeyim: stream bloğu her zaman http bloğunun dışında olmalı, UDP proxy için udp anahtar kelimesini unutmayın, proxy_responses değerini protokole göre ayarlayın ve production’da mutlaka max_fails ile fail_timeout tanımlayın.
Load balancing algoritması seçimi uygulamanızın karakterine göre değişir. Veritabanı bağlantıları için least_conn genellikle round_robin‘den daha iyi sonuç verir, çünkü bazı sorgular diğerlerinden çok daha uzun sürer ve yük dengelemesini gerçekçi yapar.
Monitoring tarafını ihmal etmeyin. Stream access log’larını düzenli izleyin, backend connection sürelerini takip edin. Prometheus + Nginx exporter kombinasyonu bu metrikler için ideal bir çözüm, ama bu başka bir yazının konusu.