Production ortamında yeni bir özellik deploy etmek her zaman biraz heyecan vericidir, değil mi? “Ya bir şeyler bozulursa?” korkusu olmadan deploy yapabilmek için grayscale ve canary deployment stratejileri tam olarak ihtiyacın olan şey. Bu yazıda OpenLiteSpeed kullanarak bu iki stratejiyi nasıl uygulayacağını, gerçek dünya senaryolarıyla birlikte adım adım anlatacağım.
Grayscale ve Canary Deployment Nedir?
İkisi de benzer ama farklı amaçlara hizmet eden deployment stratejileri. Grayscale deployment, yeni versiyonu önce kullanıcıların küçük bir yüzdesine sunup, her şey yolunda giderse bu yüzdeyi kademeli olarak artırma stratejisidir. Mesela önce %5, sonra %20, sonra %50, en sonunda %100.
Canary deployment ise daha spesifik bir hedefleme yapmanı sağlar. Belirli kullanıcı gruplarını (beta kullanıcılar, iç ekip, belirli coğrafi bölgeler gibi) yeni versiyona yönlendirirsin. İsim, madencilerin karbonmonoksit tespiti için kanarya kuşu kullanmasından geliyor. Yeni kod “zehirliyse” önce kanaryanı etkiler, tüm kullanıcıları değil.
OpenLiteSpeed bu stratejileri uygulamak için oldukça güçlü araçlar sunar: rewrite kuralları, load balancing ve header bazlı yönlendirme bunların başında gelir.
Ortam Hazırlığı
Öncelikle senaryomuzda ne olduğunu netleştirelim. Elimizde iki uygulama sunucusu var:
- v1 sunucusu: Mevcut production versiyonu, 8080 portunda çalışıyor
- v2 sunucusu: Yeni versiyon, 8081 portunda çalışıyor
OpenLiteSpeed bu iki sunucunun önünde bir reverse proxy ve load balancer olarak görev yapacak.
Backend Servislerini Hazırlama
Önce test amaçlı basit iki uygulama ayağa kaldıralım. Gerçek ortamda bunlar Node.js, PHP-FPM, Python veya ne kullanıyorsan o olacak.
# v1 için basit bir Python HTTP sunucusu
mkdir -p /opt/app/v1 /opt/app/v2
cat > /opt/app/v1/index.html << 'EOF'
<!DOCTYPE html>
<html>
<body>
<h1>Version 1 - Production</h1>
<p>Bu eski ama stabil versiyon</p>
</body>
</html>
EOF
cat > /opt/app/v2/index.html << 'EOF'
<!DOCTYPE html>
<html>
<body>
<h1>Version 2 - Canary</h1>
<p>Bu yeni ve parlak versiyon</p>
</body>
</html>
EOF
# v1 ve v2 servislerini systemd ile yönet
cat > /etc/systemd/system/app-v1.service << 'EOF'
[Unit]
Description=App Version 1
After=network.target
[Service]
WorkingDirectory=/opt/app/v1
ExecStart=/usr/bin/python3 -m http.server 8080
Restart=always
User=nobody
[Install]
WantedBy=multi-user.target
EOF
cat > /etc/systemd/system/app-v2.service << 'EOF'
[Unit]
Description=App Version 2
After=network.target
[Service]
WorkingDirectory=/opt/app/v2
ExecStart=/usr/bin/python3 -m http.server 8081
Restart=always
User=nobody
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable --now app-v1 app-v2
systemctl status app-v1 app-v2
OpenLiteSpeed’de Load Balancer Konfigürasyonu
OpenLiteSpeed Admin paneline girip worker gruplarını tanımlamak yerine, doğrudan config dosyasını düzenleyeceğiz. Bu hem daha hızlı hem de versiyon kontrolüne dahil edebileceğin bir yaklaşım.
# OpenLiteSpeed konfigürasyon dosyası
nano /usr/local/lsws/conf/httpd_config.conf
Ana konfigürasyon dosyasına extprocessor tanımlarını ekle:
# httpd_config.conf içine eklenecek kısım
extprocessor v1-backend {
type proxy
address localhost:8080
maxConns 100
pcKeepAliveTimeout 60
initTimeout 60
retryTimeout 0
respBuffer 0
}
extprocessor v2-backend {
type proxy
address localhost:8081
maxConns 100
pcKeepAliveTimeout 60
initTimeout 60
retryTimeout 0
respBuffer 0
}
# Load balancer grubu - başlangıçta %90 v1, %10 v2
extprocessor app-lb {
type loadbalancer
workers v1-backend, v2-backend
strategy RoundRobin
}
Ancak RoundRobin stratejisiyle tam olarak yüzde bazlı kontrol yapamazsın. Bunun için weighted round robin veya rewrite kuralları kullanman gerekiyor. Gelin her iki yöntemi de inceleyelim.
Yöntem 1: Cookie Bazlı Canary Deployment
Bu yöntemde belirli cookie değerine sahip kullanıcıları yeni versiyona yönlendiriyoruz. İç test ekibinin veya beta kullanıcıların cookie’si varsa v2’ye gidecekler, yoksa v1’e.
Virtual host konfigürasyonunu düzenle:
nano /usr/local/lsws/conf/vhosts/example.com/vhconf.conf
Aşağıdaki rewrite kurallarını ekle:
vhost example.com {
vhRoot /var/www/example.com
configFile $SERVER_ROOT/conf/vhosts/example.com/vhconf.conf
allowSymbolLink 1
enableScript 1
restrained 0
rewrite {
enable 1
logLevel 0
rules <<<END_RULES
# Canary cookie kontrolü
# X-Canary-User: yes cookie'si varsa v2'ye yönlendir
RewriteCond %{HTTP_COOKIE} canary_user=yes
RewriteRule ^(.*)$ lsapi://v2-backend$1 [P,L]
# Beta header kontrolü
# Internal test için header bazlı yönlendirme
RewriteCond %{HTTP:X-Beta-Tester} true
RewriteRule ^(.*)$ lsapi://v2-backend$1 [P,L]
# Diğer tüm istekler v1'e
RewriteRule ^(.*)$ lsapi://v1-backend$1 [P,L]
END_RULES
}
}
Yöntem 2: IP Bazlı Grayscale Deployment
Belirli IP aralıklarını (mesela ofis IP’nizi veya VPN subnet’ini) v2’ye yönlendirmek için:
# vhconf.conf içindeki rewrite bloğu
rewrite {
enable 1
logLevel 0
rules <<<END_RULES
# Ofis IP aralığı - 192.168.1.0/24
RewriteCond %{REMOTE_ADDR} ^192.168.1.
RewriteRule ^(.*)$ lsapi://v2-backend$1 [P,L]
# VPN kullanıcıları - 10.0.0.0/8
RewriteCond %{REMOTE_ADDR} ^10.
RewriteRule ^(.*)$ lsapi://v2-backend$1 [P,L]
# Geri kalan tüm kullanıcılar v1'e
RewriteRule ^(.*)$ lsapi://v1-backend$1 [P,L]
END_RULES
}
Yöntem 3: Yüzde Bazlı Grayscale Deployment
Bu en güçlü yöntem. Kullanıcıların belirli bir yüzdesini rastgele v2’ye yönlendiriyoruz. OpenLiteSpeed’de bunu QUERY_STRING ve modulo işlemiyle yapabiliriz ama en temiz yol Lua veya bir middleware script kullanmak.
Önce nginx gibi bir çözüme gitmek yerine, OpenLiteSpeed’in kendi RewriteMap benzeri yapısını ve session ID’yi kullanalım:
# /usr/local/lsws/conf/vhosts/example.com/vhconf.conf
rewrite {
enable 1
logLevel 0
rules <<<END_RULES
# Canary header zaten varsa doğrudan yönlendir
RewriteCond %{HTTP_COOKIE} deployment_group=canary
RewriteRule ^(.*)$ lsapi://v2-backend$1 [P,L]
RewriteCond %{HTTP_COOKIE} deployment_group=stable
RewriteRule ^(.*)$ lsapi://v1-backend$1 [P,L]
# Cookie yoksa REMOTE_ADDR'ın son oktetini kullan
# Son oktet 1-10 arasındaysa (%10) canary'ye gönder
RewriteCond %{REMOTE_ADDR} .([0-9]+)$
RewriteCond %1 ^([0-9]|10)$
RewriteRule ^(.*)$ lsapi://v2-backend$1 [P,L,CO=deployment_group:canary:.example.com:86400]
# Geri kalanlar stable
RewriteRule ^(.*)$ lsapi://v1-backend$1 [P,L,CO=deployment_group:stable:.example.com:86400]
END_RULES
}
Bu çözümde CO flag ile cookie set ediyoruz, böylece bir kullanıcı hangi gruba düştüyse orada kalıyor. Tutarlı bir deneyim sunmak için bu kritik.
Deployment Otomasyonu ile Scriptler
Manuel olarak config dosyasını değiştirmek hata yaratır. Bunun için bir deployment scripti yazalım:
#!/bin/bash
# /usr/local/bin/grayscale-deploy.sh
set -euo pipefail
VHOST_CONF="/usr/local/lsws/conf/vhosts/example.com/vhconf.conf"
BACKUP_DIR="/opt/deploy-backups"
OLS_ADMIN="http://localhost:7080"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
# Kullanım: grayscale-deploy.sh [percentage]
# Örnek: grayscale-deploy.sh 25 -> %25 canary, %75 stable
CANARY_PERCENT=${1:-10}
STABLE_PERCENT=$((100 - CANARY_PERCENT))
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"
}
# Mevcut konfigürasyonu yedekle
backup_config() {
mkdir -p "$BACKUP_DIR"
cp "$VHOST_CONF" "$BACKUP_DIR/vhconf_${TIMESTAMP}.conf"
log "Konfigürasyon yedeklendi: $BACKUP_DIR/vhconf_${TIMESTAMP}.conf"
}
# Canary yüzdesini güncelle
update_canary_percentage() {
local percent=$1
# Son oktet eşik değeri hesapla (0-255 arasından)
local threshold=$(( (255 * percent) / 100 ))
log "Canary yüzdesi ayarlanıyor: %${percent} (eşik: ${threshold}/255)"
# Mevcut threshold değerini güncelle
sed -i "s/CANARY_THRESHOLD [0-9]*/CANARY_THRESHOLD ${threshold}/" "$VHOST_CONF"
log "Konfigürasyon güncellendi"
}
# OpenLiteSpeed'i graceful restart ile yenile
reload_ols() {
log "OpenLiteSpeed reload başlatılıyor..."
/usr/local/lsws/bin/lswsctrl reload
# Reload'ın tamamlanmasını bekle
sleep 2
# Sağlık kontrolü yap
if curl -sf "http://localhost/health" > /dev/null 2>&1; then
log "Reload başarılı, servis sağlıklı"
else
log "UYARI: Sağlık kontrolü başarısız, rollback başlatılıyor..."
rollback
exit 1
fi
}
# Rollback fonksiyonu
rollback() {
local latest_backup
latest_backup=$(ls -t "$BACKUP_DIR"/vhconf_*.conf 2>/dev/null | head -1)
if [ -z "$latest_backup" ]; then
log "HATA: Yedek dosya bulunamadı!"
exit 1
fi
log "Rollback başlatılıyor: $latest_backup"
cp "$latest_backup" "$VHOST_CONF"
/usr/local/lsws/bin/lswsctrl reload
log "Rollback tamamlandı"
}
# Ana akış
case "${2:-deploy}" in
deploy)
backup_config
update_canary_percentage "$CANARY_PERCENT"
reload_ols
log "Deployment tamamlandı: %${CANARY_PERCENT} canary, %${STABLE_PERCENT} stable"
;;
rollback)
rollback
;;
status)
log "Mevcut canary yüzdesi kontrol ediliyor..."
grep "CANARY_THRESHOLD" "$VHOST_CONF" || echo "Canary threshold bulunamadı"
;;
*)
echo "Kullanım: $0 [percentage] [deploy|rollback|status]"
exit 1
;;
esac
Scripte çalıştırma izni ver:
chmod +x /usr/local/bin/grayscale-deploy.sh
# %10 canary ile başla
grayscale-deploy.sh 10 deploy
# Her şey yolundaysa %25'e çık
grayscale-deploy.sh 25 deploy
# Sorun varsa rollback
grayscale-deploy.sh 0 rollback
Monitoring ve Metrik Toplama
Deployment sonrasında iki versiyonun davranışını karşılaştırmak zorundasın. OpenLiteSpeed log formatını özelleştirerek hangi backend’in ne kadar istek aldığını takip edebilirsin.
# /usr/local/lsws/conf/httpd_config.conf içine
# Özel log formatı tanımla
accessLog /usr/local/lsws/logs/access.log {
useServer 0
logFormat "%h %l %u %t "%r" %>s %b "%{Referer}i" "%{User-Agent}i" %{X-Deployment-Version}o %D"
logHeaders 5
rollingSize 100M
keepDays 30
compressArchive 1
}
Backend uygulamanın X-Deployment-Version header’ı döndürmesi gerekiyor. v1 için v1, v2 için v2 döndürürsün. Sonra log analizi ile:
# Hangi versiyona kaç istek gittiğini öğren
awk '{print $NF}' /usr/local/lsws/logs/access.log | grep -E "^v[12]$" | sort | uniq -c
# Son 5 dakikanın istatistiği
awk -v d="$(date -d '5 minutes ago' '+%d/%b/%Y:%H:%M')"
'$4 > "["d {print $(NF-1)}' /usr/local/lsws/logs/access.log |
sort | uniq -c | sort -rn
Sağlık Kontrolü ve Otomatik Rollback
Production’da asla manuel izleme yeterli değildir. Bir cron job veya systemd timer ile sürekli kontrol yapalım:
#!/bin/bash
# /usr/local/bin/canary-health-check.sh
ERROR_THRESHOLD=5 # %5'ten fazla hata oranı varsa rollback
CHECK_WINDOW=60 # Son 60 saniyeyi kontrol et
LOG_FILE="/usr/local/lsws/logs/access.log"
get_error_rate() {
local version=$1
local total requests errors error_rate
# Son CHECK_WINDOW saniyedeki istekleri al
total=$(awk -v d="$(date -d "${CHECK_WINDOW} seconds ago" '+%d/%b/%Y:%H:%M:%S')"
'$4 > "["d && $(NF-1) == "'"$version"'" {count++} END {print count+0}' "$LOG_FILE")
errors=$(awk -v d="$(date -d "${CHECK_WINDOW} seconds ago" '+%d/%b/%Y:%H:%M:%S')"
'$4 > "["d && $(NF-1) == "'"$version"'" && $9 >= 500 {count++} END {print count+0}' "$LOG_FILE")
if [ "$total" -eq 0 ]; then
echo "0"
return
fi
echo $(( (errors * 100) / total ))
}
v1_error_rate=$(get_error_rate "v1")
v2_error_rate=$(get_error_rate "v2")
echo "V1 hata oranı: %${v1_error_rate}"
echo "V2 hata oranı: %${v2_error_rate}"
# V2 hata oranı eşiği aşıyorsa otomatik rollback
if [ "$v2_error_rate" -gt "$ERROR_THRESHOLD" ]; then
echo "ALARM: V2 hata oranı %${v2_error_rate} ile eşiği aştı!"
echo "Otomatik rollback başlatılıyor..."
# Slack bildirimi gönder (opsiyonel)
if [ -n "${SLACK_WEBHOOK_URL:-}" ]; then
curl -s -X POST "$SLACK_WEBHOOK_URL"
-H 'Content-type: application/json'
-d "{"text":"🚨 Canary deployment otomatik rollback: V2 hata oranı %${v2_error_rate}"}"
fi
/usr/local/bin/grayscale-deploy.sh 0 rollback
fi
Bu scripti cron’a ekle:
# Her 2 dakikada bir kontrol et
echo "*/2 * * * * root /usr/local/bin/canary-health-check.sh >> /var/log/canary-health.log 2>&1"
>> /etc/cron.d/canary-monitor
Gerçek Dünya Senaryosu: E-ticaret Sitesi Deploy
Bir e-ticaret sitesinde ödeme sayfasını yenilediklerini düşün. Bu en kritik akış, dolayısıyla çok dikkatli ilerlemelisin.
Adım 1: Önce sadece /payment/* path’ini canary’ye yönlendir, geri kalanı v1’de bırak:
rewrite {
enable 1
logLevel 0
rules <<<END_RULES
# Sadece ödeme sayfası için canary
RewriteCond %{HTTP_COOKIE} canary_payment=yes
RewriteCond %{REQUEST_URI} ^/payment/
RewriteRule ^(.*)$ lsapi://v2-backend$1 [P,L]
# Ödeme dışı sayfalar her zaman v1
RewriteCond %{REQUEST_URI} !^/payment/
RewriteRule ^(.*)$ lsapi://v1-backend$1 [P,L]
# Ödeme sayfasında canary cookie yoksa v1
RewriteRule ^(.*)$ lsapi://v1-backend$1 [P,L]
END_RULES
}
Adım 2: İlk gün sadece iç test ekibine cookie ver, 24 saat izle.
Adım 3: Conversion rate ve hata oranları normal görünüyorsa %5 kullanıcıya aç.
Adım 4: Bir hafta içinde kademeli olarak %100’e çık.
Bu yaklaşımla ödeme akışında yaşanabilecek bir regression problemi, tüm kullanıcıları etkilemeden yakalanabilir.
OpenLiteSpeed Admin Panel ile Görsel Yönetim
Her şeyi command line’dan yapmak zorunda değilsin. Admin panel üzerinden de extprocessor ve virtual host ayarlarını yapabilirsin.
# Admin panel portuna eriş (varsayılan 7080)
# https://sunucu-ip:7080
# Admin şifresini değiştir
/usr/local/lsws/admin/misc/admpass.sh
# Servis durumunu kontrol et
/usr/local/lsws/bin/lswsctrl status
# Graceful restart (mevcut bağlantıları kesmeden)
/usr/local/lsws/bin/lswsctrl reload
# Tam restart
/usr/local/lsws/bin/lswsctrl restart
Admin panel üzerinden Configuration > External App bölümüne girip her iki backend’i tanımlayabilir, ardından Virtual Host > Rewrite bölümünden kuralları girebilirsin. Ancak production’da her zaman config dosyalarını versiyon kontrolünde tut.
Sonuç
OpenLiteSpeed ile grayscale ve canary deployment uygulamak tahmin ettiğinden çok daha pratik. Temel mantık şu: rewrite kurallarıyla trafiği yönlendirmek, cookie veya header bazlı kullanıcı segmentasyonu yapmak ve otomatik rollback mekanizması kurmak.
En önemli noktalara bir daha değinelim:
- Cookie bazlı segmentasyon tutarlı kullanıcı deneyimi sağlar, aynı kullanıcı her seferinde farklı versiyona düşmez
- Otomatik rollback kritiktir, gece 3’te alarm almak istemiyorsan bu scripti mutlaka kur
- Log formatını özelleştir, hangi versiyona ne kadar trafik gittiğini göremezsen deployment’ın işe yarayıp yaramadığını bilemezsin
- Kademeli artış şarttır, direkt %50’ye çıkma, önce %5, bekle, sonra %15, bekle, sonra %30 gibi ilerle
- Config dosyalarını versiyon kontrolüne al, bir gün mutlaka geçmiş bir versiyona dönmen gerekecek
Bu altyapıyı bir kez kurduğunda, yeni özellik deploylarında “ya bir şeyler bozulursa” yerine “bozulursa zaten otomatik geri döner” diyeceksin. Ve bu özgüven, daha sık ve daha güvenli deploy yapmanı sağlar.