Backup và restore thủ công cho RabbitMQ
Backup trên nền tảng đã bị gỡ khỏi RabbitMQ từ 15/09/2026, nên việc bảo vệ cấu hình của cụm giờ do bạn tự vận hành. Trang này export definitions của cụm qua Management HTTP API, kiểm tra file có đầy đủ không, rồi import sang một cụm mới.
Cách này chỉ backup definitions — vhost, user, permission, exchange, queue, binding và policy. Message đang nằm trong queue không được gồm vào và không thể khôi phục bằng cách này.
Áp dụng cho RabbitMQ 3.13.x trên Erlang 26.x.
Definitions gồm những gì
| Thành phần | Nội dung |
|---|---|
| Vhost | Các virtual host |
| User và permission | Tài khoản và quyền truy cập |
| Exchange | Direct, topic, fanout và headers |
| Queue | Khai báo queue cùng các argument |
| Binding | Liên kết giữa exchange và queue |
| Policy | TTL, HA, max-length, dead-letter và các loại còn lại |
Cơ chế hoạt động
Chạy mọi thứ từ một backup server — một VM của bạn, thông tới cụm RabbitMQ ở port 15672. Nó gọi thẳng Management HTTP API, nên không phải cài gì lên cụm và không node nào bị dừng.
| Giai đoạn | Diễn ra điều gì |
|---|---|
| Backup | Backup server gọi GET /api/definitions trên cụm nguồn và lưu file JSON |
| Restore | Backup server POST file đã lưu tới POST /api/definitions trên cụm đích |
Trong lúc restore, cụm nguồn hoàn toàn không bị gọi tới.
Điều kiện tiên quyết
| Hạng mục | Yêu cầu |
|---|---|
| Mạng | Backup server thông tới cụm ở port 15672 |
| Tài khoản | User có tag administrator |
| Phiên bản đích | Khi restore, phiên bản RabbitMQ của cụm đích phải bằng hoặc cao hơn cụm nguồn |
| Phiên bản Erlang | Nên khớp với cụm nguồn |
Các lệnh bên dưới dùng những placeholder sau: <HOST> là địa chỉ cụm nguồn và <TARGET_HOST> là cụm đích, <USERNAME> và <PASSWORD> là thông tin đăng nhập administrator, <VHOST_NAME> là tên một vhost, <TIMESTAMP> là dấu thời gian trong tên file đã lưu, còn <RABBITMQ_HOST> là địa chỉ mà script chạy theo lịch đọc vào.
Backup definitions
Export toàn bộ cụm
curl -s \
-u '<USERNAME>:<PASSWORD>' \
http://<HOST>:15672/api/definitions \
-o /backup/rabbitmq/rabbitmq_definitions_$(date +%Y%m%d_%H%M%S).json
Ví dụ:
curl -s \
-u 'admin:your_password' \
http://10.0.1.10:15672/api/definitions \
-o /backup/rabbitmq/rabbitmq_definitions_$(date +%Y%m%d_%H%M%S).json
Export riêng một vhost
curl -s \
-u '<USERNAME>:<PASSWORD>' \
'http://<HOST>:15672/api/definitions/<VHOST_NAME>' \
-o /backup/rabbitmq/rabbitmq_vhost_<VHOST_NAME>_$(date +%Y%m%d_%H%M%S).json
Ví dụ — backup vhost app_production:
curl -s \
-u 'admin:your_password' \
'http://10.0.1.10:15672/api/definitions/app_production' \
-o /backup/rabbitmq/rabbitmq_vhost_production_$(date +%Y%m%d_%H%M%S).json
Export theo vhost bỏ sót user và global parameter. Hãy dùng bản export toàn cụm khi file này để phục vụ migrate đầy đủ.
Kiểm tra file trước khi tin nó
Một bản export trả về trang lỗi thì vẫn là một file nằm trên đĩa, nên hãy đếm xem nó chứa gì:
python3 << 'EOF'
import json
with open("/backup/rabbitmq/rabbitmq_definitions_<TIMESTAMP>.json") as f:
d = json.load(f)
print(f"Vhosts: {len(d.get('vhosts', []))}")
print(f"Users: {len(d.get('users', []))}")
print(f"Exchanges: {len(d.get('exchanges', []))}")
print(f"Queues: {len(d.get('queues', []))}")
print(f"Bindings: {len(d.get('bindings', []))}")
print(f"Policies: {len(d.get('policies', []))}")
EOF
Các con số phải khớp với cụm nguồn:
Vhosts: 4
Users: 5
Exchanges: 5
Queues: 8
Bindings: 8
Policies: 3
Restore sang cụm mới
Import file
curl -s -X POST \
-u '<USERNAME>:<PASSWORD>' \
-H "Content-Type: application/json" \
-d @/backup/rabbitmq/rabbitmq_definitions_<TIMESTAMP>.json \
http://<TARGET_HOST>:15672/api/definitions
Ví dụ:
curl -s -X POST \
-u 'admin:your_password' \
-H "Content-Type: application/json" \
-d @/backup/rabbitmq/rabbitmq_definitions_20260519_020001.json \
http://10.0.2.10:15672/api/definitions
Đọc mã phản hồi
Import thành công thì không in ra gì, nên hãy hỏi thẳng mã trạng thái:
curl -s -o /dev/null -w "%{http_code}\n" -X POST \
-u 'admin:your_password' \
-H "Content-Type: application/json" \
-d @/backup/rabbitmq/rabbitmq_definitions_20260519_020001.json \
http://10.0.2.10:15672/api/definitions
| Mã | Ý nghĩa |
|---|---|
| 200 | Import thành công |
| 400 | File JSON không hợp lệ |
| 401 | Sai username hoặc password |
| 403 | User không có tag administrator |
Đối chiếu hai cụm
So số lượng ở cả hai bên thay vì mặc định rằng import đã đầy đủ:
python3 << 'EOF'
import json, urllib.request, base64
def get_definitions(host, user, password):
url = f"http://{host}:15672/api/definitions"
req = urllib.request.Request(url)
token = base64.b64encode(f"{user}:{password}".encode()).decode()
req.add_header("Authorization", f"Basic {token}")
with urllib.request.urlopen(req) as r:
return json.load(r)
SOURCE = get_definitions("10.0.1.10", "admin", "your_password")
DEST = get_definitions("10.0.2.10", "admin", "your_password")
keys = ["vhosts", "users", "exchanges", "queues", "bindings", "policies"]
print(f"{'Thành phần':<15} {'Nguồn':>8} {'Đích':>8} {'Khớp':>8}")
print("-" * 45)
for k in keys:
src = len(SOURCE.get(k, []))
dst = len(DEST.get(k, []))
match = "OK" if src == dst else "LỆCH"
print(f"{k:<15} {src:>8} {dst:>8} {match:>8}")
EOF
Kết quả mong đợi:
Thành phần Nguồn Đích Khớp
---------------------------------------------
vhosts 4 4 OK
users 5 5 OK
exchanges 5 5 OK
queues 8 8 OK
bindings 8 8 OK
policies 3 3 OK
Những điều cần lường trước khi import
Password được giữ nguyên. File definitions lưu hash của password chứ không phải plaintext, và các hash đó import y nguyên. User giữ đúng mật khẩu cũ trên cụm đích.
HA policy cần đủ node mới có tác dụng. Policy ha-mode: all import được vào mọi cụm, nhưng chỉ có hiệu lực khi cụm có tối thiểu ba node. Hãy cho cụm đích topology tương đương cụm nguồn.
Import nhiều lần vẫn an toàn. Thao tác này idempotent: resource đã tồn tại sẽ được cập nhật hoặc giữ nguyên, không bao giờ bị xoá. Chạy lại một lần import thất bại không gây hại gì.
Tên cluster không bị ghi đè. cluster_name trong file chỉ mang tính tham khảo. Cụm đích giữ nguyên tên của chính nó.
Tự động hoá
Script backup
Tạo file /opt/backup/rabbitmq_backup.sh:
#!/bin/bash
# ---- Configuration ----
HOST="<RABBITMQ_HOST>"
USER="admin"
PASS='your_password'
BACKUP_DIR="/backup/rabbitmq"
RETENTION_DAYS=30
# ------------------
mkdir -p "$BACKUP_DIR"
FILENAME="rabbitmq_definitions_$(date +%Y%m%d_%H%M%S).json"
FILEPATH="${BACKUP_DIR}/${FILENAME}"
curl -s \
-u "${USER}:${PASS}" \
"http://${HOST}:15672/api/definitions" \
-o "$FILEPATH"
if [ $? -eq 0 ] && [ -s "$FILEPATH" ]; then
echo "[OK] Backup thành công: ${FILENAME}"
find "$BACKUP_DIR" -name "rabbitmq_definitions_*.json" \
-mtime +${RETENTION_DAYS} -delete
else
echo "[FAIL] Backup thất bại!"
rm -f "$FILEPATH"
exit 1
fi
Cấp quyền thực thi:
chmod +x /opt/backup/rabbitmq_backup.sh
Đặt PASS trong dấu nháy đơn. Password chứa ký tự ! sẽ kích hoạt history expansion của bash nếu để trong nháy kép, và script gửi đi sai thông tin đăng nhập.
Đặt lịch và theo dõi
crontab -e
Thêm dòng chạy hằng ngày lúc 02:00:
0 2 * * * /opt/backup/rabbitmq_backup.sh >> /var/log/rabbitmq_backup.log 2>&1
Rồi xem log sau lần chạy theo lịch đầu tiên:
tail -f /var/log/rabbitmq_backup.log
Kết quả mỗi lần chạy thành công:
[OK] Backup thành công: rabbitmq_definitions_20260519_020001.json
Bước tiếp theo
- Tổng quan Backup & Restore để biết engine nào còn backup trên nền tảng
- Tạo database khi bạn cần dựng cụm đích để import vào
- Backup và restore thủ công cho Kafka nếu bạn còn chạy Kafka, nơi message cũng có thể giữ lại được