← blog · 22 Eylül 2026

Kubernetes'te Sıfır Kesintili Dağıtım: Probe'lar, preStop ve Yavaş Kapanışın Doğru Sırası

Rolling update tek başına sıfır kesinti vermez. Readiness ve startup probe, preStop beklemesi, terminationGracePeriodSeconds, maxUnavailable ve PodDisruptionBudget birlikte ayarlanmazsa her dağıtımda kısa bir hata tepesi kalır. Dört parçanın nasıl hizalanacağı ve sık düşülen tuzaklar.

Bir Kubernetes Deployment'ını güncellediğinizde dökümantasyon size "rolling update sıfır kesinti sağlar" der. Pratikte ise her dağıtımda birkaç saniye boyunca 502, connection reset ya da zaman aşımı görürsünüz; yük dengeleyici loglarında küçük bir hata tepesi oluşur ve kimse nedenini tam açıklayamaz. Sorun Kubernetes'in vaadinde değil, o vaadin dört ayrı parçanın doğru ayarlanmasına bağlı olmasında: uygulamanın hazır olduğunu ne zaman söylediği, trafiği ne zaman kestiği, kapanırken ne kadar beklediği ve aynı anda kaç kopyanın gidebildiği. Bu dörtten biri eksikse dağıtım "çoğunlukla" temiz geçer, yani hiç temiz geçmez.

Kesinti nereden geliyor

İki ayrı pencere hata üretir.

Açılış penceresi. Yeni pod Running durumuna geçer geçmez trafiğe alınır. Uygulamanız hâlâ bağlantı havuzunu kuruyor, önbelleği ısıtıyor ya da JIT derleme yapıyorsa gelen ilk istekler ya reddedilir ya da çok yavaş cevaplanır. Readiness probe tanımlı değilse konteynerin ayağa kalkmasıyla "hazır" sayılması aynı andır.

Kapanış penceresi. Eski pod silinmeye başladığında iki şey paralel yürür: kubelet konteynere SIGTERM gönderir ve endpoint denetleyicisi pod'un adresini Service'ten çıkarır. Bu iki işlem birbirini beklemez. Endpoint listesinin kube-proxy'ye, Ingress denetleyicisine ve dış yük dengeleyiciye yayılması saniyeler alır. O saniyeler boyunca pod artık istek kabul etmiyordur ama trafik hâlâ ona yönlendirilir. Dağıtım sonrası gördüğünüz kısa hata tepesinin büyük kısmı buradan gelir.

Bu asenkron yapı bir hata değil, tasarım kararıdır. Çözüm de bu yüzden uygulamada değil, iki tarafı zamanlama ile hizalamakta yatar.

Açılışı doğru anlatmak: üç probe

Kubernetes üç probe sunar ve üçü farklı soruya cevap verir.

readinessProbe "şu anda trafik alabilir miyim" sorusudur. Başarısız olunca pod yeniden başlatılmaz, yalnızca Service endpoint'lerinden çıkarılır. Bu yüzden bağımlılık kontrolü buraya konur: veritabanına ulaşamıyorsanız trafiği kesmek doğrudur, konteyneri öldürmek değil.

livenessProbe "kilitlendim mi" sorusudur ve başarısızlığı konteyneri yeniden başlatır. En sık yapılan hata liveness probe'una bağımlılık kontrolü koymaktır: veritabanı beş dakika düştüğünde tüm pod'larınız restart döngüsüne girer ve veritabanı geri geldiğinde onu bir de bağlantı fırtınasıyla karşılarsınız. Liveness yalnızca sürecin kendi iç durumunu ölçmeli, dış dünyaya sormamalı.

startupProbe yavaş açılan uygulamalar içindir. Tanımlıysa o başarılı olana kadar diğer iki probe çalışmaz. JVM ya da büyük bir model yükleyen bir servis için liveness eşiğini gevşetmek yerine startupProbe'a uzun bir toplam süre vermek doğru yaklaşımdır; böylece açılış sonrası kilitlenmeleri hâlâ hızlı yakalarsınız.

containers:
  - name: api
    ports:
      - containerPort: 8080
    startupProbe:
      httpGet:
        path: /healthz
        port: 8080
      periodSeconds: 5
      failureThreshold: 36
    readinessProbe:
      httpGet:
        path: /ready
        port: 8080
      periodSeconds: 5
      failureThreshold: 2
    livenessProbe:
      httpGet:
        path: /healthz
        port: 8080
      periodSeconds: 10
      failureThreshold: 3

Burada startupProbe toplam 180 saniye tolerans tanır; readiness iki ardışık başarısızlıkta trafiği keser; liveness yalnızca 30 saniyelik sessizlikte devreye girer. İki ayrı uç kullanmak kasıtlıdır: /healthz süreç hayatta mı diye bakar, /ready bağımlılıklara sorar.

Kapanışı doğru sıralamak

Kapanış sırasında hedef şudur: trafik kesildikten sonra SIGTERM gelsin, SIGTERM'den sonra da açık istekler bitene kadar süre olsun.

Birinci adım: preStop ile bekleme. Pod silme başladığında endpoint'ten çıkarılma ile SIGTERM arasına yapay bir gecikme koyarsınız. preStop kancası tamamlanmadan SIGTERM gönderilmez; bu sırada endpoint değişikliği ağ katmanına yayılır ve pod, trafiği hâlâ alıyorken cevap vermeye devam eder.

lifecycle:
  preStop:
    exec:
      command: ["sh", "-c", "sleep 10"]
terminationGracePeriodSeconds: 45

Konteyner imajında kabuk yoksa, Kubernetes 1.30 ile beta olan ve sonraki sürümlerde kararlılaşan sleep eylemi (preStop.sleep.seconds) aynı işi kabuk gerektirmeden yapar; küme sürümünüzü kontrol etmeden buna güvenmeyin.

İkinci adım: SIGTERM'i uygulamada karşılamak. preStop bittikten sonra gelen SIGTERM'de uygulama yeni bağlantı kabul etmeyi bırakmalı, açık istekleri tamamlamalı, sonra çıkmalıdır. Go'da http.Server.Shutdown, Node'da server.close, Java'da Spring Boot'un server.shutdown=graceful ayarı bunu yapar. Uygulama SIGTERM'i yakalamıyorsa preStop'un kazandırdığı süre boşa gider: süreç anında ölür, açık istekler yarım kalır.

Üçüncü adım: toplam süreyi doğru hesaplamak. terminationGracePeriodSeconds sayacı preStop'un başladığı anda başlar, bittiğinde değil. Yani preStop 10 saniye sürüyorsa ve en uzun isteğiniz 30 saniyeyse, toplam en az 40 saniye olmalıdır. Varsayılan 30 saniye çoğu zaman preStop artı uygulamanın kendi kapanışına yetmez; süre dolunca SIGKILL gelir ve yine yarım istek görürsünüz.

Aynı anda kaç kopya gidebilir

Rolling update parametreleri kesinti penceresinin genişliğini belirler. Varsayılan maxUnavailable: 25% aşağı yuvarlanır: dört kopyalı bir dağıtımda bir pod eksilir ve kapasitenin çeyreği gider, üstelik bunu yalnızca hesaplayınca fark edersiniz. Yüzde yerine mutlak sayı yazmak niyeti okunur kılar.

strategy:
  type: RollingUpdate
  rollingUpdate:
    maxSurge: 1
    maxUnavailable: 0

maxUnavailable: 0 "önce yenisi hazır olsun, sonra eskisi gitsin" demektir ve readiness probe olmadan işe yaramaz; Kubernetes "hazır" kararını o probe'dan alır. minReadySeconds alanı buna ek olarak yeni pod'un hazır göründükten sonra belirli bir süre daha hazır kalmasını ister; hazır olduğunu söyleyip ilk istekte çöken uygulamaları yakalar.

Deployment dışındaki kesintiler için PodDisruptionBudget gerekir. Düğüm boşaltma, küme yükseltme ve otomatik ölçekleyici küçültmeleri Deployment stratejisine bakmaz, PDB'ye bakar.

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: api
spec:
  minAvailable: 2
  selector:
    matchLabels:
      app: api

Tuzaklar

Tek kopya ile sıfır kesinti olmaz. replicas: 1 ve maxUnavailable: 0 birlikte teknik olarak çalışır ama yeni pod hazır olana kadar eskisi trafiği taşır, kapanış penceresinde ise kimse taşımaz. En az iki kopya olmadan bu yazıdaki hiçbir şey yeterli değildir.

HTTP keep-alive kapanışı uzatır. Yük dengeleyici pod'a uzun ömürlü bağlantı açmışsa endpoint'ten çıkarılma o bağlantıyı kapatmaz. Uygulama SIGTERM'de yeni istek kabul etmeyi bırakırken açık bağlantılara Connection: close göndermeli ya da boşta bağlantıları kapatmalıdır. Aksi hâlde yük dengeleyici aynı bağlantıdan istek göndermeye devam eder ve reset alır.

preStop'ta ağır iş yapmayın. Kanca yalnızca zaman kazanmak içindir. İçine veritabanı temizliği ya da önbellek boşaltma koyarsanız hem kanca uzar hem hata verdiğinde kimse görmez; preStop çıkış kodu pod silmeyi durdurmaz, sadece olay kaydına düşer.

Readiness kontrolü ucuz olmalı. Beş saniyede bir çağrılan uç ağır bir sorgu çalıştırıyorsa yük altında probe'un kendisi zaman aşımına girer, pod endpoint'ten düşer, kalan pod'lar daha çok yük alır ve onlar da düşer. Bu zincir "sağlıklı uygulamanın probe yüzünden çökmesi" olarak bilinir ve her ölçekte görülür.

Ingress denetleyicisinin kendi yayılma süresi vardır. Endpoint değişikliği Kubernetes içinde saniyeler alsa da bazı Ingress denetleyicileri yapılandırmayı yeniden yükler ya da dış bulut yük dengeleyicisini API üzerinden günceller. Bu süre 10 saniyeyi geçebilir. preStop süresini tahminle değil ölçerek belirleyin: dağıtım sırasında hata sayısını izleyip sıfırlanana kadar süreyi artırın.

Ne zaman bu kadar uğraşmaya değmez

Toplu iş çalıştıran, kuyruk tüketen ya da yalnızca iç servislerin yeniden deneme mantığıyla çağırdığı bileşenlerde bu ayarların çoğu gereksizdir. Kuyruk tüketicisi için önemli olan tek şey SIGTERM'de elindeki mesajı bitirip onaylamasıdır; endpoint yayılma süresi onu ilgilendirmez. Kullanıcıya bakan HTTP servislerinde ise bu dört parçanın tamamı olmadan "sıfır kesinti" bir ölçüm değil, bir umuttur.