Bulut Maliyet Raporlama ve Departman Bazlı Dağıtım
Bulut faturası geldiğinde “Bu para nereye gitti?” sorusunu sormak zorunda kaldıysanız, yalnız değilsiniz. Çoğu şirkette bulut maliyetleri başlangıçta tek bir havuzda toplanıyor, sonra aylar geçiyor ve finans ekibi “Geliştirme mi daha çok harcıyor, pazarlama mı?” diye sormaya başlıyor. İşte tam bu noktada maliyet raporlama ve departman bazlı dağıtım devreye giriyor. Bu yazıda AWS, Azure ve GCP üzerinde bu yapıyı nasıl kuracağınızı, etiketleme stratejisinden gerçek raporlara kadar adım adım ele alacağız.
Neden Departman Bazlı Maliyet Takibi?
Bulut harcamalarını tek bir hesapta toplamak, başlangıçta basit görünür. Ancak şirket büyüdükçe bu yaklaşım ciddi sorunlar yaratır. Geliştirme ekibi test ortamını kapatmayı unutuyor, pazarlama ekibi büyük bir veri analizi başlatıyor, finans ekibi ise hiçbirinden habersiz. Ay sonu geldiğinde 50.000 dolarlık bir fatura geliyor ve kimse sorumluluk almıyor.
Departman bazlı maliyet takibinin getirdikleri:
- Hesap verebilirlik: Her departman kendi harcamasını görüyor ve sahipleniyor
- Bütçe kontrolü: Departman başına bütçe limitleri belirleyebiliyorsunuz
- Optimizasyon fırsatları: Hangi kaynakların atıl kaldığını departman bazında görebiliyorsunuz
- Showback ve Chargeback: Kullanıma göre maliyeti ilgili departmana yansıtabiliyorsunuz
Etiketleme (Tagging) Stratejisi: Her Şeyin Temeli
Departman bazlı maliyet takibinin kalbi iyi bir etiketleme stratejisidir. Etiket olmadan hiçbir raporlama aracı size anlamlı veri veremez.
Standart bir etiket şeması şu alanları içermeli:
- Department: engineering, marketing, finance, hr, operations
- CostCenter: 1001, 1002, 1003 gibi muhasebe merkezleri
- Project: hangi projeye ait olduğu
- Environment: production, staging, development
- Owner: kaynaktan sorumlu kişinin kullanıcı adı
- ManagedBy: terraform, ansible, manual gibi yönetim şekli
AWS’de Etiket Politikası Oluşturma
AWS Organizations kullanıyorsanız, etiket politikalarını merkezi olarak zorunlu kılabilirsiniz.
# AWS CLI ile etiket politikası oluşturma
aws organizations create-policy
--name "DepartmentTaggingPolicy"
--type TAG_POLICY
--description "Departman etiketlerini zorunlu kilar"
--content file://tag-policy.json
tag-policy.json dosyasının içeriği:
# tag-policy.json
cat > tag-policy.json << 'EOF'
{
"tags": {
"Department": {
"tag_key": {
"@@assign": "Department"
},
"tag_value": {
"@@assign": [
"engineering",
"marketing",
"finance",
"hr",
"operations"
]
},
"enforced_for": {
"@@assign": [
"ec2:instance",
"ec2:volume",
"rds:db",
"s3:bucket"
]
}
}
}
}
EOF
Politikayı oluşturduktan sonra hedef OU’ya bağlayın:
# Politikayı OU'ya ata
aws organizations attach-policy
--policy-id p-xxxxxxxxxxxx
--target-id ou-xxxxxxxxxxxx
# Etiket uyumluluğunu kontrol et
aws resourcegroupstaggingapi get-compliance-summary
--target-id-filters ou-xxxxxxxxxxxx
--region-filters us-east-1
Terraform ile Zorunlu Etiketleme
Eğer altyapınızı Terraform ile yönetiyorsanız, required_tags şartını modül seviyesinde uygulayabilirsiniz.
# variables.tf - Zorunlu etiket değişkenleri
variable "mandatory_tags" {
description = "Tum kaynaklara uygulanacak zorunlu etiketler"
type = object({
Department = string
CostCenter = string
Project = string
Environment = string
Owner = string
})
validation {
condition = contains(
["engineering", "marketing", "finance", "hr", "operations"],
var.mandatory_tags.Department
)
error_message = "Department degeri gecerli bir departman olmali."
}
}
# main.tf - EC2 instance ornek kullanimi
resource "aws_instance" "app_server" {
ami = var.ami_id
instance_type = var.instance_type
tags = merge(var.mandatory_tags, {
Name = "${var.mandatory_tags.Department}-${var.mandatory_tags.Project}-server"
ManagedBy = "terraform"
})
}
AWS Cost Explorer ile Departman Raporları
AWS Cost Explorer, etiketlerinizi aktive ettikten sonra oldukça güçlü bir raporlama aracına dönüşüyor. Ancak API üzerinden otomatik raporlar oluşturmak için CLI kullanımını bilmek şart.
#!/bin/bash
# Departman bazli aylik maliyet raporu
# Onceki ay icin tarih hesaplama
CURRENT_MONTH=$(date +%Y-%m-01)
LAST_MONTH=$(date -d "1 month ago" +%Y-%m-01)
# AWS Cost Explorer API ile departman bazli rapor
aws ce get-cost-and-usage
--time-period Start=${LAST_MONTH},End=${CURRENT_MONTH}
--granularity MONTHLY
--metrics "BlendedCost" "UnblendedCost" "UsageQuantity"
--group-by Type=TAG,Key=Department
--output json | jq -r '
.ResultsByTime[].Groups[] |
"(.Keys[0]): $(.Metrics.UnblendedCost.Amount | tonumber | floor)"
'
Bu script çalıştığında şuna benzer bir çıktı alırsınız:
Department$engineering: $12450
Department$marketing: $3200
Department$finance: $890
Department$operations: $5670
Bütçe Alarmları Kurma
Her departman için ayrı bütçe ve alarm tanımlamak, sürpriz faturaları önlemenin en iyi yolu.
#!/bin/bash
# Departman bazli butce olusturma scripti
DEPARTMENTS=("engineering" "marketing" "finance" "hr" "operations")
declare -A BUDGETS=(
["engineering"]=15000
["marketing"]=5000
["finance"]=2000
["hr"]=1000
["operations"]=8000
)
for dept in "${DEPARTMENTS[@]}"; do
budget=${BUDGETS[$dept]}
alert_80=$((budget * 80 / 100))
aws budgets create-budget
--account-id ${AWS_ACCOUNT_ID}
--budget '{
"BudgetName": "'"${dept}-monthly-budget"'",
"BudgetLimit": {
"Amount": "'"${budget}"'",
"Unit": "USD"
},
"CostFilters": {
"TagKeyValue": ["user:Department$'"${dept}"'"]
},
"TimeUnit": "MONTHLY",
"BudgetType": "COST"
}'
--notifications-with-subscribers '[
{
"Notification": {
"NotificationType": "ACTUAL",
"ComparisonOperator": "GREATER_THAN",
"Threshold": 80,
"ThresholdType": "PERCENTAGE"
},
"Subscribers": [
{
"SubscriptionType": "EMAIL",
"Address": "'"${dept}[email protected]"'"
}
]
}
]'
echo "${dept} departmani icin ${budget} USD butce olusturuldu"
done
Azure’da Maliyet Yönetimi
Azure Cost Management + Billing, departman bazlı takip için Management Groups ve Subscriptions hiyerarşisini kullanıyor. Buna ek olarak Azure Policy ile etiketleme kurallarını zorunlu kılabilirsiniz.
# Azure CLI ile departman bazli maliyet sorgusu
# Once Management Group yapisi kur
az account management-group create
--name "engineering"
--display-name "Engineering Department"
az account management-group create
--name "marketing"
--display-name "Marketing Department"
# Departman bazli maliyet sorgusu - JSON ciktisi
az consumption usage list
--billing-period-name $(date +%Y%m)
--query "[].{Department:tags.Department, Cost:pretaxCost, Service:consumedService}"
--output json | jq '
group_by(.Department) |
map({
department: .[0].Department,
total_cost: (map(.Cost | tonumber) | add)
})
'
Azure Policy ile etiketleri zorunlu kılmak:
# Etiket zorunluluk politikasi olustur
az policy definition create
--name "require-department-tag"
--display-name "Department etiketi zorunlu"
--description "Tum kaynaklar Department etiketi icermeli"
--rules '{
"if": {
"field": "tags[Department]",
"exists": "false"
},
"then": {
"effect": "deny"
}
}'
--mode All
# Politikayi subscription seviyesinde ata
az policy assignment create
--name "require-department-tag-assignment"
--policy "require-department-tag"
--scope "/subscriptions/${SUBSCRIPTION_ID}"
GCP’de Maliyet Dağıtımı
Google Cloud Platform’da Labels ve Projects hiyerarşisi maliyet takibinin temelini oluşturuyor. BigQuery ile entegrasyon ise raporlamayı güçlü kılıyor.
# GCP billing export'u BigQuery'e aktarma
# Once billing export aktif et
gcloud billing accounts list
# BigQuery dataset olustur
bq mk
--dataset
--description "Bulut maliyet verileri"
--location EU
${PROJECT_ID}:billing_data
# Departman bazli maliyet sorgusu - BigQuery SQL
bq query
--use_legacy_sql=false
--format=prettyjson
'
SELECT
labels.value AS department,
SUM(cost) AS total_cost,
SUM(credits.amount) AS total_credits,
SUM(cost) + SUM(credits.amount) AS net_cost,
invoice.month AS billing_month
FROM
`'"${PROJECT_ID}"'.billing_data.gcp_billing_export_v1_*`,
UNNEST(labels) AS labels,
UNNEST(credits) AS credits
WHERE
labels.key = "department"
AND _TABLE_SUFFIX BETWEEN
FORMAT_DATE("%Y%m", DATE_SUB(CURRENT_DATE(), INTERVAL 1 MONTH))
AND FORMAT_DATE("%Y%m", CURRENT_DATE())
GROUP BY
department,
billing_month
ORDER BY
total_cost DESC
'
Gerçek Dünya Senaryosu: Showback Sistemi Kurma
Bir e-ticaret şirketinin sysadmin ekibindesiniz. 4 departman var: Engineering, Marketing, Finance ve Operations. Ayda yaklaşık 80.000 dolar bulut harcaması yapılıyor. Finans ekibi her departmanın maliyetini raporlamak istiyor.
İşte bu durumda kullanabileceğiniz tam bir showback scripti:
#!/bin/bash
# showback_report.sh - Aylik departman maliyet raporu
# Kullanim: ./showback_report.sh 2024-01 [aws|azure|gcp]
REPORT_MONTH=${1:-$(date -d "last month" +%Y-%m)}
CLOUD_PROVIDER=${2:-"aws"}
REPORT_DIR="/var/reports/cloud-costs"
REPORT_DATE=$(date +%Y%m%d_%H%M%S)
mkdir -p ${REPORT_DIR}
echo "=== Aylik Departman Maliyet Raporu ==="
echo "Donem: ${REPORT_MONTH}"
echo "Bulut: ${CLOUD_PROVIDER}"
echo "Uretim tarihi: $(date)"
echo ""
START_DATE="${REPORT_MONTH}-01"
END_DATE=$(date -d "${START_DATE} +1 month" +%Y-%m-%d)
if [ "$CLOUD_PROVIDER" == "aws" ]; then
# AWS Cost Explorer API sorgusu
COST_DATA=$(aws ce get-cost-and-usage
--time-period Start=${START_DATE},End=${END_DATE}
--granularity MONTHLY
--metrics "UnblendedCost"
--group-by Type=TAG,Key=Department
--output json)
TOTAL=0
echo "$COST_DATA" | jq -r '.ResultsByTime[].Groups[] | "(.Keys[0])|(.Metrics.UnblendedCost.Amount)"' |
while IFS='|' read -r dept cost; do
dept_clean=$(echo $dept | sed 's/Department$//')
cost_int=$(echo $cost | awk '{printf "%.2f", $1}')
echo " ${dept_clean}: $${cost_int}"
done
TOTAL=$(echo "$COST_DATA" | jq -r '.ResultsByTime[].Total.UnblendedCost.Amount // "0"')
echo ""
echo "Toplam: $$(echo $TOTAL | awk '{printf "%.2f", $1}')"
fi
# Raporu dosyaya kaydet
REPORT_FILE="${REPORT_DIR}/showback_${REPORT_MONTH}_${REPORT_DATE}.txt"
$0 $@ 2>/dev/null > ${REPORT_FILE}
# Mail ile gonder (mailutils gerekli)
if command -v mail &> /dev/null; then
cat ${REPORT_FILE} | mail
-s "Aylik Bulut Maliyet Raporu - ${REPORT_MONTH}"
[email protected]
echo "Rapor ${REPORT_FILE} konumuna kaydedildi ve mail gonderildi."
fi
Anomali Tespiti ve Otomatik Uyarılar
Sadece ay sonu rapor almak yetmez. Anlık anomalileri de yakalamak gerekiyor. AWS Cost Anomaly Detection bu iş için biçilmiş kaftan.
#!/bin/bash
# Anomali tespiti kurulum scripti
# Her departman icin anomali monitoru olustur
DEPARTMENTS=("engineering" "marketing" "finance" "operations")
for dept in "${DEPARTMENTS[@]}"; do
# Monitor olustur
MONITOR_ARN=$(aws ce create-anomaly-monitor
--anomaly-monitor '{
"MonitorName": "'"${dept}-anomaly-monitor"'",
"MonitorType": "CUSTOM",
"MonitorSpecification": {
"Tags": {
"Key": "Department",
"Values": ["'"${dept}"'"]
}
}
}'
--query 'MonitorArn'
--output text)
echo "${dept} monitoru olusturuldu: ${MONITOR_ARN}"
# Abonelik olustur - %20 artis veya 500 dolar asiminda uyar
aws ce create-anomaly-subscription
--anomaly-subscription '{
"SubscriptionName": "'"${dept}-anomaly-alert"'",
"MonitorArnList": ["'"${MONITOR_ARN}"'"],
"Subscribers": [
{
"Address": "'"${dept}[email protected]"'",
"Type": "EMAIL"
}
],
"Threshold": 20,
"Frequency": "DAILY"
}'
echo "${dept} icin anomali aboneligi olusturuldu"
sleep 2
done
Maliyet Dağıtımında Paylaşılan Kaynaklar Sorunu
En sık karşılaşılan sorunlardan biri paylaşılan kaynakların maliyetini nasıl bölüştüreceğinizdir. Örneğin tüm departmanların kullandığı bir Kubernetes kümesi veya ortak bir NAT Gateway. AWS bu durumu Custom Cost Allocation ile çözüyor.
Departmanlara göre ağırlıklı dağıtım için basit bir Python scripti:
#!/usr/bin/env python3
# shared_cost_allocator.py
# Paylasilan kaynak maliyetlerini departmanlara dagitir
import json
import subprocess
from datetime import datetime, timedelta
# Departman agirliklari (kullanim yuzdesi veya kisi sayisi)
DEPARTMENT_WEIGHTS = {
"engineering": 0.45,
"marketing": 0.20,
"finance": 0.10,
"hr": 0.05,
"operations": 0.20
}
def get_shared_resource_cost(resource_tag, start_date, end_date):
"""Belirli bir paylaşilan kaynağin maliyetini getirir."""
result = subprocess.run([
"aws", "ce", "get-cost-and-usage",
"--time-period", f"Start={start_date},End={end_date}",
"--granularity", "MONTHLY",
"--metrics", "UnblendedCost",
"--filter", json.dumps({
"Tags": {
"Key": "Project",
"Values": [resource_tag]
}
}),
"--output", "json"
], capture_output=True, text=True)
data = json.loads(result.stdout)
total = float(data["ResultsByTime"][0]["Total"]["UnblendedCost"]["Amount"])
return total
def allocate_costs(total_cost, weights, resource_name):
"""Maliyeti departmanlara dagit ve rapor olustur."""
print(f"n{resource_name} maliyet dagilimi:")
print(f"Toplam maliyet: ${total_cost:.2f}")
print("-" * 40)
allocations = {}
for dept, weight in weights.items():
allocated = total_cost * weight
allocations[dept] = allocated
print(f" {dept}: ${allocated:.2f} (%{weight*100:.0f})")
return allocations
if __name__ == "__main__":
last_month = (datetime.now().replace(day=1) - timedelta(days=1))
start_date = last_month.replace(day=1).strftime("%Y-%m-%d")
end_date = datetime.now().replace(day=1).strftime("%Y-%m-%d")
# Ortak Kubernetes kumesi maliyeti
k8s_cost = get_shared_resource_cost("shared-k8s-cluster", start_date, end_date)
allocate_costs(k8s_cost, DEPARTMENT_WEIGHTS, "Kubernetes Kumesi")
# Ortak NAT Gateway maliyeti
nat_cost = get_shared_resource_cost("shared-network", start_date, end_date)
allocate_costs(nat_cost, DEPARTMENT_WEIGHTS, "NAT Gateway")
Raporları Görselleştirme
Ham veriler yöneticilere sunmak için yeterli olmayabiliyor. Grafana ile AWS Cost Explorer datasource’u entegre ederek canlı dashboard oluşturabilirsiniz. Bunun yanında basit bir HTML raporu da işinizi görebilir.
#!/bin/bash
# HTML maliyet raporu olusturucu
generate_html_report() {
local month=$1
local output_file="/var/www/html/cost-reports/report-${month}.html"
COST_JSON=$(aws ce get-cost-and-usage
--time-period Start="${month}-01",End="$(date -d "${month}-01 +1 month" +%Y-%m-%d)"
--granularity MONTHLY
--metrics "UnblendedCost"
--group-by Type=TAG,Key=Department
--output json)
cat > ${output_file} << 'HTMLEOF'
<!DOCTYPE html>
<html lang="tr">
<head>
<meta charset="UTF-8">
<title>Bulut Maliyet Raporu</title>
<style>
body { font-family: Arial, sans-serif; margin: 40px; }
.dept-card { background: #f5f5f5; padding: 20px; margin: 10px;
border-left: 4px solid #2196F3; border-radius: 4px; }
.cost { font-size: 24px; color: #333; font-weight: bold; }
.warning { border-left-color: #FF9800; }
.danger { border-left-color: #F44336; }
</style>
</head>
<body>
HTMLEOF
echo "<h2>Departman Maliyet Raporu - ${month}</h2>" >> ${output_file}
echo "<p>Olusturulma: $(date)</p>" >> ${output_file}
echo "$COST_JSON" | jq -r '.ResultsByTime[].Groups[] | "(.Keys[0])|(.Metrics.UnblendedCost.Amount)"' |
while IFS='|' read -r dept cost; do
dept_clean=$(echo $dept | sed 's/Department$//')
cost_fmt=$(echo $cost | awk '{printf "%.2f", $1}')
echo "<div class='dept-card'>" >> ${output_file}
echo " <h3>${dept_clean}</h3>" >> ${output_file}
echo " <div class='cost'>$${cost_fmt}</div>" >> ${output_file}
echo "</div>" >> ${output_file}
done
echo "</body></html>" >> ${output_file}
echo "HTML raporu olusturuldu: ${output_file}"
}
generate_html_report "$(date -d 'last month' +%Y-%m)"
Sonuç
Departman bazlı bulut maliyet yönetimi, bir kez kurulduğunda organizasyona çok şey kazandıran bir sistem. Etiketleme stratejisi olmadan yola çıkmak en büyük hata. Önce bu temeli sağlam atın, ardından raporlama ve alarm mekanizmalarını üstüne inşa edin.
Pratikte şu sırayla ilerlemenizi öneririm:
- İlk hafta: Etiket standardını belirleyin ve policy olarak zorunlu kılın
- İkinci hafta: Mevcut kaynakları etiketleyin, eksik etiketleri tespit edin
- Üçüncü hafta: Bütçe ve anomali alarmlarını kurun
- Dördüncü hafta: İlk showback raporunu yayınlayın ve departman liderlerine sunun
Bu yapıyı kurduktan sonra departman liderlerinin kendi kaynaklarını daha dikkatli kullandığını göreceksiniz. Hesap verebilirlik, doğal bir optimizasyon kültürü oluşturuyor. Boyutu ne olursa olsun her şirkette bu sistemi kurmak mümkün; önemli olan başlamak.
