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.

Khau Van Nam 6 phút đọc
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.

Nhắn qua Telegram