AWS Fargate 103: Size Saatler Kazandıracak Production Savaş Hikayeleri
Fargate'i ölçekte çalıştırırken karşılaşılan production olayları. Memory leak'ler, ENI limitleri, subnet arızaları ve işe yarayan debug teknikleri.
Fargate kurulumunuz konusunda kendinize güvendiğiniz, monitoring dashboard'larında her şeyin yeşil göründüğü, ve sonra sabah 3'ün geldiği hissi bilir misiniz? Evet, production'a hoş geldiniz.
İki yıl boyunca Fargate workload'larını ölçekte çalıştırdıktan sonra, küçük bir kütüphaneyi dolduracak kadar savaş hikayesi biriktirdim. Bu, Fargate serimizin son yazısı (101, 102) - en çok şey öğreten gerçek olaylar ve gerçekten işe yarayan debug teknikleri ve çözümleri.
Black Friday'in Büyük ENI Kıtlığı#
Kurulum: E-ticaret sitesi, Black Friday trafiği normalin 10 katı bekleniyor. Fargate auto-scaling yapılandırılmış, load test geçti, herkes iyi hissediyor.
Ne Oldu: Şükran Günü 23:47'de, task'larımız şu gizemli hatayla başarısız olmaya başladı:
ResourcesNotReady: The ENI allocation could not be completed
Fargate task'larımız PENDING durumunda takılı kaldı. Yeni deployment'lar başarısız oldu. Auto-scaling ölçeklenmiyordu. Dashboard her şeyi yeşil gösteriyordu, sadece küçük bir detay dışında: VPC'mizdeki mevcut ENI'lar sıfıra inmişti.
Araştırma#
Her Fargate task'ının kendi ENI'ya ihtiyacı olduğunu hatırlıyor musunuz? Peki, 3 availability zone'da 200 task'ımız çalışıyordu ve AWS'nin VPC başına ENI limitleri var. us-east-1'de default VPC için:
- Default ENI limiti: VPC başına 5.000
- Her Fargate task: 1 ENI
- Her RDS instance: 1 ENI
- VPC'deki her Lambda: ENI pool'u paylaşır
- Her ELB: Birden fazla ENI
Limitimizi kontrol ettik:
aws ec2 describe-account-attributes \
--attribute-names max-instances
# Ama gerçek limit burada:
aws service-quotas get-service-quota \
--service-code ec2 \
--quota-code L-0263D0A3 # VPC başına ENI
4.847 ENI'daydık. "Load testing"imiz ENI tüketen diğer servisleri hesaba katmamıştı.
Çözüm (Acil Durum)#
Anında çözüm (03:15):
# Önce development VPC'deki task'ları öldür
aws ecs update-service \
--cluster development \
--service api \
--desired-count 0
# Acil quota artırımı talep et (AWS Support)
aws support create-case \
--subject "URGENT: ENI quota increase needed - production impact" \
--service-code "service-quota-increase"
Gerçek çözüm:
- Birden fazla VPC: Workload'ları dev, staging ve prod VPC'lerine böl
- ENI monitoring: ENI kullanımını takip eden CloudWatch custom metric
- Doğru boyutlandırma: Fazla provision edilmiş task'ları azalt
- Lambda optimizasyonu: Mümkün olan Lambda'ları VPC dışına taşı
// ENI monitoring Lambda
export const monitorENIs = async () => {
const ec2 = new AWS.EC2();
const cloudWatch = new AWS.CloudWatch();
const enis = await ec2.describeNetworkInterfaces().promise();
const inUse = enis.NetworkInterfaces?.length || 0;
await cloudWatch.putMetricData({
Namespace: 'Custom/VPC',
MetricData: [{
MetricName: 'ENIsInUse',
Value: inUse,
Unit: 'Count'
}]
}).promise();
};
Öğrenilen dersler:
- Load testing sadece uygulamanızı değil, tüm altyapı bileşenlerini içermeli
- ENI limitleri servis başına değil, VPC başına
- AWS Support sabah 3'te şaşırtıcı derecede hızlı yanıt veriyor
Hain Subnet#
Kurulum: Üç private subnet'te Multi-AZ Fargate deployment'ı. Aylarca sorunsuz çalışıyor.
Ne Oldu: Salı sabahı, task'larımızın %40'ı aralıklı bağlantı sorunları göstermeye başladı. Bazı HTTP istekleri başarılı oluyordu, diğerleri 30 saniye sonra timeout'a düşüyordu.
Garip olan kısım? Sadece belirli bir subnet'teki (us-east-1a) task'lar etkileniyordu.
Araştırma Süreci#
İlk olarak, açık kontroller:
# Task sağlığını kontrol et
aws ecs list-tasks --cluster production --service-name api
aws ecs describe-tasks --cluster production --tasks task-abc123
# Network interface'leri kontrol et
aws ec2 describe-network-interfaces \
--filters "Name=subnet-id,Values=subnet-12345" \
--query 'NetworkInterfaces[*].[NetworkInterfaceId,Status,PrivateIpAddress]'
Task'lar sağlıklı görünüyordu. Network interface'leri bağlı ve aktifti. Ama bir şeyler yanlıştı.
Çıkış noktası: VPC Flow Logs'u etkinleştirdik ve dumanı çıkan silahı bulduk:
# Problem subnet için VPC Flow Logs'u etkinleştir
aws ec2 create-flow-logs \
--resource-type Subnet \
--resource-ids subnet-12345 \
--traffic-type ALL \
--log-destination-type cloud-watch-logs \
--log-group-name /aws/vpc/flowlogs
Flow log'lar paketlerin subnet'imizden çıktığını ama hedeflerine hiç ulaşmadığını gösterdi. Dönüş paketleri bir yerde drop ediliyordu.
Gerçek Suçlu#
Ağ ekibimizin o sabah erkenden subnet'in route table'ını değiştirdiği ortaya çıktı. NAT gateway route'unu 0.0.0.0/0 → nat-gateway-123
'ten 0.0.0.0/0 → nat-gateway-456
'ya değiştirmişlerdi, Fargate task'larının orada çalıştığını fark etmeden.
Yeni NAT gateway farklı bir AZ'deydi ve farklı security group kurallarına sahipti. Klasik.
Çözüm:
# Subnet ile ilişkili route table'ı kontrol et
aws ec2 describe-route-tables \
--filters "Name=association.subnet-id,Values=subnet-12345"
# Route'ları doğrula
aws ec2 describe-route-tables --route-table-ids rtb-abc123 \
--query 'RouteTables[*].Routes[*].[DestinationCidrBlock,GatewayId,State]'
# Route'u düzelt (orijinal NAT gateway'e geri dön)
aws ec2 replace-route \
--route-table-id rtb-abc123 \
--destination-cidr-block 0.0.0.0/0 \
--nat-gateway-id nat-gateway-123
Öğrenilen dersler:
- Route değişikliklerini her zaman production dışında test edin
- Network debugging için VPC Flow Logs dostunuz
- Hangi route table'ların hangi servislere hizmet verdiğini dokümante edin
- Route table değişiklikleri için monitoring kurun
Memory Leak Gizemi (SSH Yok Edisyonu)#
Kurulum: Fargate'te çalışan Node.js API, task başına 2GB memory limiti. Haftalarca sorunsuz çalıştı.
Ne Oldu: Memory kullanımı 3-4 saat boyunca yavaşça artıyor, sonra task'lar OOM kill ediliyor. Memory kullanım grafikleri kayak pistine benziyordu.
Ama işin can alıcı yanı: Container'a debug için SSH yapmanın yolu yoktu.
Araştırma Cephaneliği#
SSH yapamayacağımız için yaratık olmamız gerekiyor:
1. ECS Exec (kurtarıcımız):
# Önce service'te etkinleştir
aws ecs update-service \
--cluster production \
--service api \
--enable-execute-command
# Sonra çalışan task'a bağlan
aws ecs execute-command \
--cluster production \
--task task-abc123 \
--container api \
--interactive \
--command "/bin/bash"
# Container içinde memory kullanımını kontrol et
> ps aux --sort=-%mem | head -20
> cat /proc/meminfo
> pmap -x 1 # PID 1'in memory map'i
2. Application-level monitoring:
// Node.js uygulamanıza ekleyin
const express = require('express');
const app = express();
// Memory monitoring endpoint
app.get('/debug/memory', (req, res) => {
const used = process.memoryUsage();
const stats = {
rss: Math.round(used.rss / 1024 / 1024 * 100) / 100, // MB
heapTotal: Math.round(used.heapTotal / 1024 / 1024 * 100) / 100,
heapUsed: Math.round(used.heapUsed / 1024 / 1024 * 100) / 100,
external: Math.round(used.external / 1024 / 1024 * 100) / 100,
arrayBuffers: Math.round(used.arrayBuffers / 1024 / 1024 * 100) / 100
};
res.json(stats);
});
// Heap snapshot endpoint (ekstrem debugging için)
app.get('/debug/heapdump', (req, res) => {
const heapdump = require('heapdump');
const filename = `/tmp/heapdump-${Date.now()}.heapsnapshot`;
heapdump.writeSnapshot(filename, (err, filename) => {
if (err) {
res.status(500).send(err.message);
} else {
res.download(filename);
}
});
});
3. Dedektiflik çalışması:
ECS Exec kullanarak debugging araçları yükledik ve HTTP client'ımızın bağlantıları düzgün kapatmadığını bulduk:
# Container içinde
> npm install -g clinic
> clinic doctor --on-port 8080 -- node index.js &
> curl http://localhost:8080/debug/memory
# Açık file descriptor'ları kontrol et
> ls -la /proc/1/fd | wc -l
> lsof -p 1 | grep TCP
Bingo! CLOSE_WAIT durumunda binlerce TCP bağlantısı.
Kök Sebep#
Node.js HTTP client kodumuz yeterince masum görünüyordu:
// Problemli kod
const axios = require('axios');
async function callExternalAPI() {
const response = await axios.get('https://api.example.com/data');
return response.data;
}
Ama connection pooling veya timeout'ları düzgün yapılandırmıyorduk. Her istek temizlenmeyen yeni bağlantılar yaratıyordu.
Çözüm:
// Düzgün yapılandırmalı düzeltilmiş versiyon
const axios = require('axios');
const https = require('https');
const http = require('http');
// Connection pooling'i yapılandır
const httpAgent = new http.Agent({
keepAlive: true,
maxSockets: 50,
timeout: 5000,
});
const httpsAgent = new https.Agent({
keepAlive: true,
maxSockets: 50,
timeout: 5000,
});
const axiosInstance = axios.create({
httpAgent,
httpsAgent,
timeout: 10000, // 10 saniye
});
// Graceful shutdown
process.on('SIGTERM', () => {
httpAgent.destroy();
httpsAgent.destroy();
});
async function callExternalAPI() {
const response = await axiosInstance.get('https://api.example.com/data');
return response.data;
}
Öğrenilen dersler:
- ECS Exec containerized debugging için paha biçilmez
- HTTP client'ları production'da her zaman düzgün yapılandırın
- Sadece memory değil, file descriptor'ları da izleyin
- "Basit" HTTP client'lar için bile connection pool'lar önemli
30 Saniyelik Connection Timeout Hayaleti#
Kurulum: İki Fargate servisi arasında internal servis-servis iletişimi. %99 zaman sorunsuz çalışıyor.
Ne Oldu: Rastgele, isteklerin yaklaşık %1'i tam 30 saniye askıda kalıp sonra connection timeout ile başarısız oluyordu.
Pattern tamamen rastgeleydi. Yük, günün saati veya deployment geçmişi ile korelasyon yoktu.
Debugging Destanı#
Network katman araştırması:
# VPC Flow Logs analizi
aws logs filter-log-events \
--log-group-name /aws/vpc/flowlogs \
--start-time 1645564800000 \
--filter-pattern "REJECT"
# Security group kuralları denetimi
aws ec2 describe-security-groups \
--group-ids sg-12345 \
--query 'SecurityGroups[*].{GroupId:GroupId,IpPermissions:IpPermissions}'
Security group'lar iyi görünüyordu. Flow log'lar paketlerin normal akışını gösteriyordu.
Application katman araştırması:
// Detaylı bağlantı takibi eklendi
const net = require('net');
const original_connect = net.Socket.prototype.connect;
net.Socket.prototype.connect = function(...args) {
const startTime = Date.now();
console.log(`[${new Date().toISOString()}] Starting connection to ${args[0]?.host || args[0]?.path}`);
const result = original_connect.apply(this, args);
this.on('connect', () => {
const duration = Date.now() - startTime;
console.log(`[${new Date().toISOString()}] Connected after ${duration}ms`);
});
this.on('error', (err) => {
const duration = Date.now() - startTime;
console.log(`[${new Date().toISOString()}] Connection error after ${duration}ms:`, err.message);
});
return result;
};
Çıkış Noktası#
Log'lar ilginç bir şey gösterdi: başarılı bağlantılar 2-5ms sürüyordu, ama askıda kalanlar tam 30.000ms. Bu rastgele değil - bu bir timeout.
Sonra pattern'i fark ettik: sadece her iki servis aynı availability zone'da olduğunda ve bağlantı load balancer'dan geçtiğinde oluyordu.
Loading diagram...
Problem: AWS ALB'nin aynı AZ'deki bağlantıların ara sıra load balancer altyapısı üzerinden geri dönebileceği bilinen bir tuhaflığı var ve bu gecikmelere neden oluyor.
Çözüm (birden fazla strateji):
- Aynı AZ için direkt servis iletişimi:
// AZ awareness ile service discovery
const AWS = require('aws-sdk');
const ecs = new AWS.ECS();
async function getServiceEndpoints() {
const tasks = await ecs.listTasks({
cluster: 'production',
serviceName: 'target-service'
}).promise();
const taskDetails = await ecs.describeTasks({
cluster: 'production',
tasks: tasks.taskArns
}).promise();
return taskDetails.tasks.map(task => ({
ip: task.attachments[0].details.find(d => d.name === 'privateIPv4Address').value,
az: task.availabilityZone,
port: 8080
}));
}
// Akıllı routing
async function callService(endpoint, data) {
const currentAZ = process.env.AWS_AVAILABILITY_ZONE;
const endpoints = await getServiceEndpoints();
// Önce aynı AZ direkt bağlantıyı dene
const sameAZEndpoint = endpoints.find(e => e.az === currentAZ);
if (sameAZEndpoint) {
try {
return await axios.post(`http://${sameAZEndpoint.ip}:${sameAZEndpoint.port}${endpoint}`, data);
} catch (error) {
// Load balancer'a geri dön
return await axios.post(`https://internal-service.example.com${endpoint}`, data);
}
}
// Cross-AZ için load balancer kullan
return await axios.post(`https://internal-service.example.com${endpoint}`, data);
}
- Connection timeout ayarlaması:
const axiosInstance = axios.create({
timeout: 5000, // 30s beklemek yerine çabuk başarısız ol
httpsAgent: new https.Agent({
timeout: 2000, // Connection timeout
keepAlive: true,
})
});
Öğrenilen dersler:
- ALB'ler aynı AZ iletişimi için beklenmedik gecikme ekleyebilir
- Service discovery direkt iletişim pattern'lerini mümkün kılar
- Her zaman SLA'nızdan daha kısa connection timeout'lar uygulayın
- Load balancer'lar her zaman en hızlı yol değildir
Deploy Olmayan Deployment#
Kurulum: CodeDeploy kullanarak standart blue-green deployment. Daha önce yüzlerce kez çalıştı.
Ne Oldu: Yeni deployment saatlerce %50'de takılı kaldı. Task'ların yarısı yeni versiyon, yarısı eski versiyon çalışıyordu. CodeDeploy dashboard "In Progress" gösteriyordu, hata mesajı yoktu.
Auto-rollback tetiklenmiyordu çünkü teknik olarak hiçbir şey "başarısız olmuyordu".
Araştırma#
CodeDeploy log'ları işe yaramadı:
aws deploy get-deployment --deployment-id d-XXXXXXXXX
# Status: InProgress, hata bilgisi yok
aws logs filter-log-events \
--log-group-name /aws/codedeploy-agent \
--start-time $(date -d '1 hour ago' +%s)000
ECS service event'leri gerçek hikayeyi gösterdi:
aws ecs describe-services \
--cluster production \
--services api \
--query 'services[0].events[0:10]'
Event'ler şunu gösteriyordu:
"(service api) failed to launch a task with (error ECS was unable to assume role...)"
Kök Sebep#
Task execution role'ümüz başka bir ekip tarafından alakasız bir servis için değiştirilmişti ve yanlışlıkla ECS'nin role'ü assume etmesine izin veren trust relationship'i kaldırmışlardı.
Role policy şu şekildeydi:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "ec2.amazonaws.com" // YANLIŞ!
},
"Action": "sts:AssumeRole"
}
]
}
Şöyle olmalıydı:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "ecs-tasks.amazonaws.com" // DOĞRU
},
"Action": "sts:AssumeRole"
}
]
}
Çözüm:
# Role trust policy'sini kontrol et
aws iam get-role --role-name fargate-task-execution-role \
--query 'Role.AssumeRolePolicyDocument'
# Güncelle
aws iam update-assume-role-policy \
--role-name fargate-task-execution-role \
--policy-document file://trust-policy.json
Önleme stratejisi:
// Otomatik role validasyonu
export const validateTaskRoles = async () => {
const iam = new AWS.IAM();
const role = await iam.getRole({
RoleName: 'fargate-task-execution-role'
}).promise();
const trustPolicy = JSON.parse(decodeURIComponent(role.Role.AssumeRolePolicyDocument));
const ecsService = trustPolicy.Statement.some(statement =>
statement.Principal?.Service === 'ecs-tasks.amazonaws.com'
);
if (!ecsService) {
await sendAlert('Task execution role missing ECS trust relationship!');
return false;
}
return true;
};
Öğrenilen dersler:
- ECS service event'leri CodeDeploy log'larından daha detaylı
- Role trust policy'leri kırılgan ve monitoring gerektirir
- Blue-green deployment'lar limbo'da takılabilir
- İşler gizemli şekilde çalışmayı durdurduğunda her zaman IAM'ı kontrol edin
Gerçekten İşe Yarayan Debug Araç Kutusu#
Tüm bu olaylardan sonra, bize sayısız saat kazandıran debugging toolkit'i:
1. Ultimate Fargate Debug Container#
FROM node:18-alpine
RUN apk add --no-cache \
curl \
wget \
netcat-openbsd \
bind-tools \
tcpdump \
strace \
htop \
iotop \
lsof \
procps \
net-tools
# Uygulamanızı ekleyin
COPY . /app
WORKDIR /app
# Debug endpoint'leri
RUN npm install express heapdump clinic
2. Monitoring Stack#
// Detaylı teşhislerle health check endpoint
app.get('/health/detailed', async (req, res) => {
const health = {
timestamp: new Date().toISOString(),
uptime: process.uptime(),
memory: process.memoryUsage(),
cpu: process.cpuUsage(),
connections: {
active: await getActiveConnections(),
waiting: await getWaitingConnections()
},
environment: {
nodeVersion: process.version,
availabilityZone: process.env.AWS_AVAILABILITY_ZONE || 'unknown',
region: process.env.AWS_REGION || 'unknown'
}
};
res.json(health);
});
async function getActiveConnections() {
return new Promise((resolve) => {
require('child_process').exec('netstat -an | grep ESTABLISHED | wc -l',
(error, stdout) => {
resolve(parseInt(stdout.trim()) || 0);
}
);
});
}
3. Otomatik Olay Müdahalesi#
# Gerçekten yardım eden CloudWatch alarm'ları
ENIUtilizationAlarm:
Type: AWS::CloudWatch::Alarm
Properties:
AlarmName: High-ENI-Utilization
MetricName: ENIsInUse
Namespace: Custom/VPC
Statistic: Maximum
Period: 300
EvaluationPeriods: 2
Threshold: 4500 # 5000 limitinin %90'ı
ComparisonOperator: GreaterThanThreshold
AlarmActions:
- !Ref SNSTopic
MemoryUtilizationAlarm:
Type: AWS::CloudWatch::Alarm
Properties:
AlarmName: Fargate-Memory-High
MetricName: MemoryUtilized
Namespace: ECS/ContainerInsights
Statistic: Average
Period: 300
EvaluationPeriods: 3
Threshold: 80 # %80 memory kullanımı
ComparisonOperator: GreaterThanThreshold
Fargate Debugging'in Evrensel Yasaları#
Tüm bu maceralardan sonra, doğru olan pattern'ler:
-
Task'lar başlamadığında: ENI limitleri, security group'lar ve IAM role'leri kontrol et (bu sırayla)
-
Task'lar yavaş olduğunda: Genelde network'tür (route table'lar, NAT gateway'ler, DNS)
-
Memory sürekli arttığında: Her zaman connection pooling veya event listener'lar
-
Deployment'lar asılı kaldığında: Deployment log'lar değil, service event'leri kontrol et
-
İsteklerin %1'i başarısız olduğunda: Load balancer tuhaflıkları veya cross-AZ sorunları ara
-
Hiçbir şey mantıklı olmadığında: VPC Flow Logs ve ECS Exec'i etkinleştir
Gerçek ders? Fargate bir sürü altyapı karmaşıklığını kaldırır, ama işler ters gittiğinde, temel AWS networking ve compute primitive'lerini anlamanız gerekir. Abstraction sızıntılıdır ve production her zaman sızıntıları bulur.
Bu debugging tekniklerini elinizin altında bulundurun. İnanın, bir gün sabah 3'te ihtiyacınız olacak ve o zaman, önceden kurduğunuz her monitoring endpoint ve teşhis aracı için minnettarlık duyacaksınız.
Bir dahaki sefere biri size serverless container'ların "kur ve unut" olduğunu söylediğinde, onlara bu seriyi gösterin. Production'ın başka planları var, ama artık onlara hazırsınız.
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!