Cloudflare Health Checks ile Sunucu Sağlık İzleme

Sunucularınız gece yarısı çöktüğünde ilk siz mi öğreniyorsunuz, yoksa müşterileriniz mi? Bu sorunun cevabı, ne kadar iyi bir izleme altyapısı kurduğunuza bağlı. Cloudflare’in Health Checks özelliği, DNS katmanında sunucu sağlığını izleyerek hem erken uyarı hem de otomatik trafik yönlendirme imkânı sunuyor. Bu yazıda Cloudflare Health Checks’i sıfırdan nasıl kuracağınızı, API ile nasıl yöneteceğinizi ve gerçek dünya senaryolarında nasıl kullanacağınızı ele alacağız.

Cloudflare Health Checks Nedir?

Cloudflare Health Checks, belirlediğiniz sunucu veya origin’lere düzenli aralıklarla HTTP/HTTPS veya TCP istekleri göndererek sağlık durumunu izleyen bir özelliktir. Load Balancer’dan bağımsız olarak kullanılabildiği gibi, Load Balancer ile entegre çalışarak otomatik failover da sağlayabilir.

İki farklı kullanım senaryosu vardır:

  • Standalone Health Checks: Sadece izleme amaçlı, trafik yönlendirme yapmadan
  • Load Balancer Health Checks: Pool içindeki origin’leri izleyerek sağlıksız sunucuya trafik göndermeyi durdurur

Ücretsiz planda standalone health checks sınırlı sayıda gelir; Pro ve üzeri planlarda daha gelişmiş özellikler açılır. Load Balancer ise ayrı ücretlendirilir.

Gereksinimler ve Hazırlık

Başlamadan önce birkaç şeyi hazır etmeniz gerekiyor:

  • Cloudflare hesabı ve DNS yönetimini Cloudflare’e devretmiş bir domain
  • API Token veya Global API Key (API üzerinden yönetim için)
  • İzlenecek sunucu IP adresleri ve port bilgileri
  • curl ve jq yüklü bir terminal

API Token oluşturmak için Cloudflare Dashboard > My Profile > API Tokens yolunu izleyin. “Edit zone DNS” ve “Zone: Read” yetkilerini token’a ekleyin. Load Balancer yönetimi yapacaksanız “Load Balancers: Edit” yetkisini de eklemeyi unutmayın.

Ortam değişkenlerini baştan ayarlayalım:

export CF_API_TOKEN="buraya_api_tokeninizi_yazin"
export CF_ZONE_ID="zone_id_buraya"
export CF_ACCOUNT_ID="account_id_buraya"

# Token'ı test edelim
curl -s -X GET "https://api.cloudflare.com/client/v4/user/tokens/verify" 
  -H "Authorization: Bearer $CF_API_TOKEN" 
  -H "Content-Type: application/json" | jq '.result.status'

Çıktı "active" döndürüyorsa hazırsınız demektir.

İlk Health Check’i Oluşturmak

Dashboard Üzerinden Hızlı Kurulum

Dashboard’dan gitmek için: Cloudflare Dashboard > ilgili Zone > Traffic > Health Checks yolunu izleyin. “Create” butonuna tıklayıp aşağıdaki alanları doldurun.

  • Name: Tanımlayıcı bir isim verin, örneğin “prod-web-server-01”
  • URL: İzlenecek adres, örneğin https://example.com/health
  • Monitoring Type: HTTP veya TCP seçin
  • Check Frequency: 60 saniye önerilir
  • Consecutive Fails: Kaç başarısız denemeden sonra “unhealthy” sayılacağı
  • Consecutive Successes: Kaç başarılı denemeden sonra “healthy” sayılacağı

API ile Health Check Oluşturmak

Asıl gücü API’de. Şimdi bir HTTP health check oluşturalım:

curl -s -X POST "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/healthchecks" 
  -H "Authorization: Bearer $CF_API_TOKEN" 
  -H "Content-Type: application/json" 
  --data '{
    "name": "prod-web-server-01",
    "description": "Production web sunucusu ana health check",
    "address": "203.0.113.10",
    "type": "HTTPS",
    "port": 443,
    "path": "/health",
    "interval": 60,
    "retries": 2,
    "timeout": 5,
    "consecutive_up": 2,
    "consecutive_down": 2,
    "http_config": {
      "method": "GET",
      "expected_codes": ["200", "204"],
      "expected_body": "OK",
      "follow_redirects": false,
      "allow_insecure": false,
      "header": {
        "User-Agent": ["cloudflare-healthcheck/1.0"],
        "X-Health-Check": ["true"]
      }
    },
    "notification_suspended": false,
    "suspended": false
  }' | jq '.'

Bu isteği gönderdikten sonra dönen JSON’dan id alanını kaydedin, sonraki işlemlerde gerekecek.

TCP Health Check

Sadece port açıklığını kontrol etmek istiyorsanız TCP daha uygun olabilir. Özellikle database portları veya özel protokoller için kullanışlıdır:

curl -s -X POST "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/healthchecks" 
  -H "Authorization: Bearer $CF_API_TOKEN" 
  -H "Content-Type: application/json" 
  --data '{
    "name": "db-server-tcp-check",
    "description": "MySQL port kontrolu",
    "address": "203.0.113.20",
    "type": "TCP",
    "port": 3306,
    "interval": 60,
    "retries": 3,
    "timeout": 10,
    "consecutive_up": 2,
    "consecutive_down": 3
  }' | jq '.result | {id, name, type, address, port}'

Health Check Endpoint’i Sunucuda Hazırlamak

Cloudflare’in kontrol edeceği /health endpoint’ini sunucunuzda oluşturmanız gerekiyor. Sadece HTTP 200 döndürmek yeterli değil; gerçek bir sağlık kontrolü yapan endpoint yazmalısınız.

Nginx ile Basit Health Endpoint

# /etc/nginx/conf.d/health.conf
server {
    listen 80;
    listen 443 ssl;
    server_name example.com;

    location /health {
        access_log off;
        add_header Content-Type text/plain;
        
        # Sadece Cloudflare IP'lerinden gelen isteklere izin ver
        allow 103.21.244.0/22;
        allow 103.22.200.0/22;
        allow 103.31.4.0/22;
        allow 104.16.0.0/13;
        allow 104.24.0.0/14;
        allow 108.162.192.0/18;
        allow 131.0.72.0/22;
        allow 141.101.64.0/18;
        allow 162.158.0.0/15;
        allow 172.64.0.0/13;
        allow 173.245.48.0/20;
        allow 188.114.96.0/20;
        allow 190.93.240.0/20;
        allow 197.234.240.0/22;
        allow 198.41.128.0/17;
        deny all;
        
        return 200 "OK";
    }
}

Python Flask ile Kapsamlı Health Check

Sadece “sunucu ayakta” değil, “sunucu düzgün çalışıyor” kontrolü yapmak için daha kapsamlı bir endpoint:

#!/usr/bin/env python3
# health_check.py - Flask health check endpoint'i

from flask import Flask, jsonify
import mysql.connector
import redis
import os
import time

app = Flask(__name__)

def check_database():
    try:
        conn = mysql.connector.connect(
            host=os.getenv('DB_HOST', 'localhost'),
            user=os.getenv('DB_USER', 'healthcheck'),
            password=os.getenv('DB_PASS', ''),
            database=os.getenv('DB_NAME', 'app'),
            connect_timeout=3
        )
        cursor = conn.cursor()
        cursor.execute("SELECT 1")
        cursor.fetchone()
        conn.close()
        return True, "OK"
    except Exception as e:
        return False, str(e)

def check_redis():
    try:
        r = redis.Redis(
            host=os.getenv('REDIS_HOST', 'localhost'),
            port=6379,
            socket_connect_timeout=3
        )
        r.ping()
        return True, "OK"
    except Exception as e:
        return False, str(e)

def check_disk_space():
    import shutil
    total, used, free = shutil.disk_usage("/")
    percent_used = (used / total) * 100
    if percent_used > 90:
        return False, f"Disk dolu: %{percent_used:.1f}"
    return True, f"%{percent_used:.1f} kullaniliyor"

@app.route('/health', methods=['GET'])
def health():
    checks = {}
    all_healthy = True
    
    db_ok, db_msg = check_database()
    checks['database'] = {'status': 'ok' if db_ok else 'fail', 'message': db_msg}
    if not db_ok:
        all_healthy = False
    
    redis_ok, redis_msg = check_redis()
    checks['redis'] = {'status': 'ok' if redis_ok else 'fail', 'message': redis_msg}
    if not redis_ok:
        all_healthy = False
    
    disk_ok, disk_msg = check_disk_space()
    checks['disk'] = {'status': 'ok' if disk_ok else 'warn', 'message': disk_msg}
    
    response = {
        'status': 'healthy' if all_healthy else 'unhealthy',
        'timestamp': int(time.time()),
        'checks': checks
    }
    
    http_status = 200 if all_healthy else 503
    return jsonify(response), http_status

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8080)

Bu endpoint, Cloudflare’in expected_codes ayarında 200 ve 503’ü bekleyecek şekilde konfigüre edebilirsiniz; ama genellikle 200 döndüğünde healthy, 503 döndüğünde unhealthy olarak ayarlamak mantıklı.

Mevcut Health Check’leri Yönetmek

Tüm Health Check’leri Listeleme

# Tüm health check'leri listele ve özet göster
curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/healthchecks" 
  -H "Authorization: Bearer $CF_API_TOKEN" 
  -H "Content-Type: application/json" | 
  jq '.result[] | {
    id: .id,
    name: .name,
    address: .address,
    type: .type,
    port: .port,
    status: .status,
    interval: .interval
  }'

Health Check Durumunu Sorgulamak

HEALTH_CHECK_ID="buraya_health_check_id_yazin"

curl -s -X GET 
  "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/healthchecks/$HEALTH_CHECK_ID" 
  -H "Authorization: Bearer $CF_API_TOKEN" 
  -H "Content-Type: application/json" | 
  jq '.result | {
    name: .name,
    status: .status,
    failure_reason: .failure_reason,
    checked_regions: .checked_regions
  }'

Dönen status alanı healthy, unhealthy veya unknown olabilir. failure_reason alanı ise neden başarısız olduğunu açıklar.

Health Check Güncelleme

Interval’ı veya path’i değiştirmek gerektiğinde PATCH kullanın:

curl -s -X PATCH 
  "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/healthchecks/$HEALTH_CHECK_ID" 
  -H "Authorization: Bearer $CF_API_TOKEN" 
  -H "Content-Type: application/json" 
  --data '{
    "interval": 30,
    "consecutive_down": 3,
    "description": "Guncellendi: 30 saniye interval"
  }' | jq '.result | {name, interval, consecutive_down}'

Load Balancer ile Entegrasyon

Gerçek failover senaryosu için Health Checks’i Load Balancer ile birleştirmeniz gerekiyor. Önce bir Pool oluşturalım:

# Load Balancer Pool oluştur
curl -s -X POST 
  "https://api.cloudflare.com/client/v4/accounts/$CF_ACCOUNT_ID/load_balancers/pools" 
  -H "Authorization: Bearer $CF_API_TOKEN" 
  -H "Content-Type: application/json" 
  --data '{
    "name": "production-web-pool",
    "description": "Production web sunuculari",
    "enabled": true,
    "minimum_origins": 1,
    "monitor": "BURAYA_MONITOR_ID",
    "origins": [
      {
        "name": "web-01",
        "address": "203.0.113.10",
        "enabled": true,
        "weight": 1,
        "header": {
          "Host": ["example.com"]
        }
      },
      {
        "name": "web-02",
        "address": "203.0.113.11",
        "enabled": true,
        "weight": 1,
        "header": {
          "Host": ["example.com"]
        }
      }
    ],
    "notification_email": "[email protected]"
  }' | jq '.result | {id, name, healthy}'

Monitor ID’yi almak için önce bir account-level monitor oluşturmanız gerekiyor. Bu, zone-level health check’ten farklıdır:

# Account-level monitor oluştur (Load Balancer için)
curl -s -X POST 
  "https://api.cloudflare.com/client/v4/accounts/$CF_ACCOUNT_ID/load_balancers/monitors" 
  -H "Authorization: Bearer $CF_API_TOKEN" 
  -H "Content-Type: application/json" 
  --data '{
    "type": "https",
    "description": "Production HTTPS monitor",
    "method": "GET",
    "path": "/health",
    "port": 443,
    "interval": 60,
    "retries": 2,
    "timeout": 5,
    "consecutive_up": 2,
    "consecutive_down": 2,
    "expected_codes": "200",
    "expected_body": "healthy",
    "follow_redirects": false,
    "allow_insecure": false,
    "header": {
      "User-Agent": ["Cloudflare-LB-Monitor"]
    }
  }' | jq '.result | {id, type, path, interval}'

Alerting: Bildirim Almak

Health Check değeri olmaz, bildirim yoksa. Cloudflare Notifications özelliğiyle e-posta, PagerDuty, Slack veya webhook üzerinden uyarı alabilirsiniz.

Webhook ile Slack Bildirimi

Cloudflare Dashboard’dan Notifications > Create bölümüne gidin. “Health Check status change” event’ini seçin ve webhook URL’nizi girin. Webhook payload’u şu formatta gelir:

# Webhook'u test etmek icin basit bir Python listener
# Bu script gelen Cloudflare notification'larini yakalar

python3 -c "
from http.server import HTTPServer, BaseHTTPRequestHandler
import json

class Handler(BaseHTTPRequestHandler):
    def do_POST(self):
        length = int(self.headers['Content-Length'])
        body = self.rfile.read(length)
        data = json.loads(body)
        print(json.dumps(data, indent=2))
        self.send_response(200)
        self.end_headers()
    def log_message(self, format, *args):
        pass

print('Webhook dinleniyor: 0.0.0.0:9000')
HTTPServer(('0.0.0.0', 9000), Handler).serve_forever()
"

Otomatik Aksiyon Scripti

Health check durumu değiştiğinde webhook ile tetiklenecek bir script:

#!/bin/bash
# /usr/local/bin/health-check-action.sh
# Bu script webhook'tan gelen POST body'yi parse eder
# ve gerekli aksiyonları alir

LOG_FILE="/var/log/cloudflare-health-actions.log"
SLACK_WEBHOOK="https://hooks.slack.com/services/XXXXX/YYYYY/ZZZZZ"

log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> "$LOG_FILE"
}

send_slack() {
    local message="$1"
    local color="$2"
    
    curl -s -X POST "$SLACK_WEBHOOK" 
        -H "Content-Type: application/json" 
        --data "{
            "attachments": [{
                "color": "$color",
                "text": "$message",
                "footer": "Cloudflare Health Check",
                "ts": $(date +%s)
            }]
        }" > /dev/null
}

# Stdin'den JSON oku (webhook body)
PAYLOAD=$(cat)
STATUS=$(echo "$PAYLOAD" | jq -r '.data.health_status // "unknown"')
HOSTNAME=$(echo "$PAYLOAD" | jq -r '.data.hostname // "unknown"')
ORIGIN=$(echo "$PAYLOAD" | jq -r '.data.origin // "unknown"')

log "Durum degisimi: $HOSTNAME ($ORIGIN) -> $STATUS"

case "$STATUS" in
    "unhealthy")
        log "KRITIK: $HOSTNAME sagliksiz! Aksiyon aliniyor..."
        send_slack ":red_circle: *KRITIK*: $HOSTNAME ($ORIGIN) sagliksiz!" "danger"
        
        # Opsiyonel: Yedek sunucuya otomatik gecis icin script cagir
        # /usr/local/bin/failover.sh "$ORIGIN"
        ;;
    "healthy")
        log "IYILESME: $HOSTNAME tekrar saglikli."
        send_slack ":green_circle: *IYILESME*: $HOSTNAME ($ORIGIN) tekrar saglikli!" "good"
        ;;
    *)
        log "Bilinmeyen durum: $STATUS"
        ;;
esac

Gerçek Dünya Senaryoları

Senaryo 1: Çoklu Veri Merkezi İzleme

İstanbul ve Almanya’da birer sunucunuz olduğunu varsayalım. Her ikisi de aktif olarak trafik alıyor. Health Check ile her ikisini izleyip, biri düştüğünde DNS kaydını otomatik olarak kaldırabilirsiniz.

Bu senaryoda Cloudflare Load Balancer’ı kullanarak aktif-aktif yapı kurulur. Her iki origin pool’da tanımlanır. Biri unhealthy olduğunda Cloudflare otomatik olarak trafiği sağlıklı origin’e yönlendirir. TTL değeri düşük tutulduğu için geçiş süresi genellikle 60 saniyenin altında kalır.

Senaryo 2: Maintenance Mode Entegrasyonu

Bakım sürecinde health check’i geçici olarak duraklatmak:

# Bakim moduna gec - health check'i askiya al
HEALTH_CHECK_ID="your_check_id"

curl -s -X PATCH 
  "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/healthchecks/$HEALTH_CHECK_ID" 
  -H "Authorization: Bearer $CF_API_TOKEN" 
  -H "Content-Type: application/json" 
  --data '{"suspended": true}' | 
  jq '.result | {name, suspended}'

echo "Bakim islemi tamamlandiginda su komutu calistirin:"
echo "CF_ZONE_ID=$CF_ZONE_ID CF_API_TOKEN=$CF_API_TOKEN \"
echo "  HEALTH_CHECK_ID=$HEALTH_CHECK_ID /usr/local/bin/resume-health-check.sh"

# Bakim bitti, tekrar aktif et
# curl -s -X PATCH ... --data '{"suspended": false}'

Senaryo 3: Toplu Health Check Raporu

Sabah işe geldiğinizde tüm sunucuların durumunu tek komutla görmek:

#!/bin/bash
# /usr/local/bin/morning-health-report.sh

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

echo "=== Cloudflare Health Check Raporu ==="
echo "Tarih: $(date '+%Y-%m-%d %H:%M:%S')"
echo ""

CHECKS=$(curl -s -X GET 
  "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/healthchecks" 
  -H "Authorization: Bearer $CF_API_TOKEN" 
  -H "Content-Type: application/json")

TOTAL=$(echo "$CHECKS" | jq '.result | length')
HEALTHY=$(echo "$CHECKS" | jq '[.result[] | select(.status == "healthy")] | length')
UNHEALTHY=$(echo "$CHECKS" | jq '[.result[] | select(.status == "unhealthy")] | length')

echo "Toplam: $TOTAL | Saglikli: $HEALTHY | Sagliksiz: $UNHEALTHY"
echo ""

echo "--- Sagliksiz Sunucular ---"
echo "$CHECKS" | jq -r '.result[] | select(.status == "unhealthy") | 
  "[(.name)] (.address):(.port // 443) - (.failure_reason // "Bilinmiyor")"'

echo ""
echo "--- Saglikli Sunucular ---"
echo "$CHECKS" | jq -r '.result[] | select(.status == "healthy") | 
  "[(.name)] (.address):(.port // 443)"'

Bu scripti cron’a ekleyerek her sabah e-posta ile alabilirsiniz:

# Crontab'a ekle
echo "0 8 * * 1-5 /usr/local/bin/morning-health-report.sh | mail -s 'Gunluk Health Report' [email protected]" | crontab -

Sık Yapılan Hatalar ve Çözümleri

“Health check geçiyor ama sunucu yavaş” durumunda timeout değerini düşürün. Varsayılan 5 saniye timeout, yavaş yanıt veren bir sunucuyu healthy gösterebilir. Uygulamanızın normal yanıt süresi 500ms ise timeout’u 2 saniyeye çekin.

“False positive unhealthy” durumunda consecutive_down değerini 2’den 3’e çıkarın. Geçici ağ dalgalanmaları yüzünden tek başarısız kontrol alarm yaratmasın.

“Health check IP’lerim firewall’da bloklanıyor” yaygın bir sorundur. Cloudflare’in health check IP aralıklarını firewall whitelist’ine eklemeniz şart. Güncel listeyi şu komutla çekin:

# Cloudflare IP araliklerini guncelle
curl -s https://www.cloudflare.com/ips-v4 > /tmp/cf_ips_v4.txt
curl -s https://www.cloudflare.com/ips-v6 > /tmp/cf_ips_v6.txt

# UFW ile toplu izin ver
while read ip; do
    ufw allow from "$ip" to any port 443 comment "Cloudflare Health Check"
done < /tmp/cf_ips_v4.txt

echo "Cloudflare IP'leri eklendi."
ufw status numbered | grep "Cloudflare"

“Expected body eşleşmiyor” hatasında büyük/küçük harf duyarlılığına dikkat edin. Cloudflare, expected_body kontrolünü case-sensitive yapar. Endpoint’inizin döndürdüğü tam metni kullanın.

İzleme Stratejisi: Ne Kadar Sık Kontrol Edilmeli?

Health check aralığı seçerken birkaç faktörü göz önünde bulundurun:

  • Kritik production sistemleri: 30 saniye interval, 2 consecutive down
  • Staging ortamları: 60 saniye interval, 3 consecutive down
  • Düşük öncelikli servisler: 300 saniye interval, 3 consecutive down
  • Database portları (TCP): 60 saniye interval yeterli, TCP check daha az kaynak tüketir

Çok agresif check (10-15 saniye) bazı sunucularda access log’larını gereksiz şişirebilir ve küçük bir yük oluşturabilir. Makul bir denge için 30-60 saniye çoğu senaryo için idealdir.

Sonuç

Cloudflare Health Checks, DNS katmanında görünürlük sağlayan ve düzgün yapılandırıldığında ekibinize ekstra nöbet tutturma ihtiyacını ortadan kaldıran güçlü bir araç. Standalone kullanımda sadece izleme ve alarm üretir; Load Balancer ile entegre kullanımda ise sağlıksız origin’e trafik göndermeyi tamamen durdurur.

Bu yazıda ele aldığımız konuları özetlemek gerekirse: API ile health check oluşturma ve yönetme, sunucu tarafında anlamlı health endpoint yazma, Load Balancer pool entegrasyonu, webhook ve Slack bildirimleri, ve gerçek hayatta karşılaşılan yaygın sorunların çözümleri.

Bir sonraki adım olarak bu altyapıyı Terraform veya Ansible ile kodunuza gömmeyi değerlendirin. Böylece “bu health check kim oluşturdu, neden var” sorularından kurtulur, tüm izleme altyapınızı versiyon kontrolünde tutarsınız. Cloudflare’in Terraform provider’ı hem zone-level health check’leri hem de account-level load balancer monitor’larını destekliyor; ilerleyen yazılarda bunu da detaylı işleyeceğiz.

Bir yanıt yazın

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