Bu makalede7 bölüm
- Teknoloji
- Nginx, PHP-FPM, OPcache, MySQL/MariaDB, Kubernetes, HAProxy
- Konu
- Kesintisiz yayın stratejileri ve geri alma
- Seviye
- Orta–ileri
- Ön koşullar
- Linux sunucu ve web sunucusu yönetimi deneyimi
- Temel CI/CD kavramları
- Veritabanı şema değişikliği bilgisi
“Her yayında birkaç saniye 502 alıyoruz” şikâyeti, çoğu ekipte kabullenilmiş bir rutindir. Oysa o birkaç saniye; süreç yeniden başlatma, canlı dizine yarım kopyalanan dosyalar ve kodla uyuşmayan şema gibi tanımlı, giderilebilir boşluklardan oluşur. Bir web uygulamasında bu boşlukları kapatmanın yolu doğru geçiş stratejisini seçmek ve veritabanı değişikliğini koddan ayrı planlamaktır. Aşağıda PHP/WordPress, Node.js ve container tabanlı uygulamalar için blue-green, rolling, canary ve atomik sürüm geçişini; şema değişikliğini ve geri almayı sahada nasıl kurguladığımızı anlatıyoruz.
Yayın anında kesinti nereden gelir?
Yayın sonrası log’a baktığınızda tek bir neden bulmazsınız; birkaç küçük boşluk üst üste biner. Strateji seçmeden önce bunları tek tek tanıyın:
- Süreç yeniden başlatma:
restartsüreci durdurur ve başlatır; aradaki istekler reddedilir ya da yarıda kesilir.reloadile graceful geçiş (Nginx’in yeni worker’ları başlatıp eskileri işini bitirince kapatması, PHP-FPM’inSIGUSR2ile yeniden yüklenmesi) bu boşluğu büyük ölçüde kapatır. Sahada en sık gördüğümüz hata, deploy betiğine alışkanlıkla yazılmış birrestartsatırıdır. - Yarım kopyalanmış dosyalar:
rsyncveya FTP ile canlı dizinin üzerine yazmak, birkaç saniye boyunca eski ve yeni dosyaları karışık çalıştırır. Yeni sınıf dosyası gelmiş, onu çağıran dosya gelmemişse fatal error alırsınız. - Önbellekteki eski kod: PHP OPcache, dosya yolu değişmediğinde eski bytecode’u kullanmaya devam edebilir. Tersine, önbellek temizlenirken kısa bir CPU sıçraması yaşanır.
- Şema uyumsuzluğu: Yeni kod yeni sütunu bekler, migration henüz çalışmamıştır (ya da tersi). Büyük tablolarda kilitleyen bir
ALTER TABLEyayını dakikalarca dondurabilir. - Varlık (asset) uyumsuzluğu: Eski HTML’i önbellekte tutan tarayıcı veya CDN, sunucuda artık olmayan
app.3f2a1.jsdosyasını ister ve 404 alır. - Oturum ve önbellek formatı: Yeni sürüm oturum verisini veya önbellek anahtarlarını farklı yapıda yazıyorsa, iki sürümün birlikte çalıştığı pencerede kullanıcılar oturumdan düşer.
Bu altı maddenin her biri ayrı bir önlem ister. Tek bir araç hepsini çözmez.
Strateji karşılaştırması
Dört yaklaşım kaynak ihtiyacı ve geri alma hızıyla ayrışır. Kararı üç soru belirler: kaç uygulama sunucunuz var, önünde yük dengeleyici var mı, uygulama aynı anda iki sürümün çalışmasını kaldırabiliyor mu?
| Strateji | Nasıl çalışır | Ek kaynak | Geri alma |
|---|---|---|---|
| Atomik symlink | Yeni sürüm ayrı dizine hazırlanır, current bağlantısı tek adımda çevrilir |
Yok (tek sunucu yeterli) | Saniyeler; bağlantı eski dizine çevrilir |
| Blue-green | İki eş ortam; trafik yük dengeleyicide toptan yeni ortama geçer | Üretimin iki katı | Çok hızlı; trafik eski ortama döner |
| Rolling | Sunucular veya container’lar sırayla güncellenir | Düşük (bir düğüm kapasitesi) | Orta; ters yönde yeniden rolling |
| Canary | Yeni sürüm önce trafiğin küçük bir kısmına verilir, metrikler izlenir | Düşük–orta | Hızlı; canary ağırlığı sıfırlanır |
Tek sunuculu bir PHP sitesinde atomik symlink çoğu zaman yeterlidir ve en ucuz çözümdür. Birden fazla uygulama sunucusu ve yük dengeleyici varsa rolling doğal seçimdir; ancak rolling ve canary, eski ile yeni sürümün bir süre aynı anda trafik alması demektir. Bu iki stratejide geriye uyumluluk tercih değildir. Zorunluluktur. Blue-green, iki sürümün çakışma penceresini en aza indirir; karşılığında ortamı ikiye katlarsınız ve iki ortamın aynı veritabanını paylaşması meselesi masaya gelir. Yük dengeleyici tarafındaki seçenekleri yük dengeleme ve yüksek erişilebilir web mimarisiyle birlikte değerlendirin.
Sembolik link ile atomik sürüm geçişi
Capistrano, Deployer ve benzeri araçların yıllardır kullandığı yöntem basittir: her sürüm releases/ altında kendi dizinine açılır; kalıcı veriler (uploads, .env, log dizini) shared/ altında durur ve her sürüme bağlanır; web sunucusunun belge kökü her zaman current bağlantısını gösterir.
/var/www/uygulama/
├── releases/
│ ├── 20261108-1412/
│ └── 20261110-0930/
├── shared/
│ ├── .env
│ └── storage/
└── current -> releases/20261110-0930
Asıl iş, bağlantının gerçekten atomik değişmesidir. Sık kullanılan ln -sfn komutunun davranışı uygulamaya bağlıdır: eski GNU coreutils sürümleri ve bazı diğer ln uygulamaları önce eski bağlantıyı siler, sonra yenisini oluşturur; arada çok kısa bir an bağlantı yoktur. Güncel GNU coreutils geçici bağlantı oluşturup üzerine taşır. Sunucudaki sürüme güvenmeyin, geçişi açıkça yazın: geçici bağlantı oluşturup rename sistem çağrısını kullanan mv -T ile üzerine taşıyın:
REL=/var/www/uygulama/releases/20261110-0930
cd /var/www/uygulama
ln -s "$REL" current_tmp
mv -T current_tmp current # rename(2): tek adımda değişir
sudo systemctl reload php8.2-fpm # SIGUSR2; process_control_timeout ayarına bakın
PHP-FPM reload sırasında çalışan worker’ların işini bitirmesini ne kadar bekleyeceği php-fpm.conf içindeki process_control_timeout ayarına bağlıdır. Varsayılan değer 0’dır; bu durumda uzun süren istekler reload anında yarıda kesilebilir. Değeri en uzun beklenen istek süresini karşılayacak şekilde (ör. 10s) ayarlayın; reload ancak o zaman gerçekten graceful olur.
Konsolda ilk bakılacak ikinci yer Nginx’in FastCGI ayarıdır. Betik yolu symlink üzerinden değil, çözümlenmiş gerçek yol üzerinden iletilmelidir. Aksi halde OPcache ve PHP’nin realpath önbelleği bir süre eski sürüm dizinini kullanmaya devam eder ve “yayın yaptık ama eski kod çalışıyor” şikâyeti gelir.
location ~ \.php$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT $realpath_root;
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
}
$realpath_root, symlink’leri çözerek gerçek releases/… yolunu verir; her sürüm OPcache’te ayrı yolla anahtarlanır, geçiş anında karışma olmaz. Bağımlılık kurulumu (composer install --no-dev, npm ci), varlık derlemesi ve önbellek ısıtma bağlantı çevrilmeden önce, yeni dizinde bitmiş olmalıdır. Canlı dizinde composer install çalıştırmak, yukarıdaki yarım dosya sorununu yeniden üretmektir. Bu adımların bir CI hattında nasıl sıralandığını GitHub Actions ile CI/CD hattı kurulumu yazısında anlattık.
Veritabanı değişiklikleri: expand/contract
Kod geçişini atomik yapmak kolaydır. Veritabanı öyle değil. Çözüm, şema değişikliğini tek adımlık bir kırılma yerine birkaç yayına yayılan, her adımı geriye uyumlu parçalara bölmektir. Bu desen “genişlet–daralt” (expand/contract) ya da “parallel change” olarak bilinir. Örnek: musteri.ad_soyad sütununu ad ve soyad olarak ikiye ayırmak.
- Genişlet: Yeni sütunlar boş değer kabul edecek şekilde eklenir. Eski kod bu sütunları görmez, çalışmaya devam eder.
- Çift yazma: Yeni sürüm hem eski hem yeni sütunlara yazar, okumayı hâlâ eski sütundan yapar.
- Geri doldurma: Mevcut kayıtlar küçük partiler halinde (ör. 1.000’er satır) yeni sütunlara taşınır. Tek bir dev
UPDATEtabloyu ve replikasyonu kilitleyebilir. - Okumayı taşı: Sonraki sürüm yeni sütunlardan okur, eski sütuna yazmaya devam eder; geri alma hâlâ güvenlidir.
- Daralt: Hiçbir çalışan sürümün eski sütuna ihtiyacı kalmadığı doğrulandıktan sonra eski sütun ayrı bir yayında kaldırılır.
Yavaş görünür. Ama her adımda eski ve yeni kod aynı şemayla çalışabilir; rolling ve canary mümkün hale gelir, geri alma şemayı geri döndürmeyi gerektirmez. MySQL/MariaDB’de büyük tablolar için ALTER TABLE … ALGORITHM=INPLACE, LOCK=NONE gibi çevrimiçi DDL seçenekleri, istenen değişiklik desteklenmiyorsa işlemi başlatmadan hata verir; bu yüzden güvenli bir ön kontrol olarak da işe yarar. Desteklenen işlemler sürüme ve motor tipine göre değişir; kendi sürümünüzün dokümantasyonunu esas alın.
WordPress gibi migration aracı olmayan sistemlerde, eklenti veya çekirdek güncellemesinin veritabanı yükseltmesi yaptığı durumlara ayrıca dikkat edin: bu yükseltmeler genellikle geri alınamaz. Böyle bir değişikliği önce veriyle eşdeğer bir staging ortamında deneyin, yayından hemen önce veritabanı yedeği alın.
Health check ve trafik yönetimi
Rolling veya blue-green geçişte yük dengeleyici “bu düğüm hazır mı?” sorusunu doğru yanıtlayabilmelidir. Ayrı bir hazır olma (readiness) uç noktası tanımlayın: /healthz gibi bir adres, uygulamanın veritabanına ve önbelleğe bağlanabildiğini, yapılandırmanın yüklendiğini kontrol edip 200 döner; aksi halde 503 döner. Ana sayfayı health check olarak kullanmak hem pahalıdır hem de önbellekten dönen yanıt yüzünden yanıltıcıdır. Veritabanı çökmüşken ana sayfa önbellekten 200 dönmeye devam edebilir.
Açık kaynak Nginx’te upstream sağlık kontrolü pasiftir: max_fails ve fail_timeout ile, gerçek istekler başarısız olduğunda sunucu bir süre devre dışı kalır. Aktif sağlık kontrolü (periyodik test isteği) Nginx’in ticari sürümünde veya HAProxy gibi yük dengeleyicilerde bulunur. Açık kaynak Nginx ile rolling dağıtımda izlediğimiz sıra şudur: güncellenecek düğümü yapılandırmada down olarak işaretleyin, nginx -s reload ile trafiği boşaltın, güncellemeyi yapın, health check’i elle doğrulayın, düğümü geri alın.
upstream uygulama {
server 10.0.10.11:8080 max_fails=3 fail_timeout=10s;
server 10.0.10.12:8080 max_fails=3 fail_timeout=10s down; # güncelleniyor
keepalive 32;
}
Kubernetes’te aynı mantığı readiness probe ve Deployment’ın maxUnavailable/maxSurge ayarları yönetir. Ancak uygulama SIGTERM aldığında yeni istek kabulünü bırakıp eldekileri tamamlayacak şekilde yazılmamışsa, orkestratör tek başına kesintiyi önleyemez. Container’lı yayının tek sunucudaki karşılığı için Docker Compose ile üretim yayını yazısına bakın.
Varlıklar, oturumlar ve önbellek
Derlenen JS/CSS dosyalarının adına içerik hash’i ekleyin ve en az bir önceki sürümün varlıklarını sunucuda bırakın; eski HTML’i önbellekte tutan istemciler 404 almaz. Oturumları dosya sisteminde değil, Redis veya veritabanı gibi paylaşılan bir depoda tutun; oturum yapısı değişiyorsa yeni kod eski formatı da okuyabilmeli. Uygulama önbelleği anahtarlarına sürüm öneki eklemek, yeni sürümün eski formatta yazılmış girdileri okumasını engeller. CDN kullanıyorsanız yayından sonra tüm önbelleği boşaltmak yerine yalnızca değişen HTML yollarını temizleyin; tam temizlik, origin’e ani bir yük bindirir.
Geri alma planı
Geri alma yayın gününde düşünülmeye başlanırsa işe yaramaz. Her yayından önce şu sorular yanıtlanmış olmalı:
- Geri alma komutu veya pipeline adımı tanımlı ve son üç ay içinde en az bir kez denenmiş mi?
- Bu yayın geri alınamaz bir veritabanı değişikliği içeriyor mu? İçeriyorsa yedek alındı mı ve dönüş bu yedekten mi yapılacak?
- Geri alma kararını hangi metrik tetikleyecek: 5xx oranı, yanıt süresi, sipariş veya form gönderimi sayısındaki düşüş?
- Kararı kim verecek ve ne kadar beklenecek?
- Kuyrukta bekleyen arka plan işleri yeni formatta mı oluşturuldu; eski sürüm bunları işleyebilir mi?
Denenmemiş geri alma, plan değildir. Expand/contract uygulandığında kod geri alma neredeyse her zaman güvenlidir, çünkü şema iki sürümü de destekler. Symlink yönteminde geri alma, bağlantıyı aynı mv -T yöntemiyle önceki sürüm dizinine çevirip PHP-FPM’i reload etmektir. Blue-green’de trafik eski ortama döner; arada yeni ortamın yazdığı veriler kaybolmaz, çünkü iki ortam genellikle aynı veritabanını paylaşır. Bu yüzden blue-green’de de şema uyumluluğu kuralı geçerlidir.
Yayın hattını ve geri alma prosedürünü birlikte tasarlamak isterseniz, CI/CD ve web DevOps otomasyonu kapsamında bu adımları pipeline’a gömüyoruz. Başvuru için Kubernetes’in Deployment dokümantasyonu ve Nginx’in upstream modülü sayfalarını kullanın.
Sık sorulan sorular
Tek sunuculu bir WordPress sitesinde sıfır kesintili dağıtım mümkün mü?
Kod tarafında evet: sürümleri ayrı dizinlere açın, current bağlantısını mv -T ile atomik çevirin, PHP-FPM’i reload edin. Ama çekirdek veya eklenti güncellemesi veritabanı yükseltmesi yapıyorsa o adım kısa bir kilitlenmeye yol açabilir. Önce staging’de deneyin, yayından önce yedek alın.
Blue-green ile canary arasındaki temel fark nedir?
Blue-green’de trafik tek seferde yeni ortama geçer, eski ortam yedekte bekler. Canary’de yeni sürüm önce trafiğin küçük bir kısmına verilir; hata oranı ve yanıt süresi izlenerek pay kademeli artırılır. Canary daha az kaynakla daha kontrollü risk alır, ama iki sürümün uzun süre birlikte çalışabilmesini şart koşar.
ln -sfn ile bağlantıyı değiştirmek atomik midir?
Her zaman değil. Eski GNU coreutils sürümleri ve bazı diğer ln uygulamaları mevcut bağlantıyı önce silip sonra yenisini oluşturur; arada çok kısa bir an bağlantı yoktur ve o anda gelen istek hata alır. Güncel GNU coreutils geçici bağlantı ve rename kullanır, ama bunu sürüme bırakmayın: geçici bağlantı oluşturup mv -T ile üzerine taşıyın. Bu tek bir rename sistem çağrısıdır ve atomiktir.
Veritabanı sütunu silmeyi hangi aşamada yapmalıyım?
Eski sütunu okuyan veya yazan hiçbir çalışan sürüm, kuyruk işi ya da rapor kalmadığını doğruladıktan sonra, ayrı bir yayında. Bu daralt adımı geri alınamaz; öncesinde yedek alın.
- Teknoloji
- Nginx, PHP-FPM, OPcache, MySQL/MariaDB, Kubernetes, HAProxy
- Konu
- Kesintisiz yayın stratejileri ve geri alma
- Seviye
- Orta–ileri
- Ön koşullar
- Linux sunucu ve web sunucusu yönetimi deneyimi
- Temel CI/CD kavramları
- Veritabanı şema değişikliği bilgisi
Web Sunucuları ve DevOps