Chọn ELK, Loki hay log của cloud
Bài cuối phần log: chọn công cụ. Công ty mình có ba nhóm: team shop-app 4 người mới khởi nghiệp, một khách hàng doanh nghiệp đòi on-prem và tìm full-text log audit, và một nhánh chạy toàn bộ trên Kubernetes. Mỗi nhóm nên chọn cái gì?
Không có đáp án chung; mỗi lựa chọn đổi một thứ lấy một thứ. Bài 17.11 đã bàn câu "đang dùng Loki có nên chuyển ELK" trong K8s; ở đây mình làm khung quyết định rộng hơn, cho cả VM và cloud.
1. Bài toán
A. shop-app startup: 4 dev, 3 VM trên AWS, ~20GB log/ngày, không có người chuyên ops
B. Khách doanh nghiệp: on-prem, không ra Internet, log audit 1 năm, tìm full-text, phân quyền chặt
C. Nhánh K8s: 40 service, ~300GB log/ngày, đã có Prometheus + Grafana + Tempo
2. Vấn đề
- Chọn theo "cái hot nhất" → tự vận hành ES với 4 dev, cuối tuần sửa cluster thay vì làm sản phẩm.
- Chọn theo giá ingest rẻ → tìm full-text chậm, compliance không chịu.
- Không tính chi phí ẩn: người vận hành, egress, retention.
3. Giải pháp: so sánh
| ELK / OpenSearch | Loki | Log của cloud (CloudWatch, Azure Log Analytics) | |
|---|---|---|---|
| Index | Index mọi field (inverted index) | Chỉ index label; nội dung nén thành chunk | Do dịch vụ quản lý |
| Query | Full-text, aggregation mạnh, KQL/DSL/ES|QL | LogQL: chọn stream theo label rồi grep/parse | Logs Insights / KQL (Azure) khá mạnh |
| Tìm "kim trong đống rơm" khắp mọi log | Rất nhanh | Chậm nếu label rộng | Trung bình |
| Chi phí lưu | Cao (index lớn, SSD) | Thấp (object storage) | Trả theo GB ingest + GB lưu; ingest thường đắt |
| Công vận hành | Cao nếu tự chạy | Trung bình | Gần như không |
| Scale | Tốt nhưng cần sizing shard | Tốt, tách read/write | Tự động |
| On-prem | Có | Có | Không |
| Hợp với | Full-text, security/SIEM, audit, phân tích | Hệ đã dùng Grafana, log lớn, tìm theo service/time | Team nhỏ, đã ở một cloud |
Managed:
| Dịch vụ | Là gì | Lưu ý |
|---|---|---|
| Elastic Cloud | Elasticsearch + Kibana do Elastic chạy trên AWS/GCP/Azure; có bản serverless | Đủ tính năng nhất; trả theo tài nguyên |
| Amazon OpenSearch Service | OpenSearch managed, có Serverless và OpenSearch Ingestion | IAM, VPC; vẫn phải chọn instance và shard (bản không serverless) |
| CloudWatch Logs | Log có sẵn của AWS | Ingest ~0.5 USD/GB (tùy vùng), có lớp Infrequent Access |
| Azure Monitor Log Analytics | Log + KQL của Azure | Có gói Basic/Auxiliary logs rẻ hơn cho log ít truy vấn |
| Grafana Cloud Logs | Loki managed | Hợp nếu đã dùng Grafana Cloud cho metric/trace |
4. Code từng bước: giữ app trung lập với backend
Mấu chốt để đổi được về sau: app chỉ ghi JSON ra stdout/file, mọi việc gửi đi do shipper lo.
Bước 1: app (giống 19.7, không thêm appender gửi thẳng ES)
logging:
structured:
format:
console: ecs
Bước 2: shipper gửi hai nơi khi chuyển đổi (Fluent Bit trên VM)
[INPUT]
Name tail
Path /var/log/shop/*.json
Parser json
Tag shop.*
[OUTPUT]
Name opensearch
Match shop.*
Host ${OS_HOST}
Port 443
tls On
AWS_Auth On
AWS_Region ap-southeast-1
Index logs-shop-prod
Suppress_Type_Name On
[OUTPUT]
Name loki
Match shop.*
Host ${LOKI_HOST}
labels service=$service['name'], env=$service['environment']
tls On
http_user ${LOKI_USER}
http_passwd ${LOKI_TOKEN}
Bước 3: so cùng một câu hỏi trên hai bên
# ES/OpenSearch (KQL): service.name:payment-service and log.level:error and message:"timeout"
# Loki (LogQL): {service="payment-service"} | json | log_level="ERROR" |= "timeout"
# CloudWatch Insights: fields @timestamp, message | filter `service.name`="payment-service" and `log.level`="ERROR" and message like /timeout/
Bước 4: đo trước khi quyết
curl -s -H "Authorization: ApiKey $ES_API_KEY" "$ES_URL/_cat/indices/logs-shop-*?v&h=index,docs.count,pri.store.size&s=index"
# so: GB/ngày thực lưu, thời gian query 5 câu hay dùng nhất, giờ công vận hành/tháng
5. Cách hoạt động: cây quyết định
Bắt buộc on-prem / không ra Internet?
├─ có ─► cần full-text, audit, SIEM? ─ có ─► ELK hoặc OpenSearch tự chạy (B)
│ └ không ─► Loki tự chạy (rẻ, nhẹ hơn)
└─ không ─► có người chuyên vận hành search cluster?
├─ không ─► team nhỏ, log < ~50GB/ngày? ─ có ─► log của cloud (A: CloudWatch)
│ └ không ─► managed: Grafana Cloud / Elastic Cloud
└─ có ─► đã dùng Grafana + Prometheus + Tempo, tìm theo service/time là chính?
├─ có ─► Loki (C), thêm ELK riêng cho audit nếu cần
└─ không ─► Elastic Cloud / Amazon OpenSearch Service
6. So sánh: ba nhóm của bạn
| Nhóm | Chọn | Lý do | Đánh đổi |
|---|---|---|---|
| A. Startup | CloudWatch Logs (hoặc Grafana Cloud free tier) | Không ai phải vận hành; agent CloudWatch có sẵn | Ingest đắt khi lớn; đặt retention, drop DEBUG ngay |
| B. Doanh nghiệp on-prem | OpenSearch (hoặc ES có subscription) 3+ node | Full-text, DLS/FLS, audit, không cần Internet | Cần người vận hành (19.11) |
| C. K8s 300GB/ngày | Loki trên object storage | Rẻ, cùng Grafana với metric/trace | Tìm full-text toàn bộ chậm; label phải thiết kế kỹ (17.10) |
7. Khi nào nên dùng / không nên dùng
- Chuyển đổi: dual-write qua shipper 2–4 tuần, chuyển dashboard/alert, rồi tắt bên cũ; log cũ để hết retention hoặc snapshot, đừng cố migrate.
- Không gắn app với backend (appender gửi thẳng ES) → khóa chặt, mất log khi backend lỗi.
- Không chọn chỉ theo giá GB; tính cả giờ người và thời gian điều tra sự cố.
Hóa đơn CloudWatch Logs tháng này của team startup nhảy lên gấp ba. Mình nghi ingest chứ không phải lưu trữ. Kiểm tra và giảm kiểu gì?
Bạn nghi đúng hướng: với CloudWatch, phần ingest thường chiếm phần lớn hóa đơn.
- Tìm log group lớn: metric
IncomingBytestheo log group trong CloudWatch, hoặc Cost Explorer lọc usage typeDataProcessing-Bytes. - Thường thủ phạm là một service vừa bật DEBUG, hoặc log mỗi request health check, hoặc in cả payload lớn.
- Cắt ở nguồn:
logging.level.root: INFO, bỏ log health check, cắt payload; cắt ở agent bằng filter trước khi gửi. - Đặt retention cho mọi log group (mặc định là giữ mãi mãi).
- Log ít đọc: dùng lớp Infrequent Access, hoặc gửi thẳng S3 rồi query Athena.
Thêm alarm trên IncomingBytes để lần sau biết trong ngày, không đợi cuối tháng.
Vậy mình dùng Loki cho log app hằng ngày, còn đẩy log audit sang OpenSearch có ổn không, hay là phức tạp quá?
Ổn, và khá phổ biến, miễn là bạn giữ kỷ luật:
- Một shipper định tuyến theo field (
event.category: audit→ OpenSearch, còn lại → Loki), không chạy hai agent. - Một schema (ECS) cho cả hai, để chuyển query không phải học lại tên field.
- Cùng trace.id để từ Grafana nhảy sang log audit khi cần.
- Cho phạm vi OpenSearch thật nhỏ (audit chỉ vài GB/ngày) → cụm nhỏ, dễ vận hành, hoặc dùng managed.
Nếu log audit rất ít và chỉ cần khi kiểm toán, có khi S3 + Object Lock + Athena là đủ, không cần cả OpenSearch.
Phỏng vấn hỏi chọn giải pháp log tập trung thì trả lời sao?
Q1. ELK và Loki khác nhau căn bản ở đâu?
🇻🇳 Tiếng Việt:
ELK index toàn bộ nội dung nên tìm full-text và aggregation rất mạnh, nhưng lưu trữ và vận hành đắt. Loki chỉ index label như service và môi trường, nội dung nén trên object storage, nên rẻ và đơn giản, nhưng tìm chuỗi trên khoảng rộng thì chậm. Tôi chọn ELK khi cần full-text, audit hay security; chọn Loki khi đã dùng Grafana và chủ yếu tìm theo service và thời gian.
🇬🇧 English:
ELK indexes the full content, so full-text search and aggregations are powerful, but storage and operations are expensive. Loki indexes only labels like service and environment and stores compressed content on object storage, so it is cheap and simple, but searching strings across a wide range is slower. I pick ELK for full-text, audit or security needs, and Loki when we are already on Grafana and mostly search by service and time.
Q2. Khi nào nên dùng log managed của cloud?
🇻🇳 Tiếng Việt:
Khi team nhỏ, không có người chuyên vận hành cluster, đã ở một cloud và lượng log vừa phải. Đổi lại phải kiểm soát chi phí ingest bằng mức log, filter và retention, và chấp nhận query yếu hơn hoặc lock-in. Tôi giữ app chỉ ghi JSON ra stdout để có thể đổi backend sau này.
🇬🇧 English:
When the team is small, has nobody dedicated to running a cluster, is already on one cloud, and log volume is moderate. The trade-off is controlling ingest cost through log levels, filters and retention, and accepting weaker queries or lock-in. I keep applications writing JSON to stdout so we can change the backend later.
Q3. Bạn chuyển từ hệ log này sang hệ khác thế nào?
🇻🇳 Tiếng Việt:
Cho shipper ghi song song cả hai nơi vài tuần, dựng lại dashboard và alert trên hệ mới, so kết quả các query quan trọng, rồi tắt hệ cũ. Log lịch sử thường để hết retention hoặc giữ snapshot thay vì migrate. Việc này dễ nếu app chưa từng gắn chặt với backend.
🇬🇧 English:
I have the shipper dual-write to both systems for a few weeks, rebuild dashboards and alerts on the new one, compare results for key queries, then switch off the old one. Historical logs usually age out or stay in snapshots rather than being migrated. This is easy as long as the application was never coupled to the backend.
Cách nhớ ngắn gọn
- ELK: index mọi thứ, mạnh và đắt. Loki: index label, rẻ. Cloud: không vận hành, trả tiền ingest.
- Hỏi theo thứ tự: on-prem? có người vận hành? cần full-text? đã dùng Grafana?
- App ghi JSON ECS ra stdout/file; shipper quyết định gửi đi đâu.
- Chuyển đổi = dual-write rồi cắt, không migrate log cũ.
Tổng kết chương 19: Observability
| Bài | Ý chính | Tình huống trên shop-app | Công cụ |
|---|---|---|---|
| 19.1 Log, metric, trace | 4 tín hiệu; JSON log; traceId nối; cardinality và chi phí | Checkout chậm, log 12 Pod trộn lẫn | Structured logging, MDC |
| 19.2 SLI, SLO, alert | Đo trải nghiệm user, error budget, burn rate | 40 alert CPU/tuần, bỏ sót lỗi payment | Recording rule, Alertmanager |
| 19.3 Prometheus & Grafana | Pull model, PromQL, histogram_quantile | Prometheus OOM vì label ID | Prometheus, Grafana |
| 19.4 OpenTelemetry | SDK/agent, Collector, sampling, W3C traceparent | Chuyển Zipkin → Tempo | OTel, Tempo |
| 19.5 Profiling JVM | Continuous profiling, flame graph | Bản 3.2 tốn CPU | Pyroscope, JFR |
| 19.6 Incident & postmortem | Vai trò, mitigate trước, blameless | Payment 502 sau deploy | Runbook, rollback |
| 19.7 ELK tổng quan | Thành phần Elastic Stack; ES vs OpenSearch | SSH grep log trên 3 VM | ES, Kibana, Filebeat, docker-compose |
| 19.8 Elasticsearch bên trong | Shard, replica, node role, health, data stream | Cluster yellow | _cat, allocation explain, template |
| 19.9 Đưa log vào | Chọn shipper, Logstash, ingest pipeline, Kafka, multiline | Nginx text, app cũ, ES bảo trì | Filebeat, Logstash, Kafka |
| 19.10 Kibana & dashboard | KQL, saved search, dashboard, alert, quyền | Tìm đơn ORD-1042 qua 3 service | Discover, Lens, Alerting |
| 19.11 Vận hành ELK | ILM/ISM, sizing, heap, watermark, snapshot, bảo mật, chi phí | Đĩa 95%, index read-only | ILM, SLM, S3, API key |
| 19.12 Chọn công cụ log | ELK vs Loki vs cloud, managed, chuyển đổi | Startup, on-prem, K8s | Elastic Cloud, OpenSearch Service, CloudWatch, Loki |
Tự kiểm tra
Vì sao Loki rẻ hơn ELK?
Chỉ index label, nội dung nén trên object storage.
Retention mặc định của CloudWatch log group?
Giữ mãi mãi; phải tự đặt.
Khách đòi on-prem + full-text audit: chọn gì?
ELK hoặc OpenSearch tự vận hành.
Làm sao để sau này đổi backend log dễ?
App chỉ ghi JSON ra stdout/file, shipper lo việc gửi; dùng chung schema ECS.
Có nên migrate log cũ khi chuyển hệ?
Thường không; để hết retention hoặc giữ snapshot.