Kendi VPS sunucumda koşan beş farklı servis için otomatik güncellemeleri açtığım ilk hafta, PostgreSQL majör sürüm geçişinin sessizce tüm veritabanı şemasını kilitlemesiyle kendimi gece yarısı kurtarma diskinde buldum. O günden beri, self-hosted uygulamalarda güncelleme yönetimi konusuna körü körüne otomasyon gözüyle bakmayı bıraktım. Kendi altyapısını yöneten herkesin eninde sonunda yüzleştiği bu sorun, sadece “en güncel container imajını çekmek”ten ibaret değildir.
Bu yazıda, kendi yan ürünlerimde ve geçmişte çalıştığım üretim ERP’si gibi kritik projelerde edindiğim tecrübeler ışığında, güvenli bir güncelleme stratejisinin nasıl kurulması gerektiğini ele alacağım. Ne zaman otomasyona güvenmeliyiz, ne zaman manuel frene basmalıyız ve olası bir felaket anında sistemi nasıl ayağa kaldırırız sorularına pragmatik yanıtlar arayacağız.
Self-Hosted Uygulamalarda Güncelleme Yönetimi Nedir?
Self-hosted uygulamalarda güncelleme yönetimi, üçüncü parti veya kendi geliştirdiğimiz yazılımların sunucu üzerindeki yaşam döngüsünü, veri kaybı ve uzun süreli kesinti yaşamadan sürdürme sürecidir. Bulut sağlayıcılarının (SaaS) arka planda bizim adımıza hallettiği bu operasyon, self-hosted dünyasında tamamen bizim omuzlarımızdadır. Süreç; yeni sürümlerin takip edilmesi, bağımlılıkların kontrolü, veritabanı şema değişikliklerinin (migration) uygulanması ve gerektiğinde geri dönüş (rollback) planlarının işletilmesini kapsar.
Bu süreci doğru yönetmek, sistemin sadece yeni özelliklere kavuşmasını sağlamaz; aynı zamanda kritik güvenlik açıklarının (CVE) kapatılması için de hayati önem taşır. Ancak körü körüne yapılan güncellemeler, çalışan bir sistemi dakikalar içinde erişilmez hale getirebilir. Bu yüzden esnek ama disiplinli bir politika belirlemek şarttır.
Otomatik Güncelleme mi, Manuel Kontrol mü?
Bu sorunun tek bir doğru cevabı yok, ancak her iki yaklaşımın da belirgin trade-off’ları (ödünleşimleri) bulunuyor. Tamamen otomatik güncellemeler, sizi sürekli takip etme yükünden kurtarır ve sistemin her zaman en güvenli sürümde kalmasını sağlar. Öte yandan, geriye dönük uyumsuz (breaking change) bir değişiklik geldiğinde, sabah sistemin çöktüğünü görerek uyanmanıza neden olur.
Manuel kontrol ise tam bir kontrol ve huzur sağlar ancak insan hatasına ve ihmalkarlığa çok açıktır. “Haftaya güncellerim” diyerek ertelenen işler, aylar sonra aşılması zor bir versiyon uçurumuna ve kapatılmamış kritik CVE açıklarına dönüşür. Kendi projelerimde uyguladığım formül, stateless (durumsuz) servisleri otomatik, stateful (veritabanı gibi durum koruyan) servisleri ise kesinlikle kontrollü ve manuel güncellemek yönündedir.
| Güncelleme Tipi | Avantajı | Dezavantajı | Tercih Edilen Durum |
|---|---|---|---|
| Tam Otomatik | Sıfır operasyonel yük, hızlı güvenlik yamaları | Beklenmedik kesintiler, şema bozulmaları | Stateless servisler, Nginx proxy, Redis cache |
| Yarı Otomatik | Test ortamında deneme imkanı, kontrollü geçiş | Ekstra staging sunucu maliyeti, zaman kaybı | Üretim ERP’si, kullanıcı arayüzleri (frontend) |
| Tam Manuel | Maksimum güvenlik, rollback kolaylığı | İhmal edilme riski, eski sürümlerde kalma | PostgreSQL, ana veritabanları, disk bileşenleri |
Watchtower ve Benzeri Otomasyonlar Ne Zaman Tercih Edilmeli?
Docker ekosisteminde çalışanların ilk başvurduğu araç genellikle Watchtower oluyor. Watchtower, çalışan container’ları izleyip Docker Hub veya özel bir registry’de yeni bir imaj gördüğünde container’ı otomatik olarak yeniden başlatan harika bir araçtır. Ancak her servis bu kadar “hoyrat” bir döngüyü kaldıramaz.
Örneğin, sadece statik dosya sunan bir Nginx container’ı veya geçici verileri tutan bir Redis instance’ı için Watchtower kullanmak neredeyse sıfır risk taşır. Fakat içinde aktif transaction’ların döndüğü, PostgreSQL kullanan bir FastAPI backend’ini Watchtower’ın insafına bırakırsanız, veritabanı bağlantılarının yarıda kesilmesi veya migration dosyalarının henüz çalıştırılmadan yeni kodun devreye girmesi gibi sinsi hatalarla karşılaşırsınız.
# Docker Compose ile güvenli Watchtower yapılandırması örneği
version: "3.8"
services:
watchtower:
image: containrrr/watchtower
volumes:
- /var/run/docker.sock:/var/run/docker.sock
environment:
- WATCHTOWER_CLEANUP=true
- WATCHTOWER_LABEL_ENABLE=true
- WATCHTOWER_POLL_INTERVAL=86400 # Günde bir kez kontrol et
restart: always
my-app:
image: my-app:latest
labels:
# Sadece bu etikete sahip container'ları güncellemesini söylüyoruz
- "com.centurylinklabs.watchtower.enable=true"
Veritabanı Göçleri (Migrations) Güncelleme Sırasında Nasıl Yönetilir?
Self-hosted uygulamalarda güncelleme yönetimi konusunun en can yakıcı noktası veritabanı şemalarıdır. Kod tabanını güncellemek sadece saniyeler sürerken, terabaytlık bir veritabanında yeni bir index eklemek veya kolon tipini değiştirmek tüm sistemi kilitleyebilir. Özellikle PostgreSQL üzerinde çalışırken, ALTER TABLE komutlarının tabloyu kilitleyip kilitlemeyeceğini önceden kestirmeniz gerekir.
Geriye dönük uyumlu (backward compatible) şema tasarımları bu yüzden hayati önem taşır. Bir kolonu silmek yerine önce kod tarafında o kolonu kullanmayı bırakmalı, bir sonraki güncellemede ise kolonu veritabanından kaldırmalısınız. Bu yaklaşıma “Expand and Contract” (Genişlet ve Daralt) deseni denir. Böylece olası bir rollback durumunda eski kodunuz yeni şema ile sorunsuz çalışmaya devam edebilir.
graph TD;
A["Yeni Kod Sürümü Hazır"] --> B["Aşama 1: Yeni Şemayı Ekle (Expand)"];
B --> C["Aşama 2: Yeni Kodu Deploy Et"];
C --> D{"Çalışma Durumu OK mi?"};
D -- "Evet" --> E["Aşama 3: Eski Şemayı Kaldır (Contract)"];
D -- "Hayır" --> F["Eski Koda Geri Dön (Rollback)"];
Güncelleme Öncesi Güvenli Geri Dönüş (Rollback) Stratejileri Nelerdir?
Her şeyin ters gidebileceği varsayımı, sistem yöneticisinin en temel hayatta kalma güdüsüdür. Güncelleme düğmesine basmadan önce, sistemin çalışan son kararlı haline nasıl döneceğinizi adım adım planlamış olmalısınız. Sadece yedek almak yetmez; o yedeğin ne kadar sürede geri yüklenebileceğini (RTO) de bilmeniz gerekir.
Docker Compose ortamlarında bu iş nispeten kolaydır. İmaj sürümlerini her zaman spesifik tag’lerle (örneğin :v1.2.3) belirtmek, :latest tag’inin belirsizliğinden kaçınmak ilk adımdır. Bir sorun anında yapmanız gereken tek şey, compose dosyasındaki sürüm numarasını eski haline getirip docker compose up -d komutunu çalıştırmaktır. Tabii bu esnada veritabanının da güncelleme öncesi durumuna (WAL arşivleri veya snapshot yardımıyla) döndürülmesi gerekebilir.
# Hızlı bir Docker volume yedekleme komutu örneği
# Güncelleme yapmadan önce veri dizinini sıkıştırıp saklıyoruz
tar -czvf app-data-backup-$(date +%F).tar.gz /var/lib/docker/volumes/app_data/_data
Kendi Altyapımda Uyguladığım Güncelleme İş Akışı Nasıl Çalışıyor?
Ben kendi yan ürünlerimde ve yönettiğim sunucularda tamamen hibrit ve kontrollü bir boru hattı (pipeline) kullanıyorum. Kritik olmayan, stateless servisler için Watchtower her gece sessizce güncellemeleri geçerken; ana veritabanı, kuyruk mekanizmaları ve kullanıcıların doğrudan dokunduğu arayüzler için süreç manuel tetiklenen script’lerle ilerler.
Bunun için basit bir systemd timer ve bash script ikilisi kullanıyorum. Script, önce mevcut Docker volume’lerinin hızlı bir snapshot’ını alıyor, ardından yeni imajları çekiyor. Eğer yeni imaj çalışmazsa veya sağlık kontrolünden (healthcheck) geçemezse otomatik olarak bir önceki çalışan imaj sürümüne geri dönüyor ve bana bir bildirim gönderiyor. Bu sayede hem otomasyonun konforunu yaşıyorum hem de kontrolü tamamen kaybetmemiş oluyorum.
Son Söz
Self-hosted uygulamalarda güncelleme yönetimi, her zaman tetikte olmayı gerektiren bir süreçtir. “Çalışıyorsa dokunma” felsefesi sizi bir yere kadar korur, ancak günün sonunda aşılması imkansız teknik borçlar ve güvenlik açıklarıyla baş başa bırakır.
Net pozisyonum şudur: Altyapınızdaki servisleri kritiklik derecelerine göre sınıflandırın. Stateless olanları otomasyona emanet edin, ancak veritabanı gibi kalbi temsil eden bileşenlerin güncellemelerini her zaman kendi ellerinizle, kahvenizi yudumlarken ve yedeklerinizi iki kez kontrol ederek gerçekleştirin. Güvenli ve kesintisiz günler dilerim.