FinOps 2.0: AWS/Cloud Faturasını Otomatik Kısıtlayan Event-Driven Mimariler
Her ayın sonunda gelen o meşhur kabarık faturayı görüp Slack kanallarında “Bu p3.8xlarge instance’ı kim açık unuttu?” 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ı, cloud maliyet yönetimini sadece 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 nasıl kuracağımızı adım adım ele alıyoruz.
Geleneksel Bütçe Uyarıları Neden İşe Yaramaz?
AWS Budgets veya CloudWatch Billing Alarms kurduğunuzda, arka planda çalışan 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 sadece felaketin faturasını tebliğ eder.
Bize gereken şey: Anomaly Detection + Immediate Event Routing + Automated Remediation. Yani faturanın toplamını değil, kaynak oluşturma hızını (velocity), beklenmeyen metrik sıçramalarını ve yetim (orphaned) kaynakları anlık yakalayan bir mimari.
Event-Driven FinOps Mimarisi Nasıl Çalışır?
Kurgulayacağı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 edildiğinde doğrudan EventBridge’e event fırlatabilir. Aşağıdaki CloudFormation / EventBridge Pattern tanımı, belirlenen eşik ü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 etkisinin beklenen baseline’a göre en az %50 saptığı senaryoları yakalar. Anlamsız mikro dalgalanmalar için 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, tag eksikliği olan veya anomali üreten instance’ları hedef alarak devreye girer. Dry-run kontrolü ve güvenli tag filtresi içerir:
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": []}
Burada kritik olan felsefe şudur: Fail-safe by default. Prod hesabına dokunmamak için ortam etiketlerini katı şekilde izole ediyoruz ve geliştiricilere acil durumlarda kullanmaları için FinOpsImmunity=true gibi bir bypass tag mekanizması tanıyoruz.
Adım 3: Yetim (Orphaned) EBS ve Unassociated EIP Temizliği
Terraform ile altyapıyı yıkan ama lifecycle { prevent_destroy = true } veya unattached volume’leri unutan mühendislerin faturaya hediyesidir yetim kaynaklar. 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: 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ıdır:
- 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
Eğer EKS üzerinde Karpenter veya Cluster Autoscaler kullanıyorsanız, container’ların crashloop’a girip sürekli yeni node istemesi sonucu 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 alarm ile nöbetçi SRE’ye Slack üzerinden interactive button ile onay düşürmek en sağlıklı yaklaşımdır.
Least Privilege IAM İzni
FinOps otomasyon Lambda’nızın AdministratorAccess almasına gerek yoktur. Sadece 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": "*"
}
]
}
Özet: FinOps Kültür Değil, Kod Tabanıdır
Mühendisleri spreadsheet tablolarına bakarak eğitmek bir yere kadar çalışır. İ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, hem CFO’nuz daha rahat uyur hem de nöbetçi SRE hafta sonu faturayı değil asıl işi olan sistem güvenilirliğini düşünür.