İki sürümü hazır tutmak, yeni kod sorun çıkardığında geri dönmeyi kolaylaştırır. Burada Django, Gunicorn ve Nginx ile yerel bir geçiş provası yapıyorum: blue ve green ayrı Unix socket'lerde çalışıyor, Nginx trafiği seçtiğim sürüme gönderiyor. Yeni sürümü önce ayrı sağlık adresinden kontrol ediyorum; geçiş sorun çıkarırsa hedefi blue'ya geri alıyorum.
İki dizin oluşturmak tek başına kesintisiz yayın garantisi vermiyor. Bu örnek gerçek siteyi veya üretim veritabanını kullanmıyor. Küçük, stateless Django uygulamasının yanıtındaki release alanı hangi servise ulaştığımı gösteriyor.
Dosyalar ve yerel ortam
Tam örnek paketini indirin ve blue-green dizinini kullanın. Tam örnekte Compose dosyası, iki Dockerfile, Django uygulaması, Gunicorn ve Nginx ayarları, socket sağlık kontrolü ve geçiş komutu birlikte bulunuyor. Örnek paketindeki blue-green dizininin tamamını yeni, boş bir yerel dizine kopyalayın. Tek bir Nginx satırı bütün kurulumu oluşturmuyor.
Host'ta Python 3.10+ ve Linux container çalıştıran yerel Docker/Compose gerekir. Yardımcı komut Linux/macOS içindir. Dockerfile'lar resmi Python ve Nginx imajlarını digest ile sabitler; uygulama Django 5.2.16 ve Gunicorn 26.0.0 kullanır. Çalışan container'lardan okunan sürümler 7 Ekim 2026'da Python 3.13.16 ve Nginx 1.30.5'tir.
Yerel doğrulama: Aynı tarihte macOS Docker Desktop 4.92.0, Engine 29.8.0 ve Compose 5.5.1 ile yeni bir dizinde Linux aarch64 container'lar çalıştırıldı. Build ve başlangıç, ayrı green sağlık kontrolü, blue → green → blue yanıtları ve durdurulmuş green'e geçişin reddi geçti. Test kopyasına eklenen geçersiz Nginx yönergesi de reddedildi; önceki config ve aktif blue yanıtı geri doğrulandı. Geçişli Python paketleri de aynı sürümlere sabitlendikten sonra build ve smoke yeniden geçti. Ayrı boş kopyada eksik env dosyası ve boş secret reddi doğrulandı. Bu sonuç üretim yayını veya sürekli trafik altında sıfır kesinti ölçümü değildir.
Bu dizin kendi .env.local dosyasını üretir; sitenin .env dosyasını, anahtarlarını veya DB ayarlarını kopyalamayın. init yerel rastgele secret'ı 0600 izinle yazar, ekrana basmaz ve mevcut dosyayı değiştirmez.
cd /path/to/copied/blue-green
docker info
python3 lab.py init
python3 lab.py up
up önce socket dizininin izinlerini hazırlar, iki uygulamanın sağlık kontrolünü bekler, sonra proxy'yi başlatır. Compose başlangıç sırası belgesindeki service_healthy koşulu burada önemli: sürecin başlaması HTTP isteği karşılamaya hazır olduğu anlamına gelmez. Bekleme 60 saniyeyle sınırlıdır; image build ve indirme ayrıca zaman alabilir.
İlk çalıştırmanın beklenen çıktısı:
active: {"release": "blue", "message": "synthetic-local-example"}
green separate health: {"status": "ok", "release": "green"}
İki servis, iki socket, tek trafik girişi
Compose'taki blue servisi /run/example/blue.sock, green servisi /run/example/green.sock kullanır. Gunicorn'da hedef ve izin maskesi şöyledir:
bind = f"unix:/run/example/{os.environ['RELEASE']}.sock"
umask = 0o007
workers = 2
Uygulama UID/GID 10001 ile çalışır. Nginx worker aynı gruptadır; socket dizini ve socket izinleri 0770'tir. Bir permission denied hatasını socket'i herkese açarak çözmek yerine bu kullanıcı/grup ilişkisini kontrol ederim. Gunicorn 26.0.0 ayarları Unix bind ve umask davranışını açıklar.
Nginx'in başlangıç hedefi:
upstream active_release {
server unix:/run/example/blue.sock;
}
Trafik http://127.0.0.1:8088/ adresinden gelir. http://127.0.0.1:8089/health/ ise aktif hedefi takip etmez; doğrudan green socket'ini kontrol eder. İki port da host'un loopback adresine bağlanır. Bu yerel HTTP örneği internet yayını veya TLS yapılandırması değildir.
curl --fail --max-time 5 http://127.0.0.1:8088/
curl --fail --max-time 5 http://127.0.0.1:8089/health/
python3 lab.py check
Green yanıtında hem status: ok hem release: green bekliyorum. Gerçek uygulamada yalnız ana sayfanın açılması yeterli değil. DB erişimi, temel okuma/yazma, oturum ve statik dosyaların aynı sürümle çalışması ayrıca sınanmalı. Örneğin yeni HTML eski JavaScript'i çağırıyorsa, HTTP 200 tek başına hazır olunduğunu göstermez.
Green'e geçiş ve blue'ya geri dönüş
python3 lab.py switch green
curl --fail --max-time 5 http://127.0.0.1:8088/
python3 lab.py switch blue
curl --fail --max-time 5 http://127.0.0.1:8088/
İlk geçişte aktif yanıtın release alanı green, geri dönüşte blue olmalıdır. Yardımcı komut şu sırayı uygular:
- Seçilen servisin socket'inden gerçek HTTP sağlık yanıtını doğrular.
- Nginx hedef dosyasını atomik olarak değiştirir.
nginx -tbaşarılıysanginx -s reloadçalıştırır.- Aktif HTTP adresinin seçilen sürümü döndürdüğünü 10 saniye içinde doğrular.
Bu provada son iki Nginx komutu kendi container'ında çalışır:
docker compose --project-name example-blue-green-local --env-file .env.local --file compose.yaml exec -T edge nginx -t
docker compose --project-name example-blue-green-local --env-file .env.local --file compose.yaml exec -T edge nginx -s reload
Syntax testi uygulamanın sağlığını kanıtlamaz. Reload exit kodu da tek başına trafik hedefinin değiştiğini göstermez; bu nedenle yanıtın release kimliğini kontrol ediyorum. Test, reload veya yanıt doğrulaması başarısızsa komut eski config'i geri koyar, yeniden test/reload yapar ve eski release'in HTTP yanıtını doğrular. Bu geri dönüş de doğrulanamazsa ayrı hata bildirir.
Nginx süreç yönetimi reload sırasında yeni worker'ların açılmasını, eskilerin mevcut istekleri tamamlamasını açıklar. Bu mekanizma bütün uygulama bağlantılarının veya verinin uyumlu olduğunu garantilemez. Geri dönüş penceresi boyunca blue servisini hazır tutarım; trafiği green'e taşıdıktan hemen sonra eski servisi silmem.
Başarısız adayı da denemek
Blue aktifken şu komutla kontrollü prova yapılabilir:
python3 lab.py smoke
Komut ayrı green sağlığını, blue'dan green'e geçişi ve blue'ya dönüşü kontrol eder. Sonra yalnız bu örneğin green servisini durdurur; adaya geçişin reddedildiğini ve aktif blue yanıtının korunduğunu sınar. Sonunda green'i yeniden başlatır ve sağlığını bekler. Beklenen son satır:
PASS: blue -> green -> blue, separate green health, unavailable candidate rejected
Bu sınırlı prova, sürekli yük veya sıfır kesinti ölçümü değildir. Gerçek yayında uzun istekler, WebSocket, job worker'ları, oturumlar ve cache davranışı için ayrıca gözlem gerekir.
Sık hatalar
- Port kullanımda:
upbind hatası verir. Mevcut servisi kapatmak yerine bu örnekteki loopback portlarını ve yardımcı komutun URL'lerini birlikte değiştirin. - Secret eksik/boş: Compose açık hata verir. Üretim anahtarı kopyalamayın; yeni örnek dizininde
initile yerel değer üretin. - 502/socket izin hatası: Yalnız bu projenin
blue,green,edgeloglarını vesocket-initsonucunu inceleyin; GID 10001/0770 ilişkisinin bozulmadığını kontrol edin. - Green sağlık kontrolü başarısız: Hedefi değiştirmeyin.
python3 lab.py checkve green loglarıyla önce adayı düzeltin.
Tam dosyalarda log ve teşhis komutları da bulunur. Docker daemon veya image indirmesi başarısızsa örneği çalıştırılmış saymayın.
Geri dönüşün sınırı
Bu örnekte DB yoktur. Ortak veritabanı kullanan gerçek uygulamada green'in değiştirdiği şema veya yazdığı veri eski kodla uyumsuz olabilir. Proxy hedefini blue'ya çevirmek veritabanını geri almaz; silinen bir sütun blue'nun yeniden çalışmasını engelleyebilir.
Şema geçişini bu yüzden ayrı ele alıyorum: yeni alanı eklemek, iki sürümün uyumunu doğrulamak, veri geçişini kontrollü yapmak ve eski alanı sonraki yayında kaldırmak. Veri değişiklikleri için yedek ve restore provası gerekir. AWS'nin veri ve şema değişikliği notları bu sınırı ayrıntılandırıyor. Buradaki HTTP geri dönüş testi DB rollback testi yerine geçmez.
Provayı bitirirken python3 lab.py down yalnız example-blue-green-local servislerini durdurur ve bu projenin socket volume'ünü korur. Başka container'ları temizlemek veya global Docker prune çalıştırmak gerekmez.