AWS Route 53 Health Check Yapılandırması

Prodüksiyonda bir servis çöktüğünde bunu ilk sen değil de müşteriler fark ediyorsa, ciddi bir monitoring sorunun var demektir. AWS Route 53 Health Check tam olarak bu senaryoyu önlemek için var. Sadece DNS kaydı tutmakla kalmayıp, arkasındaki servislerin sağlıklı çalışıp çalışmadığını sürekli kontrol eden ve bir sorun tespit ettiğinde trafiği otomatik olarak yönlendiren akıllı bir mekanizma. Bu yazıda Route 53 Health Check’i sıfırdan yapılandıracağız, gerçek dünya senaryolarına bakacağız ve production ortamında işinize yarayacak örnekler paylaşacağım.

Route 53 Health Check Nedir, Ne İşe Yarar?

Route 53 Health Check, AWS’nin dünya genelinde dağıtılmış checker noktalarından hedefe düzenli HTTP, HTTPS veya TCP istekleri göndererek endpoint’in ayakta olup olmadığını kontrol eden bir servistir. Eğer belirlenen eşik değerlerin altına düşerse, yani yeterli sayıda checker “bu servis çalışmıyor” derse, ilgili DNS kaydı devre dışı bırakılır ve trafik sağlıklı olan kaynaklara yönlendirilir.

Üç temel health check türü var:

  • Endpoint Health Check: Doğrudan bir IP veya domain’e istek atar. En yaygın kullanılan tür.
  • Calculated Health Check: Birden fazla health check’i mantıksal operatörlerle birleştirir. Karmaşık sağlık senaryoları için idealdir.
  • CloudWatch Alarm Health Check: Bir CloudWatch alarm’ının durumuna göre sağlık kararı verir.

İlk Health Check’i AWS CLI ile Oluşturmak

Konsol üzerinden health check oluşturmak kolay ama tekrarlanabilir değil. Production ortamında her şeyi kod olarak yönetmek gerekiyor. Hadi AWS CLI ile başlayalım.

Öncelikle basit bir HTTP endpoint health check:

aws route53 create-health-check 
  --caller-reference "my-app-hc-$(date +%s)" 
  --health-check-config '{
    "Type": "HTTP",
    "ResourcePath": "/health",
    "FullyQualifiedDomainName": "app.example.com",
    "Port": 80,
    "RequestInterval": 30,
    "FailureThreshold": 3,
    "EnableSNI": false
  }'

Bu komuttaki parametreleri açıklayalım:

  • caller-reference: Her health check için benzersiz olması gereken bir referans değeri. Timestamp eklemek iyi bir pratik.
  • Type: HTTP, HTTPS veya TCP olabilir.
  • ResourcePath: Health check isteğinin gideceği path. /health, /ping, /status gibi.
  • RequestInterval: Kontrol sıklığı. 10 veya 30 saniye olabilir. 10 saniye daha hızlı tespit eder ama daha pahalıdır.
  • FailureThreshold: Kaç ardışık başarısız kontrolden sonra “unhealthy” sayılsın. 1 ile 10 arası.

HTTPS endpoint için:

aws route53 create-health-check 
  --caller-reference "my-app-https-hc-$(date +%s)" 
  --health-check-config '{
    "Type": "HTTPS",
    "ResourcePath": "/health",
    "FullyQualifiedDomainName": "app.example.com",
    "Port": 443,
    "RequestInterval": 30,
    "FailureThreshold": 3,
    "EnableSNI": true,
    "MeasureLatency": true
  }'

MeasureLatency parametresini true yaparsanız, Route 53 her checker bölgesinden latency ölçümü alır ve CloudWatch’a gönderir. Sonradan kapatılamıyor, health check oluştururken karar verin.

Health Check Durumunu Sorgulamak

Health check oluşturulduktan sonra ID’sini alıp durumunu kontrol edelim:

# Tüm health check'leri listele
aws route53 list-health-checks --query 'HealthChecks[*].[Id,HealthCheckConfig.FullyQualifiedDomainName,HealthCheckConfig.Type]' --output table

# Belirli bir health check'in durumunu al
HEALTH_CHECK_ID="12345abc-1234-1234-1234-123456789abc"

aws route53 get-health-check-status 
  --health-check-id $HEALTH_CHECK_ID 
  --query 'HealthCheckObservations[*].[IPAddress,StatusReport.Status,StatusReport.CheckedTime]' 
  --output table

Bu çıktıda AWS’nin farklı bölgelerden (us-east-1, eu-west-1, ap-southeast-1 gibi) yaptığı kontrollerin durumunu görürsünüz. Eğer bazı bölgelerden “Success” bazılarından “Failure” geliyorsa, muhtemelen bir network veya regional sorun var demektir.

Failover DNS Kaydı Yapılandırması

Health check tek başına işe yaramaz. Asıl güç, failover DNS kayıtlarıyla birleşince ortaya çıkar. Klasik senaryo: Primary sunucu ve Standby sunucu.

Önce hosted zone ID’mizi alalım:

HOSTED_ZONE_ID=$(aws route53 list-hosted-zones 
  --query "HostedZones[?Name=='example.com.'].Id" 
  --output text | cut -d'/' -f3)

echo "Hosted Zone ID: $HOSTED_ZONE_ID"

Şimdi primary ve secondary kayıtları oluşturalım:

# Primary kayıt - sağlıklı olduğunda trafik buraya gider
aws route53 change-resource-record-sets 
  --hosted-zone-id $HOSTED_ZONE_ID 
  --change-batch '{
    "Changes": [
      {
        "Action": "CREATE",
        "ResourceRecordSet": {
          "Name": "app.example.com",
          "Type": "A",
          "SetIdentifier": "primary",
          "Failover": "PRIMARY",
          "TTL": 60,
          "ResourceRecords": [{"Value": "1.2.3.4"}],
          "HealthCheckId": "12345abc-1234-1234-1234-123456789abc"
        }
      }
    ]
  }'

# Secondary kayıt - primary down olduğunda trafik buraya gelir
aws route53 change-resource-record-sets 
  --hosted-zone-id $HOSTED_ZONE_ID 
  --change-batch '{
    "Changes": [
      {
        "Action": "CREATE",
        "ResourceRecordSet": {
          "Name": "app.example.com",
          "Type": "A",
          "SetIdentifier": "secondary",
          "Failover": "SECONDARY",
          "TTL": 60,
          "ResourceRecords": [{"Value": "5.6.7.8"}]
        }
      }
    ]
  }'

Secondary kayıda health check koymak zorunda değilsiniz ama koyabilirsiniz. Eğer koyarsanız ve secondary da unhealthy olursa, Route 53 secondary kaydı da döndürmeye devam eder (en kötü ihtimalle bir şeye döndürmek gerekiyor).

TTL değerini 60 saniye yapmanız önemli. Failover sırasında eski IP’nin cache’lenmesi süresini kısa tutmak için düşük TTL kritiktir. Tabii bu daha fazla DNS sorgusu demek, maliyeti göz önünde bulundurun.

Latency-Based Routing ile Health Check Kombinasyonu

Birden fazla bölgede sunucunuz varsa, latency-based routing ile health check’i birleştirmek çok güçlü bir yapı oluşturur. Kullanıcı hem en yakın sunucuya gider, hem de o sunucu çökerse otomatik olarak başka bölgeye yönlenir.

# US East sunucu için
aws route53 change-resource-record-sets 
  --hosted-zone-id $HOSTED_ZONE_ID 
  --change-batch '{
    "Changes": [
      {
        "Action": "CREATE",
        "ResourceRecordSet": {
          "Name": "api.example.com",
          "Type": "A",
          "SetIdentifier": "us-east-1",
          "Region": "us-east-1",
          "TTL": 60,
          "ResourceRecords": [{"Value": "1.2.3.4"}],
          "HealthCheckId": "hc-us-east-id-buraya"
        }
      }
    ]
  }'

# EU West sunucu için
aws route53 change-resource-record-sets 
  --hosted-zone-id $HOSTED_ZONE_ID 
  --change-batch '{
    "Changes": [
      {
        "Action": "CREATE",
        "ResourceRecordSet": {
          "Name": "api.example.com",
          "Type": "A",
          "SetIdentifier": "eu-west-1",
          "Region": "eu-west-1",
          "TTL": 60,
          "ResourceRecords": [{"Value": "5.6.7.8"}],
          "HealthCheckId": "hc-eu-west-id-buraya"
        }
      }
    ]
  }'

Calculated Health Check ile Karmaşık Senaryolar

Gerçek dünyada tek bir endpoint’in ayakta olması yetmeyebilir. Uygulamanın database’e de ulaşabilmesi, cache servisinin çalışması, mesaj kuyruğunun sağlıklı olması gerekebilir. Bunların hepsini ayrı ayrı check edip, sonra bir “master health check” oluşturabilirsiniz.

Diyelim ki uygulamanızın üç bileşeni var:

  • Web tier health check ID: hc-web-id
  • Database connectivity check ID: hc-db-id
  • Cache health check ID: hc-cache-id
# Calculated health check - en az 2/3 bileşen sağlıklıysa "healthy" sayılsın
aws route53 create-health-check 
  --caller-reference "calculated-hc-$(date +%s)" 
  --health-check-config '{
    "Type": "CALCULATED",
    "ChildHealthChecks": [
      "hc-web-id",
      "hc-db-id",
      "hc-cache-id"
    ],
    "HealthThreshold": 2,
    "Inverted": false
  }'

HealthThreshold: Kaç alt health check’in “healthy” olması gerektiği. Burada 3 üzerinden 2 yaptık, yani cache çöktü ama web ve db ayaktaysa sistem “healthy” sayılır.

Inverted: true yaparsanız mantığı tersine çevirirsiniz. Maintenance sayfası gösterirken işe yarar, normal durumda sitenin “unhealthy” görünmesini isteyebilirsiniz.

CloudWatch Alarm ile Entegrasyon

Bazen HTTP endpoint’ine erişmek yetmez. CPU %90’da, bellek kritik seviyede ama uygulama hala “200 OK” dönüyor olabilir. Bu durumda CloudWatch alarm’larını health check’e bağlamak mantıklı.

Önce bir CloudWatch alarm oluşturalım:

# EC2 CPU kullanımı için alarm
aws cloudwatch put-metric-alarm 
  --alarm-name "app-server-high-cpu" 
  --metric-name CPUUtilization 
  --namespace AWS/EC2 
  --statistic Average 
  --period 300 
  --threshold 85 
  --comparison-operator GreaterThanThreshold 
  --dimensions Name=InstanceId,Value=i-1234567890abcdef0 
  --evaluation-periods 2 
  --alarm-actions "arn:aws:sns:us-east-1:123456789:alerts-topic" 
  --region us-east-1

Şimdi bu alarm’a bağlı health check:

aws route53 create-health-check 
  --caller-reference "cloudwatch-hc-$(date +%s)" 
  --health-check-config '{
    "Type": "CLOUDWATCH_METRIC",
    "AlarmIdentifier": {
      "Region": "us-east-1",
      "Name": "app-server-high-cpu"
    },
    "InsufficientDataHealthStatus": "Healthy"
  }'

InsufficientDataHealthStatus değerleri:

  • Healthy: Veri yoksa sağlıklı say (default tercih)
  • Unhealthy: Veri yoksa sağlıksız say (daha güvenli ama false alarm riski)
  • LastKnownStatus: Son bilinen durumu koru

SNS ile Health Check Bildirimleri

Health check’in durumu değiştiğinde e-posta veya Slack bildirimi almak istiyorsanız, CloudWatch Events (EventBridge) ile SNS’i birleştirin:

# SNS topic oluştur
aws sns create-topic --name route53-health-alerts

# E-posta subscription ekle
aws sns subscribe 
  --topic-arn "arn:aws:sns:us-east-1:123456789:route53-health-alerts" 
  --protocol email 
  --notification-endpoint "[email protected]"

# Health check için CloudWatch alarm oluştur
aws cloudwatch put-metric-alarm 
  --alarm-name "route53-hc-status-alarm" 
  --namespace AWS/Route53 
  --metric-name HealthCheckStatus 
  --dimensions Name=HealthCheckId,Value="12345abc-1234-1234-1234-123456789abc" 
  --statistic Minimum 
  --period 60 
  --threshold 1 
  --comparison-operator LessThanThreshold 
  --evaluation-periods 1 
  --alarm-actions "arn:aws:sns:us-east-1:123456789:route53-health-alerts" 
  --ok-actions "arn:aws:sns:us-east-1:123456789:route53-health-alerts" 
  --region us-east-1

Route 53 health check metrikleri us-east-1‘de bulunur. Başka bir region’da alarm oluşturmaya çalışırsanız metriği göremezsiniz, dikkat edin.

Terraform ile Health Check Yönetimi

Production ortamında tek tek CLI komutu çalıştırmak sürdürülebilir değil. Terraform ile tüm yapıyı code olarak yönetin:

# main.tf içeriği - önce çalışma dizini oluştur
mkdir -p ~/terraform/route53-health-checks
cd ~/terraform/route53-health-checks

cat > main.tf << 'EOF'
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

provider "aws" {
  region = "us-east-1"
}

# Primary endpoint health check
resource "aws_route53_health_check" "primary" {
  fqdn              = "app.example.com"
  port              = 443
  type              = "HTTPS"
  resource_path     = "/health"
  failure_threshold = "3"
  request_interval  = "30"
  enable_sni        = true
  measure_latency   = true

  tags = {
    Name        = "app-primary-health-check"
    Environment = "production"
    Team        = "platform"
  }
}

# Failover DNS kaydı - Primary
resource "aws_route53_record" "primary" {
  zone_id = var.hosted_zone_id
  name    = "app.example.com"
  type    = "A"

  failover_routing_policy {
    type = "PRIMARY"
  }

  set_identifier  = "primary"
  health_check_id = aws_route53_health_check.primary.id
  ttl             = 60
  records         = [var.primary_ip]
}

# Failover DNS kaydı - Secondary
resource "aws_route53_record" "secondary" {
  zone_id = var.hosted_zone_id
  name    = "app.example.com"
  type    = "A"

  failover_routing_policy {
    type = "SECONDARY"
  }

  set_identifier = "secondary"
  ttl            = 60
  records        = [var.secondary_ip]
}

# CloudWatch alarm for health check
resource "aws_cloudwatch_metric_alarm" "health_check_alarm" {
  alarm_name          = "route53-primary-health-check"
  comparison_operator = "LessThanThreshold"
  evaluation_periods  = 1
  metric_name         = "HealthCheckStatus"
  namespace           = "AWS/Route53"
  period              = 60
  statistic           = "Minimum"
  threshold           = 1
  alarm_description   = "Route53 primary health check failed"

  dimensions = {
    HealthCheckId = aws_route53_health_check.primary.id
  }

  alarm_actions = [var.sns_topic_arn]
  ok_actions    = [var.sns_topic_arn]
}
EOF

# Terraform'ı başlat ve planı gör
terraform init
terraform plan -var="hosted_zone_id=ZXXXXX" 
               -var="primary_ip=1.2.3.4" 
               -var="secondary_ip=5.6.7.8" 
               -var="sns_topic_arn=arn:aws:sns:us-east-1:123456789:alerts"

Private Endpoint Health Check’leri

VPC içindeki private servislerin health check’ini doğrudan yapamassınız, Route 53 checker’ları public internet üzerinden çalışır. Bunun için iki yöntem var.

İlk yöntem CloudWatch alarm tabanlı health check kullanmak. EC2 instance’ınıza CloudWatch agent kurun, custom metrikler gönderin ve bu metriklere alarm oluşturun.

İkinci yöntem ise bir Lambda fonksiyonu üzerinden internal check yapmak:

# Lambda fonksiyonu için basit bir health check wrapper
# Bu fonksiyon VPC içinde çalışır ve internal servisi kontrol eder
cat > health_check_lambda.py << 'EOF'
import boto3
import urllib.request
import json

def lambda_handler(event, context):
    # Internal servisi kontrol et
    try:
        response = urllib.request.urlopen(
            'http://internal-app.private:8080/health',
            timeout=5
        )
        status = response.status
        healthy = status == 200
    except Exception as e:
        healthy = False
        status = 0

    # CloudWatch'a custom metrik gönder
    cloudwatch = boto3.client('cloudwatch')
    cloudwatch.put_metric_data(
        Namespace='CustomHealthChecks',
        MetricData=[
            {
                'MetricName': 'InternalServiceHealth',
                'Value': 1 if healthy else 0,
                'Unit': 'Count',
                'Dimensions': [
                    {'Name': 'ServiceName', 'Value': 'internal-app'}
                ]
            }
        ]
    )

    return {
        'statusCode': 200,
        'body': json.dumps({'healthy': healthy, 'status': status})
    }
EOF

echo "Lambda fonksiyonu oluşturuldu"

Bu Lambda’yı 1 dakikada bir EventBridge ile tetikleyin, CloudWatch’taki InternalServiceHealth metriği 0’a düşünce alarm devreye girsin ve bunu Route 53 health check’e bağlayın.

Health Check Troubleshooting

Prodüksiyonda en sık karşılaşılan sorunları ve çözümlerini paylaşayım.

Senaryo 1: Health check intermittent failure veriyor

Route 53 checker’ların IP aralıklarını firewall’da izin listesine almayı unutmuş olabilirsiniz:

# Route 53 health checker IP aralıklarını çek
curl -s https://ip-ranges.amazonaws.com/ip-ranges.json | 
  python3 -c "
import json, sys
data = json.load(sys.stdin)
for prefix in data['prefixes']:
    if prefix.get('service') == 'ROUTE53_HEALTHCHECKS':
        print(prefix['ip_prefix'])
"

Bu IP’lerin tümünün 80 ve 443 portlarına erişiminin açık olması gerekiyor.

Senaryo 2: Health check endpoint yanıt veriyor ama unhealthy

Response body içinde belirli bir string aramak için:

# String matching ile health check güncelle
aws route53 update-health-check 
  --health-check-id "12345abc-1234-1234-1234-123456789abc" 
  --search-string ""status":"ok""

Health check endpoint’iniz {"status":"ok"} dönüyorsa bu string’i arayın. Endpoint 200 dönse bile bu string yoksa unhealthy sayılır.

Senaryo 3: TTL problemi – failover çok yavaş oluyor

# Mevcut DNS kayıtlarının TTL değerini kontrol et
dig app.example.com +noall +answer

# TTL'i düşür (değişiklik apply olana kadar eski TTL kadar sürer)
aws route53 change-resource-record-sets 
  --hosted-zone-id $HOSTED_ZONE_ID 
  --change-batch '{
    "Changes": [{
      "Action": "UPSERT",
      "ResourceRecordSet": {
        "Name": "app.example.com",
        "Type": "A",
        "SetIdentifier": "primary",
        "Failover": "PRIMARY",
        "TTL": 30,
        "ResourceRecords": [{"Value": "1.2.3.4"}],
        "HealthCheckId": "12345abc-1234-1234-1234-123456789abc"
      }
    }]
  }'

Maliyet Optimizasyonu

Health check maliyetleri küçük görünse de onlarca servis varsa birikir. Bazı pratik öneriler:

  • Request interval 30 saniye kullanın: 10 saniye yaklaşık 3 kat daha pahalı. Çoğu senaryo için 30 saniye yeterli.
  • String matching dikkatli kullanın: String matching ek maliyet oluşturur. Gerçekten gerekmedikçe sadece HTTP status code yeterli.
  • MeasureLatency’yi sadece gerektiğinde açın: Sonradan kapatamıyorsunuz, health check silip yeniden oluşturmanız gerekiyor.
  • Calculated health check kullanın: 10 endpoint yerine 1 calculated check oluşturmak hem daha temiz hem daha ucuz olabilir.

Aylık maliyeti tahmin etmek için:

# Aktif health check sayısını gör
aws route53 list-health-checks 
  --query 'length(HealthChecks)' 
  --output text

2024 itibarıyla standart bir HTTP/HTTPS health check aylık yaklaşık 0.50 USD, optional feature’larla birlikte 1 USD’a çıkabilir. 50 servisiniz varsa aylık 25-50 USD sadece health check için gidiyor.

Sonuç

Route 53 Health Check, doğru yapılandırıldığında gerçek bir sigorta görevi görür. Tek başına yeterli olmasa da failover routing, CloudWatch alarmları ve SNS bildirimleriyle birleşince production ortamınızda ciddi bir dayanıklılık katmanı oluşturursunuz.

En önemli çıkarımlar:

  • Health check endpoint’leriniz gerçekten anlamlı kontroller yapmalı. Sadece “200 OK” dönen bir /ping yerine database bağlantısını, kritik servisleri de kontrol eden bir /health endpoint yazın.
  • TTL değerlerini düşük tutun, özellikle failover kayıtlarında 60 saniye veya altı kullanın.
  • Route 53 health checker IP’lerini mutlaka firewall’unuza ekleyin, yoksa false alarm seli yaşarsınız.
  • Her şeyi Terraform ile yönetin. Konsol üzerinden elle yapılan değişiklikler production’da kara deliğe dönüşür.
  • Billing alarm kurmayı unutmayın. Health check sayısı zamanla artar ve farkında olmadan gereksiz maliyet oluşturabilirsiniz.

Herhangi bir konuda soru varsa yorumlarda buluşalım.

Bir yanıt yazın

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