Deepinfo'da domain verilerini tuttuğumuz Elasticsearch index'inde 1.000'den fazla TLD'ye ait 400 milyonun üzerinde kayıt var. Cluster çoğu gün sessiz sedasız çalışıyor, zaten olması gereken de bu. Bir sorun çıktığında ise sebep genelde aynı oluyor: shard'lar beklediğim yerde değil ve node'lardan biri payına düşenden fazla iş yapıyor.
Bu yazıda Elasticsearch'ün primary ve replica shard'ları nasıl yerleştirdiğini, neyi dengeleyip neyi hesaba katmadığını ve gerçekten işe yarayan birkaç ayarı anlatacağım.
Primary ve replica neredeyse aynı işi yapıyor
Her index primary shard'lara bölünür, her primary'nin de replica'ları olabilir. Primary sayısı index oluşturulurken belirlenir ve split ya da shrink yapmadıkça değişmez. Replica sayısını ise istediğin zaman değiştirebilirsin:
PUT domains/_settings
{
"index": { "number_of_replicas": 1 }
}
Bir yazma isteği önce primary'ye gelir. Primary isteği doğrular, dokümanı index'ler ve aynı işlemi replica'lara gönderir; replica'lar da dokümanı kendi tarafında yeniden index'ler. Yani replica'lar kenarda bekleyen kopyalar değil. Elasticsearch'ün document replication modelinde bir replica, indexing sırasında aşağı yukarı primary'si kadar CPU harcar. Arama istekleri ise herhangi bir kopyaya gidebilir; adaptive replica selection her isteği o an en hızlı cevap veren kopyaya yönlendirir.
Buradan iki sonuç çıkıyor:
- Bir primary ile replica'sı hiçbir zaman aynı node'a konmaz. Tek node'lu bir
cluster'da
number_of_replicas: 1ayarlarsan cluster hep yellow kalır. - Bir node'da "fazla primary" olması çoğu zaman göründüğü kadar büyük bir sorun değil. Asıl sorun, en çok yük alan index'in shard'larının tek bir node'da toplanması.
Allocator neyi dengeliyor?
Balancer her node'un ağırlığını birbirine yakın tutmaya çalışır. 8.5'e kadar bu ağırlık iki şeye bakılarak hesaplanıyordu: node'daki toplam shard sayısı ve her index'ten o node'a düşen shard sayısı.
cluster.routing.allocation.balance.shard: 0.45 # node başına toplam shard
cluster.routing.allocation.balance.index: 0.55 # aynı index'ten node başına shard
cluster.routing.allocation.balance.threshold: 1.0 # shard'lar taşınmadan önce izin verilen fark
8.6 ile gelen desired balance allocator bunlara disk kullanımını ve tahmini write load'u da ekledi. Ama primary ile replica arasında hâlâ bir ayrım yapmıyor; rebalance söz konusu olduğunda ikisi de sadece birer shard.
Bu ağırlıkları nadiren değiştiriyorum. Dağılım bana yanlış göründüğünde sebep neredeyse her seferinde başka bir şey çıktı: bir disk watermark'ı, bir allocation filtresi ya da node sayısına bölünmeyen bir shard sayısı.
Shard sayısını node sayısına göre seç
5 primary ve 1 replica'sı olan bir index'te toplam 10 shard kopyası vardır. 4
node'lu bir cluster'da iki node üçer, diğer ikisi ikişer kopya taşır. Bunu
hiçbir ayarla düzeltemezsin, sorun aritmetikte. Yeni bir index açarken primary
sayısını, primary × (replica + 1) data node sayısına tam bölünecek şekilde
seçiyorum ve cluster büyüyebilsin diye biraz pay bırakıyorum.
Çok yük alan bir index için node başına bir üst sınır koymak da işe yarıyor:
PUT domains/_settings
{
"index.routing.allocation.total_shards_per_node": 3
}
Yalnız dikkatli kullanmak lazım. Bu sınır bir node düştüğünde de geçerli; fazla sıkı tutarsan, shard'ların taşınması gereken anda unassigned kalırlar.
Shard'ların ne zaman taşınacağını belirleyen ayarlar
Pratikte en çok kurcaladığım ayarlar bunlar:
| Ayar | Varsayılan | Ne işe yarar |
|---|---|---|
cluster.routing.rebalance.enable | all | Hangi shard'ların rebalance edilebileceği: all, primaries, replicas ya da none |
cluster.routing.allocation.allow_rebalance | indices_all_active | Rebalance, cluster'daki bütün shard'lar active olana kadar bekler |
cluster.routing.allocation.cluster_concurrent_rebalance | 2 | Cluster genelinde aynı anda kaç shard taşınabileceği |
cluster.routing.allocation.node_concurrent_recoveries | 2 | Bir node'da aynı anda kaç recovery çalışabileceği |
indices.recovery.max_bytes_per_sec | 40mb | Node başına recovery bant genişliği sınırı |
Bu değerleri yükseltmek cluster'ın daha çabuk dengeye oturmasını sağlar, ama o sırada aramalar yavaşlar. Gün içinde varsayılanlarla çalışıyorum. Gece planlı bir rebalance yapacaksam değerleri artırıyor, iş bitince eski hâline getiriyorum.
Disk watermark'larına ayrıca değinmek gerekiyor, çünkü shard'ları kimseye sormadan taşıyorlar:
low(%85): node'a yeni shard atanmaz.high(%90): Elasticsearch shard'ları node'dan taşımaya başlar.flood_stage(%95): node'da shard'ı bulunan bütün index'ler read-only olur (index.blocks.read_only_allow_delete).
Bir node diğerlerinden hızlı doluyorsa allocator shard'ları ondan uzaklaştırıp durur. Bu durumda gördüğün "dengesizlik" aslında bir disk sorunudur.
Node'u yeniden başlatırken shard'ları boşuna taşımamak
Bir node cluster'dan ayrıldığında Elasticsearch, eksik replica'ları başka
node'larda yeniden oluşturmadan önce index.unassigned.node_left.delayed_timeout
kadar bekler (varsayılanı bir dakika). Restart bundan uzun sürerse veri baştan
kopyalanır, node geri geldiğinde de shard'lar tekrar taşınır. Rolling restart
yaparken şöyle ilerliyorum:
# Node'u durdurmadan önce: sadece primary'lerin atanmasına izin ver
curl -X PUT "$ES/_cluster/settings" -H 'Content-Type: application/json' -d '{
"persistent": { "cluster.routing.allocation.enable": "primaries" }
}'
# ...node'u yeniden başlat, cluster'a geri katılmasını bekle...
# Sonra: varsayılana dön
curl -X PUT "$ES/_cluster/settings" -H 'Content-Type: application/json' -d '{
"persistent": { "cluster.routing.allocation.enable": null }
}'
Bir sonraki node'a geçmeden önce de cluster'ın green olmasını bekliyorum.
Hangi shard nerede?
Çoğu sorunun cevabını üç istek veriyor:
# Node başına shard kopyaları, primary (p) ve replica (r) olarak ayrılmış
curl -s "$ES/_cat/shards?h=node,prirep" | sort | uniq -c
# Node başına disk kullanımı ve shard sayısı
curl -s "$ES/_cat/allocation?v"
# Bir shard neden orada (ya da neden orada değil)
curl -s "$ES/_cluster/allocation/explain?pretty" -H 'Content-Type: application/json' -d '{
"index": "domains", "shard": 0, "primary": false
}'
allocation/explain'i keşke çok daha önce öğrenseydim. Tahmin yürütmek yerine,
her node için hangi allocation decider'ın evet, hangisinin hayır dediğini tek
tek görüyorsun.
Sırayla neye bakıyorum
primary × (replica + 1)data node sayısına tam bölünüyor mu?- Disk watermark'larına yaklaşan bir node var mı?
- Çok yük alan bir index tek bir node'da mı toplanmış? Öyleyse
total_shards_per_node. - Yanlış yerde duran shard için
allocation/explainne diyor? - Bütün bunlardan sonra, en son: balance ağırlıkları.
