Cloudflare Load Balancing ile Yük Dengeleme

Yük dengeleme, modern web altyapısının vazgeçilmez bir parçası haline geldi. Tek bir sunucuya bağımlı kalmak artık lüks değil, bir risk. Cloudflare Load Balancing özelliği ise bu riski ortadan kaldırmak için DNS katmanında çalışan, kurulumu görece basit ama son derece güçlü bir çözüm sunuyor. Bu yazıda Cloudflare Load Balancing’i sıfırdan kurmayı, health check mekanizmalarını yapılandırmayı ve gerçek dünya senaryolarında nasıl kullanacağınızı adım adım ele alacağız.

Cloudflare Load Balancing Nedir ve Neden Kullanmalısınız?

Cloudflare Load Balancing, DNS tabanlı bir yük dengeleme çözümüdür. Geleneksel donanım load balancer’larının veya yazılım tabanlı çözümlerin (HAProxy, Nginx upstream) aksine, bu çözüm Cloudflare’in global DNS altyapısı üzerinde çalışır. Yani gelen trafik DNS seviyesinde yönlendiriliyor.

Peki bu ne anlama geliyor? Kullanıcı api.sirketiniz.com adresine bağlanmak istediğinde, Cloudflare DNS’i ona mevcut ve sağlıklı sunuculardan birinin IP adresini döndürüyor. Eğer bir sunucu çökerse, Cloudflare bunu health check mekanizmasıyla tespit ediyor ve o sunucuyu havuzdan otomatik olarak çıkarıyor.

Avantajları şöyle sıralayabiliriz:

  • Global dağıtım: Cloudflare’in 300’den fazla PoP noktası sayesinde coğrafi yönlendirme yapılabiliyor
  • Hızlı failover: Sunucu arızası durumunda saniyeler içinde trafik yönlendirilebiliyor
  • Merkezi yönetim: Dashboard veya API üzerinden tüm konfigürasyon yönetilebiliyor
  • Düşük TTL: DNS yanıt sürelerini minimize ederek failover hızını artırıyor
  • Health check entegrasyonu: Aktif izleme ile sağlıksız origin’ler otomatik devre dışı kalıyor

Temel Kavramlar

Cloudflare Load Balancing dünyasına girmeden önce birkaç terimi netleştirmek lazım.

Origin Server (Origin): Gerçek uygulamanızın çalıştığı sunucu. Bir IP adresi veya hostname olabilir.

Origin Pool: Birden fazla origin sunucusunun gruplanmış hali. Havuz içindeki sunuculara trafik dağıtılır.

Load Balancer: DNS kaydı gibi davranan, kullanıcı isteklerini pool’lara yönlendiren yapı.

Health Check (Monitor): Origin sunucularının sağlığını düzenli aralıklarla kontrol eden mekanizma.

Steering Policy: Trafiğin nasıl dağıtılacağını belirleyen strateji. Round-robin, geographic, random veya dynamic steering seçenekleri mevcut.

API ile Cloudflare Load Balancing Yönetimi

Cloudflare’in dashboard’u kullanışlı olsa da, production ortamlarında her şeyi API veya Terraform ile yönetmek çok daha mantıklı. Önce API token’ımızı ve zone ID’mizi hazırlayalım.

# Ortam değişkenlerini ayarlayalım
export CF_API_TOKEN="your-cloudflare-api-token"
export CF_ZONE_ID="your-zone-id"
export CF_ACCOUNT_ID="your-account-id"

# Mevcut load balancer'ları listele
curl -s -X GET 
  "https://api.cloudflare.com/client/v4/zones/${CF_ZONE_ID}/load_balancers" 
  -H "Authorization: Bearer ${CF_API_TOKEN}" 
  -H "Content-Type: application/json" | jq '.'

Health Check (Monitor) Oluşturma

Health check, yük dengelemenin kalbi. Düzgün yapılandırılmamış bir health check, hem yanlış alarmlara hem de gerçek arızaların gözden kaçmasına neden olabilir. HTTP health check oluşturalım:

# HTTP Health Check Monitor oluştur
curl -s -X POST 
  "https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/load_balancing/monitors" 
  -H "Authorization: Bearer ${CF_API_TOKEN}" 
  -H "Content-Type: application/json" 
  -d '{
    "type": "http",
    "description": "Ana uygulama health check",
    "method": "GET",
    "path": "/health",
    "header": {
      "Host": ["api.sirketiniz.com"],
      "X-Health-Check": ["cloudflare"]
    },
    "timeout": 5,
    "retries": 2,
    "interval": 60,
    "expected_codes": "2xx",
    "expected_body": ""status":"ok"",
    "follow_redirects": false,
    "allow_insecure": false
  }' | jq '.'

Bu komuttaki parametreleri açıklayalım:

  • type: Kontrol türü, http, https veya tcp olabilir
  • method: HTTP metodu, GET veya HEAD tercih edilir
  • path: Health check endpoint’i, uygulamanızın sağlık durumunu döndürdüğü URL
  • timeout: Saniye cinsinden zaman aşımı süresi
  • retries: Başarısız kabul etmeden önce deneme sayısı
  • interval: Kontroller arasındaki süre, saniye cinsinden
  • expected_codes: Başarılı kabul edilecek HTTP durum kodları
  • expected_body: Response body’de aranacak metin (opsiyonel ama önemli)

Uygulamanızda health check endpoint’i yoksa hemen ekleyin. İşte basit bir Node.js örneği:

// health.js - Express.js health check endpoint
const express = require('express');
const app = express();

app.get('/health', async (req, res) => {
  try {
    // Veritabanı bağlantısını kontrol et
    await checkDatabaseConnection();
    // Redis bağlantısını kontrol et
    await checkRedisConnection();
    
    res.status(200).json({
      status: 'ok',
      timestamp: new Date().toISOString(),
      version: process.env.APP_VERSION || '1.0.0',
      checks: {
        database: 'healthy',
        cache: 'healthy'
      }
    });
  } catch (error) {
    res.status(503).json({
      status: 'error',
      message: error.message
    });
  }
});

Origin Pool Oluşturma

Health check hazır, şimdi origin pool’umuzu oluşturalım. Bu örnekte üç sunuculu bir havuz yapıyoruz:

# Origin Pool oluştur
MONITOR_ID="daha_once_aldığınız_monitor_id"

curl -s -X POST 
  "https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/load_balancing/pools" 
  -H "Authorization: Bearer ${CF_API_TOKEN}" 
  -H "Content-Type: application/json" 
  -d "{
    "name": "production-web-pool",
    "description": "Production web sunucuları",
    "enabled": true,
    "minimum_origins": 1,
    "monitor": "${MONITOR_ID}",
    "notification_email": "[email protected]",
    "origins": [
      {
        "name": "web-server-01",
        "address": "185.10.10.1",
        "enabled": true,
        "weight": 1.0,
        "header": {
          "Host": ["api.sirketiniz.com"]
        }
      },
      {
        "name": "web-server-02",
        "address": "185.10.10.2",
        "enabled": true,
        "weight": 1.0
      },
      {
        "name": "web-server-03",
        "address": "185.10.10.3",
        "enabled": true,
        "weight": 0.5
      }
    ]
  }" | jq '.'

Burada weight parametresine dikkat edin. web-server-03‘e 0.5 weight verdiğimiz için bu sunucu diğerlerine oranla yarı kadar trafik alacak. Bu, kapasitesi daha düşük bir sunucuyu havuza dahil ederken işinize yarayan bir özellik.

minimum_origins: Havuzun “healthy” sayılabilmesi için en az kaç origin’in sağlıklı olması gerektiğini belirtir. 1 olarak ayarladık, yani tek sunucu ayakta kaldığı sürece pool devrede kalır.

Load Balancer Oluşturma

Pool’larımız hazır, şimdi gerçek load balancer’ı oluşturalım:

# Pool ID'leri önceki adımdan alınmış olmalı
PRIMARY_POOL_ID="primary_pool_id_buraya"
FALLBACK_POOL_ID="fallback_pool_id_buraya"

curl -s -X POST 
  "https://api.cloudflare.com/client/v4/zones/${CF_ZONE_ID}/load_balancers" 
  -H "Authorization: Bearer ${CF_API_TOKEN}" 
  -H "Content-Type: application/json" 
  -d "{
    "name": "api.sirketiniz.com",
    "description": "API yük dengeleyici",
    "ttl": 30,
    "proxied": true,
    "enabled": true,
    "steering_policy": "random",
    "session_affinity": "cookie",
    "session_affinity_ttl": 1800,
    "default_pools": ["${PRIMARY_POOL_ID}"],
    "fallback_pool": "${FALLBACK_POOL_ID}",
    "pop_pools": {},
    "region_pools": {
      "WEUR": ["${PRIMARY_POOL_ID}"],
      "EEUR": ["${PRIMARY_POOL_ID}"]
    }
  }" | jq '.'

Bu konfigürasyonda birkaç önemli nokta var:

ttl: 30 saniye olarak ayarlandı. Bu, DNS yanıtlarının 30 saniye boyunca cache’de kalacağı anlamına gelir. Daha hızlı failover için düşürülebilir ama çok düşük değerler DNS flood’a yol açabilir.

proxied: true olarak ayarlandığında trafik Cloudflare proxy’si üzerinden geçer. Bu durumda origin IP’leri gizlenir ve Cloudflare’in DDoS koruma özellikleri devreye girer.

steering_policy: Trafik dağıtım stratejisi. random dışında şu seçenekler var:

  • off: Round-robin
  • geo: Coğrafi yönlendirme
  • dynamic_latency: En düşük gecikmeli pool’a yönlendirme
  • proximity: Coğrafi yakınlığa göre yönlendirme

session_affinity: cookie seçeneği ile aynı kullanıcı her seferinde aynı origin’e yönlendiriliyor. Oturum bazlı uygulamalar için kritik.

Gerçek Dünya Senaryosu: Aktif-Pasif (Active-Passive) Failover

Şu senaryoyu düşünelim: İki veri merkeziniz var, birincil olarak İstanbul’daki sunucuları kullanıyorsunuz, arıza durumunda Frankfurt’taki yedek sunuculara geçmek istiyorsunuz.

#!/bin/bash
# active_passive_setup.sh

CF_API_TOKEN="${CF_API_TOKEN}"
CF_ACCOUNT_ID="${CF_ACCOUNT_ID}"
CF_ZONE_ID="${CF_ZONE_ID}"

# Monitor oluştur
MONITOR_RESPONSE=$(curl -s -X POST 
  "https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/load_balancing/monitors" 
  -H "Authorization: Bearer ${CF_API_TOKEN}" 
  -H "Content-Type: application/json" 
  -d '{
    "type": "https",
    "description": "HTTPS Health Check",
    "method": "GET",
    "path": "/healthz",
    "timeout": 5,
    "retries": 3,
    "interval": 60,
    "expected_codes": "200"
  }')

MONITOR_ID=$(echo $MONITOR_RESPONSE | jq -r '.result.id')
echo "Monitor ID: ${MONITOR_ID}"

# İstanbul pool
IST_POOL_RESPONSE=$(curl -s -X POST 
  "https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/load_balancing/pools" 
  -H "Authorization: Bearer ${CF_API_TOKEN}" 
  -H "Content-Type: application/json" 
  -d "{
    "name": "istanbul-primary",
    "monitor": "${MONITOR_ID}",
    "minimum_origins": 1,
    "origins": [
      {"name": "ist-web-01", "address": "10.0.1.10", "enabled": true, "weight": 1},
      {"name": "ist-web-02", "address": "10.0.1.11", "enabled": true, "weight": 1}
    ]
  }")

IST_POOL_ID=$(echo $IST_POOL_RESPONSE | jq -r '.result.id')

# Frankfurt pool
FRA_POOL_RESPONSE=$(curl -s -X POST 
  "https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/load_balancing/pools" 
  -H "Authorization: Bearer ${CF_API_TOKEN}" 
  -H "Content-Type: application/json" 
  -d "{
    "name": "frankfurt-failover",
    "monitor": "${MONITOR_ID}",
    "minimum_origins": 1,
    "origins": [
      {"name": "fra-web-01", "address": "10.1.1.10", "enabled": true, "weight": 1}
    ]
  }")

FRA_POOL_ID=$(echo $FRA_POOL_RESPONSE | jq -r '.result.id')

# Load Balancer - İstanbul primary, Frankfurt fallback
curl -s -X POST 
  "https://api.cloudflare.com/client/v4/zones/${CF_ZONE_ID}/load_balancers" 
  -H "Authorization: Bearer ${CF_API_TOKEN}" 
  -H "Content-Type: application/json" 
  -d "{
    "name": "app.sirketiniz.com",
    "ttl": 30,
    "proxied": true,
    "steering_policy": "off",
    "default_pools": ["${IST_POOL_ID}", "${FRA_POOL_ID}"],
    "fallback_pool": "${FRA_POOL_ID}"
  }" | jq '.'

echo "Aktif-Pasif kurulum tamamlandı!"

Bu yapıda default_pools array’inin sırası önemli. İlk pool primary, sıradaki fallback olarak davranır. İstanbul pool’u tamamen çökerse trafik otomatik Frankfurt’a geçer.

Pool Sağlık Durumunu İzleme

Production ortamında pool sağlığını sürekli takip etmeniz gerekiyor. Bunun için basit bir izleme scripti yazalım:

#!/bin/bash
# check_pool_health.sh - Crontab ile her 5 dakikada çalıştırın

CF_API_TOKEN="${CF_API_TOKEN}"
CF_ACCOUNT_ID="${CF_ACCOUNT_ID}"
SLACK_WEBHOOK="${SLACK_WEBHOOK_URL}"

# Tüm pool'ları çek ve sağlığını kontrol et
POOLS=$(curl -s -X GET 
  "https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/load_balancing/pools" 
  -H "Authorization: Bearer ${CF_API_TOKEN}" 
  -H "Content-Type: application/json")

echo $POOLS | jq -r '.result[] | "(.name) (.id) (.healthy)"' | while read pool_name pool_id is_healthy; do
  
  if [ "$is_healthy" = "false" ]; then
    echo "[UYARI] Pool sağlıksız: ${pool_name}"
    
    # Origin detaylarını çek
    POOL_DETAIL=$(curl -s -X GET 
      "https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/load_balancing/pools/${pool_id}" 
      -H "Authorization: Bearer ${CF_API_TOKEN}" 
      -H "Content-Type: application/json")
    
    # Slack bildirimi gönder
    curl -s -X POST "${SLACK_WEBHOOK}" 
      -H "Content-Type: application/json" 
      -d "{
        "text": ":rotating_light: *Cloudflare LB Uyarı*nPool `${pool_name}` sağlıksız durumda!nZaman: $(date)",
        "username": "CF-LoadBalancer-Bot"
      }"
  else
    echo "[OK] Pool sağlıklı: ${pool_name}"
  fi
done

Terraform ile Cloudflare Load Balancing

Gerçek production ortamlarında her şeyi Infrastructure as Code ile yönetmek şart. İşte Terraform konfigürasyonu:

# cloudflare_lb.tf

terraform {
  required_providers {
    cloudflare = {
      source  = "cloudflare/cloudflare"
      version = "~> 4.0"
    }
  }
}

provider "cloudflare" {
  api_token = var.cloudflare_api_token
}

# Health Check Monitor
resource "cloudflare_load_balancer_monitor" "web_monitor" {
  account_id     = var.cloudflare_account_id
  type           = "https"
  description    = "Web uygulaması health check"
  method         = "GET"
  path           = "/health"
  timeout        = 5
  retries        = 2
  interval       = 60
  expected_codes = "2xx"
  expected_body  = "status.*ok"
  
  header {
    header = "Host"
    values = ["app.sirketiniz.com"]
  }
}

# Primary Origin Pool
resource "cloudflare_load_balancer_pool" "primary_pool" {
  account_id         = var.cloudflare_account_id
  name               = "primary-web-pool"
  description        = "Birincil web sunucu havuzu"
  enabled            = true
  minimum_origins    = 1
  monitor            = cloudflare_load_balancer_monitor.web_monitor.id
  notification_email = "[email protected]"

  origins {
    name    = "web-01"
    address = "185.10.10.1"
    enabled = true
    weight  = 1
  }

  origins {
    name    = "web-02"
    address = "185.10.10.2"
    enabled = true
    weight  = 1
  }
}

# Fallback Pool
resource "cloudflare_load_balancer_pool" "fallback_pool" {
  account_id      = var.cloudflare_account_id
  name            = "fallback-pool"
  description     = "Yedek sunucu havuzu"
  enabled         = true
  minimum_origins = 1
  monitor         = cloudflare_load_balancer_monitor.web_monitor.id

  origins {
    name    = "backup-web-01"
    address = "185.20.20.1"
    enabled = true
    weight  = 1
  }
}

# Load Balancer
resource "cloudflare_load_balancer" "web_lb" {
  zone_id          = var.cloudflare_zone_id
  name             = "app.sirketiniz.com"
  description      = "Ana uygulama yük dengeleyici"
  proxied          = true
  ttl              = 30
  steering_policy  = "random"
  session_affinity = "cookie"
  
  session_affinity_attributes {
    samesite = "Strict"
    secure   = "Always"
    drain_duration = 60
  }

  default_pool_ids  = [cloudflare_load_balancer_pool.primary_pool.id]
  fallback_pool_id  = cloudflare_load_balancer_pool.fallback_pool.id

  rules {
    name      = "bypass-health-check"
    condition = "http.request.uri.path eq "/bypass""
    fixed_response {
      message_body  = "OK"
      status_code   = 200
      content_type  = "text/plain"
    }
  }
}

Yaygın Sorunlar ve Çözümleri

Health Check False Positive Durumu

Bazen origin sunucunuz sağlıklı olmasına rağmen Cloudflare health check’i başarısız gösterebilir. Bu genellikle şu sebeplerden kaynaklanır:

  • Origin sunucunuzun firewall’u Cloudflare IP aralıklarını engelliyor olabilir. Cloudflare’in [IP listesini](https://www.cloudflare.com/ips/) whitelist’e eklemeniz gerekir.
  • Health check path’i yanlış yapılandırılmış olabilir.
  • Timeout değeri çok düşük ayarlanmış olabilir, özellikle yoğun anlarda sunucu geç yanıt verebilir.

Cloudflare IP’lerini sunucunuza eklemek için:

#!/bin/bash
# cloudflare_whitelist.sh - Cloudflare IP'lerini iptables'a ekle

# IPv4
CF_IPS_V4=$(curl -s https://www.cloudflare.com/ips-v4)
# IPv6
CF_IPS_V6=$(curl -s https://www.cloudflare.com/ips-v6)

for ip in $CF_IPS_V4; do
  iptables -A INPUT -p tcp --dport 443 -s "$ip" -j ACCEPT
  iptables -A INPUT -p tcp --dport 80 -s "$ip" -j ACCEPT
  echo "IPv4 eklendi: $ip"
done

for ip in $CF_IPS_V6; do
  ip6tables -A INPUT -p tcp --dport 443 -s "$ip" -j ACCEPT
  ip6tables -A INPUT -p tcp --dport 80 -s "$ip" -j ACCEPT
  echo "IPv6 eklendi: $ip"
done

# Diğer kaynaklardan 80/443'ü engelle (opsiyonel, proxied=true ise önerilir)
iptables -A INPUT -p tcp --dport 443 -j DROP
iptables -A INPUT -p tcp --dport 80 -j DROP

echo "Cloudflare IP whitelist tamamlandı"

Session Affinity Sorunları

Session affinity kullanırken dikkat edilmesi gereken bir nokta var: drain_duration değeri. Bir origin bakıma alınacaksa, mevcut oturumların tamamlanması için yeterli süre tanımalısınız. Bu değeri uygulamanızın ortalama oturum süresine göre ayarlayın.

Düşük TTL Kaynaklı Sorunlar

30 saniyelik TTL DNS resolver’lara bu kadar süre cache tutabileceğini söylüyor. Ancak bazı ISP’lerin recursive DNS resolver’ları TTL değerini dikkate almadan daha uzun süre cache tutabilir. Bu yüzden failover’ın anında gerçekleşmesini beklemek gerçekçi değil. En iyi senaryoda 1-2 dakika geçiş süresi hesaplamanız gerekiyor.

Maliyet Optimizasyonu

Cloudflare Load Balancing, Pro planından başlayarak kullanılabiliyor ancak bazı gelişmiş özellikler Enterprise planı gerektiriyor. Health check sayısı ve sorgu hacmine göre ücretlendirme yapılıyor. Maliyeti düşürmek için şunlara dikkat edin:

  • Health check interval’ını gereksiz yere kısa tutmayın. Kritik olmayan servisler için 120 saniye bile yeterli olabilir.
  • Kullanılmayan monitor ve pool’ları temizleyin.
  • Her ortam için ayrı zone kullanmak yerine, staging için staging.sirketiniz.com gibi subdomain’ler üzerinde ayrı load balancer oluşturabilirsiniz.

Sonuç

Cloudflare Load Balancing, özellikle halihazırda Cloudflare kullanan altyapılar için son derece pratik ve etkili bir yük dengeleme çözümü. Ayrı bir load balancer sunucusu kurmanıza gerek kalmadan, DNS katmanında yüksek erişilebilirlik sağlayabilirsiniz.

Bu yazıda anlattıklarımı özetlemek gerekirse: Her şeyi API veya Terraform ile yönetin, manuel dashboard işlemleri production ortamlarında tehlikeli. Health check endpoint’lerinizi titizlikle tasarlayın, bu endpoint gerçekten uygulamanızın kritik bağımlılıklarını kontrol etmeli. Failover testlerini düzenli olarak yapın, yılda bir kez beklenmedik anda öğrenmek istemezsiniz. Son olarak, Cloudflare IP’lerini mutlaka whitelist’e alın, aksi takdirde health check sonuçlarına güvenemezsiniz.

Yük dengeleme kurulumu tek seferlik bir iş değil, altyapınız büyüdükçe pool yapınızı, weight değerlerini ve steering policy’nizi güncellemeniz gerekecek. Terraform kullanıyorsanız bu değişiklikleri code review sürecinize dahil etmeyi unutmayın.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir