Elasticsearch Multi-Tenancy Mimarisi: Tek Cluster’da Çoklu Müşteri Yönetimi

SaaS platformu işletiyorsanız ya da birden fazla müşteriye aynı altyapı üzerinden hizmet veriyorsanız, Elasticsearch’te multi-tenancy mimarisini doğru kurmak hem maliyet hem de yönetim açısından hayat kurtarıcı olabilir. Bu yazıda tek bir Elasticsearch cluster’ı üzerinde çoklu müşteri (tenant) yönetimini nasıl yapacağınızı, hangi izolasyon stratejilerini kullanacağınızı ve production ortamında karşılaşacağınız gerçek sorunları nasıl çözeceğinizi ele alacağız.

Multi-Tenancy Nedir ve Neden Elasticsearch’te Zordur?

Multi-tenancy, birden fazla müşterinin (tenant) aynı fiziksel veya sanal altyapıyı paylaşması anlamına gelir. Kavram basit görünse de Elasticsearch özelinde bazı zorluklar ortaya çıkar. Her müşterinin verisi birbirinden izole olmalı, bir müşterinin yoğun sorguları diğerlerini etkilememeli ve kaynak kullanımı adil bir şekilde dağıtılmalıdır.

Elasticsearch’te temel olarak üç farklı izolasyon yaklaşımı vardır:

  • Index tabanlı izolasyon: Her tenant için ayrı index oluşturulur
  • Alias tabanlı izolasyon: Shared index üzerinde alias ve filter kombinasyonu kullanılır
  • Data stream tabanlı izolasyon: Özellikle zaman serisi veriler için tercih edilir

Her yaklaşımın kendi trade-off’ları vardır. Şimdi bunları gerçek dünya senaryoları üzerinden inceleyelim.

Index Tabanlı İzolasyon Stratejisi

En yaygın ve en güvenli yaklaşım, her tenant için ayrı index oluşturmaktır. Örneğin 50 müşterisi olan bir log analitik platformunda her müşteri için logs-tenant-{tenant_id}-{date} formatında index’ler oluşturabilirsiniz.

Index Template ile Standart Yapı Oluşturma

İlk adım, tüm tenant index’leri için tutarlı bir yapı sağlayacak index template tanımlamaktır:

curl -X PUT "localhost:9200/_index_template/tenant_logs_template" 
  -H 'Content-Type: application/json' 
  -d '{
  "index_patterns": ["logs-tenant-*"],
  "template": {
    "settings": {
      "number_of_shards": 2,
      "number_of_replicas": 1,
      "index.routing.allocation.require.zone": "hot",
      "index.lifecycle.name": "tenant_logs_ilm_policy",
      "index.lifecycle.rollover_alias": "logs-tenant-active"
    },
    "mappings": {
      "properties": {
        "tenant_id": { "type": "keyword" },
        "timestamp": { "type": "date" },
        "level": { "type": "keyword" },
        "message": { "type": "text" },
        "service": { "type": "keyword" }
      }
    }
  },
  "priority": 200
}'

Bu template, logs-tenant- prefix’i ile başlayan tüm index’lere otomatik olarak uygulanır. priority değeri yüksek tutularak diğer template’lerle çakışma önlenir.

ILM Policy ile Otomatik Yaşam Döngüsü Yönetimi

Her tenant’ın verisi büyüdükçe storage maliyeti artar. ILM (Index Lifecycle Management) ile bu süreci otomatikleştirebilirsiniz:

curl -X PUT "localhost:9200/_ilm/policy/tenant_logs_ilm_policy" 
  -H 'Content-Type: application/json' 
  -d '{
  "policy": {
    "phases": {
      "hot": {
        "min_age": "0ms",
        "actions": {
          "rollover": {
            "max_primary_shard_size": "20gb",
            "max_age": "1d"
          },
          "set_priority": { "priority": 100 }
        }
      },
      "warm": {
        "min_age": "7d",
        "actions": {
          "shrink": { "number_of_shards": 1 },
          "forcemerge": { "max_num_segments": 1 },
          "set_priority": { "priority": 50 }
        }
      },
      "cold": {
        "min_age": "30d",
        "actions": {
          "freeze": {},
          "set_priority": { "priority": 0 }
        }
      },
      "delete": {
        "min_age": "90d",
        "actions": { "delete": {} }
      }
    }
  }
}'

Bu policy ile veriler önce hot node’larda tutulur, 7 gün sonra warm’a geçer, 30 gün sonra cold’a taşınır ve 90 gün sonra silinir. Tenant bazında farklı retention süreleri gerekiyorsa her tenant için ayrı ILM policy oluşturabilirsiniz.

Rol Tabanlı Erişim Kontrolü (RBAC)

Multi-tenancy mimarisinin en kritik bileşeni güvenliktir. Elasticsearch’in native security özellikleri ile her tenant yalnızca kendi verilerine erişebilir.

Tenant Rolü Oluşturma

# Tenant A için rol oluşturma
curl -X PUT "localhost:9200/_security/role/tenant_a_role" 
  -H 'Content-Type: application/json' 
  -u elastic:changeme 
  -d '{
  "cluster": [],
  "indices": [
    {
      "names": ["logs-tenant-tenant_a-*", "metrics-tenant-tenant_a-*"],
      "privileges": ["read", "write", "view_index_metadata"],
      "field_security": {
        "grant": ["*"],
        "except": ["internal_*"]
      }
    }
  ],
  "applications": [],
  "run_as": []
}'

# Tenant A kullanıcısı oluşturma
curl -X PUT "localhost:9200/_security/user/tenant_a_user" 
  -H 'Content-Type: application/json' 
  -u elastic:changeme 
  -d '{
  "password": "SecurePassword123!",
  "roles": ["tenant_a_role"],
  "full_name": "Tenant A Service Account",
  "email": "[email protected]"
}'

Bu yapıyla tenant_a_user hesabı sadece logs-tenant-tenant_a-* pattern’ine uyan index’lere erişebilir. Diğer tenant’ların index’leri bu kullanıcıya görünmez bile olmaz.

Document Level Security ile Paylaşımlı Index

Bazen performans nedenleriyle tenant’ların aynı index’i paylaşmasını isteyebilirsiniz. Bu durumda Document Level Security (DLS) kullanılır:

curl -X PUT "localhost:9200/_security/role/tenant_b_shared_role" 
  -H 'Content-Type: application/json' 
  -u elastic:changeme 
  -d '{
  "indices": [
    {
      "names": ["shared-logs-*"],
      "privileges": ["read", "write"],
      "query": "{"term": {"tenant_id": "tenant_b"}}"
    }
  ]
}'

DLS kullanırken dikkat edilmesi gereken nokta performanstır. Her sorgu, önce DLS filter’ı uygular, bu da aggregation’larda beklenmedik davranışlara yol açabilir. Küçük tenant sayıları için DLS uygun olsa da yüzlerce tenant için index bazlı izolasyon daha performanslıdır.

Kaynak Tüketimi Kontrolü: Shard Allocation ve Circuit Breaker

Tek cluster’da en büyük sorunlardan biri “noisy neighbor” problemidir. Bir tenant yoğun sorgular çalıştırdığında diğer tenant’ların response time’ı artar. Bunu önlemek için birkaç mekanizma kullanılır.

Shard Allocation Filtering

# VIP tenant'lar için dedicated node group oluşturma
# elasticsearch.yml içinde ilgili node'lara:
# node.attr.tenant_tier: premium

# Premium tenant index'lerini belirli node'lara yönlendirme
curl -X PUT "localhost:9200/logs-tenant-premium_customer-*/_settings" 
  -H 'Content-Type: application/json' 
  -d '{
  "index.routing.allocation.require.tenant_tier": "premium",
  "index.routing.allocation.total_shards_per_node": 3
}'

# Standart tenant'lar standard node'larda kalsın
curl -X PUT "localhost:9200/logs-tenant-standard-*/_settings" 
  -H 'Content-Type: application/json' 
  -d '{
  "index.routing.allocation.require.tenant_tier": "standard",
  "index.routing.allocation.total_shards_per_node": 5
}'

Search Throttling ile Kaynak Limitleme

Elasticsearch 7.x sonrası _search_shards ve thread pool ayarları ile kaynak limitlemesi yapılabilir:

# Cluster düzeyinde max concurrent shard requests limitleme
curl -X PUT "localhost:9200/_cluster/settings" 
  -H 'Content-Type: application/json' 
  -d '{
  "transient": {
    "cluster.routing.allocation.node_concurrent_recoveries": 4,
    "indices.breaker.total.use_real_memory": true,
    "indices.breaker.total.limit": "85%",
    "indices.breaker.request.limit": "60%",
    "indices.breaker.fielddata.limit": "40%"
  }
}'

Tenant Yönetimi için Otomasyon Scripti

Production ortamında yeni bir tenant eklemek her seferinde manuel işlem gerektirmemelidir. Aşağıdaki bash scripti yeni tenant onboarding sürecini otomatikleştirir:

#!/bin/bash

ES_HOST="https://localhost:9200"
ES_USER="elastic"
ES_PASS="${ES_MASTER_PASSWORD}"
TENANT_ID=$1
TENANT_TIER=${2:-"standard"}  # standard veya premium

if [ -z "$TENANT_ID" ]; then
  echo "Kullanim: $0 <tenant_id> [tier]"
  exit 1
fi

echo "[*] Tenant '$TENANT_ID' onboarding basliyor..."

# 1. Index alias olustur
curl -s -X PUT "${ES_HOST}/logs-tenant-${TENANT_ID}-$(date +%Y.%m.%d)-000001" 
  -H 'Content-Type: application/json' 
  -u "${ES_USER}:${ES_PASS}" 
  --cacert /etc/elasticsearch/certs/ca.crt 
  -d "{
  "aliases": {
    "logs-tenant-${TENANT_ID}-active": {
      "is_write_index": true
    }
  }
}"

# 2. Guvenlik rolu olustur
curl -s -X PUT "${ES_HOST}/_security/role/${TENANT_ID}_role" 
  -H 'Content-Type: application/json' 
  -u "${ES_USER}:${ES_PASS}" 
  --cacert /etc/elasticsearch/certs/ca.crt 
  -d "{
  "indices": [{
    "names": ["logs-tenant-${TENANT_ID}-*", "metrics-tenant-${TENANT_ID}-*"],
    "privileges": ["read", "write", "view_index_metadata", "manage"]
  }]
}"

# 3. Servis hesabi olustur
TENANT_PASSWORD=$(openssl rand -base64 24)
curl -s -X PUT "${ES_HOST}/_security/user/${TENANT_ID}_svc" 
  -H 'Content-Type: application/json' 
  -u "${ES_USER}:${ES_PASS}" 
  --cacert /etc/elasticsearch/certs/ca.crt 
  -d "{
  "password": "${TENANT_PASSWORD}",
  "roles": ["${TENANT_ID}_role"],
  "full_name": "${TENANT_ID} Service Account"
}"

echo "[+] Tenant '${TENANT_ID}' basariyla olusturuldu."
echo "[+] Servis hesabi: ${TENANT_ID}_svc"
echo "[+] Sifre: ${TENANT_PASSWORD}"
echo "[!] Bu sifreyi guvenli bir yerde saklayin!"

Monitoring: Her Tenant’ın Kaynak Kullanımını Takip Etme

Hangi tenant’ın ne kadar kaynak tükettiğini bilmek hem faturalandırma hem de kapasite planlaması açısından kritiktir.

#!/bin/bash
# Tenant bazinda index istatistiklerini cek

ES_HOST="localhost:9200"

echo "=== Tenant Index Istatistikleri ==="
curl -s "${ES_HOST}/_cat/indices/logs-tenant-*?v&h=index,docs.count,store.size,pri,rep,status" 
  | sort -k3 -hr 
  | head -30

echo ""
echo "=== Tenant Bazinda Toplam Storage ==="
curl -s "${ES_HOST}/_cat/indices/logs-tenant-*?h=index,store.size" 
  | awk '{
    split($1, parts, "-");
    tenant = parts[3];
    gsub(/gb/, "", $2);
    gsub(/mb/, "", $2);
    totals[tenant] += $2
  }
  END {
    for (t in totals) print t, totals[t] "mb"
  }' 
  | sort -k2 -hr

echo ""
echo "=== Aktif Shard Dagilimi ==="
curl -s "${ES_HOST}/_cat/shards/logs-tenant-*?h=index,shard,prirep,state,node" 
  | grep -v UNASSIGNED 
  | wc -l
echo "toplam aktif shard"

Slow Query Log ile Tenant Performans Analizi

Hangi tenant’ın yavaş sorgular çalıştırdığını tespit etmek için slow log’ları etkinleştirin:

curl -X PUT "localhost:9200/logs-tenant-*/_settings" 
  -H 'Content-Type: application/json' 
  -d '{
  "index.search.slowlog.threshold.query.warn": "5s",
  "index.search.slowlog.threshold.query.info": "2s",
  "index.search.slowlog.threshold.query.debug": "500ms",
  "index.search.slowlog.threshold.fetch.warn": "1s",
  "index.indexing.slowlog.threshold.index.warn": "5s",
  "index.indexing.slowlog.threshold.index.info": "2s",
  "index.search.slowlog.level": "info"
}'

Bu ayarlar etkinleştirildikten sonra /var/log/elasticsearch/ dizinindeki *_index_search_slowlog.log dosyalarını izleyerek hangi tenant’tan hangi sorgular geldiğini görebilirsiniz.

Gerçek Dünya Senaryosu: SaaS Log Analitik Platformu

Diyelim ki 30 farklı şirkete log analitik hizmeti veren bir SaaS platformu işletiyorsunuz. Günlük yaklaşık 500GB log verisi geliyor, bazı müşteriler premium tier’da, diğerleri standart planda.

Bu senaryoda mimariniz şu şekilde olabilir:

  • 3 hot node: Premium müşterilerin aktif index’leri burada, her biri 32 core / 128GB RAM
  • 5 warm node: 7 günden eski veriler, her biri 16 core / 64GB RAM, büyük disk
  • 2 cold node: Arşiv verisi, freeze edilmiş index’ler
  • 3 coordinating node: Query routing ve load balancing

Her premium tenant için dedicated hot node allocation yapılır. Standart tenant’lar hot node’ları paylaşır ama total_shards_per_node limiti ile bir tenant’ın tüm kaynakları tüketmesi engellenir.

Faturalandırma için aylık cron job’ı:

#!/bin/bash
# Aylik tenant kullanim raporu - her ayin 1'inde calisir

MONTH=$(date -d "last month" +%Y.%m)
REPORT_FILE="/var/reports/es_tenant_usage_${MONTH}.csv"

echo "tenant_id,doc_count,storage_gb,query_count" > $REPORT_FILE

for tenant in $(curl -s "localhost:9200/_cat/indices/logs-tenant-*?h=index" 
  | awk -F'-' '{print $3}' | sort -u); do

  STATS=$(curl -s "localhost:9200/logs-tenant-${tenant}-${MONTH}*/_stats")
  DOC_COUNT=$(echo $STATS | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['_all']['total']['docs']['count'])" 2>/dev/null || echo "0")
  STORE_BYTES=$(echo $STATS | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['_all']['total']['store']['size_in_bytes'])" 2>/dev/null || echo "0")
  STORE_GB=$(echo "scale=2; $STORE_BYTES / 1073741824" | bc)

  echo "${tenant},${DOC_COUNT},${STORE_GB},0" >> $REPORT_FILE
done

echo "[+] Rapor olusturuldu: $REPORT_FILE"
mail -s "ES Tenant Kullanim Raporu - $MONTH" [email protected] < $REPORT_FILE

Yaygın Hatalar ve Çözümleri

Çok fazla shard problemi: Her tenant için çok sayıda index oluşturmak ve küçük tenant’lara da büyük shard sayısı vermek cluster’ı yıkabilir. Kural olarak her node başına 1000’den fazla shard olmamalıdır. Küçük tenant’lar için number_of_shards: 1 yeterlidir.

Mapping explosion: Farklı tenant’lar farklı field’lar kullanıyorsa shared index’lerde mapping sayısı patlar. index.mapping.total_fields.limit parametresini makul bir değere (varsayılan 1000) sabit tutun ve dinamik mapping’i kapatmayı değerlendirin.

Cache kirliliği: Elasticsearch’in field data cache ve request cache tenant’lar arasında paylaşılır. Bir tenant’ın büyük aggregation sorguları cache’i doldurabilir. indices.fielddata.cache.size değerini kontrol altında tutun.

Sertifika ve kimlik doğrulama yükü: Çok sayıda tenant kullanıcısı ile LDAP/AD entegrasyonu yapıyorsanız, realm cache timeout’larını ve connection pool ayarlarını optimize edin.

Sonuç

Elasticsearch’te multi-tenancy mimarisi, doğru yapılandırıldığında hem maliyet verimliliği hem de operasyonel kolaylık sağlar. Index bazlı izolasyon en güvenli seçenek olmaya devam ederken, DLS ile desteklenen paylaşımlı index’ler küçük tenant’lar için kabul edilebilir bir alternatiftir.

En kritik noktaları özetlemek gerekirse:

  • Güvenliği en baştan kur: RBAC ve gerektiğinde DLS ile tenant izolasyonu sağla
  • ILM policy’leri zorunlu tut: Veri büyümesini kontrol altında tutmak için her tenant’a uygun lifecycle tanımla
  • Shard sayısını kontrol et: Küçük tenant’lara küçük shard sayısı ver, cluster health’i düzenli izle
  • Monitoring’i ihmal etme: Tenant bazında kaynak kullanımı raporları hem teknik hem de ticari kararlar için değerlidir
  • Onboarding’i otomatize et: Manuel tenant oluşturma süreçleri hata yaratır, scriptlerle standarize et

Production’da bu mimariyi kurarken mutlaka test ortamında yük testleri yapın ve özellikle büyük tenant’ların diğerlerini nasıl etkilediğini ölçün. Elasticsearch cluster’ı canlı bir organizma gibidir; ne kadar iyi izlerseniz o kadar az sürprizle karşılaşırsınız.

Bir yanıt yazın

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