Modern uygulama altyapılarında containerization kaçınılmaz bir standart haline geldi. Docker ile uygulamalarını paketleyen, Kubernetes ile orkestre eden ekipler için en kritik nokta dış dünyaya nasıl servis sunulacağı sorusu. İşte tam bu noktada Nginx devreye giriyor. Hem Docker Compose ortamlarında reverse proxy olarak hem de Kubernetes cluster’larında Ingress Controller olarak Nginx, trafik yönetiminin merkezine oturuyor. Bu yazıda gerçek dünya senaryolarıyla her iki kullanım şeklini de derinlemesine ele alacağız.
Docker Ortamında Nginx ile Reverse Proxy Yapılandırması
Temel Docker Nginx Kurulumu
Docker ile Nginx çalıştırmanın en basit hali tek bir komutla mümkün, ama production’da işler biraz daha karmaşık. Önce temel yapıyı kuralım.
# Nginx imajını çek ve temel container oluştur
docker run -d
--name nginx-proxy
-p 80:80
-p 443:443
-v /etc/nginx/nginx.conf:/etc/nginx/nginx.conf:ro
-v /etc/nginx/conf.d:/etc/nginx/conf.d:ro
-v /etc/ssl/certs:/etc/ssl/certs:ro
--restart unless-stopped
nginx:1.25-alpine
Ama gerçek dünyada Docker Compose kullanmak çok daha yönetilebilir bir yapı sağlar. Diyelim ki bir e-ticaret platformu kuruyorsunuz: frontend, backend API ve bir admin paneli. Bunların hepsini tek bir Nginx üzerinden yönetmek istiyorsunuz.
Docker Compose ile Multi-Service Nginx Yapılandırması
# docker-compose.yml
version: '3.8'
services:
nginx:
image: nginx:1.25-alpine
container_name: nginx-proxy
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./nginx/conf.d:/etc/nginx/conf.d:ro
- ./nginx/ssl:/etc/nginx/ssl:ro
- nginx-logs:/var/log/nginx
depends_on:
- frontend
- backend
- admin
networks:
- proxy-network
restart: unless-stopped
frontend:
image: myapp/frontend:latest
container_name: frontend-app
expose:
- "3000"
networks:
- proxy-network
environment:
- NODE_ENV=production
backend:
image: myapp/backend:latest
container_name: backend-api
expose:
- "8080"
networks:
- proxy-network
environment:
- DB_HOST=postgres
- REDIS_HOST=redis
admin:
image: myapp/admin:latest
container_name: admin-panel
expose:
- "4000"
networks:
- proxy-network
networks:
proxy-network:
driver: bridge
volumes:
nginx-logs:
Dikkat ettiyseniz frontend, backend ve admin servislerinde ports yerine expose kullandım. Bu önemli bir güvenlik pratiği. expose sadece container’lar arası iletişimi açar, host’a port açmaz. Dış dünyaya sadece Nginx üzerinden ulaşılır.
Nginx Konfigürasyon Dosyaları
Ana nginx.conf dosyası:
# nginx/nginx.conf
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
use epoll;
multi_accept on;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
# Log formatı
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" '
'upstream=$upstream_addr rt=$request_time';
access_log /var/log/nginx/access.log main;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
server_tokens off;
# Gzip sıkıştırma
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_types text/plain text/css text/xml application/json
application/javascript application/rss+xml
application/atom+xml image/svg+xml;
# Rate limiting zone'ları
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=login_limit:10m rate=5r/m;
include /etc/nginx/conf.d/*.conf;
}
Şimdi servis bazlı virtual host konfigürasyonları. Bu dosyayı nginx/conf.d/app.conf olarak kaydet:
# nginx/conf.d/app.conf
# Frontend - ana site
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name example.com www.example.com;
ssl_certificate /etc/nginx/ssl/example.com.crt;
ssl_certificate_key /etc/nginx/ssl/example.com.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
location / {
proxy_pass http://frontend:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
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;
proxy_cache_bypass $http_upgrade;
proxy_read_timeout 60s;
}
# API endpoint'leri
location /api/ {
limit_req zone=api_limit burst=20 nodelay;
proxy_pass http://backend:8080/;
proxy_http_version 1.1;
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;
proxy_connect_timeout 30s;
proxy_read_timeout 60s;
}
# Login endpoint'i için daha sıkı rate limiting
location /api/auth/login {
limit_req zone=login_limit burst=3 nodelay;
proxy_pass http://backend:8080/auth/login;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
# Admin panel - ayrı subdomain
server {
listen 443 ssl http2;
server_name admin.example.com;
ssl_certificate /etc/nginx/ssl/example.com.crt;
ssl_certificate_key /etc/nginx/ssl/example.com.key;
# Admin paneline sadece belirli IP'lerden erişim
allow 10.0.0.0/8;
allow 192.168.1.0/24;
deny all;
location / {
proxy_pass http://admin:4000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
Kubernetes Ingress Controller Kurulumu
Kubernetes dünyasına geçtiğimizde işler daha da ilginç bir hal alıyor. Ingress, cluster dışından gelen HTTP/HTTPS trafiğini servislere yönlendiren bir Kubernetes kaynağı. Nginx Ingress Controller ise bu Ingress kaynaklarını okuyarak gerçek Nginx konfigürasyonunu otomatik oluşturan bir sistemdir.
Nginx Ingress Controller Kurulumu
Helm ile kurmak hem daha kolay hem de production için daha uygun:
# Helm repo ekle
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
# ingress-nginx namespace oluştur
kubectl create namespace ingress-nginx
# Production uyumlu değerlerle kur
helm install ingress-nginx ingress-nginx/ingress-nginx
--namespace ingress-nginx
--set controller.replicaCount=2
--set controller.nodeSelector."kubernetes.io/os"=linux
--set controller.admissionWebhooks.patch.nodeSelector."kubernetes.io/os"=linux
--set controller.service.type=LoadBalancer
--set controller.metrics.enabled=true
--set controller.podAnnotations."prometheus.io/scrape"=true
--set controller.podAnnotations."prometheus.io/port"=10254
# Kurulumu doğrula
kubectl get pods -n ingress-nginx
kubectl get svc -n ingress-nginx
LoadBalancer tipi servis, bulut sağlayıcıdan (AWS, GCP, Azure) otomatik bir external IP alır. On-premise ortamda MetalLB veya benzeri bir çözüm gerekir.
Temel Ingress Kaynağı Oluşturma
Diyelim ki Kubernetes’te bir mikroservis mimarisi kuruyorsunuz. İki farklı uygulama var: bir API servisi ve bir web uygulaması.
# ingress-basic.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-ingress
namespace: production
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/proxy-body-size: "50m"
nginx.ingress.kubernetes.io/proxy-read-timeout: "60"
nginx.ingress.kubernetes.io/proxy-connect-timeout: "30"
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/use-regex: "true"
spec:
tls:
- hosts:
- api.example.com
- app.example.com
secretName: example-tls-secret
rules:
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api-service
port:
number: 8080
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web-service
port:
number: 3000
- path: /api/v2
pathType: Prefix
backend:
service:
name: api-v2-service
port:
number: 8081
TLS secret’ı da oluşturalım:
# TLS secret oluştur
kubectl create secret tls example-tls-secret
--cert=path/to/tls.crt
--key=path/to/tls.key
-n production
# Cert-manager kullanıyorsanız (önerilen)
kubectl apply -f - <<EOF
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: example-cert
namespace: production
spec:
secretName: example-tls-secret
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
dnsNames:
- api.example.com
- app.example.com
EOF
Gelişmiş Ingress Konfigürasyonları
Gerçek dünyada ihtiyaç duyulan bazı gelişmiş senaryolara bakalım.
Rate Limiting Ingress Annotation ile:
# ingress-ratelimit.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: api-ingress-ratelimited
namespace: production
annotations:
kubernetes.io/ingress.class: "nginx"
# Saniyede maksimum 10 istek
nginx.ingress.kubernetes.io/limit-rps: "10"
# Burst kapasitesi
nginx.ingress.kubernetes.io/limit-burst-multiplier: "3"
# Rate limit aşıldığında 429 dön
nginx.ingress.kubernetes.io/limit-connections: "20"
# CORS ayarları
nginx.ingress.kubernetes.io/enable-cors: "true"
nginx.ingress.kubernetes.io/cors-allow-origin: "https://app.example.com"
nginx.ingress.kubernetes.io/cors-allow-methods: "GET, POST, PUT, DELETE, OPTIONS"
nginx.ingress.kubernetes.io/cors-allow-headers: "Authorization, Content-Type, X-Request-ID"
# Backend'e özel header ekle
nginx.ingress.kubernetes.io/configuration-snippet: |
more_set_headers "X-Frame-Options: SAMEORIGIN";
more_set_headers "X-Content-Type-Options: nosniff";
spec:
tls:
- hosts:
- api.example.com
secretName: example-tls-secret
rules:
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api-service
port:
number: 8080
Canary Deployment ile A/B Testing:
Bu senaryo özellikle ilginç. Yeni bir versiyon deploy ediyorsunuz ve trafiğin %20’sini yeni versiyona yönlendirmek istiyorsunuz. Geri kalanı eski versiyonda kalsın.
# ingress-canary.yaml
# Ana ingress (v1 - mevcut production)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-ingress-stable
namespace: production
annotations:
kubernetes.io/ingress.class: "nginx"
spec:
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: app-v1-service
port:
number: 3000
---
# Canary ingress (v2 - yeni versiyon, %20 trafik)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-ingress-canary
namespace: production
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-weight: "20"
# Header bazlı yönlendirme de yapılabilir
# nginx.ingress.kubernetes.io/canary-by-header: "X-Canary"
# nginx.ingress.kubernetes.io/canary-by-header-value: "true"
spec:
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: app-v2-service
port:
number: 3000
ConfigMap ile Global Nginx Ayarları
Nginx Ingress Controller’ın global davranışını ConfigMap ile değiştirebilirsiniz. Bu production ortamlarında çok işe yarıyor:
# Mevcut ConfigMap'i düzenle
kubectl edit configmap ingress-nginx-controller -n ingress-nginx
Ya da doğrudan uygula:
# nginx-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: ingress-nginx-controller
namespace: ingress-nginx
data:
# Worker process ayarları
worker-processes: "auto"
worker-connections: "65536"
# Keep-alive ayarları
keep-alive: "75"
keep-alive-requests: "100"
# Buffer ayarları (büyük header'lar için önemli)
proxy-buffer-size: "16k"
proxy-buffers-number: "4"
large-client-header-buffers: "4 16k"
# Timeout değerleri
proxy-connect-timeout: "30"
proxy-read-timeout: "60"
proxy-send-timeout: "60"
# Log formatı - JSON yapısında (log aggregation için ideal)
log-format-upstream: >
{"time": "$time_iso8601", "remote_addr": "$proxy_protocol_addr",
"x_forwarded_for": "$proxy_add_x_forwarded_for",
"request_id": "$req_id", "remote_user": "$remote_user",
"bytes_sent": $bytes_sent, "request_time": $request_time,
"status": $status, "vhost": "$host", "request_proto": "$server_protocol",
"path": "$uri", "request_query": "$args", "request_length": $request_length,
"duration": $request_time, "method": "$request_method",
"upstream_addr": "$upstream_addr",
"upstream_status": "$upstream_status",
"upstream_response_time": "$upstream_response_time"}
# SSL ayarları
ssl-protocols: "TLSv1.2 TLSv1.3"
ssl-ciphers: "ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384"
ssl-session-cache-size: "10m"
# Gzip
use-gzip: "true"
gzip-level: "6"
gzip-types: "application/json application/javascript text/css text/plain image/svg+xml"
Troubleshooting ve Debug Senaryoları
Tüm bunları kurdunuz ama bir şeyler yolunda gitmiyor. İşte en sık karşılaşılan durumlar:
Nginx Ingress Controller Log İnceleme
# Ingress controller pod loglarını takip et
kubectl logs -f -n ingress-nginx
$(kubectl get pods -n ingress-nginx -l app.kubernetes.io/name=ingress-nginx
-o jsonpath='{.items[0].metadata.name}')
# Belirli bir Ingress kaynağının durumunu kontrol et
kubectl describe ingress app-ingress -n production
# Nginx'in ürettiği gerçek konfigürasyonu gör (çok önemli!)
kubectl exec -it -n ingress-nginx
$(kubectl get pods -n ingress-nginx -l app.kubernetes.io/name=ingress-nginx
-o jsonpath='{.items[0].metadata.name}')
-- cat /etc/nginx/nginx.conf | grep -A 20 "example.com"
# Ingress controller'ın event'lerini izle
kubectl get events -n ingress-nginx --sort-by='.lastTimestamp'
# Docker compose ortamında Nginx konfigürasyonunu test et
docker exec nginx-proxy nginx -t
docker exec nginx-proxy nginx -s reload
Yaygın Sorunlar ve Çözümleri
502 Bad Gateway sorunu genellikle backend servisin ayakta olmamasından ya da yanlış port konfigürasyonundan kaynaklanır. Docker ortamında container isimlerinin DNS olarak çalıştığını unutmayın, network’ün aynı olduğundan emin olun.
SSL sertifika sorunları için cert-manager kullanıyorsanız Certificate kaynağının durumunu kontrol edin:
kubectl describe certificate example-cert -n production
kubectl describe certificaterequest -n production
kubectl logs -n cert-manager deploy/cert-manager
Ingress kaynağı uygulanmıyor sorunu genellikle yanlış ingress.class annotation’ından gelir. Kubernetes 1.18 sonrasında ingressClassName spec alanı kullanılmalı:
spec:
ingressClassName: nginx
rules:
...
Production’da Dikkat Edilmesi Gerekenler
Canlı ortamlarda işler test ortamından farklı gelişiyor. Bazı kritik noktalar:
- Pod Disruption Budget tanımla: Ingress Controller pod’larının güncelleme sırasında aynı anda kapanmaması için
minAvailable: 1değerini mutlaka ayarla. - Resource Limits: Controller pod’larına mutlaka CPU ve memory limiti ver. Sınırsız bırakmak bir trafik artışında tüm node’u çökertebilir.
- Horizontal Pod Autoscaler: Ingress Controller için HPA kur, özellikle trafik dalgalanmaları yaşayan ortamlarda hayat kurtarır.
- Metrics ve Alerting: Controller’ın Prometheus metriklerini topla.
nginx_ingress_controller_requests_totalvenginx_ingress_controller_request_duration_secondsmetriklerini izle. - Configuration Reload Takibi: Her Ingress değişikliğinde Nginx reload edilir.
nginx_ingress_controller_config_last_reload_successfulmetriği ile başarısız reload’ları yakala.
Sonuç
Nginx’i Docker ve Kubernetes ortamlarında doğru konfigüre etmek, hem güvenli hem de performanslı bir servis kapısı oluşturmanın temel taşı. Docker Compose ortamlarında servis izolasyonu ve reverse proxy yapılandırması için güçlü bir araç olan Nginx, Kubernetes’te Ingress Controller rolüyle çok daha dinamik bir yapıya kavuşuyor. Annotation tabanlı konfigürasyon sistemi, her servis için ayrı Nginx kuralları yazmak yerine Kubernetes manifest’leri içinde trafik kurallarını tanımlamanızı sağlıyor.
Canary deployment, rate limiting, CORS yönetimi ve SSL sonlandırma gibi production gereksinimlerinin tamamı Nginx Ingress Controller üzerinden karşılanabiliyor. Önemli olan kurulumu doğru yapmak ve ardından monitoring altyapısıyla bu sistemi sürekli gözlemlemek. Bir şeyler ters gittiğinde kubectl exec ile direkt container içine girip üretilen Nginx konfigürasyonunu incelemek, sorunları hızla çözmenin en pratik yolu olmaya devam ediyor.