

Saniyede 8.500 (8.5K RPS) işlem hacmine sahip bir ödeme geçidi (Payment Gateway) mikroservisinde, Argo Rollouts kullanarak yürüttüğümüz %5, %20, %50, %100 adımlı Canary dağıtımlarında kritik bir anomali tespit ettik: Her deployment adımında ortalama 18-24 arasında HTTP 502 Bad Gateway ve 503 Service Unavailable hatası alıyorduk. Servis Seviyesi Sözleşmesi (SLA) gereği %99.99 erişilebilirlik taahhüdümüz bulunduğundan, bu kesintiler doğrudan p99 gecikme sürelerini 1.2 saniyelere çıkarıyor ve monitoring sistemlerinde alarmları tetikliyordu.
Sorunun temeline indiğimizde, Kubernetes’in asenkron pod sonlandırma (termination) mekanizması ile Istio sidecar (Envoy proxy) yaşam döngüsünün senkronize çalışmadığını gördük. Geleneksel sistemlerde göz ardı edilebilecek 1-3 saniyelik iptables güncellenme gecikmesi, 8.5K RPS gibi bir yük altında binlerce isteğin Terminating durumundaki pod’lara yönlendirilmesine, daha kötüsü uygulama konteynerinin Envoy’dan önce kapanarak gelen trafiği karşılayamamasına neden oluyordu.
Aşağıdaki bölümlerde, Kubernetes 1.28.3 ve Istio 1.18.2 ortamında bu yarış durumunu (race condition) analiz edecek, pod yaşam döngüsüne (Pod Lifecycle) ve Envoy draining süreçlerine müdahale ederek 502 hatalarını p99 180ms gecikme ile nasıl sıfırladığımızı, karşılaştığımız trade-off’lar eşliğinde somut metriklerle inceleyeceğiz.
Bir Kubernetes pod’u sonlandırılmak istendiğinde (örneğin Canary deployment’ın bir adımı tamamlandığında veya scale down sırasında), kontrol düzlemi (control plane) süreci asenkron olarak iki koldan yürütür. Bu asenkron yapı, yüksek trafiğe sahip ortamlarda HTTP 502 hatalarının bir numaralı failidir.
Süreç 1: Kubelet ve SIGTERM
Kubelet, pod içindeki tüm konteynerlere (uygulama ve Istio proxy) eşzamanlı olarak SIGTERM sinyali gönderir. Konteynerlere, terminationGracePeriodSeconds (varsayılan: 30s) süresince kapanmaları için süre tanınır. Bu süre dolduğunda SIGKILL gönderilir.
Süreç 2: EndpointSlice ve Kube-Proxy
Aynı anda, Endpoint Controller pod’un IP adresini EndpointSlice objesinden çıkarır. Cluster içindeki tüm node’larda çalışan kube-proxy, bu güncellemeyi dinler ve kendi node’undaki iptables veya IPVS kurallarını günceller. Trafik yönlendirme kurallarının tüm node’lara yayılması (propagation) küme boyutuna ve API server yüküne bağlı olarak ortalama 1 ile 4 saniye sürer.
Kritik Kesişim: Kube-proxy
iptableskurallarını güncelleyene kadar, diğer node’lardaki servisler (veya Ingress Gateway) trafiği halen kapanmakta olan pod’a göndermeye devam eder. Eğer uygulama konteyneriSIGTERMsinyalini alır almaz kapanırsa, ağ üzerinden gelmeye devam eden istekler içeride yanıtlayacak bir proses bulamaz. Sonuç: Connection Refused (TCP RST) ve dolayısıyla istemciye dönen HTTP 502 Bad Gateway.
Kubernetes mimarisine Istio eklendiğinde, durum daha kompleks bir hal alır. Pod içinde iki konteyner bulunur: app ve istio-proxy. SIGTERM sinyali her iki konteynere de aynı milisaniyede ulaşır.
SIGTERM aldığında aktif bağlantıları (inflight requests) anında kesip kapanırsa, uygulama işlemeyi bitirse bile yanıtı istemciye ulaştıramaz.iptables güncellenene kadar gelen yeni istekleri kabul etmeye devam eder. Envoy, arka planda (localhost üzerinden) uygulamaya istek atmaya çalışır; uygulama kapalı olduğu için upstream_reset_before_response_started{connection_failure} hatası alır ve istemciye 502 döner.Datadog ve Prometheus üzerinden yaptığımız analizde, istio_requests_total{response_code="502", response_flags="UC"} (Upstream Connection failure) sayılarının her pod sonlanmasında sıçrama yaptığını tespit ettik. Uygulamamız (Node.js/Express) SIGTERM sinyalini aldığında process.exit() tetikliyor, aktif veritabanı sorguları yarım kalıyor ve Istio’nun açık tuttuğu TCP bağlantıları kopuyordu.
Sorunu sıfırlamak için pod kapanma sekansını deterministik (belirlenebilir) bir sıraya oturtmamız gerekiyordu. Uyguladığımız çözüm iki aşamalıdır: Kubernetes seviyesinde trafiği absorbe etmek ve Istio seviyesinde connection draining’i yönetmek.
Uygulama konteynerinin SIGTERM almasını geciktirerek, kube-proxy’nin iptables güncellemelerini tamamlaması için zaman kazandırdık. Bunun için pod’un lifecycle.preStop kancasını kullandık.
Istio 1.15 ve sonrası sürümlerde sunulan proxy.istio.io/config anotasyonu ile Envoy’un kapanma sürecini (draining) uygulama kapanana kadar uzattık. Envoy, terminationDrainDuration süresi boyunca mevcut bağlantıları açık tutar, ancak yeni bağlantı kabul etmez.
apiVersion: apps/v1
kind: Deployment
metadata:
name: payment-gateway
namespace: payments
spec:
replicas: 20
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 10%
template:
metadata:
labels:
app: payment-gateway
annotations:
# Envoy'un mevcut istekleri tamamlaması için draining süresi
proxy.istio.io/config: '{ "terminationDrainDuration": "25s" }'
spec:
terminationGracePeriodSeconds: 65 # preStop (15) + App Shutdown (20) + Buffer (30)
containers:
- name: payment-app
image: payment-gateway:v2.4.1
lifecycle:
preStop:
exec:
# SIGTERM sinyalini 15 saniye geciktir.
# Bu sürede kube-proxy iptables'ı günceller, pod yeni trafik almayı keser.
command: ["/bin/sh", "-c", "sleep 15"]
readinessProbe:
httpGet:
path: /health/readiness
port: 8080
periodSeconds: 5
failureThreshold: 2
Bu konfigürasyon ile kapanma sekansı şu şekilde işler:
preStop kancası çalışır ve 15 saniye boyunca uyur (sleep). Bu 15 saniye içinde uygulama normal şekilde çalışmaya ve gelen (gecikmeli) istekleri karşılamaya devam eder.SIGTERM gider.terminationDrainDuration: 25s kuralı gereği mevcut TCP bağlantılarını açık tutar.Uygulanan mimari değişiklik sonrası Argo Rollouts üzerinden yapılan 5 ardışık deployment verisi kayıt altına alınmıştır. Ölçümler, Prometheus istio_requests_total ve Datadog APM metriklerine dayanmaktadır.
| Metrik / Kriter | Optimizasyon Öncesi | Optimizasyon Sonrası | Net Değişim |
|---|---|---|---|
| HTTP 502/503 Hata Sayısı (Deploy Başına) | 18 – 24 adet | 0 adet | %100 İyileşme (Sıfır Hata) |
| Deployment Sırası p99 Gecikme (Latency) | 1250ms (Sıçramalı) | 185ms (Stabil) | 1065ms Düşüş |
| Pod Kapanma Süresi (Time to Terminate) | 3s – 5s | 18s – 22s | +15s (Planlı Gecikme) |
| Toplam Rollout Süresi (4 Adım Canary) | 4dk 10s | 5dk 35s | 1dk 25s Uzama (Trade-off) |
Mimaride karar alırken her zaman alternatif yolları ve bedellerini (trade-off) değerlendiririz. Bu vakada sleep 15 tabanlı platform seviyesi çözüm ile kod seviyesi (application-level) çözüm arasında bir tercih yapmamız gerekti.
Uygulamanın Node.js tarafında process.on('SIGTERM', ...) ile sinyali yakalaması, HTTP server üzerinden server.close() çağırması (yeni bağlantı reddetme) ve aktif istekler bitene kadar beklemesi yöntemidir.
server.close() tetiklediği için Envoy’dan gelen bağlantıları reddeder ve Connection Refused (503) hatası fırlatır. Bunu aşmak için uygulamanın sinyali aldığında yeni bağlantıları reddetmek yerine 10-15 saniye boyunca kabul etmeye devam etmesi (kodu karmaşıklaştırması) gerekir.Kubernetes yaml dosyasında sleep 15 kullanmak.
Production ortamında bu mimariyi devreye alacak ekiplerin aşağıdaki kontrol listesini (checklist) takip etmesi hayati önem taşır:
PreStop sleep süresi + Uygulamanın en uzun kapanma süresi toplamından büyük olmalıdır. Aksi takdirde Kubernetes, preStop’un veya uygulamanın bitmesini beklemeden SIGKILL gönderir. Formülümüz: 15s (sleep) + 20s (app drain) + 30s (buffer) = 65s.envoy_cluster_upstream_cx_destroy_with_active_rq metriğine alarm (alert) kurulmalıdır. Bu metrik 0’dan büyükse, proxy’niz uygulamadan önce kapanıyor demektir.kubelet readiness probe kontrollerini durdurmaz ancak sonucunu dikkate almaz; pod zaten Endpoint listesinden çıkarılmıştır. Bu sebeple kapanma anında readiness probe’un hata vermesi (fail) süreci olumsuz etkilemez.Hayır. holdApplicationUntilProxyStarts konfigürasyonu sadece pod başlarken (Init / Startup fazı) Envoy’un uygulamadan önce ayağa kalkmasını ve ağ kurallarını hazır etmesini sağlar. Kapanma sekansında hiçbir işlevi yoktur. Kapanma sırasını yönetmek için Istio 1.15+ ile gelen terminationDrainDuration parametresini kullanmalısınız.
Evet ve hayır. API Server’a göre pod artık sistemden çıkarılmaktadır. Ancak diğer node’lardaki kube-proxy’lerin ağ haritasını (iptables) güncellemesi 1-4 saniye sürdüğü için, bu süre zarfında yönlendirilmiş olan paketler ağ üzerinden pod’a ulaşır. sleep 15 koymamızın yegane amacı, ağda süzülen bu son paketleri (tail traffic) güvenle içeri alıp işlemektir. 4. saniyeden sonra yeni paket gelmez.
Eğer terminationDrainDuration desteklenmeyen eski bir sürüm kullanıyorsanız, uygulamanızın preStop kancasının sonuna Envoy’u manuel olarak kapatan bir curl komutu ekleyebilirsiniz. Örnek: curl -X POST http://localhost:15020/quitquitquit. Bu sayede uygulama tam olarak işini bitirdiğinde proxy’ye kapanma emri gönderir.
Cluster boyutunuza bağlıdır. 50-100 Node’lu orta ölçekli bir Kubernetes kümesinde kube-proxy’nin tüm Endpoint güncellemelerini işlemesi 2-3 saniye sürer; 5 saniye yeterli olabilir. Ancak 1000+ Node’lu devasa bir yapıda API Server yükü (throttle) nedeniyle bu süre 10 saniyenin üzerine çıkabilir. Production ortamlarında güvenlik marjı olarak 10 ile 15 saniye arası standart kabul edilir.
Canary dağıtımlarında sıklıkla karşılaşılan HTTP 502 Bad Gateway ve 503 Service Unavailable hataları, genellikle uygulamanın içindeki bir bug’dan değil, dağıtık sistemlerin asenkron ağ senkronizasyon eksikliklerinden kaynaklanır. Yürüttüğümüz vaka çalışmasında, pod’ların kapanma sekansına 15 saniyelik kontrollü bir bekleme (preStop) ekleyerek kube-proxy gecikmelerini elimine ettik ve Istio Envoy proxy’nin mevcut bağlantıları tahliye etme (drain) yeteneklerini yapılandırdık. Sonuç olarak, p99 gecikme sürelerini 1.2 saniyeden 180ms bandına çekerek deployment anındaki SLA ihlallerini tamamen ortadan kaldırdık.
Aksiyon Adımı: Eğer cluster’ınızda sık deployment yapıldığında 5xx hatalarında anlık spike’lar görüyorsanız, ilk iş olarak ingress/gateway loglarındaki upstream_reset_before_response_started metriklerini inceleyin. Ardından, kritik servislerinizin Deployment YAML dosyalarına minimum 10 saniyelik bir preStop sleep döngüsü ve terminationGracePeriodSeconds uyumlandırması ekleyerek test ortamınızda sonucu gözlemleyin.

Ön uç çerçeveleri ve kitaplıkları, web geliştirme sürecinin önemli bir yönüdür. Yüksek performanslı duyarlı web siteleri ve web tabanlı uygulamalar oluşturmak için kitaplıkları kullanmak zorunlu…

Endüstriyel otomasyon, kalite kontrol ve güvenlik sistemleri gibi birçok alanda görüntü tabanlı anomali tespiti kritik bir rol oynamaktadır. Ancak bu karmaşık problemi bir web uygulamasına…

Merhabalar, web uygulamaları geliştirirken, çeşitli finansal verilere ihtiyaç duyabiliriz. Özellikle, kullanıcılarımızın döviz kurlarına erişebilmesini sağlamak istediğimiz durumlar olabilir. Bu noktada, Merkez Bankası’nın sağladığı güncel kurları…