Tự động hóa xoay vòng log trên Linux với Logrotate: Nén file, phân quyền và phòng tránh đầy ổ đĩa

Linux tutorial - IT technology blog
Linux tutorial - IT technology blog

Vấn đề thực tế: Sự cố đầy ổ đĩa lúc nửa đêm

Đồng hồ điểm 2:00 sáng Chủ nhật thì hệ thống cảnh báo réo liên hồi: API chính trên production đã ngừng phản hồi. Bạn SSH vào rescue console nhưng bash lập tức báo lỗi: No space left on device. PostgreSQL dừng mọi transaction ghi, Redis bị sập và hệ thống giám sát ngừng hoạt động hoàn toàn. Kiểm tra nhanh bằng lệnh df -h sẽ chỉ ra ngay thủ phạm: một file log ứng dụng duy nhất tại /var/log/app/output.log đã phình to lên 85GB chỉ trong 72 giờ, chiếm trọn 100% phân vùng root.

Việc dung lượng log tăng trưởng không kiểm soát vẫn âm thầm đánh sập các cụm máy chủ production mỗi ngày. Web server, background worker và container runtime sinh ra hàng triệu dòng log mỗi giờ. Nếu không có chính sách xoay vòng (rotation) tự động, những file này sẽ làm đầy ổ cứng và làm cạn kiệt inode của hệ thống tệp tin.

Phân tích nguyên nhân gốc rễ: Log không kiểm soát làm cạn kiệt dung lượng và Inode như thế nào

Các tiến trình Linux ghi vào file thông qua các file descriptor đang hoạt động. Khi một daemon ghi liên tục, bộ nhớ lưu trữ sẽ bị suy giảm theo một số cách cụ thể:

  • Cạn kiệt Block Storage: Các đoạn log debug chi tiết và những đợt tấn công brute-force vào web có thể xả ra 500MB văn bản thuần (plain text) chỉ trong chưa đầy một giờ. Dữ liệu văn bản thô gây lãng phí rất lớn dung lượng block storage so với các file nén gzip, vốn có thể giảm kích thước log từ 85% đến 90%.
  • Khóa File Descriptor: Chạy lệnh rm /var/log/app/output.log trên một file log đang hoạt động không hề giải phóng dung lượng đĩa. Kernel vẫn giữ inode chưa liên kết (unlinked inode) tồn tại chừng nào tiến trình ứng dụng còn giữ handle mở file. Dung lượng lưu trữ vẫn sẽ bị khóa ở mức 100% cho đến khi bạn khởi động lại daemon hoặc truncate handle thủ công qua /proc.
  • Sai lệch phân quyền và Inode: Các script xoay vòng log tự chế thường di chuyển file và tạo file thay thế với quyền sở hữu là root:root thay vì user của ứng dụng như www-data. Ứng dụng sẽ bị crash ngay lập tức ở lần ghi tiếp theo do lỗi bị từ chối quyền truy cập (permission denied).

So sánh các giải pháp: Truncate thủ công, Shell Script tự viết và Logrotate

Các kỹ sư thường xử lý việc tăng trưởng log theo một trong ba cách sau:

Phương pháp Ưu điểm Nhược điểm
Truncate thủ công (> file.log) Không gây downtime; giải phóng các block ổ đĩa ngay lập tức mà không cần tắt tiến trình. Hoàn toàn thủ công. Xóa sạch mọi dữ liệu lịch sử và không có tác dụng ngăn ngừa sự cố tiếp theo.
Shell Script tùy biến qua Cron Pipeline linh hoạt, tận dụng được find -mtime, gzip và các hook upload tùy chỉnh lên S3. Kém ổn định và dễ gãy vỡ. Không xử lý được việc hoán đổi file log có tính atomic, gửi signal hoặc khôi phục trạng thái khi các cron job chạy chồng chéo.
Tiện ích Logrotate mặc định Được cài sẵn trên Debian, Ubuntu, RHEL và Rocky Linux. Hỗ trợ sẵn việc phân quyền, reload qua signal, dọn dẹp log theo thời gian và nén file. Đòi hỏi cú pháp chỉ thị chính xác và cần hiểu rõ cách service đích xử lý các file descriptor.

Các shell script tự chế có thể chạy ổn cho đến khi gặp phải một trường hợp ngoại lệ (edge case) làm phát sinh lỗi. Tiện ích logrotate nguyên bản mang đến một hệ thống khai báo (declarative) đã được kiểm chứng qua thực tế, giúp loại bỏ hoàn toàn gánh nặng bảo trì thủ công.

Cách tiếp cận tối ưu: Triển khai xoay vòng log tự động với Logrotate

Hầu hết các bản phân phối Linux thực thi logrotate mỗi ngày một lần thông qua một systemd timer (logrotate.timer) hoặc cron script trong /etc/cron.daily/logrotate. Logrotate nạp các cấu hình mặc định từ /etc/logrotate.conf và đọc các quy tắc theo từng module từ các file riêng lẻ bên trong /etc/logrotate.d/.

Bước 1: Cấu trúc của một file cấu hình Logrotate chuẩn Production

Hãy giữ cấu hình ở dạng module. Thay vì chỉnh sửa cấu hình mặc định toàn cục, hãy tạo một file riêng cho service của bạn bên trong /etc/logrotate.d/:

sudo nano /etc/logrotate.d/my-app

Thêm khối cấu hình sau:

/var/log/my-app/*.log {
    daily
    missingok
    rotate 14
    compress
    delaycompress
    notifempty
    create 0640 myappuser myappgroup
    sharedscripts
    postrotate
        /usr/bin/systemctl reload my-app.service > /dev/null 2>&1 || true
    endscript
}

Dưới đây là chi tiết hoạt động của các chỉ thị này:

  • daily: Kiểm tra trạng thái log mỗi 24 giờ. Bạn có thể đổi sang weekly, monthly hoặc áp dụng ngưỡng kích thước file như size 250M.
  • missingok: Bỏ qua lỗi nếu file log chưa được tạo ra.
  • rotate 14: Giữ lại 14 bản lưu trữ đã xoay vòng trước khi xóa các log cũ. Điều này giúp duy trì lịch sử ghi nhận trong vòng hai tuần một cách gọn gàng.
  • compress & delaycompress: Nén các bản lưu trữ bằng gzip. delaycompress sẽ chờ một chu kỳ rồi mới nén bản lưu trữ gần nhất (app.log.1), đảm bảo các tiến trình đang chạy đã xả hết buffer ghi còn tồn đọng.
  • notifempty: Bỏ qua hoàn toàn việc xoay vòng nếu file có kích thước 0 byte, tránh làm rác hệ thống bởi các file lưu trữ rỗng.
  • create 0640 myappuser myappgroup: Tạo một file log mới với phân quyền chặt chẽ và gán đúng quyền sở hữu cho ứng dụng ngay sau khi xoay vòng.

Bước 2: Quản lý phân quyền và kích hoạt xoay vòng theo kích thước

Các môi trường thắt chặt bảo mật thường giới hạn quyền truy cập thư mục cho các service account chuyên biệt. Nếu logrotate chạy dưới quyền root trên một thư mục thuộc sở hữu của user không có đặc quyền, nó có thể từ chối thực thi. Hãy thêm chỉ thị su để chỉ định chính xác ngữ cảnh thực thi:

/var/log/my-app/*.log {
    su myappuser myappgroup
    daily
    rotate 7
    maxsize 500M
    compress
    create 0644 myappuser myappgroup
}

Chỉ thị maxsize 500M đóng vai trò như một van an toàn. Nếu một ứng dụng đột nhiên bị lặp vô tận và ghi ra 2GB lỗi trước khi đến lịch xoay vòng hằng ngày, logrotate sẽ ép xoay vòng sớm ngay khi file chạm mốc 500MB.

Bước 3: Ngăn ngừa gián đoạn dịch vụ với copytruncate và Signal

Khi logrotate đổi tên một file log đang hoạt động, daemon đang chạy vẫn tiếp tục ghi vào inode cũ đó cho đến khi nhận được chỉ thị mở lại đường dẫn file. Bạn có hai lựa chọn tùy thuộc vào cách service của bạn xử lý log:

Cách A: Ứng dụng hỗ trợ mở lại file log qua Signal (Khuyên dùng)

Các phần mềm production như Nginx, Apache và HAProxy có thể reload lại handle của file log tức thì mà không làm rớt các kết nối đang hoạt động. Gửi một POSIX signal như USR1 bên trong khối postrotate:

/var/log/nginx/*.log {
    daily
    rotate 30
    compress
    delaycompress
    sharedscripts
    postrotate
        [ -f /run/nginx.pid ] && kill -USR1 $(cat /run/nginx.pid)
    endscript
}

Cách B: Ứng dụng không hỗ trợ Signal (Binary Node.js, Python hoặc Go)

Nhiều backend service tự phát triển không hiện thực bộ xử lý signal (signal handler) để xoay vòng file. Hãy sử dụng copytruncate trong những trường hợp này. Logrotate sẽ sao chép nội dung đang ghi sang một file phụ rồi truncate (làm rỗng) file gốc tại chỗ, giữ nguyên file descriptor đang mở:

/var/log/legacy-app/*.log {
    daily
    rotate 7
    copytruncate
    compress
    missingok
}

Lưu ý rằng copytruncate có một race condition nhỏ: bất kỳ log nào được ghi trong tích tắc giữa lúc sao chép và truncate đều có thể bị mất. Đối với các API xử lý lượng giao dịch lớn, hãy cấu hình xử lý signal chuẩn xác hoặc chuyển hướng log sang journald.

Bước 4: Kiểm thử và xác thực với Dry Run

Đừng bao giờ đợi đến khi cron job chạy lúc nửa đêm mới phát hiện cấu hình bị lỗi. Hãy xác thực cú pháp và kiểm thử các quy tắc của bạn ngay lập tức với các cờ dry-run:

# Thực hiện dry run để kiểm tra các hành động dự kiến mà không thay đổi file
sudo logrotate -d /etc/logrotate.d/my-app

# Bắt buộc xoay vòng ngay lập tức để kiểm tra việc thực thi script và phân quyền file
sudo logrotate -vf /etc/logrotate.d/my-app

Kiểm tra thư mục đích để xác minh lại phân quyền, quyền sở hữu file và trạng thái nén kết quả:

ls -la /var/log/my-app/

Một quy trình logrotate được cấu hình chuẩn chỉnh sẽ giúp dung lượng đĩa luôn ở mức dự đoán được, bảo toàn lịch sử log quan trọng phục vụ điều tra sự cố và bảo vệ máy chủ production khỏi các sự cố sập hệ thống bất ngờ lúc nửa đêm.

Share: