İçeriğe atla

2025-09-04

AWS Fargate 102: Kimsenin Bahsetmediği Pattern'ler

Production workload'ları çalıştırırken öğrenilen gelişmiş Fargate pattern'leri. Maliyet optimizasyonundan stateful container'lara, dokümantasyonun size söylemeyecekleri.

Fargate 101’de temelleri ele aldık. Bu yazıda, production workload’ları çalıştırırken ortaya çıkan gelişmiş pattern’lerden bahsedelim. Bilirsiniz, troubleshooting sırasında sistemin gerçek mekaniklerini anladığınız o anlar var. Maliyet optimizasyonu, stateful container’lar, monitoring ve güvenli deployment’larla devam ediyoruz.

Maliyet Optimizasyonu: İflas Etmeme Sanatı

Fargate’in EC2’den daha pahalı olduğunu söylemiştik. İşte maliyetleri azaltmanın etkili yolları var: Spot, right-sizing, ARM ve Savings Plans. AWS faturaları beklenmedik şekilde artmaya başladığında, sistematik optimizasyon kritik hale gelir.

Fargate Spot: Kimsenin Kullanmadığı %70 İndirim

Fargate Spot normal Fargate gibi, ama AWS 2 dakikalık uyarıyla container’ınızı çekebiliyor. Kulağa korkutucu mu geliyor? Aslında çoğu workload için sorun değil.

İşte akıllıca nasıl kullanılır:

resource "aws_ecs_service" "batch_processor" {
  name  = "batch-processor"
  cluster  = aws_ecs_cluster.main.id
  task_definition = aws_ecs_task_definition.batch.arn
  
  capacity_provider_strategy {
    capacity_provider = "FARGATE_SPOT"
    weight  = 4
    base  = 0
  }
  
  capacity_provider_strategy {
    capacity_provider = "FARGATE"
    weight  = 1
    base  = 2  # Her zaman 2'sini normal Fargate'te tut
  }
  
  desired_count = 10
}

Bu konfigürasyon task’larınızın %80’ini Spot’ta çalıştırır (~%70 tasarruf) ve stabilite için bir baseline’ı normal Fargate’te tutar. Spot interruption oranları yükseldiğinde alarm’lar devreye girer. Bu pattern özellikle şunlar için uygundur:

  • Batch işleme job’ları
  • Development ve staging ortamları
  • Restart’ı kaldırabilen async worker’lar
  • CI/CD runner’ları

Kritik bir ders: Her zaman Spot kesintileri için CloudWatch alarm’ları kurun. Kesinti oranları arttığında, trafiği geçici olarak normal Fargate’e kaydırın:

// Spot kesintilerini ele alan Lambda fonksiyonu
export const handleSpotInterruption = async (event: any) => {
  const ecs = new AWS.ECS();
  
  // Geçici olarak normal Fargate ağırlığını artır
  await ecs.putClusterCapacityProviders({
    cluster: 'production',
    capacityProviders: ['FARGATE', 'FARGATE_SPOT'],
    defaultCapacityProviderStrategy: [
      { capacityProvider: 'FARGATE', weight: 10, base: 5 },
      { capacityProvider: 'FARGATE_SPOT', weight: 1, base: 0 }
    ]
  }).promise();
  
  // 30 dakika sonra geri almak için timer ayarla
  await scheduleReversion();
};

Doğru Boyutlandırma: Goldilocks Problemi

Birçok ekip OOM kill’lerden kaçınmak için Fargate task’larını fazla provision’lar. İşte doğru kaynak tahsisi için sistematik bir yaklaşım:

  1. Büyük başla, ölç, sonra küçült

    # Cömert kaynaklarla deploy et
    CPU: 1024
    Memory: 2048
    
    # Bir hafta sonra, gerçek kullanımı kontrol et
    aws cloudwatch get-metric-statistics \
      --namespace ECS/ContainerInsights \
      --metric-name MemoryUtilized \
      --dimensions Name=ServiceName,Value=my-service \
      --statistics Average,Maximum \
      --start-time 2024-01-01T00:00:00Z \
      --end-time 2024-01-08T00:00:00Z \
      --period 3600
  2. %80 kuralını kullan: %100 değil, peak kullanımın %80’i için boyutlandır. O %20 buffer spike’ları halleder.

  3. Farklı ortamlar için farklı boyutlar:

    locals {
      task_sizes = {
        production = {
          cpu  = "512"
          memory = "1024"
        }
        staging = {
          cpu  = "256"
          memory = "512"
        }
        development = {
          cpu  = "256"
          memory = "512"
        }
      }
    }
    
    resource "aws_ecs_task_definition" "app" {
      cpu  = local.task_sizes[var.environment].cpu
      memory = local.task_sizes[var.environment].memory
      # ...
    }

ARM + Savings Plans: Çifte İndirim

AWS Graviton (ARM) işlemciler %20 daha ucuz ve genelde daha hızlı. Savings Plans ile birleştirin, %20 daha indirim:

# Multi-arch Dockerfile
FROM --platform=$BUILDPLATFORM node:18-alpine AS builder
ARG TARGETPLATFORM
ARG BUILDPLATFORM
RUN echo "Building on $BUILDPLATFORM for $TARGETPLATFORM"

# Build adımlarınız burada...

FROM node:18-alpine
COPY --from=builder /app /app
CMD ["node", "index.js"]

Her iki mimari için build edin:

docker buildx build \
  --platform linux/amd64,linux/arm64 \
  --tag myapp:latest \
  --push .

Sonra task definition’ınızda:

{
  "runtimePlatform": {
    "cpuArchitecture": "ARM64",
    "operatingSystemFamily": "LINUX"
  }
}

Node.js servislerini Graviton’a taşımak genellikle %30-40 tasarruf sağlar. Yaygın uyumluluk sorunları arasında x86’ya özel JNI kütüphaneleri olan eski Java uygulamaları var, ancak modern workload’ların çoğu sorunsuz geçiş yapar.

Stateful Workload’lar: Evet, Yapabilirsiniz (Ama Yapmalı mısınız?)

Herkes container’ların stateless olması gerektiğini söyler. Herkes çoğunlukla haklı. Ama bazen state’e ihtiyacınız olur ve EFS burada hem dostunuz hem düşmanınız.

EFS: İyi, Kötü ve Çirkin

Paylaşımlı Depolama

Fargate Task'ları

Yavaş I/O

Yavaş I/O

Yavaş I/O

Task 1

Task 2

Task 3

EFS Mount Point

Fargate ile EFS kurulumu:

resource "aws_efs_file_system" "shared" {
  creation_token = "shared-storage"
  
  performance_mode = "generalPurpose"  # veya daha fazla operasyon için "maxIO"
  throughput_mode  = "bursting"  # veya tutarlı performans için "provisioned"
  
  lifecycle_policy {
    transition_to_ia = "AFTER_30_DAYS"  # Soğuk veride tasarruf
  }
}

resource "aws_ecs_task_definition" "app" {
  # ... diğer konfigürasyon ...
  
  volume {
    name = "shared-storage"
    
    efs_volume_configuration {
      file_system_id  = aws_efs_file_system.shared.id
      root_directory  = "/"
      transit_encryption  = "ENABLED"
      transit_encryption_port = 2999
      
      authorization_config {
        access_point_id = aws_efs_access_point.app.id
        iam  = "ENABLED"
      }
    }
  }
  
  container_definitions = jsonencode([{
    name = "app"
    # ...
    mountPoints = [{
      sourceVolume  = "shared-storage"
      containerPath = "/data"
    }]
  }])
}

Gerçeklik Kontrolü:

  • EFS gecikmesi: Operasyon başına 0.25–10ms (metadata daha hızlı; yerel SSD ~0.1ms)
  • Throughput: 100MB/s’ye burst, general purpose modda sürdürülen ~10MB/s
  • Maliyet: Yaklaşık 0.30/GB/ay(EBS0.30/GB/ay (EBS 0.10). Ocak 2024 itibarıyla Fargate daha hızlı kalıcı depolama için EBS volume desteği de sunuyor

EFS Ne Zaman Mantıklı:

  • Paylaşılan konfigürasyon dosyaları
  • Birden fazla container’ın ihtiyaç duyduğu kullanıcı yüklemeleri
  • Build cache’leri (dikkatli kilitlemeyle)
  • Kesinlikle paylaşımlı dosya sistemi gereken eski uygulamalar

Ne Zaman Değil:

  • Database depolama (sadece RDS kullanın)
  • Yüksek frekanslı yazma işlemleri
  • Geçici dosyalar (container’ın ephemeral storage’ını kullanın)
  • Cache katmanları (ElastiCache kullanın)

Session Affinity Pattern’i

Bazen sticky session’lara ihtiyacınız olur. Fargate ile nasıl yapılır:

resource "aws_lb_target_group" "app" {
  name  = "app-tg"
  port  = 80
  protocol = "HTTP"
  vpc_id  = aws_vpc.main.id
  target_type = "ip"
  
  stickiness {
    type  = "app_cookie"
    cookie_duration = 86400
    cookie_name  = "FARGATE_SESSION"
  }
  
  health_check {
    enabled = true
    path  = "/health"
    matcher = "200"
  }
}

Ancak sticky sessions ve auto-scaling iyi anlaşmaz. Task’lar beklenmedik şekilde sonlandığında, o session’lar kaybolur. Daha iyi bir yaklaşım:

  1. Session verisini ElastiCache Redis’te sakla
  2. Sunucu session’ları yerine JWT token’ları kullan
  3. Deployment’lar sırasında bazı kullanıcıların çıkış yapacağını kabul et

Monitoring: Orada Aslında Ne Oluyor?

Fargate’in altyapıya görünürlüğü azaltması debugging’i zorlaştırır. İşte etkili bir monitoring yaklaşımı:

Fargate Gözlemlenebilirliğinin Üç Direği

  1. CloudWatch Container Insights (Temeller)

    aws ecs put-account-setting \
      --name containerInsights \
      --value enabled

    Bu size CPU, bellek, network ve disk metriklerini verir. Temeller için iyi ama application-level şeyleri kaçırır.

  2. Distributed Tracing için X-Ray (Bağlantılar)

    // Node.js uygulamanıza ekleyin
    const AWSXRay = require('aws-xray-sdk-core');
    const AWS = AWSXRay.captureAWS(require('aws-sdk'));
    
    // Artık tüm AWS SDK çağrıları trace ediliyor
    const s3 = new AWS.S3();
    await s3.getObject({ Bucket: 'my-bucket', Key: 'file.txt' }).promise();
  3. StatsD ile Custom Metrikler (Detaylar)

    // StatsD için sidecar container çalıştırın
    const taskDef = {
      containerDefinitions: [
        {
          name: "app",
          // uygulama config'iniz
        },
        {
          name: "datadog-agent",
          image: "datadog/agent:latest",
          environment: [
            { name: "DD_API_KEY", value: process.env.DD_API_KEY },
            { name: "ECS_FARGATE", value: "true" }
          ]
        }
      ]
    };

Saatleri Kurtaran Debug Pattern’i

Fargate’e SSH yapamıyor musunuz? ECS Exec kullanın, ama faydalı hale getirin:

# Dockerfile'ınıza bunu ekleyin
RUN apk add --no-cache \
    curl \
    netstat \
    ps \
    htop \
    strace \
    tcpdump

# Task definition'da ECS Exec'i etkinleştirin
aws ecs update-service \
  --cluster production \
  --service my-service \
  --enable-execute-command

# Profesyonel gibi debug yapın
aws ecs execute-command \
  --cluster production \
  --task abc123 \
  --container app \
  --interactive \
  --command "/bin/sh"

# Container içinde:
> netstat -tulpn  # Neyin dinlediğini kontrol et
> ps aux  # Tüm process'leri gör
> strace -p 1  # System call'ları trace et
> tcpdump -i any  # Network trafiğini izle

Blue-Green Deployment’lar: Güvenli Yol

Fargate + CodeDeploy = zero-downtime deployment’lar. Bizi birçok kötü deploy’dan kurtaran setup:

resource "aws_codedeploy_deployment_group" "app" {
  app_name  = aws_codedeploy_app.app.name
  deployment_group_name  = "production"
  deployment_config_name = "CodeDeployDefault.ECSLinear10PercentEvery1Minutes"
  
  auto_rollback_configuration {
    enabled = true
    events  = ["DEPLOYMENT_FAILURE", "DEPLOYMENT_STOP_ON_ALARM"]
  }
  
  blue_green_deployment_config {
    terminate_blue_instances_on_deployment_success {
      action  = "TERMINATE"
      termination_wait_time_in_minutes  = 5
    }
    
    deployment_ready_option {
      action_on_timeout = "CONTINUE_DEPLOYMENT"
    }
    
    green_fleet_provisioning_option {
      action = "COPY_AUTO_SCALING_GROUP"
    }
  }
  
  ecs_service {
    cluster_name = aws_ecs_cluster.main.name
    service_name = aws_ecs_service.app.name
  }
}

Killer özellik? CloudWatch alarm’larında otomatik rollback:

const errorRateAlarm = new cloudwatch.Alarm(this, 'ErrorRate', {
  metric: new cloudwatch.Metric({
    namespace: 'MyApp',
    metricName: 'Errors',
    statistic: 'Sum'
  }),
  threshold: 10,
  evaluationPeriods: 2
});

// Deployment group'a bağla
deploymentGroup.addAlarm(errorRateAlarm);

Multi-Region Fargate: Çünkü Felaketler Olur

Fargate’i region’lar arası çalıştırmak zor değil, ama onları senkronize tutmak öyle. Veri tutarlılığı, failover testi ve maliyet (her region ayrı faturalanır) dikkat edilmesi gereken noktalar. İşte bizim pattern’imiz:

Primary Region

eu-west-1

us-east-1

Route 53

Replikasyon

Replikasyon

Health-based Routing

ALB

Fargate Task'ları

RDS Read Replica

ALB

Fargate Task'ları

RDS Read Replica

RDS Primary

Keşfettiğimiz tuzaklar:

  1. Region başına environment variable’lar: Endpoint’leri hardcode etmeyin; her region kendi RDS, S3 ve servis endpoint’lerine sahip olmalı.
  2. S3 bucket isimleri global olarak benzersiz olmalı: Region veya account suffix ekleyin.
  3. Cross-region gecikme: Region çiftine göre 80–150ms (us-east-1–eu-west-1 genelde ~90ms).
  4. Failover anında değil: Route 53 health check’leri tipik 30–60 saniye alır; bunu disaster recovery planınıza yazın.

Production Fargate için Temel Pattern’ler

Sidecar Pattern’i

{
  "containerDefinitions": [
    {
      "name": "app",
      "dependsOn": [{
        "containerName": "envoy",
        "condition": "HEALTHY"
      }]
    },
    {
      "name": "envoy",
      "healthCheck": {
        "command": ["CMD-SHELL", "curl -f http://localhost:9901/ready || exit 1"]
      }
    }
  ]
}

Init Container Pattern’i (Bir Nevi)

Fargate’in gerçek init container’ları yok, ama taklit edebilirsiniz:

# Entrypoint script'inizde
#!/bin/sh
echo "Initialization çalıştırılıyor..."
/app/init-db.sh
if [ $? -ne 0 ]; then
  echo "Init başarısız, çıkılıyor"
  exit 1
fi
echo "Ana uygulama başlatılıyor..."
exec node index.js

Circuit Breaker Pattern’i

Harici servis çağrıları için circuit breaker kullan. Timeout ve retry ile birlikte cascading failure’ı önle. Bir servis sürekli hata veriyorsa diğerlerinin de çökmesini engeller.

class FargateCircuitBreaker {
  private failures = 0;
  private lastFailTime = 0;
  private readonly threshold = 5;
  private readonly timeout = 60000; // 1 dakika
  
  async call<T>(fn: () => Promise<T>): Promise<T> {
    if (this.isOpen()) {
      throw new Error('Circuit breaker açık');
    }
    
    try {
      const result = await fn();
      this.onSuccess();
      return result;
    } catch (error) {
      this.onFailure();
      throw error;
    }
  }
  
  private isOpen(): boolean {
    return this.failures >= this.threshold && 
           Date.now() - this.lastFailTime < this.timeout;
  }
  
  private onSuccess(): void {
    this.failures = 0;
  }
  
  private onFailure(): void {
    this.failures++;
    this.lastFailTime = Date.now();
  }
}

Unutmayın: Fargate bir İsviçre çakısı gibi. İnanılmaz faydalı, ama dikkatli olmazsanız ara sıra kendinizi kesersiniz.

AWS Fargate Derinlemesine Serisi

AWS Fargate'e temellerden production'a tam rehber. Gerçek dünya deneyimleri ile serverless container'ları, maliyet optimizasyonu, debugging tekniklerini ve Infrastructure-as-Code deployment pattern'lerini öğrenin.

İlerleme 2 / 4 yazı

İlgili yazılar