Vận hành ELK: ILM, sizing, snapshot, bảo mật

4 câu hỏi phỏng vấn~30 phútTừ khóa: ILM, ISM, rollover, hot-warm-cold, delete phase, retention, shard sizing, oversharding, heap, compressed oops, disk watermark, flood stage, read_only_allow_delete, snapshot, S3 repository, SLM, TLS, API key, rolling upgrade, cost

Tiếp đi: vận hành ELK. Sáng nay Filebeat báo lỗi index [.ds-logs-shop-prod-...] blocked by: [TOO_MANY_REQUESTS/12/disk usage exceeded flood-stage watermark, index has read-only-allow-delete block]. Log mới không vào được nữa. Mình phải làm gì?

Đĩa ES đã vượt 95%, ES tự khóa index để tự bảo vệ. Gỡ ngay thì dễ, nhưng gốc rễ là chưa có vòng đời index. Bài này gom các việc vận hành chính.

1. Bài toán

3 node × 500GB, log 40GB/ngày × replica 1 = 80GB/ngày
Không có ILM → sau ~18 ngày đầy
_cat/allocation: disk.percent 96 96 95

2. Vấn đề

3. Giải pháp

Cứu ngay:

DELETE .ds-logs-shop-prod-2026.09.01-000001        # xóa backing index cũ nhất (data stream: không xóa write index)
# ES 7.4+ tự gỡ block khi đĩa xuống dưới high watermark; nếu không:
PUT logs-shop-*/_settings
{ "index.blocks.read_only_allow_delete": null }

Disk watermark mặc định:

MứcMặc địnhHậu quả
low85%Không đặt shard mới lên node này
high90%Chuyển shard đi node khác
flood_stage95%Index có shard trên node bị khóa ghi

ILM (Elasticsearch) / ISM (OpenSearch):

 hot (SSD, đang ghi) ──rollover 50GB / 1 ngày──► warm (ít đọc, forcemerge, giảm replica)
     ──► cold (đĩa rẻ / searchable snapshot) ──► delete

Quy tắc sizing:

shop-app: 40 × 2 × 14 × 1.15 ÷ 0.85 ≈ 1.5TB → 3 node × 500GB là sát; cần ILM, hoặc warm node.

4. Code từng bước

Bước 1: ILM policy

PUT _ilm/policy/logs-shop-policy
{
  "policy": { "phases": {
    "hot":    { "actions": { "rollover": { "max_primary_shard_size": "50gb", "max_age": "1d" } } },
    "warm":   { "min_age": "2d", "actions": {
                  "forcemerge": { "max_num_segments": 1 },
                  "allocate": { "number_of_replicas": 1 } } },
    "delete": { "min_age": "14d", "actions": { "delete": { "wait_for_snapshot": { "policy": "nightly" } } } }
  } }
}

Retention theo môi trường: template riêng cho logs-shop-dev (3 ngày, replica 0), staging (7 ngày), prod (14 ngày), audit (1 năm, trên cold/snapshot).

OpenSearch ISM tương đương:

PUT _plugins/_ism/policies/logs-shop-policy
{ "policy": {
  "default_state": "hot",
  "states": [
    { "name": "hot", "actions": [{ "rollover": { "min_primary_shard_size": "50gb", "min_index_age": "1d" } }],
      "transitions": [{ "state_name": "delete", "conditions": { "min_index_age": "14d" } }] },
    { "name": "delete", "actions": [{ "delete": {} }], "transitions": [] } ],
  "ism_template": { "index_patterns": ["logs-shop-*"], "priority": 100 } } }

Bước 2: heap và hệ điều hành (mỗi VM 16GB RAM)

# /etc/elasticsearch/jvm.options.d/heap.options
-Xms8g
-Xmx8g
# /etc/sysctl.d/99-es.conf
vm.max_map_count=262144
vm.swappiness=1

Bước 3: snapshot lên S3

# mỗi node: lưu credential vào keystore, không vào yml
bin/elasticsearch-keystore add s3.client.default.access_key
bin/elasticsearch-keystore add s3.client.default.secret_key
# (trên EC2 tốt hơn: IAM role, không cần key)

PUT _snapshot/s3-logs
{ "type": "s3", "settings": { "bucket": "shop-es-snapshots", "base_path": "prod" } }

PUT _slm/policy/nightly
{ "schedule": "0 30 1 * * ?", "name": "<nightly-{now/d}>", "repository": "s3-logs",
  "config": { "indices": ["logs-shop-prod", "logs-audit-*"] },
  "retention": { "expire_after": "90d", "min_count": 7 } }

# restore một ngày cũ vào tên khác để điều tra
POST _snapshot/s3-logs/nightly-2026.10.01/_restore
{ "indices": ".ds-logs-audit-*", "rename_pattern": "(.+)", "rename_replacement": "restored-$1" }

Snapshot là incremental theo segment; đừng sao chép thư mục data bằng rsync.

Bước 4: bảo mật và kiểm tra

# tạo CA + cert cho transport và HTTP
bin/elasticsearch-certutil ca
bin/elasticsearch-certutil cert --ca elastic-stack-ca.p12 --dns es-1,es-2,es-3

# API key quyền tối thiểu cho Filebeat
POST _security/api_key
{ "name": "filebeat-vm-app",
  "role_descriptors": { "writer": { "index": [{ "names": ["logs-shop-*"], "privileges": ["create_doc", "auto_configure"] }] } } }

curl -s --cacert ca.crt -H "Authorization: ApiKey $ES_API_KEY" "$ES_URL/_cat/allocation?v"
curl -s --cacert ca.crt -H "Authorization: ApiKey $ES_API_KEY" "$ES_URL/logs-shop-prod/_ilm/explain?pretty"

Checklist: TLS cả transport (9300) và HTTP (9200); firewall chỉ cho shipper và Kibana vào 9200; Kibana sau reverse proxy + SSO; mỗi shipper một API key; không dùng user elastic trong ngày thường; bật audit log nếu có yêu cầu.

5. Cách hoạt động: rolling upgrade

Với từng node (es-3 → es-2 → es-1, master hiện tại cuối cùng):
  1. PUT _cluster/settings {"persistent":{"cluster.routing.allocation.enable":"primaries"}}
  2. POST _flush
  3. systemctl stop elasticsearch → nâng gói → start
  4. đợi node join, bật lại allocation: "all"
  5. đợi green rồi mới sang node tiếp
Trước khi bắt đầu: snapshot, đọc deprecation (Upgrade Assistant), nâng Kibana sau ES,
chỉ nhảy major từ bản minor cuối (vd 7.17 → 8.x).

6. So sánh: chi phí và cách cắt

Nguồn chi phíCách cắtTiết kiệm
Lượng ghi (GB/ngày)Drop DEBUG, health check, log lặp ở shipper; giảm log ở codeThường 30–60%
Log thành công khối lớnSampling (giữ 10% INFO access log 200, giữ 100% lỗi)Lớn
Số ngày giữProd 14 ngày hot; audit dài hạn qua snapshot/S3Tuyến tính
Đĩa hot SSDWarm/cold tier đĩa rẻ, searchable snapshotLớn với log cũ
ReplicaReplica 0 ở dev; ở cold nếu có snapshot50% đĩa vùng đó
Mappingdynamic: false, index: false cho stack trace, match_only_text, LogsDB (ES 8.17+)Đĩa + CPU

7. Khi nào nên dùng / không nên dùng

VM của mình có 64GB RAM, vậy đặt heap ES 48GB cho nhanh luôn được không?

Không nên. Có hai lý do:

64GB RAM: heap 31g, ~33GB còn lại cho page cache
Kiểm tra: GET _nodes/_local/jvm?filter_path=**.using_compressed_ordinary_object_pointers   → "true"

Cần thêm sức thì thêm node, không phải thêm heap. Bản 8.x mặc định tự tính heap theo RAM và role khá ổn, chỉ đặt tay khi có lý do.

Bên compliance yêu cầu giữ log thanh toán 1 năm. Giữ 1 năm trên ES thì đĩa nổ mất. Mình làm sao?

Đúng, đừng giữ 1 năm ở hot. Hỏi lại compliance cho rõ: "giữ" là phải tìm được trong vài giây hay chỉ cần khôi phục được khi kiểm toán? Thường là cái sau.

Lựa chọnTìm đượcChi phí
Hot 14 ngày, rồi snapshot S3 giữ 1 năm, restore khi cầnSau vài phút–giờRẻ nhất (S3, có thể Glacier cho phần rất cũ)
Cold/frozen tier với searchable snapshot (Elastic, license trả phí)Chậm hơn hot nhưng vẫn query trực tiếpTrung bình
Logstash / Fluent Bit gửi bản sao sang S3 (NDJSON gzip), query bằng AthenaPhútRẻ

Chỉ tách riêng log audit/payment (logs-audit-*) với policy 1 năm, còn log app vẫn 14 ngày. Bật S3 Object Lock nếu cần chống sửa xóa.

Phỏng vấn hỏi về vận hành Elasticsearch cho log thì trả lời sao?

Q1. Bạn quản lý vòng đời và dung lượng log trong Elasticsearch thế nào?

🇻🇳 Tiếng Việt:

Tôi dùng data stream với ILM, hoặc ISM trên OpenSearch: rollover theo kích thước shard khoảng 50GB hoặc theo ngày, forcemerge ở warm, xóa sau thời gian retention theo môi trường. Log cần giữ lâu thì đưa sang snapshot trên S3 hoặc cold tier. Tôi tính đĩa theo lượng log mỗi ngày nhân replica nhân số ngày, chừa chỗ cho watermark 85%.

🇬🇧 English:

I use data streams with ILM, or ISM on OpenSearch: roll over at roughly 50GB per shard or daily, force-merge in warm, and delete after an environment-specific retention. Logs that must be kept longer go to S3 snapshots or a cold tier. I size disk as daily volume times replicas times retention days, leaving headroom below the 85 percent watermark.

Q2. Index bị read-only vì flood-stage watermark, bạn xử lý thế nào?

🇻🇳 Tiếng Việt:

Trước mắt giải phóng đĩa bằng cách xóa backing index cũ nhất hoặc thêm node, sau đó block tự gỡ khi đĩa xuống dưới high watermark, hoặc gỡ tay. Lâu dài thì thêm ILM, giảm lượng log, tạo alert đĩa ở 75–80% và kiểm tra shard size.

🇬🇧 English:

Short term I free disk by deleting the oldest backing indices or adding nodes; the block is released automatically once usage drops below the high watermark, or I clear it manually. Long term I add ILM, reduce log volume, alert on disk at 75 to 80 percent, and review shard sizing.

Q3. Cấu hình heap Elasticsearch thế nào, vì sao?

🇻🇳 Tiếng Việt:

Khoảng 50% RAM và không quá khoảng 31GB, Xms bằng Xmx. Dưới ngưỡng đó JVM dùng compressed oops nên con trỏ nhỏ hơn; nửa RAM còn lại để OS làm filesystem cache cho Lucene. Cần thêm sức thì scale ngang bằng thêm node.

🇬🇧 English:

About half of RAM and no more than roughly 31GB, with Xms equal to Xmx. Below that limit the JVM uses compressed oops so pointers stay small, and the other half of RAM serves as filesystem cache for Lucene. If more capacity is needed I scale out with more nodes.

Q4. Bảo mật một cụm Elasticsearch thế nào?

🇻🇳 Tiếng Việt:

Không bao giờ mở 9200 ra Internet. Bật TLS cho cả transport và HTTP, bật xác thực, mỗi shipper một API key chỉ có quyền ghi vào index của nó, người dùng vào Kibana qua SSO với role theo team. Credential để trong keystore hoặc secret manager, có snapshot để chống mất và chống ransomware, và nâng cấp định kỳ.

🇬🇧 English:

Never expose port 9200 to the internet. Enable TLS on transport and HTTP, require authentication, give each shipper its own API key that can only write to its indices, and put users behind SSO in Kibana with team-based roles. Credentials live in the keystore or a secret manager, snapshots protect against loss and ransomware, and the cluster is upgraded regularly.

Cách nhớ ngắn gọn


Tự kiểm tra

Ba mức disk watermark mặc định?

low 85%, high 90%, flood_stage 95%.

Vì sao heap không quá ~31GB?

Để giữ compressed oops và chừa RAM cho filesystem cache.

Snapshot có incremental không?

Có, theo segment; chỉ copy segment mới.

Thứ tự nâng cấp Kibana và ES?

ES trước (rolling, từng node), Kibana sau.

Filebeat cần quyền gì trên index?

create_doc (và auto_configure) trên logs-shop-*, không hơn.