Deploy Başarılı Oldu Ama Site Kapalı
Dashboard’unuz running gösteriyor, ancak kullanıcılarınız hata görüyor. Deploy başarısı ile uygulama sağlığının neden farklı sinyaller olduğunu ve yeşil bir deploy’un gerçekten yanıt veren bir uygulama anlamına gelmesini nasıl sağlayacağınızı öğrenin.
Yeşil bir onay işaretiyle başlayan özel bir kötü sabah türü vardır. Deploy başarılı olmuştur ama site kapalıdır, dashboard running gösterir ve biri size 502 ekran görüntüsü gönderir.
Bu nadir görülen bir edge case değildir. Bir platformun bir şeyi raporlayıp başka bir şeyi ölçmesinin öngörülebilir sonucudur. Bunu tam olarak anlamaya değer, çünkü çözüm "daha dikkatli kontrol etmek" değil — running kelimesinin ne anlama gelmesine izin verildiğini değiştirmektir.
Üç farklı soru, tek durum ışığı
Bir platform bir servisin running olduğunu söylediğinde aslında şu sorulardan herhangi birini yanıtlıyor olabilir:
- Container başladı mı? Process mevcut ve sonlanmadı.
- Port açık mı? Platformun beklediği yerde bir şey dinleme yapıyor.
- Uygulama doğru şekilde yanıt veriyor mu? Bir istek, uygulamanın çalışmaya hazır olduğunu gösteren bir yanıt alıyor.
Bunlar birbirinden oldukça farklı garantilerdir. Bu türdeki incident’ların çoğu, dashboard’un 1. soruyu yanıtlamasından, sizin ise 3. sorunun yanıtlandığını varsaymanızdan kaynaklanır.
Boot olan, database’ine bağlanamayan ve retry loop’unda kalan bir Node process’i 1. soruyu sonsuza kadar karşılar. Crash olmamıştır. Hiçbir zaman request serve edemez. Orchestrator’ın önem verdiği her anlamda container "running" durumundadır.
Outage’ın yaşandığı boşluk
Tehlikeli aralık, "yeni version başladı" ile "yeni version çalışabiliyor" arasındadır. Naive bir platform bu aralıkta trafiği çoktan taşımıştır, çünkü ölçtüğü tek şey başlamaktır.
Bunu basit bir crash’ten daha kötü yapan şey rollback senaryosudur. Crash loop gürültülüdür: container çıkar, yeniden başlar, tekrar çıkar ve platform sonunda bunu fark eder. Boot edip asılı kalan bir uygulama sessizdir. Hiçbir şey yeniden başlamaz, hiçbir alarm tetiklenmez ve önceki çalışan version genellikle çoktan kaldırılmıştır.
Asıl zarar da bu son kısımdadır. Eski version sorunsuzdu. Yeni bir version başladığı için kaldırıldı ve başlamak, çalışmakla karıştırıldı.
Gerçek bir health gate ne yapar?
Çözüm prosedürel değil, yapısaldır. Yeni version bir request’e yanıt verene kadar trafik taşınmamalıdır.
Dockup’ta bir release şu şekilde çalışır: Yeni version izole bir ortamda build edilir, o anda serve eden version’ın yanında başlatılır ve ardından kendisine bir soru sorulur. Yalnızca yanıt verdiğinde domain yeni version’a yönlendirilir. Hiç yanıt vermezse release orada durur ve önceki version serve etmeye devam eder — dashboard’unuzun dışında hiç kimse bir deploy denendiğini bilmez.
Dockup’ta başarısız bir deploy’un outage olmamasının nedeni budur. Yeni container’ın sorunsuz olacağı varsayımıyla eski container hiçbir zaman kaldırılmaz.
# The health gate is per-service configuration, not a platform default you inherit
dockup info my-project/my-api --json
Bu çıktıda yer alan healthCheck bloğu tüm contract’tır: Hangi path’in request edileceği, yanıt için ne kadar bekleneceği, kaç kez deneneceği ve denemeler arasında ne kadar süre olacağı.
Check’i 3. soruyu yanıtlayacak şekilde yapılandırın
Koşulsuz olarak 200 döndüren bir health endpoint’i hiç olmamasından daha kötüdür, çünkü gerçek bir gate’i formaliteye dönüştürür. Check’in amacı, uygulama işini yapamadığında fail etmektir.
Kullanışlı bir readiness endpoint’i, uygulamanın çalışmak için mutlaka ihtiyaç duyduğu şeyleri doğrular:
// Not this — it proves only that the process is alive
app.get('/healthz', (req, res) => res.send('ok'))
// This — it proves the app can actually serve a request
app.get('/healthz', async (req, res) => {
try {
await db.query('select 1') // the dependency that is usually the problem
if (!cacheReady) throw new Error('cache warming')
res.status(200).json({ ok: true })
} catch (err) {
res.status(503).json({ ok: false, reason: err.message })
}
})
Bunun pratikte çalışmasını sağlayan iki kural vardır:
Onsuz serve edemeyeceğiniz dependency’leri check edin, başka hiçbir şeyi değil. Uygulamanız search index kullanılamadığında graceful degradation uygulayabiliyorsa readiness’i search index’e bağlı hâle getirmeyin — outage olmayan bir durum yüzünden deploy’ları engellersiniz.
Ucuza mal olsun. Endpoint her release sırasında tekrar tekrar çağrılır. Pahalı bir query çalıştıran readiness check’i, kendi kendinize yarattığınız bir load problemidir.
Yeterince zaman tanıyın, ama sınırsız değil
Gate’in yardımcı mı yoksa zararlı mı olacağını iki ayar belirler:
- Deneme başına timeout, meşru en yavaş cold start sürenizden uzun olmalıdır. Database’e bağlanan ve cache’i sekiz saniyede ısıtan bir uygulama, üç saniyelik check’te her seferinde fail olur. Siz de gate’i devre dışı bırakarak "çözersiniz" — bu da sizi başladığınız noktaya geri götürür.
- Retry sayısı, tek bir denemeyi değil toplam start süresini kapsamalıdır. Interval × retry sayısı gerçek bütçedir.
Dockup’ta bunlar healthCheckInterval, healthCheckTimeout ve healthCheckRetries ayarlarıdır. Ayarlar servis başınadır, çünkü bir Rails monolith’i ile Go sidecar aynı zamanlamayla başlamaz.
Site zaten kapalıysa
Bunu bir incident sırasında okuyorsanız, sorunu en hızlı çözen sıra şöyledir:
- Uygulamanın doğrudan yanıt verip vermediğini kontrol edin, domain’i devre dışı bırakarak. Port’unda yanıt veriyor ama domain üzerinden yanıt vermiyorsa bu bir routing problemidir, application problem’i değildir; kodunuzda debug yapmayı bırakmalısınız.
- Build log’larını değil, runtime log’larını okuyun. Build başarılı oldu — çıkış noktamız bu. Öğrenmek istediğiniz, process’in başladıktan sonra ne yaptığıdır.
- Diagnose etmeden önce rollback yapın. Kimse izlemiyorken diagnosis yapmak daha ucuzdur.
dockup logs my-project/my-api --follow # what the running process is saying
dockup deployments my-project/my-api # what was live before this
dockup rollback <deployment-id> my-project/my-api # put that back
Dockup’ta rollback rebuild yerine bir switch işlemidir, çünkü önceki version hâlâ disk üzerindedir. Bu, sabaha karşı saat 3’te önemlidir: En hızlı recovery, herhangi bir şeyi compile etmek zorunda olmayan recovery’dir.
Bir platforma sorulacak soru
Production’ı nerede çalıştıracağınıza karar verirken şu, bilinçli olarak test etmek için iyi bir yöntemdir: Başarıyla başlayan, ardından database’ine erişemeyen bir uygulama deploy edin. Dashboard’un ne gösterdiğini izleyin.
Running diyorsa, bir sonraki incident sırasında bu kelimenin tam olarak ne kadar değer taşıdığını artık biliyorsunuz.
Sık sorulan sorular
Site kapalıyken dashboard’um neden running gösteriyor? Çünkü "running" genellikle uygulamanın request serve edebildiği anlamına değil, container process’inin mevcut olduğu anlamına gelir. Database connection’ını retry etmeye takılmış bir process bu tanımı süresiz olarak karşılar.
Bir health check database’e request göndermeli mi? Evet, uygulamanız onsuz request serve edemiyorsa göndermelidir. Gerçekten ihtiyaç duyduğunuz dependency’leri check edin; onsuz çalışmaya devam edebildiğiniz dependency’leri dahil etmeyin.
Liveness ile readiness arasındaki fark nedir? Liveness, process’in yeniden başlatılıp başlatılmayacağını sorar. Readiness ise process’in trafik alıp almaması gerektiğini sorar. Bu hatayı önleyen gate readiness’tir ve trafik taşınmadan önce çalıştırılmalıdır.
Kötü bir deploy’un siteyi tamamen down etmesini nasıl önlerim? Trafiği yalnızca yeni version gerçek bir request’e yanıt verdikten sonra taşıyın ve switch onaylanana kadar önceki version’ı koruyun. Böylece başarısız bir release outage yerine hiç gerçekleşmemiş bir release olur.
