ELK Stack tổng quan, Elasticsearch vs OpenSearch

4 câu hỏi phỏng vấn~25 phútTừ khóa: Elasticsearch, Logstash, Kibana, Beats, Filebeat, Metricbeat, Elastic Agent, Fleet, OpenSearch, OpenSearch Dashboards, SSPL, AGPL, Apache 2.0, single-node, xpack.security

Tiếp đi: ELK cho log, nhưng không phải trên Kubernetes. shop-app của mình đang chạy order, payment, inventory trên 3 VM Ubuntu. Mỗi lần có lỗi là phải SSH vào từng máy rồi grep file log. Có cách nào đỡ cực hơn không?

Có. Đây đúng là bài toán ELK sinh ra để giải. Ở bài 17.11 bạn đã thấy ELK chạy trong Kubernetes bằng operator. Phần 19.7 → 19.12 nhìn ELK như một hệ thống độc lập: chạy được trên VM, on-prem, cloud và cả K8s. Bài này là bức tranh tổng.

1. Bài toán

shop-app hiện tại:

vm-app-1 (10.0.1.11)  order-service      /var/log/shop/order.json
vm-app-2 (10.0.1.12)  payment-service    /var/log/shop/payment.json
vm-app-3 (10.0.1.13)  inventory-service  /var/log/shop/inventory.json

Khách báo: "Đơn ORD-1042 trừ tiền rồi mà báo lỗi". Bạn phải làm thế này:

ssh vm-app-1 'grep ORD-1042 /var/log/shop/order.json*'
ssh vm-app-2 'zgrep ORD-1042 /var/log/shop/payment.json*.gz'   # file đã rotate
ssh vm-app-3 'grep ORD-1042 /var/log/shop/inventory.json'
# rồi tự ghép theo thời gian bằng mắt...

2. Vấn đề

3. Giải pháp: gom log về một chỗ có index

ELK = Elasticsearch + Logstash + Kibana. Sau này có thêm Beats nên hay gọi là Elastic Stack.

Thành phầnVai tròChạy ở đâu
ElasticsearchLưu và index log, trả lời query (full-text, filter, aggregation)Cụm riêng, 1–N node
LogstashPipeline xử lý: nhận → parse/biến đổi → gửi đi. Nặng (JVM), mạnhMáy trung gian
KibanaGiao diện web: tìm log, dashboard, alert, quản trịCạnh cụm ES
FilebeatAgent nhẹ (Go) đọc file log, gửi đi. Không mất chỗ khi restart nhờ registryMỗi VM app
MetricbeatAgent lấy metric hệ thống/serviceMỗi VM
Elastic Agent + FleetMột agent duy nhất thay cho nhiều Beats, cấu hình tập trung từ Kibana (Fleet)Mỗi VM

Lưu ý: Elasticsearch ở đây là kho log. Cùng một engine với bài 6.11 (search sản phẩm), nhưng cách dùng khác: log là dữ liệu chỉ ghi thêm, theo thời gian, xóa theo tuổi.

4. Code từng bước: chạy ES + Kibana ở máy dev

Bước 1: file .env (không commit, thêm vào .gitignore):

ELASTIC_PASSWORD=     # tự sinh: openssl rand -base64 24
KIBANA_PASSWORD=      # mật khẩu cho user kibana_system
STACK_VERSION=8.15.3

Bước 2: docker-compose.yml (single-node, bật security):

services:
  es:
    image: docker.elastic.co/elasticsearch/elasticsearch:${STACK_VERSION}
    environment:
      - discovery.type=single-node
      - xpack.security.enabled=true
      - xpack.security.http.ssl.enabled=false   # chỉ ở máy dev; production bật TLS (bài 19.11)
      - ELASTIC_PASSWORD=${ELASTIC_PASSWORD}
      - ES_JAVA_OPTS=-Xms1g -Xmx1g
    ports: ["127.0.0.1:9200:9200"]              # chỉ bind localhost
    volumes: [esdata:/usr/share/elasticsearch/data]
    healthcheck:
      test: ["CMD-SHELL", "curl -s -u elastic:$${ELASTIC_PASSWORD} localhost:9200/_cluster/health | grep -q status"]
      interval: 10s
      retries: 30

  setup:   # đặt mật khẩu cho user kibana_system rồi thoát
    image: docker.elastic.co/elasticsearch/elasticsearch:${STACK_VERSION}
    depends_on: { es: { condition: service_healthy } }
    environment:
      - ELASTIC_PASSWORD=${ELASTIC_PASSWORD}
      - KIBANA_PASSWORD=${KIBANA_PASSWORD}
    command: >
      bash -c 'curl -s -u "elastic:$${ELASTIC_PASSWORD}" -H "Content-Type: application/json"
      -X POST http://es:9200/_security/user/kibana_system/_password
      -d "{\"password\":\"$${KIBANA_PASSWORD}\"}"'

  kibana:
    image: docker.elastic.co/kibana/kibana:${STACK_VERSION}
    depends_on: [setup]
    environment:
      - ELASTICSEARCH_HOSTS=http://es:9200
      - ELASTICSEARCH_USERNAME=kibana_system
      - ELASTICSEARCH_PASSWORD=${KIBANA_PASSWORD}
    ports: ["127.0.0.1:5601:5601"]

volumes:
  esdata:

Kibana không được dùng user elastic (superuser), nên phải có bước setup.

Bước 3: log JSON từ Spring Boot (Boot 3.4+ có sẵn structured logging theo ECS):

# application.yml của payment-service
spring:
  application:
    name: payment-service
logging:
  file:
    name: /var/log/shop/payment.json
  structured:
    format:
      file: ecs          # ghi file theo Elastic Common Schema
      console: logfmt    # console vẫn dễ đọc cho người
  structured.ecs.service:
    environment: ${APP_ENV:prod}

Một dòng log ra sẽ như sau:

{"@timestamp":"2026-10-14T08:15:02.113Z","log.level":"ERROR","process.thread.name":"http-nio-8080-exec-7",
 "service.name":"payment-service","service.environment":"prod","log.logger":"com.shop.payment.ChargeService",
 "message":"charge failed orderId=ORD-1042","trace.id":"4bf92f3577b34da6a3ce929d0e0e4736",
 "error.type":"java.net.SocketTimeoutException","ecs.version":"8.11"}

Bước 4: test bằng curl

source .env
curl -s -u "elastic:$ELASTIC_PASSWORD" localhost:9200/_cluster/health?pretty
# "status" : "green"  (single-node, index không có replica)

# ghi thử một document log
curl -s -u "elastic:$ELASTIC_PASSWORD" -H 'Content-Type: application/json' \
  -X POST localhost:9200/logs-shop-dev/_doc -d '{"@timestamp":"2026-10-14T08:15:02Z","service.name":"payment-service","message":"hello"}'
# mở http://localhost:5601 đăng nhập elastic / mật khẩu trong .env

Cách đưa file log thật từ 3 VM vào là bài 19.9.

5. Cách hoạt động

 vm-app-1 order        vm-app-2 payment       vm-app-3 inventory
 [order.json]          [payment.json]         [inventory.json]
     │ Filebeat              │ Filebeat              │ Filebeat
     └──────────┬────────────┴───────────┬───────────┘
                ▼   (tùy chọn: Kafka làm buffer)
         ┌──────────────┐
         │   Logstash   │  parse, thêm field, lọc DEBUG
         └──────┬───────┘
                ▼
   ┌───────────────────────────────┐
   │ Elasticsearch cluster          │  index theo ngày / data stream
   │  es-1  es-2  es-3              │
   └──────────────┬────────────────┘
                  ▼
           ┌────────────┐
 dev  ───► │   Kibana   │  Discover, dashboard, alert
           └────────────┘

Logstash không bắt buộc. Log đã là JSON thì Filebeat có thể gửi thẳng ES, dùng ingest pipeline để xử lý nhẹ (bài 19.9).

6. So sánh: Elasticsearch vs OpenSearch

Năm 2021 Elastic đổi license Elasticsearch/Kibana từ Apache 2.0 sang SSPL + Elastic License, để cloud provider không bán lại dịch vụ miễn phí. AWS fork bản 7.10.2 cuối cùng còn Apache 2.0 thành OpenSearch và OpenSearch Dashboards. Năm 2024 Elastic thêm AGPL làm một lựa chọn license nữa. OpenSearch giờ thuộc OpenSearch Software Foundation (Linux Foundation).

Elasticsearch (Elastic)OpenSearch
LicenseElastic License / SSPL / AGPLApache 2.0
UIKibanaOpenSearch Dashboards (fork Kibana 7.10)
SecurityCó sẵn từ bản free (user, role, TLS)Security plugin có sẵn
Vòng đời indexILM, data streamISM (cú pháp khác), có data stream
AlertingKibana alerting rulesAlerting plugin (monitor, trigger)
AgentBeats, Elastic Agent, FleetKhông có Fleet; dùng Fluent Bit, Data Prepper, Logstash
Tính năng riêngES|QL, APM, Elastic Security, LogsDBPPL, SQL, Security Analytics, plugin cộng đồng
ManagedElastic Cloud (AWS/GCP/Azure)Amazon OpenSearch Service, Aiven…

Tương thích: API REST cơ bản (_search, _bulk, mapping, query DSL) gần giống. Nhưng hai bên đã tách xa từ 7.10:

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

Vậy mình có cần cả Logstash không, hay Filebeat gửi thẳng vào Elasticsearch là đủ? Logstash nghe nói ăn RAM lắm.

Bạn hiểu đúng: Logstash là JVM, thường cần 1–2GB heap mỗi instance. Với shop-app thì bắt đầu không cần Logstash.

Tình huốngKiến trúc
Log đã là JSON ECS, chỉ cần thêm vài fieldFilebeat → ES (ingest pipeline)
Nhiều nguồn lạ: Nginx text, syslog thiết bị mạng, log app cũFilebeat → Logstash (grok) → ES
Cần gửi một bản sang nơi khác (S3, SIEM) hoặc định tuyến phức tạpLogstash
Lượng log lớn, ES hay bảo trì, cần replayFilebeat → Kafka → Logstash → ES

Ý chính: Spring Boot đã ghi đúng ECS thì phần "parse" gần như không còn. Logstash chỉ thêm khi có lý do cụ thể.

Công ty mình đang ở AWS. Sếp bảo dùng Amazon OpenSearch cho đỡ phải vận hành. Nhưng tài liệu trên mạng toàn ghi Elasticsearch với Kibana, vậy mình làm theo có được không?

Được phần lớn, chỉ cần chỉnh vài điểm:

Nên viết tài liệu nội bộ bằng thuật ngữ trung tính ("search cluster", "dashboard") để đổi bên không phải sửa hết.

Phỏng vấn hỏi về ELK Stack và Elasticsearch vs OpenSearch thì trả lời sao?

Q1. ELK Stack gồm những gì, vai trò từng phần?

🇻🇳 Tiếng Việt:

ELK gồm Elasticsearch để lưu và index log, Logstash để xử lý pipeline, Kibana để tìm kiếm và vẽ dashboard. Thực tế còn có Beats như Filebeat làm agent nhẹ trên từng máy, hoặc Elastic Agent quản lý tập trung qua Fleet. Với log đã là JSON, tôi thường cho Filebeat gửi thẳng Elasticsearch và dùng ingest pipeline, chỉ thêm Logstash khi cần parse phức tạp hay định tuyến nhiều nơi.

🇬🇧 English:

ELK is Elasticsearch for storing and indexing logs, Logstash for processing pipelines, and Kibana for search and dashboards. In practice we add Beats such as Filebeat as lightweight agents on each host, or Elastic Agent managed centrally through Fleet. When logs are already structured JSON, I ship them from Filebeat straight to Elasticsearch with an ingest pipeline and only add Logstash for heavy parsing or multi-destination routing.

Q2. Elasticsearch và OpenSearch khác nhau thế nào?

🇻🇳 Tiếng Việt:

Năm 2021 Elastic đổi license sang SSPL và Elastic License, AWS fork bản 7.10 thành OpenSearch theo Apache 2.0. API cơ bản vẫn giống, nhưng đã tách xa: Kibana thành OpenSearch Dashboards, ILM thành ISM, client Java và Beats mới không tương thích, snapshot không khôi phục chéo. Tôi chọn theo license, nơi chạy managed và tính năng cần, ví dụ Fleet và APM thì Elastic, đã ở AWS thì Amazon OpenSearch Service.

🇬🇧 English:

In 2021 Elastic moved to SSPL and the Elastic License, and AWS forked version 7.10 into OpenSearch under Apache 2.0. The core APIs are still similar, but they have diverged: Kibana became OpenSearch Dashboards, ILM became ISM, the new Java client and recent Beats are not compatible, and snapshots do not restore across them. I choose based on licensing, the managed offering available, and features: Fleet and APM point to Elastic, being all-in on AWS points to Amazon OpenSearch Service.

Q3. Vì sao cần log tập trung thay vì SSH grep?

🇻🇳 Tiếng Việt:

Vì log trên máy bị rotate, mất khi máy chết, dev không nên có quyền SSH production, và grep không trả lời được câu hỏi tổng hợp như số lỗi theo service. Log tập trung cho phép tìm theo trace ID qua mọi service, giữ log theo chính sách retention, phân quyền và làm dashboard, alert.

🇬🇧 English:

Because local logs get rotated, disappear when the host dies, developers should not have SSH access to production, and grep cannot answer aggregate questions like error counts per service. Centralized logging lets us search by trace ID across all services, keep logs under a retention policy, control access, and build dashboards and alerts.

Cách nhớ ngắn gọn


Tự kiểm tra

Logstash có bắt buộc trong ELK không?

Không. Filebeat có thể gửi thẳng ES, dùng ingest pipeline. Logstash cho parse nặng, nhiều đích, hoặc đọc từ Kafka.

Vì sao Kibana không dùng user elastic?

elastic là superuser. Kibana dùng user hệ thống kibana_system với quyền tối thiểu.

OpenSearch fork từ bản Elasticsearch nào, vì sao?

7.10.2, bản cuối Apache 2.0, sau khi Elastic đổi license năm 2021.

Filebeat 8.x gửi vào OpenSearch được không?

Không ổn định: Beats 7.13+ kiểm tra sản phẩm và từ chối. Dùng Fluent Bit, Logstash plugin opensearch hoặc Data Prepper.

Spring Boot 3.4+ bật log ECS thế nào?

logging.structured.format.file: ecs (hoặc console: ecs).