FinOps 2.0: AWS/Cloud Faturasını Otomatik Kısıtlayan Event-Driven Mimariler
Her ayın sonunda gelen kabarık faturayı görüp Slack kanallarında “Bu p3.8xlarge instance’ı kim açık unuttu?” diye cadı avı başlatmaktan yorulduysanız yalnız değilsiniz. Geleneksel bütçe uyarıları reaktiftir; fatura eşiği aşıldığında iş işten çoktan geçmiş olur. Modern FinOps yaklaşımı ise cloud maliyet yönetimini raporlama tablosu olmaktan çıkarıp AWS altyapısında gerçek zamanlı çalışan bir otomasyon zincirine dönüştürmeyi gerektiriyor.
Bu yazıda gecikmeli Cost and Usage Report (CUR) analizlerini bir kenara bırakıp EventBridge, CloudWatch Metric Streams ve Lambda tabanlı, event-driven bir maliyet kısıtlama (circuit breaker) mimarisini adım adım kuruyoruz.
Geleneksel bütçe uyarıları neden işe yaramaz?
AWS Budgets veya CloudWatch Billing Alarms kurduğunuzda arka plandaki telemetri verisi gerçek zamanlı değildir. CloudWatch billing metrikleri genellikle 8 ila 14 saatlik bir gecikmeyle (delay) güncellenir. Bir developer cuma akşamı yanlışlıkla 10 nodeluk bir GPU kümesini ayağa kaldırıp unuttuysa, pazartesi sabahı gelen e-posta yalnızca felaketin faturasını tebliğ eder.
Bize gereken şey şu: Anomaly Detection + Immediate Event Routing + Automated Remediation. Faturanın toplamını değil, kaynak oluşturma hızını (velocity), beklenmeyen metrik sıçramalarını ve yetim (orphaned) kaynakları anında yakalayan bir mimari.
Event-driven FinOps mimarisi nasıl çalışır?
Kuracağımız mimari üç ana ayaktan oluşuyor:
- Sinyal katmanı: AWS Cost Anomaly Detection, AWS Health Events, CloudTrail Management Events ve Config Rule ihlalleri.
- Yönlendirme katmanı (Routing): Amazon EventBridge default bus veya custom bus. Gelen payload’u filtreleyip uygun remediation hedefine yönlendirir.
- Aksiyon katmanı (Execution): AWS Lambda veya Step Functions. Kaynağı durdurur, scale-in yapar, volume snapshot’ı alıp diski siler ya da doğrudan security group seviyesinde trafiği keser.
Adım 1: Cost Anomaly Detection için EventBridge kuralı
AWS Cost Anomaly Detection, makine öğrenmesi modelleriyle harcama paternlerinizi analiz eder. Bir anomali tespit ettiğinde doğrudan EventBridge’e event gönderebilir. Aşağıdaki CloudFormation / EventBridge Pattern tanımı, belirlenen eşiğin üzerindeki anomalileri dinler:
{
"source": ["aws.cost-anomaly-detection"],
"detail-type": ["Cost Anomaly Detected"],
"detail": {
"anomalyScore": [{ "numeric": [">=", 80] }],
"impact": {
"totalImpactPercentage": [{ "numeric": [">=", 50] }]
}
}
}
Bu pattern, anomali skoru 80’in üzerinde olan ve harcama etkisi beklenen baseline’dan en az %50 sapan senaryoları yakalar. Anlamsız mikro dalgalanmalarda Lambda tetiklenmez.
Adım 2: Dev/Staging ortamlarında kaçak kaynakları kesen Lambda
Birçok şirkette faturayı patlatan şey prod ortamı değil, dev/stage hesaplarında “test edip sileceğim” denilerek unutulan yüksek profilli kaynaklardır. Aşağıdaki Python tabanlı Lambda fonksiyonu, dev/staging ortamında çalışan pahalı instance ailelerini (GPU, yüksek bellekli tipler) hedef alır ve bağışıklık tag’i taşıyanları atlar:
import boto3
import os
import json
import logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)
ec2 = boto3.client('ec2')
TAG_IMMUNITY_KEY = "FinOpsImmunity"
ALLOWED_ENVIRONMENTS = ["development", "staging", "sandbox"]
def lambda_handler(event, context):
logger.info(f"Received event: {json.dumps(event)}")
# Hedef filtreleri: Sadece dev/stage hesaplar ve belirli instance tipleri
filters = [
{'Name': 'instance-state-name', 'Values': ['running']},
{'Name': 'tag:Environment', 'Values': ALLOWED_ENVIRONMENTS}
]
response = ec2.describe_instances(Filters=filters)
instances_to_stop = []
for reservation in response['Reservations']:
for instance in reservation['Instances']:
instance_id = instance['InstanceId']
instance_type = instance['InstanceType']
tags = {t['Key']: t['Value'] for t in instance.get('Tags', [])}
# Bağışıklık tag'i var mı?
if tags.get(TAG_IMMUNITY_KEY, "false").lower() == "true":
logger.info(f"Instance {instance_id} has immunity tag. Skipping.")
continue
# Büyük/Pahalı aileleri hedef al (GPU, High Memory vb.)
if instance_type.startswith(('p3', 'p4', 'g4', 'g5', 'r5b', 'x2gd')):
logger.warning(f"High cost instance detected without immunity: {instance_id} ({instance_type})")
instances_to_stop.append(instance_id)
if instances_to_stop:
logger.info(f"Stopping instances: {instances_to_stop}")
ec2.stop_instances(InstanceIds=instances_to_stop)
return {"status": "SUCCESS", "stopped_instances": instances_to_stop}
return {"status": "NO_ACTION", "stopped_instances": []}
Buradaki felsefe şu: Fail-safe by default. Prod hesabına dokunmamak için ortam etiketlerini katı biçimde izole ediyoruz. Geliştiricilere de acil durumlarda kullanabilmeleri için FinOpsImmunity=true gibi bir bypass tag mekanizması tanıyoruz.
Adım 3: Yetim (Orphaned) EBS ve Unassociated EIP temizliği
Yetim kaynaklar, Terraform ile altyapıyı yıkıp lifecycle { prevent_destroy = true } ayarını ya da unattached volume’leri unutan mühendislerin faturaya hediyesidir. CloudWatch Event yerine her gece EventBridge Schedule (Cron) ile tetiklenen hafif bir garbage collector kuralı tanımlayalım:
# AWS CLI ile Unattached (Available) EBS Volume'leri sorgulama
aws ec2 describe-volumes \
--filters Name=status,Values=available \
--query "Volumes[*].{ID:VolumeId,Size:Size,Created:CreateTime}" \
--output table
Bunu otomatize ederken dikkat edin: Silmeden önce mutlaka snapshot alın. Event-driven mimarilerde en büyük risk, veri kaybına yol açabilecek agresif otomasyonlardır. Lambda’nın adımları şöyle olmalı:
- Volume state
availablemı? - Tag’lerinde
SnapshotBeforeDelete=trueoluştur. - Snapshot tetikle, snapshot tamamlandığında EventBridge’den gelen
EC2 Snapshot Successfuleventiyle eski volume’ü drop et.
Kubernetes / Karpenter için runaway cost breaker
EKS üzerinde Karpenter veya Cluster Autoscaler kullanıyorsanız, container’lar crashloop’a girip sürekli yeni node istediğinde dakikalar içinde onlarca nodeluk bir cluster anomalisi yaşayabilirsiniz.
Karpenter’ın NodePool limitleri bu işin ilk savunma hattıdır, ama tek başına yetmez. Event-driven bir Lambda ile Karpenter NodePool CRD’sinin limitlerini dinamik olarak sıkılaştırabilirsiniz:
apiVersion: karpenter.sh/v1beta1
kind: NodePool
metadata:
name: default
spec:
limits:
cpu: "100"
memory: 400Gi
template:
spec:
requirements:
- key: "karpenter.sh/capacity-type"
operator: In
values: ["spot"]
- key: "karpenter.k8s.aws/instance-category"
operator: In
values: ["c", "m", "r"]
Limit aşıldığında cluster’a yeni node eklenmesi engellenir. Bu aşamada Prometheus Alertmanager’dan EventBridge Webhook’una düşen bir alarmla nöbetçi SRE’ye Slack üzerinden interactive button ile onay göndermek en sağlıklı yaklaşımdır.
Least privilege IAM izni
FinOps otomasyon Lambda’nızın AdministratorAccess almasına gerek yok. Yalnızca durdurma ve tanımlama yetkilerini içeren sıkı bir IAM policy tanımlayın:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "FinOpsEC2Operations",
"Effect": "Allow",
"Action": [
"ec2:DescribeInstances",
"ec2:DescribeVolumes",
"ec2:StopInstances",
"ec2:CreateSnapshot",
"ec2:DeleteVolume"
],
"Resource": "*"
}
]
}
FinOps kültür değil, kod tabanıdır
Mühendisleri spreadsheet tablolarına bakarak eğitmek bir yere kadar işe yarar. İnsan hata yapar, acil durumlarda makineler açık unutulur. FinOps 2.0, kuralları şirket wikisine yazmak yerine EventBridge kurallarına, Lambda fonksiyonlarına ve Kubernetes admission controller’larına yazmaktır.
Reaktif fatura şoklarından proaktif event-driven kısıtlamalara geçtiğinizde CFO’nuz daha rahat uyur, nöbetçi SRE de hafta sonu faturayı değil, asıl işi olan sistem güvenilirliğini düşünür.