- Zarif kapanış (graceful shutdown), uygulama bir sonlandırma sinyali aldıktan sonra yeni istekleri engelleme, mevcut istekleri tamamlama ve kaynakları temizleme sürecinden oluşur
- Go'da
os/signal paketi kullanılarak SIGINT, SIGTERM gibi sonlandırma sinyalleri doğrudan işlenebilir; signal.NotifyContext ile context tabanlı kapanış kontrolü de mümkündür
- HTTP sunucusu kapatılırken
Server.Shutdown() çağrısından önce readiness probe'u başarısız duruma getirerek trafiği kesmek ve birkaç saniye bekledikten sonra shutdown uygulamak daha kararlıdır
- Tüm handler'ların context kapanış sinyalini algılayıp sonlanabilmesi gerekir; bu,
BaseContext veya middleware ile merkezi biçimde ele alınabilir
- Sonlandırma sinyali alındıktan sonra veritabanı, mesaj broker'ı, cache gibi harici kaynaklar kasıtlı olarak temizlenmelidir;
defer ile kaydedildiğinde kapanış sırasını yönetmek kolaylaşır
Graceful Shutdown nedir?
- Zarif kapanış, uygulama kapanırken yeni istekleri engelleme, devam eden isteklerin tamamlanmasını bekleme ve kaynakları temizleme adımlarından geçen bir süreçtir
- Bu yazı ağırlıklı olarak HTTP sunucuları ve container ortamlarını ele alsa da, tüm uygulamalara uygulanabilecek bir kavramdır
1. Sonlandırma sinyallerini işleme
- Unix benzeri sistemlerde SIGTERM, SIGINT, SIGHUP gibi sinyaller sonlandırma sinyali olarak kullanılır
- Go runtime'ı
SIGTERM, SIGINT aldığında varsayılan olarak uygulamayı sonlandırır; ancak os/signal.Notify ile bu sinyaller doğrudan işlenebilir
- Buffer'lı kanal (kapasite 1) kullanmak, başlatma sırasında sinyal kaybını önlemeye yardımcı olur
- Go 1.16 sonrasında
signal.NotifyContext kullanılarak context tabanlı sinyal yönetimi daha kolay hale gelmiştir
2. Kapanış süresini dikkate alma
- Kubernetes'te varsayılan olarak 30 saniyelik bir kapanış lütuf süresi verilir (
terminationGracePeriodSeconds)
- Güvenli kapanış için %20 pay bırakıp işlemleri 25 saniye içinde tamamlamak tavsiye edilir
3. Yeni istek almayı durdurma
http.Server.Shutdown(), yeni bağlantıları engeller ve mevcut istekler tamamlanana kadar bekler
- Kubernetes ortamında önce readiness probe'un başarısız dönmesi sağlanarak gelen trafiği kesmek, ardından kısa bir beklemeden sonra shutdown uygulamak gerekir
- Readiness handler'ında genel bir değişken üzerinden kapanış durumu kontrol edilerek HTTP 503 döndürmek mümkün olabilir
4. İstek işlemeyi tamamlama
- Kapanış için kullanılan context üzerinde uygun bir timeout tanımlamak gerekir (
context.WithTimeout)
- Shutdown context süresi dolarsa kalan bağlantılar zorla kapatılır
- Tüm handler'lar,
context.Context kullanarak kapanış sinyalini algılayıp durabilecek şekilde tasarlanmalıdır
- Bunun için middleware veya
BaseContext aracılığıyla tüm isteklere kapanış context'i enjekte edilebilir
5. Kaynak temizliği
- Sonlandırma sinyali gelir gelmez kaynakları kapatmak, işlenmekte olan handler'larda sorunlara yol açabilir
- Shutdown tamamlandıktan sonra veritabanı bağlantıları, mesaj broker'ları, cache'ler vb. temizlenmelidir
- Go'daki
defer kullanımı, başlatmanın ters sırasıyla kapanış rutinlerini çalıştırmayı sağlayarak bağımlılık yönetimini kolaylaştırır
- Bellek, dosya tanımlayıcıları gibi işletim sisteminin otomatik temizlediği kaynakların yanı sıra veri flush etme, transaction rollback gibi açıkça kapatılması gereken kaynaklar da vardır
Tam örnek özeti
signal.NotifyContext ile sonlandırma sinyali alma
/healthz readiness endpoint'i uygulama
BaseContext ile tüm isteklere kapanış context'i enjekte etme
- Readiness sonrası 5 saniye bekleyip shutdown uygulama
server.Shutdown çağrısı başarısız olursa zorunlu kapatma için fallback ekleme
Kaynakça ve ilgili kaynaklar
1 yorum
Hacker News görüşü
Kubernetes'te load balancer hedef IP güncellemesinin uzun sürdüğü durumlar olabiliyor. Sorunların %90'ı, trafiğin gerçekten drain edilip edilmediğini doğrulamakla ilgili
preStophook'una 15 saniyelik bekleme süresi eklemek, HTTP 503 oranını belirgin biçimde iyileştirdilog.Fatalkullanıldığındadeferiçindeki içerik çalışmazlog.Fatal,os.Exitçağırdığı için hemen sonlanırpanickullanılırsadeferiçeriği çalışırPrometheus
/metricsendpoint'i periyodik olarak scrape edildiğinde, son scrape ile sürecin kapanması arasında kaydedilen metrikler yayılmayabilirDağıtık bir sistem istemcinin düzgün kapanmasına bağımlıysa, sistem ciddi şekilde arızalanabilir
Yeni servis instance'ı önceki instance'dan socket devraldığında, bağlantıları kesmeden uygulamayı yeniden başlatma yöntemine dair açıklama eksik
Liveness konusundaki tartışma yetersiz
Bir program
ctrl cgibi komutları temiz biçimde işleyemiyorsa kötü yazılmıştırElixir, process'leri küçük VM process'leri olarak tasarlayarak düzgün kapanış rutinlerini kasıtlı olarak yazma ihtiyacını ortadan kaldırıyor
Projede düzgün kapanışı ele almak için küçük bir kütüphane oluşturuldu
Readiness probe güncellendikten sonra sistemin yeni istek göndermemesi için birkaç saniye beklenmeli