Mở đầu — vì sao bài này quan trọng
Hãy tưởng tượng bạn đang trực đêm cho một hệ thống thương mại điện tử. Lúc 2 giờ sáng, khách hàng báo rằng họ nhấn "Thanh toán" nhưng đơn hàng không được tạo. Bạn mở terminal, chạy kubectl logs cho pod của service checkout — nhưng service này chạy 12 bản sao (replica) rải trên 6 node khác nhau, và request của khách đã đi qua ít nhất bốn service: API Gateway, order-service, payment-service và inventory-service. Log của mỗi service nằm ở một chỗ. Chưa kể pod vừa bị Kubernetes khởi động lại vì OOM, và toàn bộ log trước đó đã biến mất theo container cũ.
Đây chính là bức tường mà mọi kỹ sư DevOps đều đâm vào khi hệ thống lớn lên. Ở giai đoạn đầu — một server, một ứng dụng — bạn ssh vào máy, tail -f /var/log/app.log là xong. Nhưng khi bạn có 50 node, hàng trăm pod ephemeral (tồn tại tạm thời), thì log trở nên phân tán, dễ mất, và không thể tìm kiếm được. Bạn không thể SSH vào 50 máy để grep cùng một dòng lỗi.
Bài học hôm nay giải quyết đúng vấn đề đó: làm sao thu thập (collect) log từ mọi nơi, tập trung (centralize) về một chỗ, để bạn có thể tìm kiếm, lọc và tương quan chúng lại. Chúng ta sẽ đi sâu vào hai stack phổ biến nhất hiện nay — ELK (Elasticsearch + Logstash + Kibana) và Loki (của Grafana Labs) — hiểu rõ chúng khác nhau ở đâu và khi nào nên chọn cái nào. Lưu ý: log chỉ là một trong ba trụ cột observability; metrics và traces được bàn ở các bài khác. Ở đây ta tập trung riêng vào logs tập trung.
Khái niệm cốt lõi
Kiến trúc chung của một hệ thống centralized logging
Dù chọn stack nào, mọi hệ thống logging tập trung đều có bốn tầng giống nhau:
- Collection (thu thập) — Một agent chạy trên mỗi node, đọc log từ file (
/var/log/containers/*.log) hoặc từ stdout/stderr của container, rồi gửi đi. Các agent phổ biến: Fluentd, Fluent Bit, Promtail, Filebeat, Vector. - Processing / Buffering (xử lý và đệm) — Tầng này parse log, trích xuất field, thêm nhãn (label) như tên pod, namespace, node. Đôi khi có thêm một message queue (Kafka, Redis) làm bộ đệm chống mất dữ liệu khi backend bị quá tải.
- Storage & Indexing (lưu trữ và đánh chỉ mục) — Nơi log được cất giữ và tổ chức để tìm kiếm nhanh. Đây là điểm khác biệt lớn nhất giữa ELK và Loki.
- Visualization & Query (trực quan hóa và truy vấn) — Giao diện để bạn gõ truy vấn, xem log, tạo dashboard. Kibana cho ELK, Grafana cho Loki.
ELK Stack — chỉ mục toàn văn (full-text index)
ELK là viết tắt của Elasticsearch + Logstash + Kibana. Ngày nay người ta hay gọi là "Elastic Stack" và thay Logstash bằng Beats (Filebeat nhẹ hơn) hoặc thêm chữ B thành ELKB.
- Elasticsearch: cơ sở dữ liệu tìm kiếm phân tán. Nó index toàn bộ nội dung của mỗi dòng log — mọi từ đều được đánh chỉ mục ngược (inverted index). Nhờ vậy bạn có thể tìm
error AND timeout AND user_id:12345trên hàng tỷ dòng log trong tích tắc. - Logstash: pipeline xử lý log — nhận vào, dùng bộ lọc
grokđể parse text thô thành field có cấu trúc, rồi đẩy sang Elasticsearch. - Kibana: giao diện web để truy vấn, vẽ dashboard, tạo alert.
Loki — chỉ mục theo nhãn (label index)
Loki ra đời với triết lý ngược lại, được Grafana Labs quảng bá là "Prometheus, but for logs". Ý tưởng cốt lõi:
> Đừng index nội dung của log. Chỉ index metadata (nhãn) thôi. Còn nội dung log thì nén lại và cất vào object storage rẻ tiền.
Cụ thể, Loki chỉ đánh chỉ mục các label như {namespace="checkout", pod="order-service-abc", level="error"}. Còn phần text của dòng log thì được nén (compress) thành các chunk và lưu vào object storage như AWS S3, GCS, hoặc MinIO. Vì object storage rẻ hơn SSD nhiều lần, chi phí lưu trữ của Loki thường thấp hơn ELK đáng kể — có thể rẻ hơn 5–10 lần ở cùng khối lượng log.
Các thành phần Loki:
- Promtail (hoặc Grafana Alloy mới hơn): agent thu thập log, gắn label và gửi về Loki.
- Loki: nơi lưu trữ và truy vấn, dùng ngôn ngữ LogQL (rất giống PromQL).
- Grafana: giao diện xem log — cùng một chỗ bạn xem metrics, nên trải nghiệm rất liền mạch.
user_id:12345 mà không phải là label) sẽ chậm hơn — Loki phải quét (brute-force scan) qua các chunk phù hợp với label. Nên thiết kế label tốt là sống còn với Loki.So sánh nhanh
| Tiêu chí | ELK / Elastic | Loki |
|---|---|---|
| Cách index | Toàn văn (full-text) | Chỉ label |
| Tìm kiếm tùy ý | Rất nhanh | Chậm hơn, dựa vào label |
| Chi phí lưu trữ | Cao | Thấp (object storage) |
| Tài nguyên (RAM/CPU) | Nặng | Nhẹ |
| Tích hợp metrics | Tách rời | Liền mạch với Grafana/Prometheus |
| Phù hợp | Phân tích log sâu, security (SIEM) | Log vận hành, gắn với metrics |
Tình huống thực tế
Ví dụ 1 — Sàn TMĐT Việt Nam đổi từ ELK sang Loki để cắt chi phí
Một công ty thương mại điện tử tại TP.HCM (gọi là "ShopViet") vận hành khoảng 40 microservice trên cụm Kubernetes 25 node. Ban đầu họ dùng ELK. Mỗi ngày hệ thống sinh ra khoảng 300 GB log. Cluster Elasticsearch của họ gồm 6 node data, mỗi node 32 GB RAM và 1 TB SSD NVMe — chi phí hạ tầng riêng cho logging lên tới khoảng 90 triệu đồng/tháng, chưa kể một kỹ sư phải dành nửa thời gian để tune cluster, xử lý shard bị đỏ (red status) và dọn index cũ.
Vấn đề thực tế: 90% truy vấn của đội vận hành chỉ là "cho tôi xem log của service X trong 30 phút qua, lọc theo level error". Họ hiếm khi cần tìm kiếm toàn văn phức tạp. Đội DevOps nhận ra họ đang trả tiền cho một chiếc siêu xe chỉ để đi chợ.
Họ chuyển sang Loki, lưu chunk vào một bucket S3-compatible (dùng dịch vụ object storage nội địa). Kết quả sau 2 tháng: chi phí hạ tầng logging giảm còn khoảng 18 triệu đồng/tháng, RAM tiêu thụ giảm mạnh, và vì Grafana đã có sẵn cho metrics nên đội vận hành xem log ngay cạnh biểu đồ CPU/memory.
Bài học: Đừng mặc định chọn ELK vì "ai cũng dùng". Hãy nhìn vào mẫu hình truy vấn thực tế của đội bạn. Nếu phần lớn là lọc theo service/level trong khoảng thời gian ngắn, Loki tiết kiệm rất nhiều tiền.
Ví dụ 2 — Fintech giữ ELK vì nhu cầu điều tra bảo mật
Một công ty fintech ở Singapore phục vụ khách khu vực Đông Nam Á phải tuân thủ yêu cầu audit nghiêm ngặt. Đội security thường xuyên phải truy vấn kiểu: "Tìm tất cả request trong 90 ngày qua có chứa một địa chỉ IP cụ thể, một device fingerprint, hoặc một mẫu SQL injection", trên hàng chục tỷ dòng log.
Với họ, khả năng tìm kiếm toàn văn trên dữ liệu lịch sử dài hạn là bắt buộc — đó là chức năng của một SIEM. Loki sẽ phải quét quá nhiều chunk và chậm không chấp nhận được. Họ giữ Elastic Stack, tận dụng thêm các tính năng security (Elastic SIEM), và chấp nhận chi phí hạ tầng cao vì nó phục vụ trực tiếp nghĩa vụ tuân thủ (compliance).
Bài học: Khi log là công cụ điều tra bảo mật và bằng chứng audit, sức mạnh tìm kiếm toàn văn của Elasticsearch xứng đáng với chi phí. Chọn công cụ theo yêu cầu nghiệp vụ, không theo giá.
Ví dụ 3 — Startup mất log vì không có buffer
Một startup logistics tại Hà Nội triển khai Fluentd đẩy thẳng vào Elasticsearch, không có message queue ở giữa. Trong một đợt khuyến mãi, lưu lượng tăng gấp 5 lần, log sinh ra ồ ạt. Elasticsearch bị quá tải, từ chối nhận (backpressure), Fluentd không đẩy kịp và buffer bộ nhớ tràn — mất hẳn 40 phút log đúng ngay lúc cần điều tra sự cố nhất.
Sau sự cố, họ thêm một tầng đệm: Fluent Bit đẩy vào Kafka, rồi consumer đọc từ Kafka đẩy vào Elasticsearch với tốc độ ổn định. Kafka hấp thụ đỉnh tải, Elasticsearch không còn bị "dội". Từ đó không mất log dù lưu lượng tăng đột biến.
Bài học: Ở quy mô lớn, hãy đặt một bộ đệm bền vững (Kafka/Redis) giữa agent thu thập và backend lưu trữ. Log bị mất đúng lúc sự cố là kịch bản tệ nhất.
Hướng dẫn từng bước
Dưới đây là quy trình triển khai một stack logging tập trung trên Kubernetes. Tôi lấy Loki làm ví dụ vì nó nhẹ và dễ bắt đầu, nhưng các bước tư duy áp dụng cho cả ELK.
Bước 1 — Chuẩn hóa log ở tầng ứng dụng. Yêu cầu mọi service ghi log ra stdout/stderr (theo nguyên tắc 12-factor), định dạng JSON có cấu trúc, gồm các field: timestamp, level, service, trace_id, message. Log có cấu trúc dễ parse hơn text thô rất nhiều.
Bước 2 — Triển khai agent thu thập dưới dạng DaemonSet. Cài Promtail (hoặc Fluent Bit) như một DaemonSet — Kubernetes sẽ tự chạy đúng một pod agent trên mỗi node. Agent này mount thư mục /var/log/containers để đọc log của mọi container trên node đó.
# Cài Loki + Promtail nhanh bằng Helm
helm repo add grafana https://grafana.github.io/helm-charts
helm install loki grafana/loki-stack \
--set promtail.enabled=true \
--set loki.persistence.enabled=true
Bước 3 — Cấu hình label hợp lý. Đây là bước quan trọng nhất với Loki. Gắn label từ metadata Kubernetes: namespace, app, pod, level. Tuyệt đối tránh dùng những giá trị có độ biến thiên cao (high cardinality) như user_id, request_id, trace_id làm label — chúng sẽ làm nổ chỉ mục.
Bước 4 — Cấu hình storage. Với Loki, trỏ chunk store vào object storage (S3/GCS/MinIO) và đặt retention (thời gian giữ log), ví dụ 30 ngày. Với ELK, cấu hình ILM (Index Lifecycle Management) để tự động chuyển index cũ sang tầng lưu trữ rẻ hơn (warm/cold) rồi xóa.
Bước 5 — Kết nối tầng trực quan hóa. Thêm Loki làm data source trong Grafana. Thử truy vấn LogQL:
{namespace="checkout", app="order-service"} |= "error" | json | duration > 500ms
Dòng này nghĩa là: lấy log của order-service trong namespace checkout, lọc dòng chứa "error", parse JSON, rồi lọc tiếp field duration lớn hơn 500ms.
Bước 6 — Kiểm thử và thiết lập alert. Sinh log lỗi giả, xác nhận nó xuất hiện trong Grafana trong vòng vài giây. (Cấu hình cảnh báo chi tiết thuộc về bài Alerting riêng — ở đây chỉ cần xác nhận đường dẫn log thông suốt.)
Lỗi thường gặp & mẹo
- High cardinality label giết Loki. Đây là lỗi số một. Mỗi tổ hợp label duy nhất tạo ra một "stream" riêng. Nếu bạn đưa
request_idvào label, bạn tạo ra hàng triệu stream và Loki sẽ sập. Quy tắc: label để lọc nhóm log, còn giá trị chi tiết thì để trong nội dung dòng log rồi dùng filter khi truy vấn. - Không đặt retention → ổ đĩa đầy. Log tăng vô hạn. Luôn cấu hình thời gian giữ log ngay từ đầu (ELK dùng ILM, Loki dùng retention config). Rất nhiều sự cố "disk full lúc nửa đêm" đến từ index Elasticsearch không được dọn.
- Quên buffer/queue ở quy mô lớn. Như ví dụ 3 — đẩy thẳng agent vào backend dễ mất log khi tải cao. Thêm Kafka/Redis làm đệm.
- Log không cấu trúc. Nếu app in ra text thô lộn xộn, bạn sẽ khổ sở viết
grok/regex để parse. Chuẩn hóa JSON logging từ phía ứng dụng ngay từ đầu sẽ tiết kiệm hàng trăm giờ về sau. - Log dữ liệu nhạy cảm. Đừng bao giờ log mật khẩu, số thẻ, token. Điều này vừa vi phạm bảo mật (PCI DSS) vừa gây rò rỉ. Thêm bước masking/redaction ở tầng agent.
- Mẹo giảm chi phí: Không phải log nào cũng đáng giữ 30 ngày. Log DEBUG có thể chỉ giữ 3 ngày, log audit giữ 1 năm. Tách retention theo mức độ quan trọng.
- Mẹo hiệu năng ELK: Điều chỉnh số shard hợp lý. Quá nhiều shard nhỏ làm cluster chậm; quá ít shard lớn gây nghẽn. Một shard nên vào khoảng 10–50 GB.
Bài tập thực hành
- Triển khai cơ bản: Dựng một cụm Kubernetes cục bộ (minikube hoặc kind), cài Loki + Promtail bằng Helm. Deploy một app đơn giản in log JSON ra stdout. Xác nhận log xuất hiện trong Grafana.
- Viết truy vấn LogQL: Từ app trên, viết ba truy vấn LogQL: (a) tất cả log của một app cụ thể; (b) chỉ các dòng có
level=error; (c) đếm số lỗi mỗi phút bằng hàmrate().
- Phân tích thiết kế label: Cho một service có các thông tin:
namespace,pod,http_method,status_code,user_id,trace_id. Hãy chỉ ra cái nào nên làm label và cái nào không nên, giải thích lý do dựa trên cardinality.
- Bài tập quyết định: Công ty bạn sinh 500 GB log/ngày, đội vận hành chủ yếu lọc theo service và thời gian, ngân sách hạn chế, nhưng đội security thỉnh thoảng cần điều tra sâu 90 ngày. Đề xuất kiến trúc — bạn sẽ chọn ELK, Loki, hay kết hợp cả hai? Viết một đoạn ngắn bảo vệ lựa chọn.
Tóm tắt
Khi hệ thống của bạn vượt qua ngưỡng một vài máy, log trở nên phân tán và dễ mất — kubectl logs không còn đủ. Centralized logging giải quyết điều đó qua bốn tầng: thu thập → xử lý/đệm → lưu trữ và index → trực quan hóa.
Hai lựa chọn chủ đạo phản ánh hai triết lý đối lập. ELK/Elastic index toàn văn — mạnh mẽ cho tìm kiếm và điều tra bảo mật, nhưng nặng và tốn kém. Loki chỉ index label và nén nội dung vào object storage rẻ tiền — tiết kiệm chi phí, nhẹ, tích hợp liền mạch với Grafana và metrics, nhưng đòi hỏi thiết kế label kỷ luật.
Không có lựa chọn "đúng" tuyệt đối. Hãy chọn dựa trên mẫu hình truy vấn thực tế và yêu cầu nghiệp vụ của đội bạn: nếu chủ yếu lọc theo service và thời gian, Loki thắng về chi phí; nếu cần tìm kiếm toàn văn dài hạn cho compliance/security, ELK xứng đáng. Và dù chọn gì, ba nguyên tắc luôn đúng: log có cấu trúc (JSON) từ tầng ứng dụng, đặt retention ngay từ đầu, và thêm buffer ở quy mô lớn để không bao giờ mất log đúng lúc cần nó nhất.