Nginx Upstream Bağlantı Hataları: Nedenleri ve Çözümleri
Production sunucunda gece 2’de telefon çalıyor, ekran açılıyor ve ilk gördüğün şey “502 Bad Gateway” hatası. Nginx upstream bağlantı hataları, sysadmin hayatının en sinir bozucu anlarından birini oluşturur. Çünkü bu hata tek bir nedenden kaynaklanmaz; arkasında onlarca farklı senaryo yatabilir. Bu yazıda upstream bağlantı hatalarını kökten çözmeyi, log analizinden başlayarak gerçek dünya senaryolarına kadar adım adım ele alacağız.
Upstream Bağlantı Hatalarını Anlamak
Nginx, reverse proxy olarak çalışırken backend uygulamalara (upstream) istekleri iletir. Bu upstream sunucular PHP-FPM, Node.js, Python/Gunicorn, Java/Tomcat veya başka bir Nginx instance’ı olabilir. Bağlantı zincirinde herhangi bir kopukluk olduğunda, kullanıcı 502 veya 504 hatası görür.
Temel hata kodlarını şöyle ayırt etmek gerekir:
- 502 Bad Gateway: Upstream sunucu geçersiz ya da anlaşılmaz bir yanıt döndürdü veya bağlantı tamamen reddedildi
- 504 Gateway Timeout: Upstream sunucu belirlenen süre içinde yanıt vermedi
- 499 Client Closed Request: İstemci beklemeyi bırakıp bağlantıyı kapattı (upstream yavaş demek)
- 503 Service Unavailable: Tüm upstream sunucular devre dışı
Log Dosyalarına İlk Bakış
Her şey logları okumakla başlar. Nginx’in error log formatını düzgün yapılandırmamışsan, hata ayıklama süreci gereksiz yere uzar.
# Temel error log takibi
tail -f /var/log/nginx/error.log
# Sadece upstream hatalarını filtrele
tail -f /var/log/nginx/error.log | grep -i "upstream"
# Son 1 saatteki upstream hatalarını say
grep "$(date +'%Y/%m/%d %H')" /var/log/nginx/error.log | grep upstream | wc -l
# Hangi upstream sunucular hata veriyor?
grep "upstream" /var/log/nginx/error.log | grep -oP '(?<=upstream: ")[^"]+' | sort | uniq -c | sort -rn
Karşılaşacağın tipik hata mesajları şunlar:
- connect() failed (111: Connection refused): Upstream process çalışmıyor
- connect() failed (113: No route to host): Ağ erişim problemi
- upstream timed out (110: Connection timed out): Upstream yanıt vermiyor
- no live upstreams while connecting to upstream: Tüm upstream’ler down
- upstream sent invalid header: Upstream bozuk HTTP header döndürüyor
Access Log’dan Sorunları Tespit Etme
Error log kadar access log da değerlidir. Upstream sorunlarında request_time ve upstream_response_time değerlerini karşılaştırmak çok işe yarar.
# Access log için özel format tanımla (nginx.conf içine)
log_format upstream_log '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'rt=$request_time uct=$upstream_connect_time '
'uht=$upstream_header_time urt=$upstream_response_time '
'us=$upstream_status ua="$upstream_addr"';
Bu format aktifken log analizi yapalım:
# 502 veren istekleri bul ve upstream adreslerini listele
awk '$9 == 502 {print $NF}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20
# Upstream response time ortalamasını hesapla
awk '$9 == 200 {sum += $NF; count++} END {print "Ortalama:", sum/count, "saniye"}' /var/log/nginx/access.log
# 3 saniyeden uzun süren istekleri bul
awk -F'urt=' '{print $2}' /var/log/nginx/access.log | awk '$1 > 3 {print}' | wc -l
Senaryo 1: PHP-FPM Upstream Hatası
En yaygın senaryo bu. WordPress veya Laravel çalıştırıyorsun, birden 502 başlıyor.
# PHP-FPM durumunu kontrol et
systemctl status php8.1-fpm
# veya
ps aux | grep php-fpm
# PHP-FPM error log
tail -50 /var/log/php8.1-fpm.log
# PHP-FPM socket var mı?
ls -la /run/php/php8.1-fpm.sock
# Socket üzerinden test
curl --unix-socket /run/php/php8.1-fpm.sock http://localhost/status
PHP-FPM pool yapılandırmasındaki sorunlar sıklıkla upstream hatasına yol açar. /etc/php/8.1/fpm/pool.d/www.conf dosyasına bak:
; Worker sayısı az mı? Dinamik ayarla
pm = dynamic
pm.max_children = 50
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 20
pm.max_requests = 500
; Request queue doluyor mu? Bu değeri artır
pm.status_path = /status
Nginx tarafında PHP-FPM için doğru upstream yapılandırması:
upstream php_backend {
server unix:/run/php/php8.1-fpm.sock;
# TCP kullanıyorsan:
# server 127.0.0.1:9000;
keepalive 32;
}
server {
location ~ .php$ {
fastcgi_pass php_backend;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
# Timeout değerlerini yükselt
fastcgi_connect_timeout 60s;
fastcgi_send_timeout 60s;
fastcgi_read_timeout 60s;
# Buffer ayarları
fastcgi_buffer_size 128k;
fastcgi_buffers 4 256k;
fastcgi_busy_buffers_size 256k;
}
}
Senaryo 2: Node.js veya Gunicorn Upstream Timeout
Node.js uygulamaları bazen event loop bloğuna girer, Gunicorn worker’ları kilitlenir. Bunların belirtileri farklıdır.
# Node.js process çalışıyor mu?
ps aux | grep node
netstat -tlnp | grep 3000 # veya kullandığın port
# Gunicorn worker durumu
ps aux | grep gunicorn
kill -TTIN $(cat /tmp/gunicorn.pid) # Yeni worker ekle (graceful)
# Port dinleniyor mu?
ss -tlnp | grep :8000
Load balancer senaryosunda birden fazla upstream sunucu varsa ve bir tanesi yavaşlamışsa:
upstream app_servers {
least_conn; # En az bağlantıyı olan'a gönder
server 10.0.0.1:8000 weight=3 max_fails=3 fail_timeout=30s;
server 10.0.0.2:8000 weight=3 max_fails=3 fail_timeout=30s;
server 10.0.0.3:8000 backup; # Yedek sunucu
keepalive 64;
keepalive_requests 100;
keepalive_timeout 60s;
}
server {
location / {
proxy_pass http://app_servers;
proxy_http_version 1.1;
proxy_set_header Connection "";
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_connect_timeout 10s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503;
proxy_next_upstream_tries 3;
proxy_next_upstream_timeout 30s;
}
}
proxy_next_upstream direktifi çok değerlidir. Bir upstream hata verdiğinde Nginx otomatik olarak sıradaki sunucuya geçer. Ancak dikkatli kullan; POST isteklerinde idempotent olmayan işlemler için bunu devre dışı bırakmak gerekebilir.
Senaryo 3: “No Live Upstreams” Hatası
Bu hata tüm upstream sunucuların passive health check’ten geçemediğini gösterir. Nginx error log’da şunu görürsün:
no live upstreams while connecting to upstream
# Tüm upstream adreslerine erişim testi
for host in 10.0.0.1 10.0.0.2 10.0.0.3; do
echo -n "$host:8000 - "
timeout 3 bash -c "echo > /dev/tcp/$host/8000" 2>/dev/null && echo "AÇIK" || echo "KAPALI"
done
# Nginx upstream cache'ini temizlemek için reload
nginx -s reload
# Passive health check parametrelerini gözden geçir
# max_fails ve fail_timeout değerlerini kontrol et
grep -r "max_fails|fail_timeout" /etc/nginx/
Nginx Plus’ta aktif health check mevcut ama açık kaynak versiyonda bunu ngx_http_healthcheck_module veya harici araçlarla halletmen gerekiyor. Alternatif olarak basit bir health check endpoint yapılandırması:
upstream backend {
server 10.0.0.1:8000 max_fails=2 fail_timeout=20s;
server 10.0.0.2:8000 max_fails=2 fail_timeout=20s;
}
# Health check için ayrı location
server {
listen 8080;
location /nginx-health {
access_log off;
return 200 "healthyn";
add_header Content-Type text/plain;
}
location /upstream-check {
proxy_pass http://backend/health;
proxy_connect_timeout 2s;
proxy_read_timeout 2s;
}
}
Buffer ve Timeout Ayarları Derinlemesine
Upstream hatalarının önemli bir kısmı yanlış yapılandırılmış buffer ve timeout değerlerinden gelir. Büyük dosya yüklemeleri veya uzun süren API çağrıları bu sorunları tetikler.
http {
# Proxy buffer ayarları
proxy_buffer_size 128k;
proxy_buffers 4 256k;
proxy_busy_buffers_size 256k;
proxy_temp_file_write_size 256k;
# Büyük header'lar için
proxy_buffer_size 16k;
large_client_header_buffers 4 16k;
# Timeout değerleri - senaryoya göre ayarla
proxy_connect_timeout 10s; # TCP handshake için
proxy_send_timeout 60s; # Request gönderme için
proxy_read_timeout 60s; # Response okuma için
# Upstream keepalive
upstream app {
server 127.0.0.1:8000;
keepalive 32;
}
server {
location / {
proxy_pass http://app;
proxy_http_version 1.1;
proxy_set_header Connection ""; # Keepalive için boş bırak
}
}
}
Uzun polling veya WebSocket bağlantıları için ek ayarlar gerekir:
location /ws {
proxy_pass http://websocket_backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
# WebSocket için timeout'ları uzat
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
# Heartbeat için
proxy_socket_keepalive on;
}
Debug Mode ile Derinlemesine Analiz
Üretim ortamında debug seviyesi log tehlikeli olabilir ama test ortamında ya da çok kısa süreli olarak production’da şunları kullanabilirsin:
# Nginx debug logunu geçici olarak etkinleştir
# error_log direktifini değiştir
error_log /var/log/nginx/error.log debug;
# Sadece belirli bir IP için debug
events {
debug_connection 192.168.1.100;
}
# Nginx'i test et ve yeniden yükle
nginx -t && nginx -s reload
# Debug logunu izle
tail -f /var/log/nginx/error.log | grep -E "upstream|connect|timeout"
# Birkaç dakika sonra normal seviyeye dön
# error_log /var/log/nginx/error.log warn;
strace ile Nginx worker process’ini izlemek, bağlantı sorunlarını gerçek zamanlı görmek için güçlü bir yöntemdir:
# Nginx worker PID'lerini bul
ps aux | grep "nginx: worker"
# Worker'ı strace ile izle (dikkatli kullan, performans etkisi var)
strace -p <worker_pid> -e trace=network -s 128 2>&1 | grep -i "connect|recv|send"
# Daha hafif alternatif: ss ile bağlantı durumlarını izle
watch -n 1 'ss -s && echo "---" && ss -tnp | grep nginx'
Gerçek Dünya Senaryosu: E-ticaret Sitesinde Kampanya Günü Çöküşü
Bizzat yaşadığım bir vakayı paylaşayım. Bir e-ticaret müşterisinin büyük kampanya günü sabah 9’da trafik 10 katına çıkınca 502 yağmuru başladı.
# İlk müdahale: Durumu anla
tail -100 /var/log/nginx/error.log | grep upstream | awk '{print $1, $2, $10}' | sort | uniq -c
# Sonuç: PHP-FPM max_children dolmuş
# /var/log/php8.1-fpm.log içinde:
# WARNING: [pool www] seems busy (you may need to increase pm.max_children)
# NOTICE: pm.max_children reached, consider increasing it
# Anlık düzeltme: pm.max_children artır
sed -i 's/pm.max_children = 20/pm.max_children = 80/' /etc/php/8.1/fpm/pool.d/www.conf
systemctl reload php8.1-fpm
# Aynı anda Nginx worker sayısını kontrol et
grep worker_processes /etc/nginx/nginx.conf
# CPU çekirdek sayısıyla eşleşmeli
nproc --all
# Worker bağlantı limiti
grep worker_connections /etc/nginx/nginx.conf
# Minimum 1024, yüksek trafik için 4096-10240
Bu anlık müdahalenin ardından kalıcı çözüm için sistem limitlerini de kontrol etmek gerekti:
# Nginx'in açabileceği maksimum dosya sayısı
cat /proc/$(pgrep nginx | head -1)/limits | grep "open files"
# Sistem geneli limit
ulimit -n
# Nginx systemd servis dosyasına limit ekle
# /etc/systemd/system/nginx.service.d/override.conf
[Service]
LimitNOFILE=65535
systemctl daemon-reload
systemctl restart nginx
# Kernel parametreleri
sysctl net.core.somaxconn
sysctl net.ipv4.tcp_max_syn_backlog
# Gerekirse artır:
echo 'net.core.somaxconn = 65535' >> /etc/sysctl.conf
sysctl -p
Upstream Hata İzleme ve Alerting
Sorunları reaktif değil proaktif olarak yakalamak için monitoring kurman şart.
# Basit bir upstream kontrol script'i
#!/bin/bash
# /usr/local/bin/check_upstream.sh
UPSTREAM_HOSTS=("10.0.0.1:8000" "10.0.0.2:8000")
ERROR_THRESHOLD=5
LOG_FILE="/var/log/upstream_check.log"
for host in "${UPSTREAM_HOSTS[@]}"; do
IP=$(echo $host | cut -d: -f1)
PORT=$(echo $host | cut -d: -f2)
if timeout 3 bash -c "echo > /dev/tcp/$IP/$PORT" 2>/dev/null; then
echo "$(date): $host - OK" >> $LOG_FILE
else
echo "$(date): $host - HATA" >> $LOG_FILE
# Slack veya email notification gönder
curl -s -X POST -H 'Content-type: application/json'
--data "{"text":"ALARM: $host upstream erişilemiyor!"}"
https://hooks.slack.com/services/YOUR_WEBHOOK_URL
fi
done
# Crontab'a ekle:
# * * * * * /usr/local/bin/check_upstream.sh
# Nginx status modülü ile anlık istatistik
# nginx.conf içinde:
location /nginx_status {
stub_status on;
access_log off;
allow 127.0.0.1;
deny all;
}
# Durumu oku
curl -s http://127.0.0.1/nginx_status
# Aktif bağlantılar, kabul edilen ve işlenen istek sayıları görünür
Yaygın Hataları Önleme Checklist
Upstream sorunlarını minimize etmek için yapılandırma gözden geçirmesinde kontrol edilmesi gerekenler:
- worker_processes: CPU çekirdek sayısıyla eşleşmeli, genellikle
autoen iyisi - worker_connections: Sunucu kapasitesine göre 1024-10240 arası ayarlanmalı
- keepalive_timeout: Gereksiz yere yüksek tutma, 65s makul bir değer
- upstream keepalive: Backend bağlantı havuzu için mutlaka yapılandır
- max_fails ve fail_timeout: Passive health check için uygun değer seç, çok agresif olma
- proxy_next_upstream: Hata toleransı için yapılandır ama idempotent olmayan istekleri gözet
- Buffer boyutları: Uygulama response boyutlarına göre ayarla
- Timeout değerleri: Gerçek uygulama işlem sürelerine göre belirle, çok kısa koyma
- Log formatı: upstream_response_time ve upstream_status mutlaka logla
- Sistem limitleri: ulimit ve kernel parametrelerini üretim için optimize et
Sonuç
Nginx upstream bağlantı hataları, tek bir reçetesi olmayan ama sistematik yaklaşımla her seferinde çözülebilen sorunlardır. İlk adım her zaman log analizi olmali; error log ve access log birlikte okunduğunda hatanın kaynağı çoğunlukla hemen ortaya çıkar.
Şunu da unutma: Aynı 502 hatası bazen PHP-FPM’in çökmesinden, bazen buffer yetersizliğinden, bazen de upstream sunucunun kernel TCP kuyruğunun dolmasından kaynaklanabilir. Bu yüzden semptomdan değil kökten düşünmek gerekir. Log, metric ve bağlantı durumu üçlüsünü birlikte değerlendirdiğinde cevap genellikle net ortaya çıkar.
Proaktif izleme kurman, gece 2’deki telefon çağrılarının sayısını dramatik şekilde azaltır. Upstream health check’leri, Nginx status metriklerini ve sistem kaynak izlemeyi otomasyona bağladığında, sorunları kullanıcılar etkilenmeden yakalama şansın çok artar. İyi sysadmin reaktif değil proaktif olandır.
