Elasticsearch DatasourceNoData: Debug Từ Spam Log Đến Shard Limit
Câu chuyện thực tế về việc debug Grafana alert DatasourceNoData, phát hiện index bị spam, và cluster chạm giới hạn 1000 shards khiến toàn bộ log hệ thống ngừng hoạt động.
Mục lục
Bối cảnh
Một buổi sáng, Grafana bắn alert liên tục:
DatasourceNoData - Spam Alert
datasource_uid: P31C819B24CF3C3C7
Kibana phản hồi chậm, dashboard không load được. Câu hỏi đầu tiên: Elasticsearch chết hay chỉ quá tải?
Giai đoạn 1: Chẩn đoán cluster
curl http://localhost:9200/_cluster/health
{
"status": "yellow",
"active_shards": 992,
"unassigned_shards": 8,
"active_shards_percent_as_number": 99.2
}
Status yellow có nghĩa cluster vẫn hoạt động, chỉ có một số shard chưa được assign (thường do single-node). Elasticsearch không chết, vấn đề nằm ở chỗ khác.
Giai đoạn 2: Tìm index bất thường
curl "http://localhost:9200/_cat/indices?v&s=store.size:desc"
Ngay lập tức thấy bất thường:
tch_system-2026-02-09 13.6gb 814,586 docs
tch_system-2026-02-08 8.6gb 482,296 docs
tch_system-2026-02-06 9.6gb 593,340 docs
tch_system-2026-02-04 8.9gb 509,598 docs
So sánh với các index bình thường:
tch_system_function-2026-02-23 560kb 520 docs
tch_system_function-2026-02-21 368kb 300 docs
Chênh lệch gấp 10,000 lần. Đây là thủ phạm.
Giai đoạn 3: Xác định nguyên nhân spam
Xem nội dung document để hiểu tại sao index lại phình to:
curl -s "http://localhost:9200/tch_system-2026-02-21/_search?pretty" \
-H "Content-Type: application/json" \
-d '{"size": 2}'
Phát hiện mỗi document chứa toàn bộ response body dài hàng KB, bao gồm thông tin đơn hàng, coupon, shipper… Một document nặng 5-10KB.
Tìm endpoint spam nhiều nhất bằng aggregation:
curl -s "http://localhost:9200/tch_system-2026-02-21/_search?pretty" \
-H "Content-Type: application/json" \
-d '{
"size": 0,
"aggs": {
"top_paths": {
"terms": { "field": "path.keyword", "size": 10 }
}
}
}'
{
"buckets": [
{ "key": "api/store/current-process", "doc_count": 29177 },
{ "key": "api/user/profile", "doc_count": 2376 },
{ "key": "api/cancel_reason", "doc_count": 879 }
]
}
api/store/current-process tạo ra 29,177 document trong một ngày. Nhân với kích thước mỗi document → hàng GB rác mỗi ngày.
Giai đoạn 4: Xóa index spam
curl -X DELETE "http://localhost:9200/tch_system-2026-02-03,\
tch_system-2026-02-04,tch_system-2026-02-06,tch_system-2026-02-08,\
tch_system-2026-02-09,tch_system-2026-02-16,tch_system-2026-02-17,\
tch_system-2026-02-18,tch_system-2026-02-19,tch_system-2026-02-20,\
tch_system-2026-02-21,tch_system-2026-02-23?ignore_unavailable=true"
Nếu gặp lỗi index_not_found_exception, thêm ?ignore_unavailable=true để bỏ qua index không tồn tại.
Kết quả: {"acknowledged":true} và giải phóng hơn 70GB disk.
Giai đoạn 5: Vấn đề mới — Log ngừng hoàn toàn
Vài ngày sau, tất cả service đều không có log mới. Kiểm tra Logstash:
sudo journalctl -u logstash -n 100 --no-pager
Toàn bộ log là HTTP 400:
[ERROR][logstash.outputs.http] [HTTP Output Failure]
Encountered non-2xx HTTP code 400
{:url=>"http://localhost:9200/tch_bot_sale-2026.02.24/_doc"}
Logstash pipeline stats vẫn bình thường (29M events processed), nghĩa là Logstash không lỗi. Vấn đề nằm ở Elasticsearch từ chối nhận document.
Thử insert thủ công để đọc error message thực sự:
curl -s -X POST "http://localhost:9200/tch_bot_sale-2026.02.24/_doc?pretty" \
-H "Content-Type: application/json" \
-d '{"@timestamp": "2026-02-24T10:00:00Z", "test": "debug"}'
{
"error": {
"type": "validation_exception",
"reason": "Validation Failed: 1: this action would add [1] shards,
but this cluster currently has [1000]/[1000] maximum normal shards open;"
},
"status": 400
}
Cluster đã chạm giới hạn 1000 shards. Không thể tạo bất kỳ index mới nào.
Hiểu về Shard và Shard Limit
Mỗi index khi được tạo ra sẽ được chia thành các mảnh nhỏ gọi là shard. Mặc định 1 index = 1 primary shard. Elasticsearch giới hạn tổng số shard trên cluster mặc định là 1000.
1 index = 1 shard
...
999 index = 999 shard → gần chạm giới hạn
1000 index = 1000 shard → ĐẦY
1001 → VƯỢT GIỚI HẠN → HTTP 400 → toàn bộ log bị drop
Hình dung đơn giản: giống như bãi đỗ xe 1000 chỗ. Mỗi ngày Logstash tạo vài index mới. Khi bãi đầy, không có index mới nào được tạo. Xóa index cũ = dọn xe cũ, bãi có chỗ, xe mới vào được.
Giai đoạn 6: Xử lý shard limit
Kiểm tra tình trạng thực tế trước khi xử lý
# Số shard hiện tại
curl -s "http://localhost:9200/_cluster/health?pretty" | grep -E "active_shards|unassigned"
# Heap memory Elasticsearch đang dùng
curl -s "http://localhost:9200/_nodes/stats/jvm?pretty" \
| grep -E "heap_used_percent|heap_used_in_bytes|heap_max_in_bytes" | head -4
Kết quả trong trường hợp này:
active_shards: 992
heap_used_percent: 24
heap_max_in_bytes: 21105737728 (~21GB)
Server 40GB RAM, Elasticsearch cấp 21GB heap, chỉ dùng 24% (~5GB). Còn dư ~16GB — an toàn để tăng shard limit.
Bước 1: Enable wildcard delete
curl -X PUT "http://localhost:9200/_cluster/settings" \
-H "Content-Type: application/json" \
-d '{"persistent": {"action.destructive_requires_name": false}}'
Bước 2: Xóa index cũ trước tháng 11/2025
curl -X DELETE "http://localhost:9200/tch_*-2025.01*,tch_*-2025.02*,\
tch_*-2025.03*,tch_*-2025.04*,tch_*-2025.05*,tch_*-2025.06*,\
tch_*-2025.07*,tch_*-2025.08*,tch_*-2025.09*,tch_*-2025.10*\
?ignore_unavailable=true"
Bước 3: Tăng shard limit
curl -X PUT "http://localhost:9200/_cluster/settings" \
-H "Content-Type: application/json" \
-d '{"persistent": {"cluster.max_shards_per_node": 2000}}'
Verify setting đã được apply:
curl -s "http://localhost:9200/_cluster/settings?pretty"
{
"persistent": {
"cluster": {
"max_shards_per_node": "2000"
}
}
}
Downside của việc tăng shard limit cần cân nhắc:
- Tốn RAM hơn (~10-30MB heap/shard)
- Query chậm hơn do phải fan-out qua nhiều shard hơn
- Không giải quyết gốc rễ, vài tháng nữa lại chạm giới hạn mới nếu không có ILM
Giai đoạn 7: Verify toàn bộ service hoạt động lại
curl -s "http://localhost:9200/_cat/indices/*2026.02.24?v&s=index"
health index docs.count store.size
green tch_api_data-2026.02.24 410 1.4mb
green tch_api_data_function-2026.02.24 369 2.3mb
green tch_app_api-2026.02.24 38679 162.7mb
green tch_bot_sale-2026.02.24 556 1.4mb
green tch_bot_sale_order_function-2026.02.24 10591 15.6mb
green tch_loyalty-2026.02.24 3317 1.2mb
green tch_payment_hub-2026.02.24 10 319.8kb
green tch_pos_order-2026.02.24 153 2mb
12 index hôm nay đã được tạo, tất cả green, log chạy bình thường.
Tổng kết chuỗi nguyên nhân
Python bot polling api/store/current-process liên tục
↓
29,000+ document/ngày được tạo ra
↓
Mỗi document chứa full response body (5-10KB)
↓
Index tch_system-* phình lên 8-13GB/ngày
↓
Elasticsearch quá tải khi query → Grafana timeout
↓
Tích lũy index qua nhiều tháng không được dọn
↓
Cluster chạm giới hạn 1000 shards
↓
Toàn bộ log từ tất cả service ngừng hoạt động
↓
Grafana báo DatasourceNoData
Checklist xử lý khi gặp lại
[ ] 1. curl localhost:9200/_cluster/health → check status và active_shards
[ ] 2. _cat/indices?v&s=store.size:desc → tìm index bất thường
[ ] 3. _search với aggs terms trên field path → tìm endpoint spam nhiều nhất
[ ] 4. journalctl -u logstash → check Logstash có lỗi không
[ ] 5. Nếu lỗi 400 → insert thủ công 1 doc → đọc error message chi tiết
[ ] 6. Nếu shard limit → xóa index cũ + tăng max_shards_per_node
[ ] 7. _cat/indices/*<ngày-hôm-nay>?v → verify index mới được tạo
[ ] 8. Setup ILM để tự động xóa index cũ hơn 90 ngày
Việc cần làm để tránh lặp lại
Setup ILM (Index Lifecycle Management) để tự động xóa index cũ, giữ shard count luôn ở mức an toàn:
curl -X PUT "http://localhost:9200/_ilm/policy/tch_logs_policy" \
-H "Content-Type: application/json" \
-d '{
"policy": {
"phases": {
"delete": {
"min_age": "90d",
"actions": { "delete": {} }
}
}
}
}'
ILM sẽ tự động xóa index cũ hơn 90 ngày, không cần can thiệp thủ công mỗi khi shard count tăng cao.