Máy chủ Linux và backup Docker volumes

Backup Docker Volumes trên Linux với restic: chiến lược an toàn và restore drill

Backup Docker Volumes trên Linux: Thiết kế chiến lược an toàn, tự động hóa và kiểm thử restore

Nếu server đang chạy Docker cho database (PostgreSQL/MySQL), ứng dụng stateful (Nextcloud, Gitea, Ghost, WordPress), thì câu hỏi quan trọng nhất không phải là “service đang up không?” mà là: “Mất server bây giờ, bao lâu khôi phục lại được?”

Bài này hướng dẫn một mô hình backup thực chiến: snapshot dữ liệu Docker volume, mã hóa, đẩy offsite, và kiểm thử restore định kỳ.

Vì sao backup Docker volume thường bị làm sai?

Nhiều hệ thống chỉ backup source code hoặc file docker-compose.yml rồi nghĩ là đủ. Thực tế, dữ liệu sống ở:

  • Docker named volumes (/var/lib/docker/volumes/.../_data)
  • Bind mounts (vd: /srv/data/...)
  • Database write-ahead logs / transaction logs
  • Secrets/config runtime

Các lỗi phổ biến:

  1. Backup “nóng” không có quiesce cho database → file backup tồn tại nhưng restore lỗi/inconsistent.
  2. Lưu cùng server (same disk/same VM) → hỏng máy là mất cả backup.
  3. Không test restore → backup “đẹp” nhưng không dùng được khi sự cố thật.
  4. Không versioning/retention → bị ransomware/xóa nhầm lan sang backup.

Mục tiêu kiến trúc backup chuẩn cho Docker

Một baseline tốt cho SME/production nhỏ:

  • 3-2-1 rule: 3 bản sao, 2 loại storage, 1 bản offsite.
  • Encrypted backup trước khi rời máy chủ.
  • Incremental + dedup để tiết kiệm băng thông/chi phí.
  • Immutable/versioned storage (nếu provider hỗ trợ).
  • Restore drill hàng tuần/tháng với checklist rõ ràng.

Trong bài này, stack đề xuất:

  • restic: backup engine (mạnh, nhanh, dedup, mã hóa mặc định)
  • rclone (optional): đồng bộ nếu cần multi-target
  • cron/systemd timer: tự động hóa
  • Docker pre/post hooks: đảm bảo nhất quán khi backup DB

Prerequisites

  • Linux server (Ubuntu 22.04+ hoặc Debian 12+)
  • Docker + Docker Compose plugin
  • Quyền root hoặc sudo
  • 1 remote storage (S3-compatible/B2/Wasabi/MinIO)

Cài packages:


sudo apt update
sudo apt install -y restic jq curl

Nếu dùng S3-compatible, chuẩn bị:

  • AWS_ACCESS_KEY_ID
  • AWS_SECRET_ACCESS_KEY
  • endpoint URL (nếu không phải AWS S3)
  • bucket dành riêng backup

Bước 1: Inventory dữ liệu cần backup

Liệt kê volume:


docker volume ls

Inspect volume để biết mountpoint:


docker volume inspect <volume_name> | jq '.[0].Mountpoint'

Ngoài volumes, rà soát bind mounts trong compose:


services:
  app:
    volumes:
      - /srv/app/uploads:/app/uploads
      - postgres_data:/var/lib/postgresql/data

Tạo danh sách backup rõ ràng (ví dụ /etc/backup/include.txt):


/var/lib/docker/volumes/postgres_data/_data
/var/lib/docker/volumes/gitea_data/_data
/var/lib/docker/volumes/redis_data/_data
/var/lib/docker/volumes/caddy_data/_data
/srv/app/uploads

Bước 2: Chuẩn hóa backup database (quiesce/snapshot-safe)

PostgreSQL

Ưu tiên logical dump + volume backup kết hợp:


docker exec -t postgres \
  pg_dumpall -U postgres | gzip > /backup-staging/pg_dumpall.sql.gz
  • Logical dump giúp portable giữa version.
  • Volume backup giữ full state để recovery nhanh.

MySQL/MariaDB


docker exec -t mysql \
  sh -c 'mysqldump -uroot -p"$MYSQL_ROOT_PASSWORD" --all-databases --single-transaction' \
  | gzip > /backup-staging/mysql_all.sql.gz

Redis

Nếu Redis có persistence (appendonly yes hoặc RDB), cần đảm bảo file flush:


docker exec redis redis-cli SAVE

Bước 3: Khởi tạo restic repository (encrypted by default)

Ví dụ dùng S3-compatible:


export RESTIC_REPOSITORY="s3:https://s3.example.com/docker-backup-prod"
export RESTIC_PASSWORD="<strong-random-passphrase>"
export AWS_ACCESS_KEY_ID="<key>"
export AWS_SECRET_ACCESS_KEY="<secret>"

restic init

Khuyến nghị: đưa secrets vào /etc/backup/restic.env (chmod 600), không hard-code vào script.

Ví dụ file env:


RESTIC_REPOSITORY=s3:https://s3.example.com/docker-backup-prod
RESTIC_PASSWORD=replace_me
AWS_ACCESS_KEY_ID=replace_me
AWS_SECRET_ACCESS_KEY=replace_me

Bước 4: Script backup tự động

Tạo /usr/local/bin/docker-backup.sh:


#!/usr/bin/env bash
set -euo pipefail

source /etc/backup/restic.env

STAGING_DIR="/backup-staging"
INCLUDE_LIST="/etc/backup/include.txt"
HOST_TAG="$(hostname -s)"
DATE_TAG="$(date +%F)"

mkdir -p "$STAGING_DIR"

# 1) Pre-backup database dumps
# PostgreSQL
if docker ps --format '{{.Names}}' | grep -q '^postgres$'; then
  docker exec -t postgres pg_dumpall -U postgres | gzip > "$STAGING_DIR/pg_dumpall.sql.gz"
fi

# MySQL
if docker ps --format '{{.Names}}' | grep -q '^mysql$'; then
  docker exec -t mysql sh -c 'mysqldump -uroot -p"$MYSQL_ROOT_PASSWORD" --all-databases --single-transaction' \
    | gzip > "$STAGING_DIR/mysql_all.sql.gz"
fi

# Redis flush (optional)
if docker ps --format '{{.Names}}' | grep -q '^redis$'; then
  docker exec redis redis-cli SAVE || true
fi

# 2) Backup with restic
restic backup \
  --files-from "$INCLUDE_LIST" \
  "$STAGING_DIR" \
  --tag docker --tag "$HOST_TAG" --tag "$DATE_TAG"

# 3) Retention policy
restic forget --prune \
  --keep-daily 7 \
  --keep-weekly 4 \
  --keep-monthly 6

# 4) Verify latest snapshots metadata quickly
restic snapshots --json | jq '.[-1] | {id, time, hostname, tags}'

# 5) Cleanup staging
rm -f "$STAGING_DIR"/*.sql.gz

Set quyền:


sudo chmod 700 /usr/local/bin/docker-backup.sh
sudo chmod 600 /etc/backup/restic.env

Bước 5: Scheduling bằng cron

Crontab root:


# Backup mỗi ngày lúc 02:30
30 2 * * * /usr/local/bin/docker-backup.sh >> /var/log/docker-backup.log 2>&1

Nếu hạ tầng cần alert:

  • Gửi log qua Telegram/webhook khi exit code != 0
  • Hoặc dùng healthchecks.io để monitor job heartbeat

Bước 6: Restore runbook (quan trọng nhất)

Không có restore test thì chưa gọi là backup strategy.

Kịch bản restore nhanh

  1. Provision host mới (Docker + network + compose files)
  2. Pull snapshot gần nhất
  3. Restore volumes/data
  4. Khởi động containers
  5. Verify application và data integrity

Ví dụ restore ra thư mục tạm:


source /etc/backup/restic.env
mkdir -p /restore-test
restic restore latest --target /restore-test

Copy data về volume path tương ứng (khi container đang stop):


docker compose down
rsync -aHAX --delete \
  /restore-test/var/lib/docker/volumes/postgres_data/_data/ \
  /var/lib/docker/volumes/postgres_data/_data/
docker compose up -d

Validate sau restore

  • App health endpoint trả 200
  • DB record count khớp baseline
  • User login + thao tác critical flow thành công
  • Kiểm tra log lỗi 10-15 phút sau startup

Troubleshooting: lỗi thường gặp và cách xử lý

1) Fatal: unable to open config file: Stat: Access Denied

Nguyên nhân:

  • Sai credentials S3 hoặc policy bucket thiếu quyền.

Cách xử lý:

  • Verify IAM policy có s3:ListBucket, s3:GetObject, s3:PutObject, s3:DeleteObject.
  • Kiểm tra endpoint/region đúng.

2) repository is already locked

Nguyên nhân:

  • Job backup cũ chết giữa chừng, để lại lock.

Fix:


restic unlock

Sau đó check không còn tiến trình backup chạy song song.

3) Backup xong nhưng restore DB fail

Nguyên nhân:

  • Backup file-level khi DB đang write mạnh, không quiesce.

Fix:

  • Luôn tạo logical dump trước backup.
  • Với workload lớn, cân nhắc snapshot cấp block (LVM/ZFS/EBS snapshot).

4) Dung lượng backup tăng nhanh bất thường

Nguyên nhân:

  • Include nhầm log/temp/cache hoặc artifacts CI.

Fix:

  • Dùng exclude list:

restic backup --files-from /etc/backup/include.txt --exclude-file /etc/backup/exclude.txt

exclude.txt ví dụ:


*.log
*.tmp
node_modules
cache/

5) Job chạy ổn nhưng disk local đầy

Nguyên nhân:

  • Staging dump không được cleanup khi script fail giữa chừng.

Fix:

  • Dùng trap để cleanup:

trap 'rm -f /backup-staging/*.sql.gz' EXIT

Checklist production-ready

  • [ ] Có danh sách data inventory (volumes + bind mounts)
  • [ ] DB có pre-backup dump/quiesce
  • [ ] Backup lưu offsite và mã hóa
  • [ ] Retention policy rõ ràng
  • [ ] Có giám sát job backup thất bại
  • [ ] Test restore định kỳ (ít nhất monthly)
  • [ ] Có runbook khôi phục (RTO/RPO mục tiêu)

Gợi ý nâng cao

1) Immutable backup

Dùng object lock/versioning để chống ransomware xóa backup.

2) Multi-destination

Backup primary vào S3 A, replicate sang B2/Wasabi (giảm single-provider risk).

3) Cross-host restore drill

Không chỉ restore trên máy cũ; test trên host mới để kiểm tra dependency ẩn.

4) SLA theo service tier

  • Tier 1 (DB/payment): backup 4-6 lần/ngày + PITR
  • Tier 2 (CMS/internal tools): daily backup
  • Tier 3 (cache/temp): có thể rebuild

FAQ

Backup Docker volumes có cần stop container không?

Không phải lúc nào cũng cần. Nhưng với database, nên có cơ chế quiesce (logical dump, flush, snapshot-aware) để tránh inconsistency.

Chỉ backup docker-compose.yml có đủ không?

Không. File compose chỉ mô tả hạ tầng, không chứa dữ liệu runtime của volume.

Nên dùng restic hay chỉ rsync?

rsync hữu ích cho mirror đơn giản, nhưng restic mạnh hơn về mã hóa, dedup, retention, snapshot history và restore theo thời điểm.

Bao lâu nên test restore một lần?

Tối thiểu mỗi tháng 1 lần cho production; hệ thống critical nên test hàng tuần (ít nhất partial restore).

Kết luận

Một chiến lược backup Docker tốt không dừng ở “đã có file backup”. Chuẩn đúng là: backup nhất quán + offsite + mã hóa + retention + restore test. Nếu làm đủ 5 phần này, bạn giảm mạnh rủi ro downtime kéo dài khi gặp sự cố disk, xóa nhầm, hay tấn công ransomware.

Nếu bạn đang vận hành Docker cho production, hãy bắt đầu ngay từ hôm nay bằng 3 việc: inventory data, tự động hóa backup bằng restic, và lên lịch restore drill. Đó là cách biến backup từ “cảm giác an toàn” thành khả năng phục hồi thực tế.

Leave a Comment

Your email address will not be published. Required fields are marked *