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.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir