OpenLiteSpeed ile Grayscale ve Canary Deployment Nasıl Yapılır

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.

Yorum yapın