Xây dựng hệ thống sao lưu tự động tích hợp Ansible và Bash trên Linux
Trong môi trường vận hành hạ tầng (DevOps và Sysadmin), việc bảo vệ dữ liệu là ưu tiên hàng đầu. Tuy nhiên, các giải pháp sao lưu truyền thống thường gặp phải những hạn chế như cấu hình thủ công trên từng máy, thiếu tính linh hoạt khi mở rộng quy mô, hoặc các script Bash quá phức tạp dẫn đến khó bảo trì. Bài viết này sẽ hướng dẫn bạn xây dựng một giải pháp hoàn chỉnh, kết hợp sức mạnh của Bash scripting để xử lý logic chi tiết, tính năng cron của Linux để lên lịch tự động, và khả năng quản lý cấu hình tập trung của Ansible để triển khai giải pháp này lên hàng trăm máy chủ chỉ với một lệnh duy nhất.
Phân tích lợi ích của việc kết hợp Ansible và Bash
Việc sử dụng Ansible không có nghĩa là từ bỏ Bash scripting. Ngược lại, đây là một sự kết hợp cộng hưởng. Ansible đóng vai trò như người quản lý, đảm bảo rằng môi trường trên tất cả các máy chủ đều giống hệt nhau, các biến số được đặt chuẩn, và các script đã được triển khai đúng vị trí. Trong khi đó, Bash đóng vai trò là động cơ thực thi, xử lý các logic phức tạp về nén dữ liệu, xoay vòng file, kiểm tra dung lượng ổ đĩa, và gọi các API lưu trữ đám mây mà các module có sẵn của Ansible đôi khi chưa hỗ trợ hết.
Khi bạn viết một script Bash để sao lưu, bạn cần đảm bảo script đó chạy an toàn trên mọi bản phân phối Linux khác nhau. Nếu viết script thủ công trên từng máy, bạn sẽ mất hàng giờ để kiểm tra lỗi. Với Ansible, bạn chỉ cần viết Playbook một lần để phân phối script đó, tạo các biến môi trường cần thiết, và thiết lập cron job. Sự kết hợp này mang lại tính nhất quán cao, giảm thiểu lỗi cấu hình và tăng tốc độ triển khai đáng kể.
Thiết kế script Bash chuyên nghiệp cho nhiệm vụ sao lưu
Trước khi sử dụng Ansible để phân phối, chúng ta cần xây dựng một script Bash vững chắc. Script này sẽ thực hiện các nhiệm vụ: tạo thư mục lưu trữ, nén thư mục cần sao lưu, đổi tên file theo ngày hiện tại, và loại bỏ các bản sao lưu cũ hơn 7 ngày để tiết kiệm dung lượng.
Script sẽ được đặt tại đường dẫn /usr/local/bin/backup-service.sh để dễ dàng quản lý. Chúng ta sẽ sử dụng các biến môi trường do Ansible cung cấp để linh hoạt thay đổi thư mục nguồn và đích, thay vì hardcode cứng nhắc. Dưới đây là mã nguồn của script với các biện pháp bảo vệ lỗi cơ bản như kiểm tra quyền root và xử lý lỗi khi thư mục nguồn không tồn tại.
#!/bin/bash
set -euo pipefail
# Lấy biến từ Ansible hoặc sử dụng giá trị mặc định
SOURCE_DIR=${BACKUP_SOURCE_DIR:-/var/www}
DEST_DIR=${BACKUP_DEST_DIR:-/backup}
RETENTION_DAYS=${BACKUP_RETENTION_DAYS:-7}
# Định dạng ngày hiện tại làm tên file
TIMESTAMP=$(date +%F_%H%M%S)
BACKUP_FILE="backup_${SOURCE_DIR//\//_}_${TIMESTAMP}.tar.gz"
# Kiểm tra quyền root
if [ "$EUID" -ne 0 ]; then
echo "Lỗi: Vui lòng chạy script này với quyền root."
exit 1
fi
# Kiểm tra thư mục nguồn
if [ ! -d "$SOURCE_DIR" ]; then
echo "Lỗi: Thư mục nguồn $SOURCE_DIR không tồn tại."
exit 1
fi
# Tạo thư mục đích nếu chưa có
mkdir -p "$DEST_DIR"
echo "Bắt đầu quá trình sao lưu vào thời điểm: $(date)"
echo "Nguồn: $SOURCE_DIR"
echo "Đích: $DEST_DIR"
# Thực hiện nén dữ liệu
tar -czf "$DEST_DIR/$BACKUP_FILE" -C "$(dirname $SOURCE_DIR)" "$(basename $SOURCE_DIR)"
if [ $? -eq 0 ]; then
echo "Thành công: Đã tạo file $BACKUP_FILE"
# Xoay vòng: Xóa các bản sao lưu cũ hơn ngày quy định
find "$DEST_DIR" -name "backup_*.tar.gz" -type f -mtime +$RETENTION_DAYS -delete
echo "Hoàn tất việc xóa các bản sao lưu cũ hơn $RETENTION_DAYS ngày."
else
echo "Lỗi: Không thể sao lưu dữ liệu."
exit 1
fi
Đoạn mã trên sử dụng cú pháp set -euo pipefail để đảm bảo script dừng ngay lập tức nếu gặp lỗi, giúp phát hiện sự cố sớm. Biến SOURCE_DIR được truyền vào từ môi trường, giúp script linh hoạt hơn. Phần xử lý xóa file cũ sử dụng lệnh find kết hợp với tham số -mtime và -delete, một cách hiệu quả để quản lý vòng đời dữ liệu.
Triển khai script và cấu hình tự động bằng Ansible
Khi đã có script Bash hoàn chỉnh, bước tiếp theo là sử dụng Ansible để phân phối nó đến các máy chủ mục tiêu (inventory). Chúng ta sẽ tạo một Playbook bao gồm các task: tạo thư mục đích, sao chép script, cấp quyền thực thi, và quan trọng nhất là cấu hình cron job để chạy script định kỳ.
Ansible có module template cho phép bạn sử dụng file Jinja2 template để đưa các biến vào script trước khi sao chép, hoặc module copy nếu script đã tĩnh. Trong trường hợp này, ta giả định script đã cứng các biến mặc định nhưng sẽ được override bởi biến môi trường khi cron chạy. Tuy nhiên, để chuyên nghiệp hơn, ta nên dùng template để inject biến trực tiếp vào script hoặc tạo file config riêng. Ở đây, để đơn giản và minh họa rõ ràng, chúng ta sẽ dùng module copy để đưa script vào, sau đó dùng module cron để lên lịch.
Playbook sẽ đảm bảo rằng trên mọi máy chủ, thư mục /backup được tạo với quyền hạn thích hợp, script backup-service.sh được đặt vào /usr/local/bin với quyền 755. Đặc biệt, cron job sẽ được thiết lập để chạy vào lúc 2 giờ sáng mỗi ngày, đảm bảo việc sao lưu diễn ra khi lưu lượng truy cập thấp nhất.
---
- name: Deploy Backup Automation System
hosts: webservers
become: yes
vars:
backup_script_src: files/backup-service.sh
backup_script_dest: /usr/local/bin/backup-service.sh
backup_source_path: /var/www
backup_dest_path: /backup
backup_retention: 7
tasks:
- name: Ensure backup directory exists
file:
path: "{{ backup_dest_path }}"
state: directory
owner: root
group: root
mode: '0755'
- name: Copy backup script to remote server
copy:
src: "{{ backup_script_src }}"
dest: "{{ backup_script_dest }}"
owner: root
group: root
mode: '0755'
- name: Set up cron job for automatic backup
cron:
name: "Daily backup at 2 AM"
minute: "0"
hour: "2"
job: "BACKUP_SOURCE_DIR={{ backup_source_path }} BACKUP_DEST_DIR={{ backup_dest_path }} BACKUP_RETENTION_DAYS={{ backup_retention }} {{ backup_script_dest }}"
Trong Playbook trên, biến job của module cron đóng vai trò then chốt. Nó không chỉ gọi script mà còn khởi tạo các biến môi trường BACKUP_SOURCE_DIR, BACKUP_DEST_DIR và BACKUP_RETENTION_DAYS ngay trước khi thực thi lệnh. Điều này giúp bạn có thể tùy chỉnh cấu hình cho từng nhóm máy chủ khác nhau ngay trong file inventory hoặc biến group_vars mà không cần sửa đổi file script gốc.
Quản trị và kiểm tra hệ thống
Sau khi triển khai Playbook, bạn cần xác nhận hệ thống đã hoạt động đúng ý muốn. Bạn có thể chạy Playbook với cờ --check để xem trước các thay đổi mà Ansible sẽ thực hiện mà không làm ảnh hưởng gì đến máy chủ thực tế. Nếu kết quả kiểm tra sạch sẽ, hãy chạy lại Playbook thông thường để áp dụng các thay đổi.
Để kiểm tra cron job đã được tạo chưa, bạn có thể sử dụng lệnh crontab -u root -l trên máy chủ mục tiêu. Bạn sẽ thấy dòng lệnh tương ứng với cấu hình đã đề cập trong Playbook. Một cách kiểm tra nhanh hơn và trực quan hơn là kích hoạt chạy thủ công lần đầu tiên bằng lệnh /usr/local/bin/backup-service.sh và kiểm tra log trong thư mục /backup để xem file nén đã được tạo và file cũ (nếu có) đã bị xóa chưa.
Ngoài ra, trong môi trường production, bạn nên mở rộng giải pháp này bằng cách thêm bước gửi thông báo qua email hoặc Slack khi quá trình sao lưu thất bại. Điều này có thể dễ dàng thực hiện bằng cách bổ sung thêm logic vào script Bash hoặc tạo một Playbook riêng biệt để cài đặt các công cụ cảnh báo. Sự kết hợp giữa sự linh hoạt của Bash và sức mạnh tự động hóa của Ansible là nền tảng vững chắc cho mọi hệ thống Linux quy mô lớn, giúp bạn yên tâm tập trung vào phát triển sản phẩm thay vì lo lắng về mất mát dữ liệu.