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 hafta, sadece bir süredir production workload'ları çalıştırdıktan sonra ortaya çıkan şeylerden bahsedelim. Bilirsiniz, bir olay sırasında sabah 2'de "aa, demek böyle çalışıyormuş" dedirten şeyler.
Maliyet Optimizasyonu: İflas Etmeme Sanatı#
Fargate'in EC2'den daha pahalı olduğunu söylemiştim ya? İşte acıyı azaltmanın yolları var. AWS faturamız bir ay beş haneli rakamlara ulaştıktan sonra (sormayın), optimizasyon konusunda ciddileştik.
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. Bunu şunlar için kullanıyoruz:
- Batch işleme job'ları
- Development ortamları
- Kritik olmayan async worker'lar
- CI/CD runner'ları
Pro ipucu: Spot kesintileri için CloudWatch alarm'ları kurun. Bir spike gördüğümüzde, trafiği geçici olarak normal Fargate'e kaydırıyoruz:
// 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#
Çoğu kişi OOM kill'lerden korktuğu için Fargate task'larını fazla provision'lar. İşte doğru boyutu gerçekten nasıl buluyoruz:
-
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
-
%80 kuralını kullan: %100 değil, peak kullanımın %80'i için boyutlandır. O %20 buffer spike'ları halleder.
-
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 servislerimizi Graviton'a taşıyarak %35 tasarruf ettik. Çalışmayan tek şey? x86'ya özel JNI kütüphaneleri olan eski bir Java uygulaması. Geri kalan her şey sorunsuzdı.
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#
Loading diagram...
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 5-10ms (yerel SSD için 0.1ms)
- Throughput: 100MB/s'ye burst, 10MB/s'de sürdürür (daha fazla ödemezseniz)
- Maliyet: $0.30/GB/ay (EBS için $0.10)
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"
}
}
Ama şu var: sticky sessions + auto-scaling = üzüntü. Bir task öldüğünde, o session'lar gitti. Öğrendiklerimiz:
- Session verisini ElastiCache Redis'te sakla
- Sunucu session'ları yerine JWT token'ları kullan
- Deployment'lar sırasında bazı kullanıcıların çıkış yapacağını kabul et
Monitoring: Orada Aslında Ne Oluyor?#
Fargate bir kara kutu, bu da debugging'i... ilginç yapıyor. İşte gerçekten çalışan monitoring stack'imiz:
Fargate Gözlemlenebilirliğinin Üç Direği#
-
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.
-
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();
-
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. İşte bizim pattern'imiz:
Loading diagram...
Keşfettiğimiz tuzaklar:
- Region başına environment variable'lar: Endpoint'leri hardcode etmeyin
- S3 bucket isimleri global olarak benzersiz olmalı: Region suffix ekleyin
- Cross-region gecikme: US ve EU arasında ~100ms
- Failover anında değil: Route 53 health check'leri 30-60 saniye alır
Daha Önce Bilmeyi Dilediğimiz 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#
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.
Bu Serideki Tüm Yazılar
Yorumlar (0)
Sohbete katıl
Düşüncelerini paylaşmak ve toplulukla etkileşim kurmak için giriş yap
Henüz yorum yok
Bu yazı hakkında ilk düşüncelerini paylaşan sen ol!
Yorumlar (0)
Sohbete katıl
Düşüncelerini paylaşmak ve toplulukla etkileşim kurmak için giriş yap
Henüz yorum yok
Bu yazı hakkında ilk düşüncelerini paylaşan sen ol!