Chặn đứng cơn ác mộng ngốn RAM: Xây dựng hệ thống Log tập trung với ClickHouse và Vector

Database tutorial - IT technology blog
Database tutorial - IT technology blog

Tại sao mình từ bỏ ELK Stack để chuyển sang ClickHouse + Vector?

Nếu anh em từng vận hành ELK Stack (Elasticsearch, Logstash, Kibana) cho hệ thống lớn, chắc hẳn không lạ gì cảnh Elasticsearch “ngốn” RAM như nước. Mình nhớ đợt làm cho dự án e-commerce xử lý 500GB log mỗi ngày. Cụm Elasticsearch 3 node, mỗi node 32GB RAM nhưng liên tục dính lỗi Java Heap Space và bị OOM Killer ghé thăm. Chi phí lưu trữ cũng phình to chóng mặt do Elasticsearch đánh index dữ liệu quá nặng.

Sau khi thử đủ loại “thuốc”, mình quyết định chốt phương án ClickHouse và Vector. Kết quả thực tế khiến cả team bất ngờ: RAM tiêu thụ giảm 75%, tốc độ query nhanh gấp 10 lần và tỷ lệ nén dữ liệu cực kỳ ấn tượng. Dưới đây là những kinh nghiệm thực chiến mình rút ra khi triển khai kiến trúc này.

Kiến trúc bộ đôi ClickHouse + Vector

Thay vì dùng Logstash nặng nề, chúng ta chuyển sang Vector. Thay vì dùng Elasticsearch, chúng ta chọn ClickHouse làm kho lưu trữ. Mô hình hoạt động như sau:

  • Vector (Agent): Thu thập log từ file, stdout hoặc syslog. Công cụ này viết bằng Rust nên cực nhẹ và nhanh.
  • ClickHouse (Storage): Database dạng cột (Column-oriented) tối ưu cho việc ghi dữ liệu hàng loạt và phân tích tốc độ cao.
  • Grafana (Visualization): Kết nối trực tiếp vào ClickHouse để vẽ dashboard và soi log thời gian thực.

1. ClickHouse – Cỗ máy tốc độ vượt trội

Khác với MySQL hay PostgreSQL lưu dữ liệu theo hàng, ClickHouse lưu theo cột. Khi bạn cần lọc log theo level='ERROR', ClickHouse chỉ đọc đúng cột đó trên đĩa và bỏ qua phần còn lại. Nhờ cơ chế này, việc quét qua 1 tỷ dòng log để tìm lỗi chỉ mất chưa đầy 1 giây. Đây là điều mà Elasticsearch khó lòng đạt được nếu không có dàn phần cứng cực khủng.

2. Vector – Agent thu thập log siêu nhẹ

Trước đây, Fluentd hoặc Logstash thường chiếm từ 500MB đến hơn 1GB RAM trên mỗi server ứng dụng. Khi lượng log tăng, agent rất dễ bị treo. Vector giải quyết triệt để vấn đề này. Nó chỉ tiêu tốn khoảng 20-30MB RAM nhưng vẫn xử lý throughput cực lớn nhờ lợi thế của ngôn ngữ Rust.

Thực hành: Triển khai hệ thống Log tập trung

Bước 1: Cài đặt ClickHouse

Để triển khai nhanh, anh em nên dùng Docker Compose. Cách này giúp quản lý tài nguyên và cấu hình tập trung hơn.

version: '3.7'
services:
  clickhouse:
    image: clickhouse/clickhouse-server:latest
    ports:
      - "8123:8123"
      - "9000:9000"
    volumes:
      - ./ch_data:/var/lib/clickhouse
    ulimits:
      nofile:
        soft: 262144
        hard: 262144

Bước 2: Thiết kế bảng lưu trữ tối ưu

Đừng tạo bảng theo kiểu mặc định. Hãy tận dụng MergeTree engine và nén dữ liệu bằng ZSTD để tiết kiệm ổ cứng. Đây là cấu trúc bảng mình thường dùng:

CREATE TABLE IF NOT EXISTS logs (
    timestamp DateTime64(3, 'UTC'),
    service_name LowCardinality(String),
    level LowCardinality(String),
    message String,
    metadata String,
    INDEX idx_message message TYPE tokenbf_v1(4096, 2, 0) GRANULARITY 1
) ENGINE = MergeTree()
PARTITION BY toYYYYMMDD(timestamp)
ORDER BY (service_name, timestamp)
TTL timestamp + INTERVAL 30 DAY;

Lưu ý: Dùng LowCardinality cho các trường ít giá trị lặp lại như level (INFO, ERROR). Thủ thuật này giúp giảm dung lượng lưu trữ và tăng tốc độ tìm kiếm đáng kể.

Bước 3: Cấu hình Vector để đẩy dữ liệu

Tạo file vector.yaml để định nghĩa nguồn log. Vector có tính năng remap rất mạnh để xử lý dữ liệu thô trước khi nạp vào database.

sources:
  app_logs:
    type: "file"
    include: ["/var/log/myapp/*.log"]

sinks:
  clickhouse_sink:
    type: "clickhouse"
    inputs: ["app_logs"]
    endpoint: "http://clickhouse:8123"
    database: "default"
    table: "logs"
    skip_unknown_fields: true
    compression: "gzip"
    batch:
      max_events: 1000
      timeout_secs: 5

ClickHouse không thích việc ghi dữ liệu lẻ tẻ từng dòng. Bạn bắt buộc phải dùng batch. Hãy gom log lại và đẩy theo đợt (ví dụ mỗi 5 giây) để đạt hiệu năng ghi tốt nhất.

Kinh nghiệm “xương máu” khi vận hành

Xử lý dữ liệu JSON động

Log thường có cấu trúc JSON không cố định. Thay vì tạo cột cho từng field mới, mình lưu toàn bộ vào một cột String. Khi cần truy vấn, hãy dùng các hàm JSON của ClickHouse:

SELECT 
    JSONExtractString(metadata, 'user_id') as user_id,
    count()
FROM logs
WHERE level = 'ERROR'
GROUP BY user_id

Nén dữ liệu và tiết kiệm đĩa cứng

Khi mình chuyển từ Elasticsearch sang ClickHouse, dung lượng đĩa cứng giảm từ 1.2TB xuống còn vỏn vẹn 150GB. Tỷ lệ nén gần 8 lần này giúp tiết kiệm rất nhiều chi phí hạ tầng. Nếu muốn nén mạnh hơn, bạn có thể thử CODEC(ZSTD(3)) cho cột log message.

Đừng quên Partitioning

Mình từng quên cấu hình PARTITION BY theo ngày. Hậu quả là sau 6 tháng, việc xóa log cũ (retention) cực kỳ chậm. Khi có Partition, việc xóa dữ liệu cũ chỉ là thao tác drop một phân vùng, diễn ra trong tích tắc mà không gây áp lực lên hệ thống.

Tổng kết

Combo ClickHouse + Vector là lựa chọn sáng suốt nếu bạn muốn tối ưu chi phí mà vẫn giữ được hiệu năng cao. Hệ thống này không chỉ giúp tiết kiệm RAM, ổ cứng mà còn mang lại trải nghiệm truy vấn mượt mà.

Dù không có sẵn giao diện bóng bẩy như Kibana, nhưng khi kết hợp với Grafana, bạn hoàn toàn có thể xây dựng một hệ thống quan sát (observability) chuyên nghiệp. Chúc anh em triển khai thành công và thoát khỏi nỗi lo lỗi OutOfMemory mỗi đêm!

Share: