Kubernetes ortamında bir pod’un “çalışıyor” görünmesi ile gerçekten “sağlıklı ve iş yapmaya hazır” olması arasında ciddi bir fark var. Bu farkı görmezden geldiğinizde production ortamınızda ilginç şeyler yaşanmaya başlar: Kullanıcılar 500 hatası alır, servisler birbirine hatalı istek atar ve siz de gecenin üçünde Slack bildirimleriyle uyanırsınız. İşte bu noktada Kubernetes’in sağlık kontrol mekanizmaları devreye giriyor.
Neden Sağlık Kontrollerine İhtiyaç Duyuyoruz?
Bir Node.js uygulaması düşünün. Process ayakta, port dinleniyor, her şey güzel görünüyor. Ama içeride bir veritabanı bağlantısı kopmuş, bellek sızıntısı yüzünden uygulama yanıt veremez hale gelmiş ya da henüz başlangıç konfigürasyonunu tamamlamamış. Kubernetes bu durumu bilmeden trafiği o pod’a yönlendirmeye devam eder.
Kubernetes, container’ların sağlık durumunu izlemek için üç temel probe mekanizması sunuyor:
- Liveness Probe: “Bu container hala yaşıyor mu? Yeniden başlatmam gerekiyor mu?”
- Readiness Probe: “Bu container trafik almaya hazır mı?”
- Startup Probe: “Bu container henüz başlangıç sürecinde mi?”
Bu üçlü birlikte doğru konfigüre edildiğinde Kubernetes, sorunlu pod’ları otomatik olarak devre dışı bırakır, yeniden başlatır veya trafik akışından çıkarır. Yanlış konfigüre edildiğinde ise production ortamınız güzel bir kaosa dönüşür.
Liveness Probe: “Hayatta mısın?”
Liveness probe, Kubernetes’e bir container’ın hala çalışır durumda olup olmadığını söyler. Eğer liveness probe başarısız olursa, Kubernetes container’ı öldürür ve pod’un restart politikasına göre yeniden başlatır.
Klasik senaryo şu: Uygulamanız bir deadlock’a girdi. Process çalışıyor ama hiçbir şey yapmıyor. Portunu dinliyor ama hiçbir isteğe yanıt vermiyor. Liveness probe olmadan bu pod sonsuza kadar “Running” durumunda kalır.
HTTP GET ile Liveness Probe
En yaygın kullanılan yöntem HTTP GET. Kubernetes belirtilen endpoint’e istek atar ve 200-399 arası bir HTTP kodu dönmesini bekler.
# deployment-liveness-http.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app
namespace: production
spec:
replicas: 3
selector:
matchLabels:
app: web-app
template:
metadata:
labels:
app: web-app
spec:
containers:
- name: web-app
image: myapp:1.0
ports:
- containerPort: 8080
livenessProbe:
httpGet:
path: /health/live
port: 8080
httpHeaders:
- name: X-Health-Check
value: kubernetes
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
successThreshold: 1
Buradaki parametreleri açıklayalım:
- initialDelaySeconds: Container başladıktan kaç saniye sonra probe başlasın. Uygulama ayağa kalkmadan probe başlarsa sürekli başarısız olur.
- periodSeconds: Probe kaç saniyede bir çalışsın.
- timeoutSeconds: Probe kaç saniye içinde yanıt almazsa başarısız sayılsın.
- failureThreshold: Kaç ardışık başarısızlıktan sonra container yeniden başlatılsın.
- successThreshold: Kaç ardışık başarıdan sonra probe geçti sayılsın (liveness için her zaman 1 olmalı).
TCP Socket ile Liveness Probe
Eğer uygulamanız HTTP sunmuyorsa (örneğin bir veritabanı ya da mesaj kuyruğu), TCP socket kontrolü kullanabilirsiniz.
# tcp-liveness-probe.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis-app
spec:
replicas: 2
selector:
matchLabels:
app: redis-app
template:
metadata:
labels:
app: redis-app
spec:
containers:
- name: redis
image: redis:7.0
ports:
- containerPort: 6379
livenessProbe:
tcpSocket:
port: 6379
initialDelaySeconds: 15
periodSeconds: 20
failureThreshold: 3
Exec Command ile Liveness Probe
Bazen en iyi kontrol bir komut çalıştırmak. Komut 0 exit code döndürürse başarılı, diğer her şey başarısız sayılır.
# exec-liveness-probe.yaml
apiVersion: v1
kind: Pod
metadata:
name: postgres-pod
spec:
containers:
- name: postgres
image: postgres:15
env:
- name: POSTGRES_DB
value: mydb
- name: POSTGRES_USER
value: admin
- name: PGPASSWORD
valueFrom:
secretKeyRef:
name: postgres-secret
key: password
livenessProbe:
exec:
command:
- /bin/sh
- -c
- pg_isready -U admin -d mydb
initialDelaySeconds: 30
periodSeconds: 10
failureThreshold: 5
timeoutSeconds: 5
Readiness Probe: “Trafik Almaya Hazır mısın?”
Readiness probe, bir container’ın trafik almaya hazır olup olmadığını kontrol eder. Eğer readiness probe başarısız olursa, Kubernetes o pod’u Service’in endpoint listesinden çıkarır. Yani trafik artık o pod’a gitmez. Container yeniden başlatılmaz, sadece trafikten izole edilir.
Bu kritik bir fark. Liveness başarısız olunca restart, readiness başarısız olunca trafik izolasyonu.
Gerçek dünya senaryosu: Bir Java Spring Boot uygulamanız var. Uygulama başladığında cache’i dolduruyor, veritabanı bağlantı havuzunu oluşturuyor, bazı harici API’lere bağlanıyor. Bu işlemler 45 saniye sürüyor. Bu süre zarfında uygulamaya gelen istekler ya hata alır ya çok yavaş döner. Readiness probe olmadan Kubernetes deployment güncellemesi sırasında yeni pod’lar hazır olmadan trafiği alır ve kullanıcılar etkilenir.
# spring-boot-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: spring-app
namespace: production
spec:
replicas: 3
selector:
matchLabels:
app: spring-app
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
labels:
app: spring-app
spec:
containers:
- name: spring-app
image: spring-app:2.1.0
ports:
- containerPort: 8080
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 20
periodSeconds: 5
failureThreshold: 10
successThreshold: 2
timeoutSeconds: 3
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
initialDelaySeconds: 60
periodSeconds: 10
failureThreshold: 3
timeoutSeconds: 5
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "1Gi"
cpu: "500m"
Spring Boot Actuator kullanıyorsanız /actuator/health/liveness ve /actuator/health/readiness endpoint’leri kutudan çıkar gelir. Diğer framework’lerde bu endpoint’leri kendiniz oluşturmanız gerekir.
Startup Probe: “Daha Başlamadın mı?”
Startup probe Kubernetes 1.16 ile geldi ve büyük problemleri çözdü. Bazı uygulamalar (özellikle eski enterprise Java uygulamaları) başlamak için çok uzun zaman alır. initialDelaySeconds ile bu sorunu çözmek mümkün ama bu sefer de uygulamanın gerçekten donmasında geç tepki veriyorsunuz.
Startup probe aktif olduğu sürece liveness ve readiness probe’lar çalışmaz. Startup probe başarılı olunca diğerleri devreye girer.
# startup-probe-example.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: legacy-java-app
namespace: production
spec:
replicas: 2
selector:
matchLabels:
app: legacy-java-app
template:
metadata:
labels:
app: legacy-java-app
spec:
containers:
- name: legacy-app
image: legacy-app:5.2
ports:
- containerPort: 9090
startupProbe:
httpGet:
path: /health
port: 9090
failureThreshold: 30
periodSeconds: 10
livenessProbe:
httpGet:
path: /health
port: 9090
periodSeconds: 15
failureThreshold: 3
timeoutSeconds: 5
readinessProbe:
httpGet:
path: /ready
port: 9090
periodSeconds: 5
failureThreshold: 3
successThreshold: 1
Bu konfigürasyonda startup probe maksimum 300 saniye (30 x 10) bekler. Bu süre içinde uygulama ayağa kalkarsa liveness ve readiness devreye girer. Kalkamazsa container restart olur.
Gerçek Dünya Senaryoları ve Yaygın Hatalar
Senaryo 1: Veritabanı Bağımlılığı
En sık yapılan hata: Readiness probe endpoint’inin içinde veritabanına sorgu atmak ve bu sorgu başarısız olunca pod’u trafikten çıkarmak. Kulağa mantıklı geliyor ama şunu düşünün: Veritabanı geçici olarak erişilemez olduğunda tüm pod’larınız aynı anda readiness probe’u başarısız sayar ve tüm servis çöker.
Daha iyi yaklaşım: Readiness probe sadece uygulamanın hazır olup olmadığını kontrol etsin. Veritabanı bağlantısı için ayrı bir monitoring mekanizması kurun.
# app-health-endpoint.go benzeri mantık için ConfigMap ile mock örnek
# Uygulamanızda sağlık endpoint'i şöyle olmalı:
# /health/live -> Sadece process sağlıklı mı? (bellek, deadlock yok mu?)
# /health/ready -> Uygulama istekleri işleyebilir mi? (başlangıç tamamlandı mı?)
# /health/startup -> Uygulama başlangıç adımlarını tamamladı mı?
# Kötü örnek (bunu YAPMAYIN):
# GET /ready -> veritabanına SELECT 1 at -> başarısız olursa 503 dön
# İyi örnek:
# GET /ready -> uygulama başlatma flag'i set edilmiş mi? -> evet ise 200 dön
Senaryo 2: Rolling Update Sırasında Sıfır Kesinti
# zero-downtime-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-service
namespace: production
annotations:
kubernetes.io/change-cause: "v2.3.1 - performance improvements"
spec:
replicas: 4
selector:
matchLabels:
app: api-service
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 2
maxUnavailable: 0
template:
metadata:
labels:
app: api-service
spec:
terminationGracePeriodSeconds: 30
containers:
- name: api
image: api-service:2.3.1
ports:
- containerPort: 3000
readinessProbe:
httpGet:
path: /api/health/ready
port: 3000
initialDelaySeconds: 10
periodSeconds: 3
successThreshold: 2
failureThreshold: 3
timeoutSeconds: 2
livenessProbe:
httpGet:
path: /api/health/live
port: 3000
initialDelaySeconds: 30
periodSeconds: 10
failureThreshold: 3
timeoutSeconds: 5
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "sleep 5"]
maxUnavailable: 0 ayarı, rolling update sırasında mevcut pod’ların hiçbirinin kaldırılmayacağını söyler. Yeni pod’lar readiness probe’u geçmeden eski pod’lar silinmez. preStop hook’u ise pod’un service endpoint listesinden çıkarılması için küçük bir bekleme süresi sağlar.
Senaryo 3: Probe Durumlarını İzlemek
# Pod'un probe durumunu görmek için:
kubectl describe pod <pod-name> -n production
# Özellikle Events bölümüne bakın:
# Warning Unhealthy 2m kubelet Liveness probe failed: HTTP probe failed
# Warning Unhealthy 45s kubelet Readiness probe failed: connection refused
# Probe başarısızlıklarını gerçek zamanlı izlemek:
kubectl get events -n production --field-selector reason=Unhealthy -w
# Pod'un restart sayısını kontrol etmek:
kubectl get pods -n production -o wide
# Detaylı pod durumu için:
kubectl get pod <pod-name> -n production -o jsonpath='{.status.containerStatuses[0].restartCount}'
Probe Parametrelerini Doğru Ayarlamak
Probe konfigürasyonunda yanlış değerler hem yanlış pozitiflere hem de gecikmiş tepkilere yol açar.
initialDelaySeconds için ipucu: Uygulamanızı production benzeri bir ortamda başlatın ve ne kadar sürede hazır hale geldiğini ölçün. Bu değere %50 buffer ekleyin ve initialDelaySeconds olarak kullanın.
# Uygulamanızın başlangıç süresini ölçmek için:
kubectl run test-pod --image=myapp:1.0 --restart=Never
kubectl get pod test-pod -w
# Log'lardan başlangıç süresini kontrol edin:
kubectl logs test-pod -f | grep -E "started|ready|listening"
# Pod'un başlangıç zamanlamasını görmek:
kubectl describe pod test-pod | grep -E "Started|Ready|Conditions" -A 5
Doğru threshold değerleri için öneriler:
- Liveness failureThreshold: En az 3 olsun. Geçici ağ sorunları yüzünden gereksiz restart istemezsiniz.
- Readiness failureThreshold: 3-5 arası. Trafikten hızlı çıkarmak istersiniz ama titreme (flapping) istemezsiniz.
- Readiness successThreshold: 1-2 arası. 2 yaparsanız pod iki ardışık başarılı probe sonrası trafik alır, bu daha güvenli.
- timeoutSeconds: Uygulamanızın normal yanıt süresinin 3-5 katı olsun.
- periodSeconds: Liveness için 10-30 saniye yeterli. Readiness için 3-10 saniye daha uygun.
Probe Endpoint’lerini Doğru Tasarlamak
Sağlık endpoint’leri doğru tasarlanmazsa probe’lar yanlış sonuçlar üretir.
# Örnek: Node.js uygulamasında sağlık endpoint'leri
# Bu yapıyı gösteren bir ConfigMap ve Deployment kombinasyonu:
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
namespace: production
data:
health-check-notes: |
/health/live endpoint kurallari:
- Sadece uygulama process durumunu kontrol et
- Veritabani, harici servis kontrolu YAPMA
- 200ms altinda donmeli
- Bellek limiti asildiysa 500 don
- Deadlock varsa 500 don
/health/ready endpoint kurallari:
- Uygulama baslatma tamamlandi mi kontrol et
- Kritik baglantilarin kurulu oldugunu dogrula
- Cache on-load tamamlandi mi kontrol et
- 500ms altinda donmeli
Liveness endpoint’i mümkün olduğunca basit tutun. Eğer liveness endpoint’i karmaşık işler yapıyorsa ve bu işler başarısız olursa gereksiz restart’lara yol açar. Liveness için sadece “process hayatta ve yanıt verebilir mi?” sorusunu sormak yeterli.
Troubleshooting: Probe Sorunlarını Çözmek
# Probe başarısızlıklarını anlık görmek:
kubectl describe pod <pod-name> | grep -A 10 "Liveness|Readiness|Startup"
# Probe endpoint'ini manuel test etmek (pod içinden):
kubectl exec -it <pod-name> -- curl -v http://localhost:8080/health/live
# Probe endpoint'ini cluster içinden test etmek:
kubectl run debug-pod --image=curlimages/curl --restart=Never --rm -it
-- curl -v http://<pod-ip>:8080/health/live
# Pod'un neden sürekli restart ettiğini anlamak:
kubectl get pod <pod-name> -o jsonpath='{.status.containerStatuses[0].lastState}'
# Son ölen container'ın loglarını görmek:
kubectl logs <pod-name> --previous
# Tüm namespace'deki sağlıksız pod'ları listelemek:
kubectl get pods -A | grep -v "Running|Completed"
Probe sorunlarında en sık karşılaştığım durumlar:
- Sürekli restart: initialDelaySeconds çok düşük, uygulama hazır olmadan liveness başlıyor
- Pod hiç Ready olmuyor: readiness endpoint yanlış path ya da port
- Flapping (sürekli Ready/NotReady geçişi): failureThreshold çok düşük ya da periodSeconds çok kısa
- Deployment takılı kalıyor: Yeni pod’lar readiness’ı geçemiyor, eski pod’lar silinmiyor
Production için Kontrol Listesi
Bir uygulamayı production’a almadan önce probe konfigürasyonu için şunları doğrulayın:
- Liveness ve readiness için ayrı endpoint’ler tanımlandı mı?
- Startup süresi uzun uygulamalar için startup probe kullanıldı mı?
- initialDelaySeconds gerçek başlangıç süresine göre ayarlandı mı?
- Liveness probe, veritabanı ya da harici servis sorgulamıyor mu?
- failureThreshold geçici sorunları tolere edecek kadar yüksek mi?
- timeoutSeconds uygulamanın normal yanıt süresiyle uyumlu mu?
- Rolling update stratejisi probe parametreleriyle uyumlu mu?
- preStop hook, pod’un trafikten sorunsuz çıkması için eklendi mi?
Sonuç
Liveness ve readiness probe’lar Kubernetes’in self-healing özelliğinin temel taşlarıdır. Doğru konfigüre edildiğinde gece uyandıran alarmları ciddi ölçüde azaltır, deployment’ları güvenli hale getirir ve kullanıcı deneyimini korur.
En kritik nokta şu: Bu iki probe’u birbirinden ayırın. Liveness = “container yeniden başlatılmalı mı?”, readiness = “trafik göndermeli miyim?”. Bu soruları ayrı ayrı cevaplayın ve endpoint’lerinizi buna göre tasarlayın.
Startup probe’u ise özellikle başlangıç süresi öngörülemeyen ya da uzun olan uygulamalar için kullanın. Hem gereksiz restart’ları önler hem de liveness probe’unuzun daha agresif çalışmasına izin verir.
Son olarak, probe değerlerini varsayılan ya da tahmine dayalı bırakmayın. Uygulamanızı test edin, başlangıç sürelerini ölçün, normal ve yük altındaki yanıt sürelerini kaydedin ve bu verilerle probe parametrelerinizi bilin bilin ayarlayın. Monitoring ve alerting ile probe başarısızlıklarını da takip etmeyi unutmayın. Bir probe başarısız olduğunda, bu bir erken uyarı sinyalidir ve genellikle daha büyük bir sorunun habercisidir.