AWS CloudFront CDN Dağıtımı Oluşturma ve Yapılandırma
CloudFront kurulumuna başlamadan önce şunu söylemeliyim: CDN konusu çoğu sysadmin’in “gerektiğinde bakarım” diyerek ertelediği, ama bir gün production’da performans sorunu çıktığında panikte öğrenmek zorunda kaldığı konuların başında geliyor. Ben de öyle yaşadım. Bir e-ticaret sitesinin Black Friday öncesinde statik asset’leri direkt S3’ten servis ettiğini keşfettiğimde, CloudFront’u hızla yapılandırmak zorunda kalmıştım. Bu yazıda o deneyimden de yararlanarak CloudFront’u baştan sona, gerçekçi senaryolarla ele alacağım.
CloudFront Nedir ve Neden Kullanmalısınız
AWS CloudFront, Amazon’un global CDN (Content Delivery Network) hizmetidir. Dünya genelinde 400’den fazla edge location noktasına sahip olan bu sistem, içeriğinizi kullanıcıya en yakın sunucudan servis ederek gecikmeyi minimize eder.
Sadece statik dosya dağıtımı için değil, dinamik içerik, API hızlandırma, video streaming ve hatta Lambda@Edge ile edge computing için de kullanabilirsiniz. Yani CloudFront’u “sadece resim ve CSS dağıtan bir şey” olarak düşünmek büyük bir yanılgı.
Temel kullanım senaryoları şunlardır:
- S3 bucket’ı CDN arkasına almak: Statik web sitesi veya medya dosyaları
- Application Load Balancer önüne koymak: Web uygulaması hızlandırma ve DDoS koruması
- API Gateway önüne koymak: API caching ve global dağıtım
- On-premise origin: Hybrid mimarilerde kendi datacenter’ınızı origin olarak kullanmak
Ön Gereksinimler
Başlamadan önce şunların hazır olması gerekiyor:
- AWS hesabı ve yeterli IAM izinleri
- AWS CLI kurulu ve yapılandırılmış
- Bir S3 bucket (ya da başka bir origin kaynağı)
- Opsiyonel: Route53’te bir domain ve ACM sertifikası
IAM policy açısından şu izinlere ihtiyacınız var:
# IAM politikanızın içermesi gereken temel izinler
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"cloudfront:CreateDistribution",
"cloudfront:UpdateDistribution",
"cloudfront:GetDistribution",
"cloudfront:ListDistributions",
"cloudfront:CreateInvalidation",
"s3:GetBucketPolicy",
"s3:PutBucketPolicy"
],
"Resource": "*"
}
]
}
S3 Bucket Oluşturma ve Yapılandırma
Önce origin olarak kullanacağımız S3 bucket’ını hazırlayalım. Bu aşamada kritik bir nokta var: bucket’ı public yapmayın. CloudFront, Origin Access Control (OAC) mekanizması ile private bucket’a erişebilir. Bu hem daha güvenli hem de best practice.
# Bucket oluşturma
aws s3 mb s3://benim-cdn-bucket-2024 --region eu-west-1
# Örnek bir statik site dosyası yükleme
echo "<html><body><h1>Merhaba CloudFront!</h1></body></html>" > index.html
aws s3 cp index.html s3://benim-cdn-bucket-2024/
# Birkaç test dosyası daha yükleyelim
mkdir -p assets/css assets/js assets/images
echo "body { margin: 0; }" > assets/css/style.css
echo "console.log('CDN test');" > assets/js/app.js
aws s3 sync ./assets s3://benim-cdn-bucket-2024/assets/
Bucket’ın public erişimini kapatalım:
# Public access block aktifleştirme (zaten default kapalı olabilir ama doğrulayalım)
aws s3api put-public-access-block
--bucket benim-cdn-bucket-2024
--public-access-block-configuration
"BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true"
# Durumu doğrulama
aws s3api get-public-access-block --bucket benim-cdn-bucket-2024
Origin Access Control Oluşturma
OAC, CloudFront’un S3 bucket’a güvenli erişimini sağlayan modern yöntemdir. Eski OAI (Origin Access Identity) yerine artık OAC kullanılmasını öneriyorum çünkü daha esnek ve güvenli.
# OAC oluşturma
aws cloudfront create-origin-access-control
--origin-access-control-config '{
"Name": "benim-cdn-oac",
"Description": "S3 bucket icin OAC",
"SigningProtocol": "sigv4",
"SigningBehavior": "always",
"OriginAccessControlOriginType": "s3"
}'
Bu komut size bir OAC ID döndürecek. Bu ID’yi bir yere not edin, CloudFront distribution oluştururken kullanacaksınız.
CloudFront Distribution Oluşturma
Şimdi asıl iş başlıyor. CloudFront distribution’ı AWS Console üzerinden de oluşturabilirsiniz, ama CLI ile yapmak hem daha tekrarlanabilir hem de Infrastructure as Code mantığına uygun.
Önce bir JSON konfigürasyon dosyası hazırlayalım:
# distribution-config.json dosyası oluşturma
cat > distribution-config.json << 'EOF'
{
"CallerReference": "benim-dagitim-2024-001",
"Comment": "Production CDN dagitimi",
"DefaultRootObject": "index.html",
"Origins": {
"Quantity": 1,
"Items": [
{
"Id": "S3-benim-cdn-bucket",
"DomainName": "benim-cdn-bucket-2024.s3.eu-west-1.amazonaws.com",
"S3OriginConfig": {
"OriginAccessIdentity": ""
},
"OriginAccessControlId": "BURAYA_OAC_ID_YAZIN"
}
]
},
"DefaultCacheBehavior": {
"TargetOriginId": "S3-benim-cdn-bucket",
"ViewerProtocolPolicy": "redirect-to-https",
"CachePolicyId": "658327ea-f89d-4fab-a63d-7e88639e58f6",
"Compress": true,
"AllowedMethods": {
"Quantity": 2,
"Items": ["GET", "HEAD"]
}
},
"CacheBehaviors": {
"Quantity": 1,
"Items": [
{
"PathPattern": "/assets/*",
"TargetOriginId": "S3-benim-cdn-bucket",
"ViewerProtocolPolicy": "redirect-to-https",
"CachePolicyId": "658327ea-f89d-4fab-a63d-7e88639e58f6",
"Compress": true,
"AllowedMethods": {
"Quantity": 2,
"Items": ["GET", "HEAD"]
}
}
]
},
"PriceClass": "PriceClass_100",
"Enabled": true,
"HttpVersion": "http2and3"
}
EOF
# Distribution'ı oluşturma
aws cloudfront create-distribution
--distribution-config file://distribution-config.json
PriceClass_100 değeri önemli: Sadece Kuzey Amerika ve Avrupa edge location’larını kullanır. Eğer Türkiye’deki kullanıcılara odaklanıyorsanız bu yeterli çünkü Istanbul’a en yakın edge’ler bu sınıfa dahil. Global kullanım için PriceClass_All seçebilirsiniz ama maliyet artar.
CachePolicyId olarak kullandığım değer AWS’nin hazır “CachingOptimized” politikasının ID’si. TTL değerleri için bu politika çoğu statik site senaryosunda yeterli.
S3 Bucket Policy Güncelleme
Distribution oluşturulduktan sonra S3 bucket’a CloudFront’un erişmesine izin veren policy’yi eklememiz gerekiyor. Distribution ID’nizi ve hesap numaranızı kullanarak şu komutu çalıştırın:
# Distribution bilgilerini alma
DISTRIBUTION_ID=$(aws cloudfront list-distributions
--query "DistributionList.Items[?Comment=='Production CDN dagitimi'].Id"
--output text)
ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
echo "Distribution ID: $DISTRIBUTION_ID"
echo "Account ID: $ACCOUNT_ID"
# Bucket policy dosyası oluşturma
cat > bucket-policy.json << EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowCloudFrontAccess",
"Effect": "Allow",
"Principal": {
"Service": "cloudfront.amazonaws.com"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::benim-cdn-bucket-2024/*",
"Condition": {
"StringEquals": {
"AWS:SourceArn": "arn:aws:cloudfront::${ACCOUNT_ID}:distribution/${DISTRIBUTION_ID}"
}
}
}
]
}
EOF
# Policy'yi uygulama
aws s3api put-bucket-policy
--bucket benim-cdn-bucket-2024
--policy file://bucket-policy.json
Distribution’ın Hazır Olmasını Bekleme
CloudFront distribution’ı oluşturduktan sonra “Deploying” durumuna geçer. Bu süreç genellikle 5-15 dakika sürer. Script’lerinizde beklemeniz gerekiyorsa şu yöntemi kullanabilirsiniz:
# Distribution'ın "Deployed" durumuna geçmesini bekleme
echo "Distribution deploy ediliyor, bekleniyor..."
aws cloudfront wait distribution-deployed
--id $DISTRIBUTION_ID
echo "Distribution hazir!"
# Domain adını alma
CLOUDFRONT_DOMAIN=$(aws cloudfront get-distribution
--id $DISTRIBUTION_ID
--query "Distribution.DomainName"
--output text)
echo "CloudFront URL: https://${CLOUDFRONT_DOMAIN}"
# Test isteği gönderme
curl -I "https://${CLOUDFRONT_DOMAIN}/index.html"
Curl çıktısında X-Cache: Hit from cloudfront veya X-Cache: Miss from cloudfront header’larını göreceksiniz. İlk istekte Miss, sonrakilerde Hit almanız beklenen davranış.
Özel Domain ve SSL Sertifikası Ekleme
Production ortamda kendi domain adınızı kullanmak isteyeceksiniz. ACM (AWS Certificate Manager) ile ücretsiz SSL sertifikası alabilirsiniz. Kritik not: CloudFront için sertifikayı mutlaka us-east-1 bölgesinde oluşturmalısınız, hangi region’da çalışıyor olursanız olun.
# us-east-1'de sertifika oluşturma
aws acm request-certificate
--domain-name cdn.sirketim.com
--subject-alternative-names "*.sirketim.com"
--validation-method DNS
--region us-east-1
# Sertifika ARN'ini alma
CERT_ARN=$(aws acm list-certificates
--region us-east-1
--query "CertificateSummaryList[?DomainName=='cdn.sirketim.com'].CertificateArn"
--output text)
echo "Sertifika ARN: $CERT_ARN"
DNS doğrulaması için ACM size bir CNAME kaydı verecek. Bu kaydı Route53 veya kullandığınız DNS provider’ınıza eklemeniz gerekiyor. Route53 kullanıyorsanız bu işlemi otomatize etmek mümkün.
Sertifika doğrulandıktan sonra distribution’ı güncelleyin:
# Mevcut distribution config'i alma (ETag gerekli)
aws cloudfront get-distribution-config
--id $DISTRIBUTION_ID
--query "DistributionConfig" > current-config.json
ETAG=$(aws cloudfront get-distribution-config
--id $DISTRIBUTION_ID
--query "ETag"
--output text)
# ViewerCertificate ve Aliases bloklarını güncelleyerek
# update-distribution komutunu çalıştırın
aws cloudfront update-distribution
--id $DISTRIBUTION_ID
--if-match $ETAG
--distribution-config '{
...mevcut config buraya...,
"Aliases": {
"Quantity": 1,
"Items": ["cdn.sirketim.com"]
},
"ViewerCertificate": {
"ACMCertificateArn": "'$CERT_ARN'",
"SSLSupportMethod": "sni-only",
"MinimumProtocolVersion": "TLSv1.2_2021"
}
}'
Cache Invalidation: İçerik Güncellemesi
En sık yapılan işlemlerden biri cache invalidation. Dosyalarınızı güncelledikten sonra CloudFront’un eski cache’i temizlemesini sağlamak için invalidation oluşturursunuz.
# Belirli bir dosya için invalidation
aws cloudfront create-invalidation
--distribution-id $DISTRIBUTION_ID
--paths "/index.html"
# Tüm assets klasörü için invalidation
aws cloudfront create-invalidation
--distribution-id $DISTRIBUTION_ID
--paths "/assets/*"
# Tüm içerik için invalidation (dikkatli kullanın, maliyetli olabilir)
aws cloudfront create-invalidation
--distribution-id $DISTRIBUTION_ID
--paths "/*"
# Invalidation durumunu kontrol etme
INVALIDATION_ID=$(aws cloudfront create-invalidation
--distribution-id $DISTRIBUTION_ID
--paths "/*"
--query "Invalidation.Id"
--output text)
aws cloudfront get-invalidation
--distribution-id $DISTRIBUTION_ID
--id $INVALIDATION_ID
--query "Invalidation.Status"
Önemli maliyet notu: AWS her ay ilk 1000 invalidation path’i ücretsiz sunuyor. Sonrası için ücret alıyor. Bu nedenle her deployment’ta “/*” yapmak yerine sadece değişen dosyaları hedef alan bir strateji geliştirin. Bunun yerine dosya adlarına versiyon hash’i eklemek (örneğin app.v2a3b4.js) çok daha iyi bir yaklaşım.
Monitoring ve Logging
CloudFront’u kurduktan sonra ne olduğunu görmek isteyeceksiniz. Access logging’i aktifleştirmek için ayrı bir S3 bucket’a ihtiyacınız var:
# Log bucket oluşturma
aws s3 mb s3://benim-cdn-logs-2024 --region eu-west-1
# CloudFront'un log bucket'a yazabilmesi için ACL gerekiyor
aws s3api put-bucket-acl
--bucket benim-cdn-logs-2024
--acl log-delivery-write
# Distribution'da logging'i aktifleştirme
# update-distribution ile mevcut config'e şu bloğu ekleyin:
# "Logging": {
# "Enabled": true,
# "IncludeCookies": false,
# "Bucket": "benim-cdn-logs-2024.s3.amazonaws.com",
# "Prefix": "cloudfront-logs/"
# }
CloudWatch’ta CloudFront metriklerini takip etmek için:
# Cache hit rate metriğini çekme
aws cloudwatch get-metric-statistics
--namespace AWS/CloudFront
--metric-name CacheHitRate
--dimensions Name=DistributionId,Value=$DISTRIBUTION_ID
Name=Region,Value=Global
--start-time $(date -u -d '1 hour ago' +%Y-%m-%dT%H:%M:%SZ)
--end-time $(date -u +%Y-%m-%dT%H:%M:%SZ)
--period 300
--statistics Average
--query "Datapoints[*].[Timestamp,Average]"
--output table
# 4xx hata oranını kontrol etme
aws cloudwatch get-metric-statistics
--namespace AWS/CloudFront
--metric-name 4xxErrorRate
--dimensions Name=DistributionId,Value=$DISTRIBUTION_ID
Name=Region,Value=Global
--start-time $(date -u -d '24 hours ago' +%Y-%m-%dT%H:%M:%SZ)
--end-time $(date -u +%Y-%m-%dT%H:%M:%SZ)
--period 3600
--statistics Average
Sağlıklı bir CloudFront distribution’ında cache hit rate %80’in üzerinde olmalı. Altındaysa cache policy’nizi veya TTL değerlerinizi gözden geçirin.
Gerçek Dünya Senaryosu: CI/CD Pipeline ile Entegrasyon
Bir frontend projesinde her deployment sonrasında otomatik invalidation yapan bir script:
#!/bin/bash
# deploy-and-invalidate.sh
set -euo pipefail
BUCKET_NAME="benim-cdn-bucket-2024"
DISTRIBUTION_ID="E1234ABCDEF5678"
BUILD_DIR="./dist"
echo "==> Build dizini S3'e yukleniyor..."
aws s3 sync "$BUILD_DIR" "s3://${BUCKET_NAME}/"
--delete
--cache-control "max-age=31536000,public,immutable"
--exclude "index.html"
--exclude "*.json"
# HTML ve JSON dosyaları icin farkli cache politikasi
aws s3 sync "$BUILD_DIR" "s3://${BUCKET_NAME}/"
--exclude "*"
--include "*.html"
--include "*.json"
--cache-control "no-cache,no-store,must-revalidate"
echo "==> CloudFront cache temizleniyor..."
INVALIDATION_ID=$(aws cloudfront create-invalidation
--distribution-id "$DISTRIBUTION_ID"
--paths "/index.html" "/manifest.json"
--query "Invalidation.Id"
--output text)
echo "==> Invalidation tamamlanmasi bekleniyor (ID: $INVALIDATION_ID)..."
aws cloudfront wait invalidation-completed
--distribution-id "$DISTRIBUTION_ID"
--id "$INVALIDATION_ID"
echo "==> Deployment basarili!"
DOMAIN=$(aws cloudfront get-distribution
--id "$DISTRIBUTION_ID"
--query "Distribution.DomainName"
--output text)
echo "Site URL: https://${DOMAIN}"
Bu scriptte dikkat edin: CSS, JS ve resim gibi versiyonlanmış dosyalar için max-age=31536000 (1 yıl) kullanıyorum ve sadece index.html ile manifest.json’u no-cache yapıyorum. Bu strateji hem performansı maximize eder hem de deployment sonrası kullanıcıların hep güncel sürümü görmesini sağlar.
Yaygın Sorunlar ve Çözümleri
403 Access Denied hatası: Bucket policy’nin doğru ayarlanmadığı anlamına gelir. OAC ID’nin distribution ile eşleştiğini ve bucket policy’deki ARN’nin doğru olduğunu kontrol edin.
Cache miss oranı çok yüksek: Query string veya cookie’lerin cache key’e dahil edilip edilmediğini kontrol edin. Gereksiz yere query string cache’e dahilse her farklı URL ayrı bir cache girdisi oluşturur.
SSL sertifika hatası: Sertifikanın us-east-1’de oluşturulduğundan ve “Issued” durumunda olduğundan emin olun.
Origin timeout: Default timeout 30 saniye. Ağır backend işlemleriniz varsa origin response timeout değerini artırın:
# Origin request timeout'u artırma
aws cloudfront update-distribution
--id $DISTRIBUTION_ID
--if-match $ETAG
--distribution-config '{
...
"Origins": {
"Items": [{
...
"CustomOriginConfig": {
"OriginResponseTimeout": 60,
"OriginReadTimeout": 60
}
}]
}
}'
Maliyet Optimizasyonu
CloudFront’ta maliyeti etkileyen başlıca faktörler:
- Veri transferi: Edge location’lardan kullanıcıya giden veri miktarı
- HTTP request sayısı: Her GET/HEAD isteği için küçük bir ücret
- Invalidation: 1000 path’ten sonra ücretli
- PriceClass: Kullandığınız edge location bölgesi
Maliyet düşürme için şunları yapabilirsiniz:
- TTL değerlerini mümkün olduğunca yüksek tutun, dosya adlarında versiyon hash’i kullanın
- Sadece ihtiyacınız olan bölgeler için PriceClass seçin
- Compression’ı aktif tutun (Compress: true), bu hem hızı artırır hem veri transferini azaltır
- S3 ile CloudFront aynı region’da olduğunda origin’den CloudFront’a veri transferi ücretsiz
Sonuç
CloudFront’u doğru konfigüre etmek başta karmaşık görünebilir, ama temel prensipleri kavradığınızda gerisi kolaylaşıyor. OAC ile güvenli S3 erişimi, doğru cache policy seçimi, versiyonlanmış asset stratejisi ve otomatik invalidation pipeline’ı bu konunun dört temel direği.
Production’a geçmeden önce şu checklist’i gözden geçirin:
- Bucket public erişimi kapalı, OAC kullanılıyor
- HTTPS zorunlu kılınmış (redirect-to-https)
- Compression aktif
- Uygun PriceClass seçilmiş
- Access logging aktif
- Cache hit rate monitöring’i kurulu
- Deployment scripti otomatik invalidation yapıyor
Bu yapıyı bir kez doğru kurduğunuzda hem performans kazanımı hem maliyet optimizasyonu açısından ciddi fark görüyorsunuz. Özellikle Türkiye’den Avrupa’ya yönelik trafikte CloudFront’un Frankfurt ve Londra edge’lerini kullanması gecikmeyi dramatik biçimde düşürüyor. Deneyin, CloudWatch metriklerinize bakın ve cache hit rate’inizi %80’in üzerine çekmeyi hedef alın.
