Chuyển đến nội dung chính

Sao lưu tự động PostgreSQL trong Docker với cron job và gzip

Hướng dẫn xây dựng script tự động backup database PostgreSQL chạy trong Docker container: gzip, cron hàng ngày, tự động xóa bản sao lưu cũ và kiểm thử phục hồi.

Sao lưu tự động PostgreSQL Docker

Tầm quan trọng của chiến lược sao lưu dữ liệu (Backup Strategy)

Dữ liệu là tài sản quan trọng nhất của bất kì hệ thống phần mềm nào. Cho dù bạn có thiết lập hệ thống mạnh mẽ đến đâu, các sự cố như lỗi phần cứng ổ đĩa, thao tác xóa nhầm của con người, lỗi migration cơ sở dữ liệu hoặc tấn công mã độc ransomware vẫn luôn có thể xảy ra....

Một nguyên tắc vàng trong quản trị hệ thống là: Một bản sao lưu chưa từng được kiểm tra khôi phục thì không được coi là một bản sao lưu (An untested backup is not a backup).

Trong bài viết này, chúng ta sẽ cùng nhau xây dựng một giải pháp sao lưu tự động, nén dữ liệu, dọn dẹp các bản backup cũ và phục hồi toàn diện cho cơ sở dữ liệu PostgreSQL đang chạy dưới dạng Docker container..


1. Nguyên lí hoạt động của docker execpg_dump

Thay vì phải cài đặt các công cụ PostgreSQL client trên máy chủ chủ (Host OS), chúng ta có thể tận dụng chính tiện ích pg_dump đã có sẵn bên trong container PostgreSQL bằng lệnh docker exec.

Lệnh sao lưu cốt lõi có dạng:

bash
docker exec -t <CONTAINER_NAME> pg_dump -U <DB_USER> <DB_NAME> | gzip > /path/to/backup/db_backup.sql.gz
  • -t: Cấp quyền TTY ảo để nhận dữ liệu luồng.
  • pg_dump -U <user> <db>: Xuất cấu trúc và toàn bộ dữ liệu bảng dưới dạng mã SQL.
  • | gzip: Nén luồng dữ liệu ngay lập tức trên RAM trước khi ghi xuống đĩa, giúp tiết kiệm tới 80-90% dung lượng ổ cứng.

2. Viết Script Sao lưu tự động chuyên nghiệp (backup_postgres.sh)

Tạo một thư mục chuyên dụng chứa các script quản trị trên máy chủ:

bash
sudo mkdir -p /opt/scripts /var/backups/postgres
sudo chmod 700 /var/backups/postgres

Tạo file script /opt/scripts/backup_postgres.sh:

bash
sudo nano /opt/scripts/backup_postgres.sh

Dán nội dung script hoàn chỉnh dưới đây:

bash
#!/bin/bash
# ==============================================================================
# SCRIPT TỰ ĐỘNG SAO LƯU POSTGRESQL TRONG DOCKER
# Tác giả: TechBlog Admin
# ==============================================================================

set -eo pipefail

# Cấu hình thông tin
CONTAINER_NAME="production_postgres"
DB_USER="pgadmin"
DB_NAME="core_production"
BACKUP_DIR="/var/backups/postgres"
DATE_STAMP=$(date +"%Y%m%d_%H%M%S")
FILENAME="${BACKUP_DIR}/${DB_NAME}_${DATE_STAMP}.sql.gz"
LOG_FILE="/var/log/postgres_backup.log"
RETENTION_DAYS=7 # Lưu trữ tối đa trong 7 ngày

log_message() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}

log_message "=== BẮT ĐẦU TIẾN TRÌNH SAO LƯU CHO DATABASE: ${DB_NAME} ==="

# Kiểm tra xem container có đang chạy hay không
if ! docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
    log_message "LỖI: Container ${CONTAINER_NAME} không hoạt động hoặc không tồn tại!"
    exit 1
fi

# Thực hiện trích xuất dữ liệu và nén gzip
if docker exec -t "$CONTAINER_NAME" pg_dump -U "$DB_USER" "$DB_NAME" | gzip > "$FILENAME"; then
    FILE_SIZE=$(du -h "$FILENAME" | cut -f1)
    log_message "THÀNH CÔNG: Đã tạo bản sao lưu tại ${FILENAME} (Dung lượng: ${FILE_SIZE})"
else
    log_message "LỖI: Tiến trình pg_dump thất bại!"
    rm -f "$FILENAME"
    exit 1
fi

# Tự động dọn dẹp các file backup cũ quá số ngày quy định
log_message "Đang dọn dẹp các bản backup cũ hơn ${RETENTION_DAYS} ngày..."
DELETED_COUNT=$(find "$BACKUP_DIR" -type f -name "${DB_NAME}_*.sql.gz" -mtime +${RETENTION_DAYS} -delete -print | wc -l)
log_message "Đã xóa ${DELETED_COUNT} bản sao lưu cũ."

log_message "=== HOÀN TẤT SAO LƯU THÀNH CÔNG ==="
exit 0

Phân quyền thực thi nghiêm ngặt (chỉ cho phép user root được đọc và chạy script):

bash
sudo chmod 700 /opt/scripts/backup_postgres.sh

3. Chạy thử nghiệm Script thủ công

Hãy kiểm tra chạy thử script lần đầu để đảm bảo không có lỗi phân quyền hay sai tên container:

bash
sudo /opt/scripts/backup_postgres.sh

Kiểm tra file sao lưu đã được tạo trong thư mục backup:

bash
ls -lh /var/backups/postgres/

Kiểm tra nội dung log:

bash
cat /var/log/postgres_backup.log

4. Thiết lập Lịch chạy tự động với Cron Job

Để máy chủ tự động thực hiện sao lưu vào 02:00 sáng mỗi ngày (thời điểm lượng truy cập hệ thống thấp nhất):

Mở bảng lập lịch Cron của tài khoản root:

bash
sudo crontab -e

Thêm dòng sau vào cuối file crontab:

cron
# Chạy sao lưu PostgreSQL vào lúc 02:00 AM hàng ngày
0 2 * * * /opt/scripts/backup_postgres.sh > /dev/null 2>&1

Lưu và đóng file (Ctrl + O, Enter, Ctrl + X trong nano).

Kiểm tra danh sách cron job đang hoạt động:

bash
sudo crontab -l

5. Qui trình kiểm thử phục hồi dữ liệu (Database Restore)

Đừng đợi đến khi xảy ra thảm họa mới tìm cách phục hồi. Hãy thực hành quy trình khôi phục định kỳ theo các bước sau:

Bước 5.1: Giải nén và nạp dữ liệu vào container

Giả sử bạn cần khôi phục file /var/backups/postgres/core_production_20241020_020000.sql.gz:

bash
# Giải nén và nạp trực tiếp qua lệnh psql trong Docker
gunzip -c /var/backups/postgres/core_production_20241020_020000.sql.gz | docker exec -i production_postgres psql -U pgadmin -d core_production

Trong môi trường thử nghiệm khôi phục, bạn nên tạo một cơ sở dữ liệu tạm thời (ví dụ: core_restore_test) để kiểm tra tính toàn vẹn của dữ liệu trước khi đè lên cơ sở dữ liệu chính:

bash
# 1. Tạo database test
docker exec -it production_postgres psql -U pgadmin -c "CREATE DATABASE core_restore_test;"
 
# 2. Nạp dữ liệu vào database test
gunzip -c /var/backups/postgres/core_production_20241020_020000.sql.gz | docker exec -i production_postgres psql -U pgadmin -d core_restore_test
 
# 3. Xóa database test sau khi kiểm tra xong
docker exec -it production_postgres psql -U pgadmin -c "DROP DATABASE core_restore_test;"

6. Chiến lược mở rộng: Sao lưu Offsite lên Cloud Storage (S3 / R2)

Lưu trữ bản backup trên cùng một máy chủ VPS vẫn tiềm ẩn rủi ro nếu toàn bộ ổ cứng máy chủ bị hỏng vật lý hoặc nhà cung cấp VPS gặp sự cố trung tâm dữ liệu.

Bạn nên mở rộng script trên bằng cách cài đặt công cụ AWS CLI hoặc rclone để tự động đẩy file backup lên các dịch vụ lưu trữ đám mây giá rẻ như Cloudflare R2 hoặc AWS S3 Glacier:

bash
# Ví dụ đẩy file lên S3 / R2 bucket
aws s3 cp "$FILENAME" s3://my-company-backups/postgres/ --endpoint-url https://<ACCOUNT_ID>.r2.cloudflarestorage.com

Bảng so sánh các phương pháp Backup PostgreSQL

Phương phápƯu điểmNhược điểmPhù hợp nhất
pg_dump (Logical)File SQL dạng text, dễ đọc, khôi phục được giữa các phiên bản PostgreSQL khác nhauTốc độ chậm với database dung lượng cực lớn (>100GB)Hầu hết các website và ứng dụng vừa & nhỏ (<50GB)
pg_basebackup (Physical)Sao chép nguyên khối nhị phân, tốc độ cực nhanhYêu cầu cùng kiến trúc và phiên bản PostgreSQL chính xácHệ thống lớn hàng trăm GB, thiết lập Streaming Replication
Volume SnapshotSnapshot tức thời ở mức ổ đĩa VPSPhải lock IO để đảm bảo tính nhất quán (Crash-consistent)Sao lưu hạ tầng toàn diện

Tổng kết

Bằng cách kết hợp script bash tối ưu, nén gzip và Cron Job tự động, bạn đã hoàn thiện một lá chắn bảo vệ an toàn cho cơ sở dữ liệu của mình. Hãy nhớ kiểm tra log /var/log/postgres_backup.log định kỳ để đảm bảo các bản sao lưu luôn được tạo ra đều đặn mỗi đêm.

💬

Thảo luận & Bình luận

Đăng nhập bằng tài khoản GitHub để đặt câu hỏi hoặc đóng góp ý kiến cho bài viết.