Thiết lập cơ chế tự động sao lưu và phục hồi log trên Linux sử dụng Bash, Cron và Ansible
Trong môi trường quản trị hệ thống hiện đại, việc đảm bảo tính sẵn sàng của dữ liệu và khả năng phân tích sự cố là ưu tiên hàng đầu. Log (nhật ký hệ thống) đóng vai trò then chốt trong việc theo dõi hiệu năng và truy vết lỗi. Tuy nhiên, các tập tin log tăng kích thước rất nhanh, nếu không được xử lý sẽ làm đầy disk, gây treo dịch vụ. Bài viết này sẽ hướng dẫn bạn xây dựng một giải pháp toàn diện: sử dụng Ansible để triển khai đồng bộ các kịch bản Bash trên hàng loạt máy chủ, và Cron để kích hoạt các kịch bản này định kỳ. Mục tiêu là tự động hóa việc nén, sao lưu log vào một thư mục lưu trữ an toàn, đồng thời làm sạch các log cũ theo chính sách vòng quay (rotation policy).
Lợi ích của việc kết hợp Ansible, Bash và Cron
Việc chỉ sử dụng Bash script trên từng máy chủ thủ công là cách làm không khả thi khi quy mô hạ tầng lớn. Ansible giúp bạn đóng gói logic của Bash script thành các module playbook, từ đó triển khai đồng loạt lên hàng chục, thậm chí hàng trăm máy chủ chỉ với một lần chạy. Trong khi đó, Cron là bộ điều phối thời gian tin cậy đã có sẵn trên mọi hệ thống Linux, đảm bảo các tác vụ được thực thi đúng giờ. Kết hợp ba công cụ này tạo nên một quy trình DevOps chuẩn mực: Infrastructure as Code (Ansible) điều phối Logic (Bash) theo Lịch trình (Cron).
Cấu trúc bài toán và yêu cầu kỹ thuật
Chúng ta sẽ xây dựng một kịch bản sao lưu tập trung cho các log của Apache hoặc Nginx (thường nằm ở thư mục /var/log/nginx hoặc /var/log/apache2). Kịch bản này sẽ thực hiện các hành động sau mỗi ngày: di chuyển toàn bộ log cũ ra thư mục sao lưu có kèm ngày tháng, nén file bằng gzip để tiết kiệm dung lượng, và xóa các bản sao lưu quá 7 ngày để tránh tràn disk. Sau khi viết xong script, bạn sẽ dùng Ansible để copy script lên tất cả các node và cài đặt cron job tương ứng.
Môi trường giả định
Giả sử bạn đang quản trị một cụm máy chủ chạy CentOS hoặc Ubuntu, có SSH key cấu hình sẵn để Ansible kết nối. Bạn sẽ đóng vai trò là Control Node để điều khiển các Host. Thư mục đích đến để lưu log đã nén sẽ là /var/backups/logs. Tất cả các tài khoản người dùng thực thi script và cron sẽ là root để đảm bảo quyền truy cập đầy đủ vào thư mục system.
Viết kịch bản Bash cho quá trình sao lưu log
Trước khi tích hợp vào Ansible, chúng ta cần xây dựng cốt lõi logic bằng ngôn ngữ Bash. Script này cần đủ thông minh để xử lý các trường hợp lỗi cơ bản, chẳng hạn như thư mục đích chưa tồn tại hoặc quá trình nén bị gián đoạn. Chúng ta sẽ tạo một file script với tên là backup_logs.sh.
Đầu tiên, script cần khai báo môi trường và các biến cấu hình. Việc đặt các biến ở đầu script giúp dễ dàng điều chỉnh sau này mà không cần đọc lại toàn bộ logic. Tiếp theo, chúng ta cần tạo thư mục đích nếu chưa tồn tại. Quan trọng nhất là phần logic "rotate and compress", nơi script sẽ di chuyển file log sang thư mục backup, đổi tên theo timestamp, và chạy lệnh gzip. Cuối cùng là phần "cleanup", sử dụng lệnh find để tìm các file có ngày tạo cách ngày hiện tại hơn 7 ngày và thực hiện xóa.
Dưới đây là nội dung chi tiết của file backup_logs.sh:
#!/bin/bash
set -euo pipefail
# Cấu hình
BACKUP_SOURCE="/var/log/nginx"
BACKUP_DEST="/var/backups/logs"
RETENTION_DAYS=7
LOG_FILE="/var/log/system_backup.log"
# Tạo thư mục đích nếu chưa tồn tại
if [ ! -d "$BACKUP_DEST" ]; then
mkdir -p "$BACKUP_DEST"
echo "$(date '+%F %T'): Created backup directory $BACKUP_DEST" >> "$LOG_FILE"
fi
# Lấy ngày tháng hiện tại để đặt tên file
TIMESTAMP=$(date '+%Y%m%d_%H%M%S')
# Kiểm tra xem thư mục nguồn có tồn tại không
if [ ! -d "$BACKUP_SOURCE" ]; then
echo "$(date '+%F %T'): ERROR: Source directory $BACKUP_SOURCE does not exist." >> "$LOG_FILE"
exit 1
fi
# Di chuyển và nén log
cd "$BACKUP_SOURCE"
# Chỉ sao lưu các file đuôi .log
for file in *.log; do
if [ -f "$file" ]; then
dest_file="${BACKUP_DEST}/${file%.log}_${TIMESTAMP}.log"
mv "$file" "$dest_file"
gzip "$dest_file"
echo "$(date '+%F %T'): Backed up $file to ${dest_file}.gz" >> "$LOG_FILE"
# Tạo file log mới rỗng để dịch vụ tiếp tục ghi (keep service running)
touch "$file"
chmod 644 "$file"
fi
done
# Xóa các bản sao lưu quá hạn
if [ -d "$BACKUP_DEST" ]; then
find "$BACKUP_DEST" -type f -name "*.gz" -mtime +$RETENTION_DAYS -delete
echo "$(date '+%F %T'): Cleanup completed. Deleted logs older than $RETENTION_DAYS days." >> "$LOG_FILE"
fi
echo "Backup script completed successfully."
Khi viết script này, tôi đã sử dụng set -euo pipefail để đảm bảo script dừng ngay lập tức nếu có lỗi xảy ra, biến chưa được khai báo, hoặc một phần trong pipeline bị lỗi. Đây là tiêu chuẩn vàng cho bất kỳ script automation nào trong môi trường sản xuất. Ngoài ra, việc sử dụng vòng lặp for giúp xử lý từng file log một cách cẩn thận, đảm bảo tên file được đặt đúng định dạng và quyền hạn được khôi phục sau khi tạo file mới.
Triển khai script bằng Ansible
Sau khi hoàn thiện logic Bash, bước tiếp theo là đưa script này vào Ansible để phân phối ra toàn cụm. Thay vì chạy lệnh SSH thủ công, chúng ta sẽ tạo một Playbook. Playbook này sẽ thực hiện ba nhiệm vụ chính: tạo thư mục đích, copy script đã viết ở trên lên server, và tạo file crontab để định kỳ gọi script.
Chúng ta sẽ sử dụng module copy của Ansible để đưa file script lên, module file để đảm bảo quyền thực thi (chmod +x), và module cron để quản lý lịch trình. Ưu điểm của module cron là nó tự động viết vào file crontab của user, tránh việc ghi đè toàn bộ file cron nếu có các task khác đang chạy. Điều này giúp quy trình an toàn hơn so với việc copy một file 00-backup thủ công vào thư mục /etc/cron.d/ nếu không cẩn thận.
Dưới đây là cấu trúc Playbook deploy_backup.yml:
---
- name: Deploy Log Backup Automation
hosts: all_servers
become: yes
gather_facts: yes
vars:
backup_script_path: /usr/local/bin/backup_logs.sh
backup_destination: /var/backups/logs
log_source: /var/log/nginx
retention_days: 7
cron_minute: "0"
cron_hour: "2"
tasks:
- name: Create backup destination directory
file:
path: "{{ backup_destination }}"
state: directory
mode: '0755'
owner: root
group: root
- name: Copy backup script to remote servers
copy:
src: ./backup_logs.sh
dest: "{{ backup_script_path }}"
owner: root
group: root
mode: '0755'
remote_src: no
delegate_to: localhost
no_log: false
- name: Ensure backup script has correct execution permissions
file:
path: "{{ backup_script_path }}"
state: file
mode: '0755'
owner: root
group: root
- name: Configure cron job for daily log rotation
cron:
name: "Daily Log Backup and Cleanup"
minute: "{{ cron_minute }}"
hour: "{{ cron_hour }}"
job: "{{ backup_script_path }}"
user: root
state: present
- name: Validate cron installation
shell: |
crontab -l -u root | grep "{{ backup_script_path }}"
register: cron_check
failed_when: cron_check.rc != 0 and cron_check.stdout is not defined
- name: Notify success
debug:
msg: "Log backup automation deployed successfully on {{ inventory_hostname }}. Script path: {{ backup_script_path }}"
Cần lưu ý trong Playbook trên là dòng delegate_to: localhost trong task copy. Điều này nghĩa là Ansible sẽ lấy file script từ máy điều khiển (Control Node) nơi bạn chạy playbook, sau đó push lên máy đích. Nếu bạn đã đặt file script vào thư mục /playbooks/files/, hãy đảm bảo đường dẫn src tương đối hoặc tuyệt đối là chính xác. Ngoài ra, việc sử dụng biến vars giúp bạn dễ dàng thay đổi thời gian chạy cron hoặc đường dẫn thư mục log mà không cần chỉnh sửa lại toàn bộ logic playbook.
Cấu hình Cron và các lưu ý quan trọng
Việc cấu hình Cron trong Ansible khá đơn giản nhờ module chuyên biệt, nhưng có những điểm cần lưu ý để đảm bảo hệ thống hoạt động ổn định trong dài hạn. Thứ nhất là vấn đề môi trường biến số. Khi Cron chạy, nó thường không thừa hưởng các biến môi trường của user như khi bạn mở terminal. Do đó, script Bash của chúng ta nên khai báo rõ ràng các biến hoặc sử dụng đường dẫn tuyệt đối cho tất cả các command (như /bin/gzip thay vì gzip nếu cần thiết, mặc dù trên Linux chuẩn thì PATH thường có sẵn). Tuy nhiên, việc kiểm tra path trong script như ở ví dụ trên là một thói quen tốt.
Thứ hai là vấn đề xung đột tài nguyên. Nếu log của bạn rất lớn (hàng chục GB), quá trình nén có thể chiếm dụng toàn bộ CPU và I/O của disk, ảnh hưởng đến hiệu năng dịch vụ web đang chạy. Để giải quyết điều này, bạn có thể thêm lệnh ionice hoặc nice vào script để giảm ưu tiên CPU của quá trình sao lưu. Ví dụ, bạn có thể thay thế dòng gzip "$dest_file" bằng ionice -c 3 gzip "$dest_file" để đặt nó ở mức ưu tiên thấp nhất.
Thứ ba là xử lý lỗi. Nếu một lần backup bị lỗi, bạn không muốn cron tiếp tục chạy và tạo các file lỗi hoặc lặp lại lỗi đó. Trong ví dụ script ở trên, tôi đã thêm set -e để script dừng khi gặp lỗi, nhưng bạn cũng nên cân nhắc thêm cơ chế gửi cảnh báo (thông qua email hoặc Slack webhook) nếu quá trình chạy không thành công. Ansible cũng có thể mở rộng để gọi một script check-status và báo cáo kết quả sau khi task cron được setup.
Chạy Playbook và kiểm tra kết quả
Để triển khai, bạn chỉ cần lưu file deploy_backup.yml vào một thư mục playbook, đặt file backup_logs.sh vào cùng thư mục hoặc thư mục files tùy cấu hình. Sau đó, chạy lệnh ansible-playbook deploy_backup.yml. Ansible sẽ hiển thị chi tiết từng task đang thực hiện trên từng host. Sau khi playbook chạy xong, bạn có thể ssh vào một máy chủ bất kỳ và kiểm tra bằng lệnh crontab -l để thấy cron job đã được thêm vào. Bạn cũng nên kiểm tra thư mục /var/backups/logs để chắc chắn thư mục đã được tạo với đúng quyền hạn.
Kết luận và hướng phát triển
Qua bài hướng dẫn này, chúng ta đã xây dựng được một quy trình tự động hóa chuyên nghiệp để quản lý log trên hệ thống Linux. Sự kết hợp giữa linh hoạt của Bash scripting, sức mạnh phân phối của Ansible và tính định kỳ của Cron đã tạo nên một giải pháp vừa mạnh mẽ vừa dễ bảo trì. Giải pháp này giúp giảm tải công việc thủ công cho Sysadmin, giảm nguy cơ tràn disk do log không được xoay vòng, và chuẩn bị dữ liệu sẵn sàng cho các công cụ phân tích.
Tương lai, bạn có thể mở rộng giải pháp này bằng cách thêm tính năng gửi alert qua email hoặc Slack khi backup thất bại, tích hợp với AWS S3 hoặc MinIO để lưu trữ backup cloud thay vì chỉ ở local disk, hoặc sử dụng Ansible Vault để mã hóa các cấu hình nhạy cảm. Việc tự động hóa không chỉ là chạy một script, mà là thiết lập một quy trình vận hành bền vững, giúp hệ thống tự chăm sóc chính nó. Hãy luôn ghi nhớ nguyên tắc: bất kỳ thao tác nào bạn phải làm thủ công nhiều hơn một lần trên nhiều máy chủ thì đều nên được tự động hóa.