AWS Lambda Soğuk Başlatma Sorunları ve Çözümleri

Serverless mimariye geçiş yapan ekiplerin ilk karşılaştığı ve uzun süre baş ağrısına yol açan bir problemdir soğuk başlatma. Production ortamında Lambda fonksiyonunuz mükemmel çalışıyor, testleriniz geçiyor, her şey yolunda görünüyor. Sonra bir kullanıcı sabahın 3’ünde uygulamanıza giriyor ve “sayfa açılmıyor” diye şikayet ediyor. Logları açıyorsunuz, 8 saniyelik bir gecikme görüyorsunuz. İşte bu, soğuk başlatma problemidir ve ciddiye alınması gerekiyor.

Soğuk Başlatma Nedir ve Neden Olur?

AWS Lambda, fonksiyonlarınızı çalıştırmak için arka planda container’lar kullanır. Bir Lambda fonksiyonu ilk kez çağrıldığında ya da uzun süre çağrılmadıktan sonra tekrar tetiklendiğinde, AWS’nin yeni bir execution environment hazırlaması gerekir. Bu süreç şu adımlardan oluşur:

  • Container imajının indirilmesi ve başlatılması
  • Runtime ortamının (Node.js, Python, Java vb.) yüklenmesi
  • Deployment paketinizin çözülmesi ve yüklenmesi
  • Initialization kodunuzun çalıştırılması (handler dışındaki kod)

Tüm bu adımların toplam süresi soğuk başlatma süresi olarak adlandırılır. Sıcak başlatmada (warm start) ise mevcut container yeniden kullanılır ve sadece handler fonksiyonu çalıştırılır.

Soğuk başlatma ne zaman tetiklenir diye soracak olursanız:

  • İlk deployment’tan sonra fonksiyon ilk kez çağrıldığında
  • Uzun süreli inaktivite sonrasında (genellikle 15-45 dakika)
  • Eş zamanlı istek sayısı arttığında ve yeni container’lar oluşturulması gerektiğinde
  • Fonksiyon konfigürasyonu veya kodu güncellendiğinde

Runtime’a Göre Soğuk Başlatma Süreleri

Hangi dili kullandığınız soğuk başlatma süresini doğrudan etkiler. Genel olarak şunları söyleyebiliriz:

  • Java ve .NET: En uzun soğuk başlatma sürelerine sahip. JVM başlatması ciddi zaman alır, 2-10 saniye arasında değişebilir
  • Python ve Node.js: Orta düzey, genellikle 100-500ms
  • Go ve Rust: En hızlı başlatma süreleri, genellikle 50ms’nin altında
  • Ruby: Python’a yakın performans gösterir

Ancak bu değerler teorik. Gerçek hayatta bağımlılık sayınız, memory allocation’ınız ve initialization kodunuzun karmaşıklığı bu süreleri katlayabilir.

Gerçek Dünya Senaryosu: E-ticaret API’si

Bir e-ticaret platformu için Node.js tabanlı bir ürün arama API’si geliştirdiğinizi düşünün. Gündüz saatlerinde trafik yüksek, gece saatlerinde düşük. Sabah 6’da bir kullanıcı siteye girdiğinde soğuk başlatma tetikleniyor ve ürün araması 5-6 saniye sürüyor. Bu kullanıcı büyük ihtimalle sayfayı kapatıp başka siteye gidiyor.

İlk olarak problemi ölçmemiz gerekiyor:

# CloudWatch Logs Insights ile soğuk başlatma metriklerini analiz et
aws logs start-query 
  --log-group-name "/aws/lambda/product-search-api" 
  --start-time $(date -d '24 hours ago' +%s) 
  --end-time $(date +%s) 
  --query-string 'filter @type = "REPORT" | stats count(*) as totalInvocations, count(@initDuration) as coldStarts, avg(@initDuration) as avgColdStartMs, max(@initDuration) as maxColdStartMs by bin(1h)'

Bu sorgu bize saatlik bazda soğuk başlatma istatistiklerini verir. Kaç çağrının soğuk başlatma yaşadığını ve ortalama süreyi görebilirsiniz.

# Sonucu kontrol et
aws logs get-query-results --query-id <query-id>

# Alternatif olarak Lambda PowerTools kullanarak özel metrik gönder
aws cloudwatch get-metric-statistics 
  --namespace "AWS/Lambda" 
  --metric-name "InitDuration" 
  --dimensions Name=FunctionName,Value=product-search-api 
  --start-time $(date -d '24 hours ago' -u +%Y-%m-%dT%H:%M:%SZ) 
  --end-time $(date -u +%Y-%m-%dT%H:%M:%SZ) 
  --period 3600 
  --statistics Average Maximum

Çözüm 1: Provisioned Concurrency

AWS’nin resmi çözümü Provisioned Concurrency. Bu özellik, belirttiğiniz sayıda execution environment’ı her zaman hazır ve sıcak tutar. Soğuk başlatma pratikte sıfıra iner.

# Provisioned Concurrency ayarla
aws lambda put-provisioned-concurrency-config 
  --function-name product-search-api 
  --qualifier production 
  --provisioned-concurrent-executions 10

# Mevcut durumu kontrol et
aws lambda get-provisioned-concurrency-config 
  --function-name product-search-api 
  --qualifier production

# Application Auto Scaling ile dinamik ölçeklendirme
aws application-autoscaling register-scalable-target 
  --service-namespace lambda 
  --resource-id function:product-search-api:production 
  --scalable-dimension lambda:function:ProvisionedConcurrency 
  --min-capacity 5 
  --max-capacity 50

Provisioned Concurrency’nin dezavantajı maliyet. Fonksiyonunuz çağrılmasa bile hazır tutulan instance’lar için ödeme yaparsınız. Bu yüzden scheduled scaling kullanmak mantıklıdır:

# Sabah 6'dan akşam 23'e kadar yüksek kapasiteyle çalış
aws application-autoscaling put-scheduled-action 
  --service-namespace lambda 
  --resource-id function:product-search-api:production 
  --scalable-dimension lambda:function:ProvisionedConcurrency 
  --scheduled-action-name scale-up-morning 
  --schedule "cron(0 6 * * ? *)" 
  --scalable-target-action MinCapacity=10,MaxCapacity=50

# Gece küçült
aws application-autoscaling put-scheduled-action 
  --service-namespace lambda 
  --resource-id function:product-search-api:production 
  --scalable-dimension lambda:function:ProvisionedConcurrency 
  --scheduled-action-name scale-down-night 
  --schedule "cron(0 23 * * ? *)" 
  --scalable-target-action MinCapacity=2,MaxCapacity=10

Çözüm 2: Keep-Warm (Isıtma) Stratejisi

Provisioned Concurrency pahalıya geliyor ve bütçeniz kısıtlıysa, fonksiyonunuzu düzenli aralıklarla ping atarak sıcak tutabilirsiniz. Bu yaklaşım production için çok sağlam olmasa da development ve staging ortamlarında işe yarar.

# EventBridge ile her 5 dakikada bir ping
aws events put-rule 
  --name "keep-warm-product-search" 
  --schedule-expression "rate(5 minutes)" 
  --state ENABLED

# Lambda'yı hedef olarak ekle
aws events put-targets 
  --rule "keep-warm-product-search" 
  --targets '[{
    "Id": "1",
    "Arn": "arn:aws:lambda:eu-west-1:123456789:function:product-search-api",
    "Input": "{"source": "keep-warm", "action": "ping"}"
  }]'

# Lambda'ya EventBridge izni ver
aws lambda add-permission 
  --function-name product-search-api 
  --statement-id "keep-warm-permission" 
  --action "lambda:InvokeFunction" 
  --principal events.amazonaws.com 
  --source-arn "arn:aws:events:eu-west-1:123456789:rule/keep-warm-product-search"

Fonksiyonunuzun handler kodunda bu ping’leri yakalamanız gerekir:

# Node.js fonksiyonu için keep-warm mantığını test et
cat << 'EOF' > /tmp/test-keep-warm.json
{
  "source": "keep-warm",
  "action": "ping"
}
EOF

aws lambda invoke 
  --function-name product-search-api 
  --payload file:///tmp/test-keep-warm.json 
  --cli-binary-format raw-in-base64-out 
  /tmp/response.json

cat /tmp/response.json

Çözüm 3: Kod Optimizasyonu ile Başlatma Süresini Kısaltma

Soğuk başlatmayı tamamen önleyemesek de süresini dramatik olarak kısaltabiliriz. Burada yapabileceğiniz en etkili şey initialization kodunuzu optimize etmektir.

Lazy Loading Kullanın

Handler fonksiyonunun dışındaki her şey soğuk başlatmada çalışır. Database bağlantınızı, SDK client’larınızı handler dışında başlatıyorsanız bunlar her soğuk başlatmada yeniden oluşturulur.

# Fonksiyonunuzun paket boyutunu analiz et
aws lambda get-function 
  --function-name product-search-api 
  --query 'Configuration.CodeSize'

# Deployment paketini indirip analiz et
aws lambda get-function 
  --function-name product-search-api 
  --query 'Code.Location' 
  --output text | xargs curl -o /tmp/function.zip

# Paket içeriğini listele ve büyük dosyaları bul
unzip -l /tmp/function.zip | sort -k1 -rn | head -20

Gereksiz Bağımlılıkları Temizle

Node.js projesindeyseniz node_modules klasörü soğuk başlatmayı öldürebilir. Sadece production bağımlılıklarını pakete dahil edin:

# Sadece production bağımlılıklarını yükle
npm install --production

# Esbuild ile bundle oluştur (tree-shaking yapar)
npx esbuild src/handler.js 
  --bundle 
  --platform=node 
  --target=node18 
  --minify 
  --outfile=dist/handler.js

# Bundle boyutunu kontrol et
du -sh dist/handler.js

# Lambda Layer kullanarak ortak bağımlılıkları ayır
zip -r dependencies-layer.zip nodejs/
aws lambda publish-layer-version 
  --layer-name common-dependencies 
  --zip-file fileb://dependencies-layer.zip 
  --compatible-runtimes nodejs18.x nodejs20.x

Çözüm 4: Memory ve Architecture Optimizasyonu

Lambda fonksiyonunuza atadığınız bellek miktarı hem CPU gücünü hem de ağ bant genişliğini etkiler. Daha fazla memory, genellikle daha hızlı soğuk başlatma anlamına gelir.

# Mevcut memory konfigürasyonunu gör
aws lambda get-function-configuration 
  --function-name product-search-api 
  --query '[MemorySize, Architectures]'

# Memory'i artır (2000MB genellikle tatlı nokta)
aws lambda update-function-configuration 
  --function-name product-search-api 
  --memory-size 2048

# ARM64 mimarisine geç (hem ucuz hem hızlı)
aws lambda update-function-configuration 
  --function-name product-search-api 
  --architectures arm64

# AWS Lambda Power Tuning ile optimal memory bul
# Bu tool farklı memory ayarlarını test eder
aws stepfunctions start-execution 
  --state-machine-arn "arn:aws:states:eu-west-1:123456789:stateMachine:powerTuningMachine" 
  --input '{
    "lambdaARN": "arn:aws:lambda:eu-west-1:123456789:function:product-search-api",
    "powerValues": [128, 256, 512, 1024, 2048, 3008],
    "num": 10,
    "payload": {},
    "parallelInvocation": true,
    "strategy": "cost"
  }'

Çözüm 5: SnapStart (Java için)

Java Lambda kullanıyorsanız ve soğuk başlatmalar cehennem gibiyse, SnapStart özelliği hayat kurtarır. Bu özellik fonksiyonunuzun initialized halinin snapshot’ını alır ve yeni execution environment’lar bu snapshot’tan başlar.

# SnapStart'ı etkinleştir (Java 11+ gerektirir)
aws lambda update-function-configuration 
  --function-name java-order-processor 
  --snap-start ApplyOn=PublishedVersions

# Yeni versiyon yayınla (SnapStart versiyonlarda çalışır)
aws lambda publish-version 
  --function-name java-order-processor 
  --description "SnapStart enabled version"

# Alias oluştur ve yeni versiyona yönlendir
aws lambda create-alias 
  --function-name java-order-processor 
  --name production 
  --function-version 5

# SnapStart durumunu kontrol et
aws lambda get-function-configuration 
  --function-name java-order-processor 
  --qualifier production 
  --query 'SnapStart'

Monitoring ve Alerting Kurulumu

Soğuk başlatma sorunlarını proaktif olarak izlemek için CloudWatch alarmları kurmanız gerekir.

# Soğuk başlatma oranı için alarm kur
aws cloudwatch put-metric-alarm 
  --alarm-name "LambdaColdStartRate-product-search" 
  --alarm-description "Lambda cold start rate above 5%" 
  --namespace "AWS/Lambda" 
  --metric-name "InitDuration" 
  --dimensions Name=FunctionName,Value=product-search-api 
  --statistic Average 
  --period 300 
  --threshold 2000 
  --comparison-operator GreaterThanThreshold 
  --evaluation-periods 3 
  --alarm-actions "arn:aws:sns:eu-west-1:123456789:ops-alerts" 
  --ok-actions "arn:aws:sns:eu-west-1:123456789:ops-alerts"

# X-Ray ile detaylı tracing aktif et
aws lambda update-function-configuration 
  --function-name product-search-api 
  --tracing-config Mode=Active

# X-Ray üzerinden soğuk başlatma analizini sorgula
aws xray get-service-graph 
  --start-time $(date -d '1 hour ago' +%s) 
  --end-time $(date +%s)

VPC Lambda’larında Özel Durum

Lambda fonksiyonunuzu VPC içine koyduysanız soğuk başlatma süresi eskiden çok daha uzundu. 2019 sonrasında AWS bu konuda önemli iyileştirmeler yaptı ama VPC Lambda’ları hala bazı ekstra gecikmelere yol açabilir.

# VPC konfigürasyonunu kontrol et
aws lambda get-function-configuration 
  --function-name product-search-api 
  --query 'VpcConfig'

# Gerçekten VPC'ye ihtiyaç var mı kontrol et
# RDS Proxy kullanıyorsanız belki VPC şart değildir
# Secrets Manager üzerinden credential yönetimi daha iyi alternatif olabilir

# VPC'de olmayan Lambda için Secrets Manager kullanımını test et
aws secretsmanager get-secret-value 
  --secret-id prod/database/credentials 
  --query 'SecretString' 
  --output text

# Lambda'nın ENI'lerini listele (çok fazlaysa sorun işareti)
aws ec2 describe-network-interfaces 
  --filters Name=requester-id,Values=*lambda* 
  --query 'NetworkInterfaces[*].[NetworkInterfaceId, Status, Description]' 
  --output table

VPC Lambda kullanmak zorundaysanız şu noktalara dikkat edin:

  • Subnet’lerin yeterli IP adresi kapasitesi olduğundan emin olun
  • Birden fazla Availability Zone kullanın
  • NAT Gateway yerine VPC Endpoint kullanmayı değerlendirin, hem ucuz hem hızlı

Lambda Container Image Kullanıyorsanız

ECR’den çekilen container image’larıyla çalışıyorsanız soğuk başlatma süreleri farklı dinamikler gösterir.

# Container image boyutunu kontrol et
aws ecr describe-images 
  --repository-name product-search-api 
  --image-ids imageTag=latest 
  --query 'imageDetails[0].imageSizeInBytes'

# Multi-stage build ile image boyutunu küçült
# Dockerfile'ınızı optimize ettikten sonra yeniden push
docker build -t product-search-api:optimized .
docker tag product-search-api:optimized 
  123456789.dkr.ecr.eu-west-1.amazonaws.com/product-search-api:optimized

aws ecr get-login-password --region eu-west-1 | 
  docker login --username AWS --password-stdin 
  123456789.dkr.ecr.eu-west-1.amazonaws.com

docker push 123456789.dkr.ecr.eu-west-1.amazonaws.com/product-search-api:optimized

# Lambda'yı yeni image ile güncelle
aws lambda update-function-code 
  --function-name product-search-api 
  --image-uri 123456789.dkr.ecr.eu-west-1.amazonaws.com/product-search-api:optimized

Container image’larıyla çalışırken şunlara dikkat edin:

  • Base image seçimi: public.ecr.aws/lambda/nodejs:18 gibi AWS’nin resmi Lambda base image’larını kullanın, bunlar önceden optimize edilmiştir
  • Image boyutu: 250MB’ın altında tutmaya çalışın, büyük image’lar soğuk başlatmayı uzatır
  • Layer caching: Sık değişmeyen katmanları (bağımlılıklar) Dockerfile’ın başına koyun

Gerçek Dünya Optimizasyon Sonuçları

Yukarıdaki optimizasyonları uyguladığımız bir e-ticaret projesinde şu sonuçları aldık:

  • Öncesi: Ortalama soğuk başlatma 4200ms, maksimum 8900ms
  • Paket boyutunu 45MB’dan 3MB’a indirdik (esbuild ile bundling)
  • Memory’yi 512MB’dan 2048MB’a çıkardık
  • ARM64 mimarisine geçtik
  • Sonrası: Ortalama soğuk başlatma 180ms, maksimum 420ms

Maliyet açısından ise memory artışına rağmen ARM64 geçişi ve daha hızlı execution süresi sayesinde toplam Lambda maliyeti %23 düştü.

Hangi Çözümü Ne Zaman Kullanmalısınız?

  • Provisioned Concurrency: Latency-sensitive, kullanıcıya doğrudan dokunan API’ler için. Maliyet önemli değilse en sağlıklı çözüm
  • Keep-warm ping: Development/staging ortamlar, düşük trafikli internal servisler. Production’da tek başına yeterli değil
  • Kod optimizasyonu: Her zaman yapılmalı, diğer çözümlerle birlikte kullanılmalı
  • SnapStart: Java fonksiyonları için kesinlikle denenmeli, dramatik fark yaratıyor
  • ARM64 geçişi: Neredeyse sıfır efor, hem performans hem maliyet iyileştirmesi

Sonuç

Soğuk başlatma, serverless mimarinin doğasında var olan bir tradeoff. Sıfır server yönetimi ve otomatik ölçeklendirme karşılığında bazı gecikmeleri kabul etmek gerekiyor. Ama bu gecikmeler kader değil.

Önce ölçün: CloudWatch Logs Insights ile gerçek verilerinize bakın. Kaç çağrının soğuk başlatma yaşadığını, ortalama ve maksimum süreleri bilin. Sonra optimize edin: Paket boyutunu küçültün, gereksiz bağımlılıklardan kurtulun, memory’yi doğru ayarlayın, ARM64’e geçin. Bunlar ücretsiz ve büyük fark yaratır. Son olarak gerekiyorsa Provisioned Concurrency veya SnapStart ile garantili düşük latency sağlayın.

Serverless’ın güzelliği, doğru yapılandırıldığında hem operasyonel yükü azaltması hem de ölçeklenebilirlik sağlaması. Soğuk başlatma sorunlarını çözdükten sonra bu güzelliği tam anlamıyla yaşayabilirsiniz.

Bir yanıt yazın

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