Quản lý log khi hệ thống lớn dần: Vấn đề không ai muốn nhưng ai cũng gặp
Hồi mới setup 2–3 server, mình vẫn SSH thẳng vào từng máy rồi tail -f /var/log/syslog để debug. Cách đó ổn — cho đến khi số server tăng lên 8, rồi 15. Khi production bị lỗi lúc 2 giờ sáng và bạn phải SSH vào từng machine để tìm error message, đó là lúc bạn thật sự cần centralized logging.
Graylog là thứ mình đưa vào production sau khi đã “đau” đủ rồi. Sau 6 tháng chạy thực tế, mình viết lại những gì thực sự hữu ích — không phải copy từ tài liệu chính thức.
Graylog là gì và tại sao không dùng ELK Stack?
Nói thẳng: Graylog nhận log từ mọi server, index tất cả vào OpenSearch, rồi cho bạn search và tạo alert từ một UI duy nhất. Không còn cảnh mở 10 terminal tab để grep trên từng máy nữa.
Kiến trúc gồm 3 thành phần chính:
- Graylog Server: xử lý, parse, route log
- MongoDB: lưu metadata — cấu hình, user, stream, alert
- OpenSearch (hoặc Elasticsearch): full-text search engine — nơi log thực sự được lưu và query
ELK Stack thì sao? Mình đã chạy thử cả hai. ELK mạnh hơn về customization, nhưng mất khoảng 2–3 ngày để setup đúng cách. Graylog thì nửa buổi là xong. Với team nhỏ không có DevOps chuyên biệt, đó là sự khác biệt đáng kể — nhất là phần alert và stream management, Graylog thắng rõ ràng.
Cài đặt Graylog bằng Docker Compose
Docker Compose là cách nhanh nhất để chạy Graylog. Mình dùng cách này cả trong dev lẫn small production — ổn định với workload dưới 50GB log/ngày.
Tạo file docker-compose.yml:
version: '3.8'
services:
mongodb:
image: mongo:6.0
volumes:
- mongodb_data:/data/db
restart: unless-stopped
opensearch:
image: opensearchproject/opensearch:2.11.0
environment:
- discovery.type=single-node
- DISABLE_SECURITY_PLUGIN=true
- OPENSEARCH_JAVA_OPTS=-Xms1g -Xmx1g
ulimits:
memlock:
soft: -1
hard: -1
volumes:
- opensearch_data:/usr/share/opensearch/data
restart: unless-stopped
graylog:
image: graylog/graylog:5.2
environment:
- GRAYLOG_PASSWORD_SECRET=somepasswordpepper1234567890abc
- GRAYLOG_ROOT_PASSWORD_SHA2=8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918
- GRAYLOG_HTTP_EXTERNAL_URI=http://YOUR_SERVER_IP:9000/
- GRAYLOG_ELASTICSEARCH_HOSTS=http://opensearch:9200
- GRAYLOG_MONGODB_URI=mongodb://mongodb:27017/graylog
depends_on:
- mongodb
- opensearch
ports:
- "9000:9000" # Web UI
- "12201:12201/udp" # GELF UDP input
- "514:514/udp" # Syslog UDP input
volumes:
- graylog_data:/usr/share/graylog/data
restart: unless-stopped
volumes:
mongodb_data:
opensearch_data:
graylog_data:
Quan trọng: GRAYLOG_ROOT_PASSWORD_SHA2 ở trên là hash của chuỗi “admin”. Đổi ngay trước khi deploy thật:
echo -n "your_strong_password" | sha256sum | cut -d' ' -f1
Khởi động stack:
docker compose up -d
# Chờ khoảng 60–90 giây để Graylog khởi động hoàn toàn
docker compose logs -f graylog
Web UI chạy tại http://YOUR_SERVER_IP:9000. Đăng nhập với admin và password vừa tạo.
Tạo Input để nhận log
Mở System → Inputs trên Web UI. Tạo 2 input này trước:
1. Syslog UDP (port 514)
Chọn Syslog UDP, bind address 0.0.0.0, port 514. Input này nhận log từ rsyslog trực tiếp — nhanh, đơn giản, không cần cài agent thêm.
2. GELF UDP (port 12201)
Chọn GELF UDP, port 12201. GELF (Graylog Extended Log Format) là format JSON có cấu trúc — hỗ trợ custom fields, phù hợp với application log từ Docker containers hoặc app tự viết.
Cấu hình rsyslog trên server client
Trên mỗi server Linux cần gửi log về Graylog, cấu hình rsyslog:
sudo nano /etc/rsyslog.d/90-graylog.conf
Thêm vào file một dòng duy nhất:
# Gửi tất cả log qua UDP đến Graylog
*.* @GRAYLOG_SERVER_IP:514;RSYSLOG_SyslogProtocol23Format
Restart rsyslog rồi test ngay:
sudo systemctl restart rsyslog
# Gửi test message
logger -t test "Hello from $(hostname)"
Mở Graylog Web UI → Search, tìm source:hostname_cua_ban — thấy message “Hello from…” là xong.
Gửi log Docker container qua GELF
Với Docker, thêm log driver vào docker-compose.yml của service cần monitor:
services:
my_app:
image: my_app:latest
logging:
driver: gelf
options:
gelf-address: "udp://GRAYLOG_SERVER_IP:12201"
tag: "my_app"
Tạo Stream và Alert
Stream là tính năng mình thích nhất của Graylog. Nó route log theo rule vào các “kênh” riêng — stream riêng cho Nginx, cho SSH auth, cho application errors. Cực kỳ tiện khi cần debug theo từng service mà không bị ngập trong log chung.
Tạo stream tại Streams → Create Stream. Một số rule hữu dụng:
- Field
sourcecontainswebserver-01— lọc log từ 1 server cụ thể - Field
messagematches regexERROR|CRITICAL|FATAL— bắt tất cả error - Field
facilityequalsauth— chỉ lấy auth log (SSH, sudo)
Xong stream rồi thì thêm Alert Condition. Ví dụ: “gửi notification nếu có hơn 10 ERROR message trong 5 phút”. Kết hợp với Notification gửi về Slack, email, hoặc webhook — tuỳ bạn.
Thật ra mình phải thừa nhận: alert fatigue là vấn đề thực tế mình gặp ngay từ đầu. Set threshold quá thấp — alert bắn liên tục, rồi bắt đầu ignore hết. Phải tune lại nhiều lần: tăng threshold, thêm điều kiện AND, whitelist một số source nhất định. Kinh nghiệm rút ra: bắt đầu với threshold cao, quan sát 1–2 tuần để nắm baseline của hệ thống, rồi dần điều chỉnh xuống. Production của bạn có traffic pattern riêng — đừng copy số liệu từ blog nào.
Kết luận sau 6 tháng chạy thực tế
Graylog giải quyết bài toán “log ở đâu” một cách thực sự hiệu quả. Những gì mình ghi nhận sau nửa năm:
- Thời gian debug incident giảm từ ~30 phút xuống ~5 phút vì có thể search cross-server trong 1 UI
- Phát hiện được 2 lần brute-force SSH nhờ alert trên failed auth logs
- Retention policy (tự xóa log cũ hơn 30 ngày) giữ disk usage ổn định — không lo bị đầy disk đột ngột
Một điểm cần biết trước: Graylog khá ngốn RAM. Cụm 1 node của mình cần tối thiểu 8GB để chạy ổn — riêng OpenSearch đã chiếm 2–4GB. Server nhỏ hơn thì xem xét Grafana Loki: nhẹ hơn nhiều, search kém hơn một chút, nhưng đủ dùng cho hầu hết trường hợp.
Còn nếu bạn có ngân sách và quản lý 20+ server — Graylog vẫn là lựa chọn đáng tin. Setup một lần, dùng lâu dài. Lần sau debug lúc 2 giờ sáng sẽ bớt khổ hơn nhiều.
