AWS Maliyet Optimizasyonu Araç Seti - Production Workload'lar için Pratik Stratejiler
AWS maliyet çalışmasının sırası: önce Cost Explorer ve Budgets ile görünürlük, sonra Compute Optimizer ile right-sizing, ardından commitment kararı.
AWS maliyet çalışmasının bir varsayılan sırası var: önce görünürlük, sonra right-sizing, en son commitment. Cost Explorer ve Budgets paranın nereye gittiğini gösterir, Compute Optimizer kimsenin kullanmadığı kapasiteyi işaretler, Savings Plans veya Reserved Instances de bu iki adımdan geriye kalanı indirime bağlar. Sırayı ters çeviren ekipler, aslında silmeleri gereken kapasiteyi indirimli fiyattan satın almış olur.
Ekipleri bu işe iten belirtiler production hesaplarında birbirine benziyor: trafikte karşılığı olmadan oynayan aylık faturalar, yalnızca mesai saatlerinde gerekirken 7/24 açık duran development kaynakları ve CPU’su boşta dururken tam kapasite faturalanan instance’lar.
Tekrar Eden Beş Maliyet Sürücüsü#
AWS’nin native maliyet araçları yeterli; eksik bir özellik nadiren engel oluyor. Asıl soru hangi belirtiye hangi aracın cevap verdiği ve bunları hangi sırayla uygulayacağın.
Neredeyse her production hesabında beş pattern tekrar ediyor:
- Maliyet öngörülemezliği: İş büyümesine karşılık gelmeden önemli ölçüde değişen aylık faturalar
- Idle kaynak israfı: İş saatleri dışında bütçe yakan non-production kaynaklar
- Over-provisioned instance’lar: Nadiren kullanılan kapasite için ödeme
- Commitment paralizi: Reserved Instances, Savings Plans veya Spot Instances arasında seçim yapma zorluğu
- Attribution eksikliği: Hangi projelerin veya ekiplerin AWS harcamasını yönlendirdiğini track edememe
Temel: Cost Explorer ve AWS Budgets#
Maliyetleri optimize etmeden önce görünürlüğe ihtiyacın var. Cost Explorer ve AWS Budgets, paranın nereye gittiğini anlamak ve anomalileri erken yakalamak için temeli kuruyor.
Cost Explorer’a Yakından Bakış#
Cost Explorer 12 aylık geçmiş veri ve 12 aya kadar forecasting sunuyor. İşte maliyet trendlerini analiz etmek ve anomalileri tespit etmek için pratik bir implementation:
import boto3
from datetime import datetime, timedelta
ce_client = boto3.client('ce', region_name='us-east-1')
def analyze_cost_trends(months_back=3):
"""
Servisler arasında maliyet trendlerini analiz et ve anomalileri tespit et
Ay bazında %20'den fazla maliyet değişikliği olan servisleri döndürür
"""
end_date = datetime.now().date()
start_date = (datetime.now() - timedelta(days=30 * months_back)).date()
response = ce_client.get_cost_and_usage(
TimePeriod={
'Start': start_date.strftime('%Y-%m-%d'),
'End': end_date.strftime('%Y-%m-%d')
},
Granularity='MONTHLY',
Metrics=['UnblendedCost'],
GroupBy=[
{'Type': 'DIMENSION', 'Key': 'SERVICE'},
]
)
# Sonuçları işle
cost_by_service = {}
for result in response['ResultsByTime']:
period = result['TimePeriod']['Start']
for group in result['Groups']:
service = group['Keys'][0]
cost = float(group['Metrics']['UnblendedCost']['Amount'])
if service not in cost_by_service:
cost_by_service[service] = []
cost_by_service[service].append({
'period': period,
'cost': cost
})
# %20'den fazla maliyet artışı olan servisleri tespit et
trending_services = []
for service, costs in cost_by_service.items():
if len(costs) >= 2:
recent_cost = costs[-1]['cost']
previous_cost = costs[-2]['cost']
if previous_cost > 0:
change_pct = ((recent_cost - previous_cost) / previous_cost) * 100
if abs(change_pct) > 20:
trending_services.append({
'service': service,
'change': change_pct,
'current_cost': recent_cost,
'previous_cost': previous_cost
})
return sorted(trending_services, key=lambda x: abs(x['change']), reverse=True)
Bu script AWS servisleri arasında maliyet anomalilerini tespit ediyor. Haftalık çalıştırmak, yanlış configure edilmiş Auto Scaling group’ları veya unutulmuş test kaynaklarını önemli maliyet birikmeden yakalar.
Takip Edilmeyen Maliyetleri Bulmak#
En ucuz optimizasyon çoğu zaman kimsenin takip etmediği harcamayı bulmak. Tag’lenmemiş harcama toplamın içinde saklanır ve tag’e göre filtrelenen hiçbir dashboard onu ayrı göstermez:
def analyze_cost_allocation_gaps():
"""
Cost allocation için düzgün tag'lenmemiş maliyetleri tespit et
"""
end_date = datetime.now().date()
start_date = (datetime.now() - timedelta(days=30)).date()
# Tag'lere göre maliyetleri kontrol et
tagged_response = ce_client.get_cost_and_usage(
TimePeriod={
'Start': start_date.strftime('%Y-%m-%d'),
'End': end_date.strftime('%Y-%m-%d')
},
Granularity='MONTHLY',
Metrics=['UnblendedCost'],
GroupBy=[
{'Type': 'TAG', 'Key': 'Project'},
]
)
total_tagged_cost = 0
for result in tagged_response['ResultsByTime']:
for group in result['Groups']:
if group['Keys'][0]: # Project tag'i var
total_tagged_cost += float(group['Metrics']['UnblendedCost']['Amount'])
# Toplam maliyeti al
total_response = ce_client.get_cost_and_usage(
TimePeriod={
'Start': start_date.strftime('%Y-%m-%d'),
'End': end_date.strftime('%Y-%m-%d')
},
Granularity='MONTHLY',
Metrics=['UnblendedCost']
)
total_cost = float(total_response['ResultsByTime'][0]['Total']['UnblendedCost']['Amount'])
untagged_cost = total_cost - total_tagged_cost
untagged_percentage = (untagged_cost / total_cost) * 100
return {
'total_cost': total_cost,
'tagged_cost': total_tagged_cost,
'untagged_cost': untagged_cost,
'untagged_percentage': untagged_percentage
}
Cost allocation tag’lerine güvenmeden önce Billing Console’da aktif et. Tag’ler yalnızca aktivasyondan sonraki maliyetleri track ediyor; retroaktif tag’leme diye bir şey yok.
AWS Budgets ve Otomatik Aksiyonlar#
Budget’lar proaktif maliyet monitoring sağlıyor. İşte birden fazla alert eşiği olan production-ready bir implementation:
import boto3
budgets_client = boto3.client('budgets')
def create_department_budget_with_alerts(
account_id: str,
department: str,
monthly_limit: float
):
"""
Birden fazla alert eşiği ve SNS notification'larıyla budget oluştur
%70 = Info, %90 = Warning, %100 = Critical, Forecasted = Predictive
"""
budget_name = f"{department}-monthly-budget"
budgets_client.create_budget(
AccountId=account_id,
Budget={
'BudgetName': budget_name,
'BudgetLimit': {
'Amount': str(monthly_limit),
'Unit': 'USD'
},
'TimeUnit': 'MONTHLY',
'BudgetType': 'COST',
'CostFilters': {
'TagKeyValue': [f'user:Department${department}']
}
},
NotificationsWithSubscribers=[
# %70 eşiği - Info alert
{
'Notification': {
'NotificationType': 'ACTUAL',
'ComparisonOperator': 'GREATER_THAN',
'Threshold': 70.0,
'ThresholdType': 'PERCENTAGE',
'NotificationState': 'ALARM'
},
'Subscribers': [
{
'SubscriptionType': 'SNS',
'Address': f'arn:aws:sns:us-east-1:{account_id}:budget-alerts-info'
}
]
},
# %90 eşiği - Warning alert
{
'Notification': {
'NotificationType': 'ACTUAL',
'ComparisonOperator': 'GREATER_THAN',
'Threshold': 90.0,
'ThresholdType': 'PERCENTAGE',
'NotificationState': 'ALARM'
},
'Subscribers': [
{
'SubscriptionType': 'SNS',
'Address': f'arn:aws:sns:us-east-1:{account_id}:budget-alerts-warning'
}
]
},
# %100 eşiği - Critical alert
{
'Notification': {
'NotificationType': 'ACTUAL',
'ComparisonOperator': 'GREATER_THAN',
'Threshold': 100.0,
'ThresholdType': 'PERCENTAGE',
'NotificationState': 'ALARM'
},
'Subscribers': [
{
'SubscriptionType': 'SNS',
'Address': f'arn:aws:sns:us-east-1:{account_id}:budget-alerts-critical'
},
{
'SubscriptionType': 'EMAIL',
'Address': f'{department}-leads@company.com'
}
]
},
# Aşma tahmini - Predictive alert
{
'Notification': {
'NotificationType': 'FORECASTED',
'ComparisonOperator': 'GREATER_THAN',
'Threshold': 100.0,
'ThresholdType': 'PERCENTAGE',
'NotificationState': 'ALARM'
},
'Subscribers': [
{
'SubscriptionType': 'SNS',
'Address': f'arn:aws:sns:us-east-1:{account_id}:budget-forecast-alerts'
}
]
}
]
)
print(f"Budget oluşturuldu: {budget_name} - ${monthly_limit} aylık limit")
Budget eşikleri günde yaklaşık üç kez değerlendiriliyor; bu da bir anomaliyi günlük email özetinden çok daha önce yakalıyor. FORECASTED alert’ler AWS’nin tahmin modelini kullanıyor ve bir şey üretebilmek için en az 5 haftalık geçmiş veriye ihtiyaç duyuyor.
Kaçınılması gereken yaygın hatalar:
- Çok fazla budget oluşturmak alert fatigue’e neden olur; ana maliyet merkezlerine odaklan
- FORECASTED alert’leri 5+ haftalık geçmiş veri gerektirdiğini anlamadan kullanmak
- Tag’lere göre budget filtrelemeden önce cost allocation tag’lerini aktif etmemek
- Tag’lenmemiş kaynak maliyetlerini göz ardı etmek; tag’e göre filtrelenen her raporda görünmez kalırlar
Compute Optimizer ile Right-Sizing#
AWS Compute Optimizer, CloudWatch metriklerini analiz etmek ve optimal instance type’ları, Lambda memory konfigürasyonları ve EBS volume’ları önermek için machine learning kullanıyor. Bu tipik olarak düşük implementation riski ile %20-40 maliyet tasarrufu sağlıyor.
Otomatik Right-Sizing Uygulaması#
import boto3
from typing import List, Dict
from dataclasses import dataclass
compute_optimizer = boto3.client('compute-optimizer')
ec2_client = boto3.client('ec2')
@dataclass
class RightsizingRecommendation:
instance_id: str
current_type: str
recommended_type: str
current_cost_monthly: float
recommended_cost_monthly: float
savings_monthly: float
cpu_utilization_avg: float
memory_utilization_avg: float
def get_underutilized_instances(
max_cpu_threshold: float = 40.0,
lookback_days: int = 14
) -> List[RightsizingRecommendation]:
"""
Underutilized instance'lar için Compute Optimizer önerilerini getir
Default: 14 gün boyunca ortalama <%40 CPU kullanan instance'lar
"""
recommendations = []
# EC2 instance önerilerini al
paginator = compute_optimizer.get_paginator('get_ec2_instance_recommendations')
for page in paginator.paginate():
for rec in page.get('instanceRecommendations', []):
instance_id = rec['instanceArn'].split('/')[-1]
current_instance_type = rec['currentInstanceType']
# Utilization metriklerini al
cpu_util = next(
(m['value'] for m in rec.get('utilizationMetrics', [])
if m['name'] == 'CPU'),
0.0
)
memory_util = next(
(m['value'] for m in rec.get('utilizationMetrics', [])
if m['name'] == 'MEMORY'),
0.0
)
# Instance underutilized mı kontrol et
if cpu_util < max_cpu_threshold:
# En iyi öneri seçeneğini al
if rec.get('recommendationOptions'):
best_option = rec['recommendationOptions'][0]
# Tasarrufu hesapla
estimated_savings = best_option.get('estimatedMonthlySavings', {}).get('value', 0)
recommendations.append(RightsizingRecommendation(
instance_id=instance_id,
current_type=current_instance_type,
recommended_type=best_option['instanceType'],
current_cost_monthly=estimated_savings,
recommended_cost_monthly=0,
savings_monthly=estimated_savings,
cpu_utilization_avg=cpu_util,
memory_utilization_avg=memory_util
))
# Potansiyel tasarrufa göre sırala (en yüksek önce)
return sorted(recommendations, key=lambda x: x.savings_monthly, reverse=True)
Right-Sizing Önerilerini Uygulama#
Öneriyi uygulamanın dikkatli yolu şöyle; instance’ın önce durdurulmuş olması gerekiyor:
def apply_rightsizing_recommendation(
instance_id: str,
new_instance_type: str,
dry_run: bool = True
) -> Dict:
"""
Instance type değiştirerek right-sizing önerisini uygula
Instance'ın önce durdurulması gerekir
"""
try:
response = ec2_client.describe_instances(InstanceIds=[instance_id])
instance_state = response['Reservations'][0]['Instances'][0]['State']['Name']
if instance_state != 'stopped':
return {
'success': False,
'message': f'Instance durdurulmuş olmalı. Mevcut durum: {instance_state}'
}
ec2_client.modify_instance_attribute(
InstanceId=instance_id,
InstanceType={'Value': new_instance_type},
DryRun=dry_run
)
return {
'success': True,
'message': f'{instance_id} başarıyla {new_instance_type} olarak değiştirildi',
'instance_id': instance_id,
'new_type': new_instance_type
}
except Exception as e:
return {
'success': False,
'message': f'Değişiklik başarısız: {str(e)}'
}
Bu önerileri hızlı yaymaktan çok güvenli yaymak önemli. Önce development’ta test et, sonra production instance’larının %10’una uygula, bir hafta monitor et, ardından kapsamı genişlet.
Lambda Memory Optimizasyonu#
Compute Optimizer Lambda fonksiyonlarını da analiz ediyor. İşte Lambda-spesifik öneriler nasıl alınır:
def get_lambda_optimization_recommendations():
"""
Lambda memory konfigürasyon önerilerini al
Performans etkisi olmadan memory azaltılabilen fonksiyonları döndürür
"""
paginator = compute_optimizer.get_paginator('get_lambda_function_recommendations')
recommendations = []
for page in paginator.paginate():
for rec in page.get('lambdaFunctionRecommendations', []):
function_arn = rec['functionArn']
current_memory = rec['currentMemorySize']
if rec.get('recommendationOptions'):
best_option = rec['recommendationOptions'][0]
recommended_memory = best_option['memorySize']
# Sadece öneri mevcut'tan farklıysa dahil et
if recommended_memory != current_memory:
estimated_savings = best_option.get('estimatedMonthlySavings', {})
recommendations.append({
'function_arn': function_arn,
'function_name': function_arn.split(':')[-1],
'current_memory_mb': current_memory,
'recommended_memory_mb': recommended_memory,
'savings_monthly': estimated_savings.get('value', 0),
'savings_currency': estimated_savings.get('currency', 'USD')
})
return sorted(recommendations, key=lambda x: x['savings_monthly'], reverse=True)
Compute Optimizer default olarak son 14 günlük CloudWatch metriklerini analiz ediyor. Aylık kullanım pattern’leri olan production workload’lar için enhanced infrastructure metrics’i aktif et (kaynak başına saat başına $0.0003360215, kaynak başına ayda ~$0.25 ekler) ve lookback period’u 93 güne çıkar.
Compute Optimizer ile yaygın hatalar:
- Maintenance window olmadan iş saatlerinde öneriler uygulama
- Önerilen instance type’ların uyumluluk için test edilmemesi (bazı type’lar tüm özellikleri desteklemez)
- “Under-provisioned” uyarılarını göz ardı etme; maliyet tasarrufu performanstan ödün vermemeli
- Production workload’lar için enhanced metrics aktif etmeme; 14 gün aylık spike’ları kaçırabilir
Commitment Stratejisi: Savings Plans vs Reserved Instances#
Reserved Instances, Savings Plans veya on-demand’da kalma arasında seçim yapmak, workload karakteristiklerini anlamayı gerektiriyor. İşte bir karar framework’ü:
Önerileri Programatik Olarak Almak#
AWS’nin commitment önerilerini koddan nasıl alırsın:
import boto3
from typing import List, Dict
from dataclasses import dataclass
ce_client = boto3.client('ce')
@dataclass
class CommitmentRecommendation:
recommendation_type: str # 'RI' veya 'SavingsPlan'
service: str
term: str # '1_YEAR' veya '3_YEAR'
payment_option: str
monthly_commitment: float
estimated_savings: float
estimated_savings_percentage: float
def get_savings_plan_recommendations(
term_years: int = 1,
payment_option: str = 'NO_UPFRONT'
) -> List[CommitmentRecommendation]:
"""
Savings Plans satın alma önerilerini al
Son 30 günlük kullanım pattern'lerine dayalı
"""
response = ce_client.get_savings_plans_purchase_recommendation(
SavingsPlansType='COMPUTE_SP', # veya 'EC2_INSTANCE_SP'
TermInYears=f'{term_years}_YEAR',
PaymentOption=payment_option,
LookbackPeriodInDays='THIRTY_DAYS',
AccountScope='PAYER'
)
recommendations = []
for rec in response.get('SavingsPlansPurchaseRecommendation', {}).get('SavingsPlansPurchaseRecommendationDetails', []):
savings_details = rec.get('SavingsPlansDetails', {})
recommendations.append(CommitmentRecommendation(
recommendation_type='SavingsPlan',
service='Compute',
term=f'{term_years}_YEAR',
payment_option=payment_option,
monthly_commitment=float(rec.get('HourlyCommitmentToPurchase', 0)) * 730,
estimated_savings=float(rec.get('EstimatedMonthlySavingsAmount', 0)),
estimated_savings_percentage=float(rec.get('EstimatedSavingsPercentage', 0))
))
return recommendations
Karşılaştırma Çerçevesi#
Değişken stabiliteye sahip workload’lar için karşılaştırma motoru:
def compare_commitment_options(
monthly_spend: float,
workload_stability: str # 'stable', 'variable', 'mixed'
) -> Dict:
"""
Workload karakteristiklerine göre commitment stratejilerini karşılaştır
"""
if workload_stability == 'stable':
return {
'recommendation': 'Reserved Instances',
'reason': 'Stabil, öngörülebilir workload\'lar için en yüksek indirim',
'expected_savings': monthly_spend * 0.72, # 3 yıllık RI ile %72'ye kadar
'flexibility': 'Düşük - instance family ve region\'a kilitli',
'best_for': 'Production veritabanları, sürekli çalışan servisler'
}
elif workload_stability == 'variable':
return {
'recommendation': 'Compute Savings Plans',
'reason': 'Instance family, region ve compute servisleri arasında esneklik',
'expected_savings': monthly_spend * 0.66,
'flexibility': 'Yüksek - EC2, Fargate, Lambda\'ya uygulanır',
'best_for': 'Çok servisli mimariler, evrimleşen workload\'lar'
}
else:
stable_portion = monthly_spend * 0.6
variable_portion = monthly_spend * 0.4
return {
'recommendation': 'Hibrit Strateji',
'reason': 'Baseline için RI, esneklik için Savings Plans birleştir',
'breakdown': {
'reserved_instances': {
'monthly_commitment': stable_portion,
'savings': stable_portion * 0.72
},
'savings_plans': {
'monthly_commitment': variable_portion,
'savings': variable_portion * 0.66
}
},
'expected_total_savings': (stable_portion * 0.72) + (variable_portion * 0.66),
'flexibility': 'Dengeli - her iki senaryo için optimize'
}
Üç commitment tipinin farkı:
- Reserved Instances: %72’ye kadar tasarruf, ama belirli instance family ve region’a kilitli. İhtiyaçlar değişirse RI Marketplace’te satılabilir.
- Compute Savings Plans: %66’ya kadar tasarruf, herhangi bir region veya instance family’de EC2, Fargate ve Lambda’ya uygulanır. Maximum esneklik.
- EC2 Instance Savings Plans: %72’ye kadar tasarruf, instance family ve region içinde esnek. RI’lar ve Compute SP’ler arası orta yol.
- Ödeme seçenekleri: All Upfront (en yüksek indirim), Partial Upfront (dengeli), No Upfront (en düşük indirim ama sermaye commitment’ı yok)
Savings Plans satın alması artık geri dönülmez bir karar olmaktan çıktı. AWS kısıtlamalarla birlikte 7 günlük iade penceresi sunuyor (saatlik commitment $100 veya altı, iadeler aynı takvim ayı içinde, yılda en fazla 10 iade); bu da ilk satın almanın stresini azaltıyor.
Commitment’larla yaygın hatalar:
- Baseline yerine peak kullanıma dayalı over-commitment; kullanılmayan commitment’lar sonucu
- Teknoloji evrimini düşünmeden 3 yıllık term’ler seçmek; instance type’lar hızla gelişiyor
- Satın alma sonrası RI/SP kullanımını monitor etmemek; underutilized commitment’lar para israfı
- Net strateji olmadan RI’ları ve SP’leri karıştırmak; coverage gap’leri veya overlap’lere yol açabilir
Temkinli bir ramp, hataların maliyetini düşük tutar: 1. ayda baseline kullanımın %40’ını kapsa, utilization %95’i aşarsa %60’a çıkar, uzun vadede %70-80 coverage hedefle. Kalan %20-30 esneklik ve büyüme için on-demand kalsın.
Batch Workload’lar için Spot Instances#
Spot Instances %70-90 maliyet tasarrufu sunuyor ama interruption-resilient mimari gerektiriyor. İşte ne zaman ve nasıl etkili kullanılır:
Çeşitlendirme ile Spot Fleet#
Spot Instance dayanıklılığının anahtarı instance type’ları ve availability zone’ları arasında diversification:
import boto3
from typing import List, Dict
ec2_client = boto3.client('ec2')
autoscaling_client = boto3.client('autoscaling')
def create_diversified_spot_fleet(
target_capacity: int,
instance_types: List[str],
subnets: List[str],
user_data_script: str
) -> str:
"""
Diversified Spot instance'larla EC2 Auto Scaling group oluştur
Interruption'ları minimize etmek için capacity-optimized allocation strategy kullanır
"""
# Launch template konfigürasyonu
launch_template_overrides = []
for instance_type in instance_types:
for subnet in subnets:
launch_template_overrides.append({
'InstanceType': instance_type,
'SubnetId': subnet,
'WeightedCapacity': 1.0
})
# Mixed instances policy ile Auto Scaling group oluştur
asg_config = {
'AutoScalingGroupName': 'spot-optimized-asg',
'MinSize': target_capacity,
'MaxSize': target_capacity * 2,
'DesiredCapacity': target_capacity,
'VPCZoneIdentifier': ','.join(subnets),
'MixedInstancesPolicy': {
'InstancesDistribution': {
'OnDemandBaseCapacity': 0, # Tüm Spot instance'lar
'OnDemandPercentageAboveBaseCapacity': 0,
'SpotAllocationStrategy': 'capacity-optimized',
'SpotInstancePools': len(instance_types) * len(subnets)
},
'LaunchTemplate': {
'LaunchTemplateSpecification': {
'LaunchTemplateName': 'spot-fleet-template',
'Version': '$Latest'
},
'Overrides': launch_template_overrides
}
},
'Tags': [
{
'Key': 'CostOptimization',
'Value': 'SpotInstances',
'PropagateAtLaunch': True
}
]
}
response = autoscaling_client.create_auto_scaling_group(**asg_config)
return asg_config['AutoScalingGroupName']
Buradaki her şey çeşitlendirmeye bağlı. Capacity-optimized allocation strategy kullan ve 10+ instance type ile 3+ availability zone arasında dağıt. Tek bir instance type’a sabitlenmiş fleet tek bir havuz için yarışır; o havuz daraldığı anda geri alınır.
Kesinti Yönetimi#
Spot instance’lar sonlandırmadan önce EventBridge üzerinden 2 dakika uyarı verir. Graceful shutdown için Lambda fonksiyonu:
# Spot kesintisi için Lambda fonksiyonu
import boto3
import json
ec2_client = boto3.client('ec2')
sqs_client = boto3.client('sqs')
def lambda_handler(event, context):
'''
EC2 Spot Instance kesinti uyarılarını işle (2 dakika bildirim)
Strateji: Task'ları boşalt ve işi kuyruğa geri gönder
'''
detail = event.get('detail', {})
instance_id = detail.get('instance-id')
instance_action = detail.get('instance-action')
if not instance_id:
return {'statusCode': 400, 'body': 'Olayda instance ID yok'}
# Instance detaylarını ve tag'lerini al
response = ec2_client.describe_instances(InstanceIds=[instance_id])
instance = response['Reservations'][0]['Instances'][0]
task_queue_url = get_tag_value(instance.get('Tags', []), 'TaskQueueUrl')
if task_queue_url:
sqs_client.send_message(
QueueUrl=task_queue_url,
MessageBody=json.dumps({
'action': 'drain_instance',
'instance_id': instance_id,
'interruption_time': detail.get('time')
})
)
return {'statusCode': 200, 'body': f'{instance_id} kesintisi işlendi'}
def get_tag_value(tags: list, key: str) -> str:
for tag in tags:
if tag['Key'] == key:
return tag['Value']
return None
Uzun Süren İşler için Checkpoint#
2 saatten uzun job’lar için checkpoint ile kesinti sonrası kaldığı yerden devam et:
def save_checkpoint(state, bucket, prefix='checkpoints'):
"""Job checkpoint'ini S3'e kaydet - Spot kesintisi sonrası kurtarma için"""
checkpoint_key = f"{prefix}/{state.job_id}/checkpoint-{state.current_step}.pkl"
s3_client.put_object(
Bucket=bucket,
Key=checkpoint_key,
Body=pickle.dumps(state),
ServerSideEncryption='AES256'
)
def restore_checkpoint(job_id, bucket, prefix='checkpoints'):
"""Kesintiye uğrayan job için son checkpoint'i geri yükle"""
response = s3_client.list_objects_v2(Bucket=bucket, Prefix=f"{prefix}/{job_id}/")
latest = sorted(response['Contents'], key=lambda x: x['LastModified'], reverse=True)[0]
obj = s3_client.get_object(Bucket=bucket, Key=latest['Key'])
return pickle.loads(obj['Body'].read())
Spot nereye oturur:
- İyi uyum: batch işleme, CI/CD runner’ları, ML training, veri analizi, container workload’ları
- Kötü uyum: fallback’i olmayan user-facing uygulamalar, checkpoint’siz stateful uygulamalar
- Çeşitlendirme: benzer CPU/memory oranına sahip type’ları seç (genel amaçlı compute için m5, m5a, m5n, m6i, m6a)
- Checkpoint sıklığı: 30 dakikadan uzun job’lar için 5-10 dakikada bir
S3 Depolama Optimizasyonu#
S3 depolama maliyetleri Intelligent-Tiering ve lifecycle policy’leri ile %40-95 azaltılabilir. Etkili implementation:
def create_intelligent_lifecycle_policy(
bucket_name: str,
prefix: str = '',
enable_deep_archive_tier: bool = True
) -> Dict:
"""
Intelligent-Tiering'e geçiş ve opsiyonel deep archive tier ile
S3 lifecycle policy oluştur
"""
lifecycle_rules = [
{
'ID': 'transition-to-intelligent-tiering',
'Filter': {'Prefix': prefix},
'Status': 'Enabled',
'Transitions': [{'Days': 0, 'StorageClass': 'INTELLIGENT_TIERING'}]
},
{
'ID': 'cleanup-incomplete-multipart-uploads',
'Filter': {'Prefix': prefix},
'Status': 'Enabled',
'AbortIncompleteMultipartUpload': {'DaysAfterInitiation': 7}
},
{
'ID': 'expire-old-versions',
'Filter': {'Prefix': prefix},
'Status': 'Enabled',
'NoncurrentVersionExpiration': {'NoncurrentDays': 90}
}
]
s3_client.put_bucket_lifecycle_configuration(
Bucket=bucket_name,
LifecycleConfiguration={'Rules': lifecycle_rules}
)
if enable_deep_archive_tier:
s3_client.put_bucket_intelligent_tiering_configuration(
Bucket=bucket_name,
Id='deep-archive-config',
IntelligentTieringConfiguration={
'Id': 'deep-archive-config',
'Status': 'Enabled',
'Tierings': [
{'Days': 90, 'AccessTier': 'ARCHIVE_ACCESS'},
{'Days': 180, 'AccessTier': 'DEEP_ARCHIVE_ACCESS'}
]
}
)
return {'bucket': bucket_name, 'rules_applied': len(lifecycle_rules)}
Intelligent-Tiering pratikte ne yapıyor:
- Otomatik erişim katmanları: Frequent Access, Infrequent Access (30 gün), Archive Instant Access (90 gün), opsiyonel Archive Access (90 gün), opsiyonel Deep Archive Access (180 gün)
- Tasarruf: Archive Instant Access ile %68’e kadar, Deep Archive ile %95’e kadar
- Monitoring ücreti: izlenen 1.000 obje başına $0.0025; büyük objelerde ihmal edilebilir, 128KB eşiğinin hemen üstündeki objelerde depolama ücretiyle başa baş
- Otomatik katmanlama için minimum obje boyutu: 128KB; daha küçük objeler Frequent Access katmanında kalır, izlenmez ve monitoring ücreti ödemez
- Frequent, Infrequent ve Archive Instant Access katmanlarında retrieval ücreti yok
Lambda Maliyet Optimizasyonu#
Lambda maliyeti üç bileşenden oluşur: request, duration (GB-saniye) ve opsiyonel provisioned concurrency. Her birini nasıl optimize edeceğin:
Power Tuning ile Memory Optimizasyonu#
AWS Lambda Power Tuning (açık kaynak) veri odaklı memory optimizasyonu sağlıyor:
def run_lambda_power_tuning(
function_name: str,
power_values: List[int] = [128, 256, 512, 1024, 1536, 2048, 3008],
num_invocations: int = 10,
strategy: str = 'balanced' # 'cost', 'speed' veya 'balanced'
) -> Dict:
"""
Optimal memory konfigürasyonunu bulmak için AWS Lambda Power Tuning çalıştır
GitHub: alexcasalboni/aws-lambda-power-tuning
"""
input_payload = {
'lambdaARN': f'arn:aws:lambda:us-east-1:123456789012:function:{function_name}',
'powerValues': power_values,
'num': num_invocations,
'payload': {},
'parallelInvocation': True,
'strategy': strategy
}
response = stepfunctions_client.start_execution(
stateMachineArn='arn:aws:states:us-east-1:123456789012:stateMachine:lambda-power-tuner',
input=json.dumps(input_payload)
)
# Execution bitene kadar bekle, sonra sonucu oku
execution_arn = response['executionArn']
stepfunctions_client.get_waiter('execution_succeeded').wait(executionArn=execution_arn)
output = json.loads(
stepfunctions_client.describe_execution(executionArn=execution_arn)['output']
)
return {'recommended_memory': output.get('power'), 'optimization_summary': output}
Provisioned Concurrency Maliyet Analizi#
Provisioned concurrency cold start’ı ortadan kaldırıyor; sıcak tutulan her GB için ayda yaklaşık $11 ödüyorsun. Bu takasın ne zaman değdiği:
def calculate_lambda_costs(config: LambdaCostBreakdown) -> Dict:
"""
Provisioned concurrency ile ve olmadan Lambda maliyetlerini hesapla
Provisioned concurrency'nin maliyet-etkin olup olmadığını belirlemeye yardımcı olur
"""
price_per_request = 0.20 / 1_000_000
price_per_gb_second = 0.0000166667
provisioned_price_per_gb_second = 0.0000041667
memory_gb = config.memory_mb / 1024
duration_seconds = config.avg_duration_ms / 1000
request_cost = config.invocations_monthly * price_per_request
compute_cost = config.invocations_monthly * duration_seconds * memory_gb * price_per_gb_second
total_on_demand = request_cost + compute_cost
provisioned_cost = 0
if config.provisioned_concurrency:
seconds_per_month = 730 * 3600
provisioned_cost = (config.provisioned_concurrency * memory_gb *
seconds_per_month * provisioned_price_per_gb_second)
total_with_provisioned = request_cost + compute_cost + provisioned_cost
return {
'on_demand_cost_monthly': round(total_on_demand, 2),
'provisioned_cost_monthly': round(total_with_provisioned, 2),
# 1.5x, "gecikme primi ödemeye değer" için başlangıç eşiği;
# kendi latency bütçene göre ayarla
'recommendation': 'Provisioned concurrency kullan' if total_with_provisioned < total_on_demand * 1.5 else 'On-demand\'da kal'
}
Lambda faturasını asıl hareket ettirenler:
- Memory allocation CPU ve network payını da belirliyor; daha fazla memory çoğu zaman daha kısa duration ve daha düşük toplam demek
- Pratikte 1024-1536MB aralığı maliyet/performans dengesi için iyi bir başlangıç, ama karar vermeden önce ölç
- Compute Savings Plans, Lambda duration ücretlerine de uygulanıyor (%17’ye kadar indirim)
- Kodun kendi çalışma süresini kısaltmak genelde memory tuning’i geçer ve aylık ek maliyeti yoktur
- Provisioned concurrency, sıkı latency bütçesi olan user-facing API’lere aittir; seyrek çalışan fonksiyonlara değil
Maliyet Tahsisi ve Etiketleme#
Kapsamlı tagging ekipler, projeler ve environment’lar arasında maliyet attribution sağlar. Production-ready yaklaşım:
def create_tagging_compliance_rule():
"""
Tag'lenmemiş kaynakları tespit eden AWS Config kuralı oluştur
Environment, Project, CostCenter tag'lerini zorunlu kılar
"""
config_client.put_config_rule(ConfigRule={
'ConfigRuleName': 'required-tags-compliance',
'Description': 'Kaynakların gerekli cost allocation tag\'lerine sahip olduğunu kontrol et',
'Source': {'Owner': 'AWS', 'SourceIdentifier': 'REQUIRED_TAGS'},
'InputParameters': '{"tag1Key":"Environment","tag2Key":"Project","tag3Key":"CostCenter"}',
'Scope': {
'ComplianceResourceTypes': [
'AWS::EC2::Instance', 'AWS::RDS::DBInstance', 'AWS::S3::Bucket',
'AWS::Lambda::Function', 'AWS::DynamoDB::Table'
]
}
})
return {'rule_name': 'required-tags-compliance'}
Maliyet Tahsis Raporlaması#
Tag’lere göre gruplanmış aylık maliyet raporları chargeback/showback için:
def generate_cost_allocation_report(
month: str,
group_by_tags: List[str] = ['Project', 'Environment', 'CostCenter']
) -> Dict:
"""
Tag'lere göre gruplanmış maliyet tahsis raporu oluştur
Dikkat gerektiren tag'lenmemiş maliyetleri tespit eder
"""
start_date = datetime.strptime(f'{month}-01', '%Y-%m-%d')
if start_date.month == 12:
end_date = start_date.replace(year=start_date.year + 1, month=1, day=1)
else:
end_date = start_date.replace(month=start_date.month + 1, day=1)
end_date = end_date - timedelta(days=1)
cost_by_tags = {}
for tag_key in group_by_tags:
response = ce_client.get_cost_and_usage(
TimePeriod={'Start': start_date.strftime('%Y-%m-%d'), 'End': end_date.strftime('%Y-%m-%d')},
Granularity='MONTHLY',
Metrics=['UnblendedCost'],
GroupBy=[{'Type': 'TAG', 'Key': tag_key}]
)
tag_costs = {}
for result in response['ResultsByTime']:
for group in result['Groups']:
tag_value = group['Keys'][0].split('$')[1] if '$' in group['Keys'][0] else 'Untagged'
tag_costs[tag_value] = float(group['Metrics']['UnblendedCost']['Amount'])
cost_by_tags[tag_key] = tag_costs
total_response = ce_client.get_cost_and_usage(
TimePeriod={'Start': start_date.strftime('%Y-%m-%d'), 'End': end_date.strftime('%Y-%m-%d')},
Granularity='MONTHLY', Metrics=['UnblendedCost'])
total_cost = float(total_response['ResultsByTime'][0]['Total']['UnblendedCost']['Amount'])
return {'month': month, 'total_cost': round(total_cost, 2), 'breakdown_by_tags': cost_by_tags}
Ayakta kalan tag’leme kuralları:
- Gerekli tag’ler: Environment (production/staging/development), Project, CostCenter, Owner
- Cost allocation tag’lerini kullanmadan önce Billing Console’da aktif et; geriye dönük hiçbir şey izlenmiyor
- İzin verilen tag değerlerini sabitle;
production/prod/Productiontek bir maliyet merkezini sessizce üçe böler - Tag’lenmemiş kaynakları AWS Config ile tespit et ve kaynak oluşturulurken zorla; elle düzeltmek pahalı
- Hedef: <%10 tag’lenmemiş kaynak maliyeti
Optimizasyon Teknikleri Karşılaştırması#
| Teknik | Tasarruf | Uygulama Çabası | Risk | En İyi Kullanım |
|---|---|---|---|---|
| Right-Sizing (Compute Optimizer) | %20-40 | Düşük | Düşük | Over-provisioned instance’lar |
| Savings Plans | %66’ya kadar | Düşük | Orta | Öngörülebilir baseline |
| Reserved Instances | %72’ye kadar | Düşük | Orta | Stabil workload’lar |
| Spot Instances | %70-90 | Orta | Orta | Fault-tolerant workload’lar |
| Lambda memory optimizasyonu | %20-50 | Düşük | Düşük | Lambda-ağır mimariler |
| S3 Intelligent-Tiering | %40-95 | Düşük | Düşük | Büyük depolama, karışık erişim |
| Instance scheduling | %70 | Orta | Düşük | Non-production environment’lar |
Sıranın İşlemediği Durumlar#
Önce görünürlük, sonra right-sizing, en son commitment sırası production hesaplarının çoğunda geçerli; sebebi de sıralama riski. Ölçülmemiş ve fazla boyutlandırılmış kullanımın üzerine alınan bir Savings Plan, yanlış baseline’ı bir ila üç yıl boyunca sabitliyor. Önce yapılan bir right-sizing turu ise satın alman gereken commitment’ı doğrudan küçültüyor.
Bu sırayı bozmayı haklı çıkaran üç durum var. Şekli senin kontrolün dışında sabitlenmiş bir workload (belirli bir instance family’ye bağlı lisanslı bir veritabanı gibi) doğrudan Reserved Instance alabilir; right-sizing onu zaten oynatmayacak. Acil bir harcama tavanı altındaki bir hesap önce No Upfront commitment alıp right-sizing’i sonraya bırakabilir; indirim aktive edildiği saatten itibaren işliyor ve No Upfront peşin sermaye istemiyor. Kesintiye zaten dayanıklı bir batch katmanı da hiçbir ölçüm yapılmadan Spot’a geçebilir; %70-90 indirim, right-sizing’in orada bulacağı her şeyi gölgede bırakır.
Hangi noktadan girersen gir, ilk satın almadan önce cost allocation tag’lerini aktif et. Tag’ler geriye dönük çalışmıyor; onlarsız geçen her ay, sonradan kimsenin kime yazacağını bilemeyeceği bir harcama ayı demek.
Kaynaklar#
- AWS Cost Explorer (yeni sekmede açılır) - Maliyet verilerini analiz etmek ve trendleri görselleştirmek için temel araç
- AWS Budgets (yeni sekmede açılır) - Bütçe eşikleri ve proaktif uyarılar
- AWS Compute Optimizer (yeni sekmede açılır) - EC2, Lambda ve EBS için otomatik öneriler
- Savings Plans (yeni sekmede açılır) - Esnek commitment seçenekleri ve tasarruf modelleri
- Spot Instances (yeni sekmede açılır) - Kesintiye dayanıklı iş yükleri için maliyet optimizasyonu
- S3 Intelligent-Tiering (yeni sekmede açılır) - Erişim paternine göre otomatik depolama geçişleri
- FinOps Foundation (yeni sekmede açılır) - Bulut maliyet yönetimi pratikleri ve topluluk kaynakları
İlgili yazılar
Global uygulamalar için AWS edge computing çözümlerini seçme ve uygulama üzerine pratik örnekler ve maliyet optimizasyonu stratejileri içeren kapsamlı teknik rehber.
aws · cloudfront · lambda +6
İç servis katmanı kurmadan önce kurup kurmayacağınıza karar verin. Katmanın çağrı başına maliyeti, VPC Lattice'in kazandığı hacim ve direct invoke'un hâlâ kazandığı an.
aws · aws-cdk · lambda +4
Private REST API gRPC’yi yapısal olarak taşıyamaz ve gRPC konuşan her AWS yüzeyi Lambda hedeflerini dışlar. gRPC’den neyi tutmalı, neyi bırakmalı.
aws · aws-cdk · lambda +4
Private REST API, onu açan resource policy, route bazlı AWS_IAM yetkileri, iki CDK stack'i ve Node 22 Lambda'dan çağrıyı imzalamak.
aws · aws-cdk · lambda +4
SigV4 hangi servisin çağırdığını kanıtlar, kimin adına çağırdığını değil. Doğrulanmış özneyi nasıl taşırsınız ve taşıma katmanı gerçekte neyi şifreler.
aws · aws-cdk · lambda +4