SSD Nodes Learn 🎉 VPS từ $5.50/tháng
Hướng dẫn Matt ConnorBởi Matt Connor

VPS snapshot, backup và clone khác nhau thế nào?

Snapshot nằm trên hạ tầng provider nên không phải backup. Xem mỗi loại restore được gì và cần sửa identity nào trên VPS clone trước khi chạy.

Snapshot, backup và clone thực chất là gì

Snapshot của một VPS là image ổ đĩa của máy chủ, được provider lưu trên hạ tầng của chính provider đó, trong account của bạn. Backup là bản sao độc lập của dữ liệu mà bạn có thể restore ở nơi khác, không cần provider đang lưu bản gốc hỗ trợ. Clone là một instance mới được triển khai từ snapshot. Vì vậy, nó bắt đầu hoạt động như một bản sao chính xác của máy gốc, bao gồm cả identity.

Chúng giải quyết các vấn đề khác nhau. Snapshot cho phép rollback một lần nâng cấp bị lỗi trong vài phút, nhưng không có tác dụng nếu account bị đóng. Backup vẫn tồn tại ngay cả khi provider ngừng hoạt động, nhưng restore lâu hơn vì trước hết bạn phải dựng lại máy. Clone tạo một server thứ hai đang chạy chỉ với một bước, nhưng đồng thời tạo ra hai máy tin rằng chúng là cùng một máy.

Vì sao snapshot của VPS không phải là backup

Vấn đề nằm ở failure domain, không phải chất lượng của image. Snapshot nằm trên nền tảng lưu trữ của provider, thường ở cùng region với server nguồn và luôn trong cùng account. Một sự cố có thể làm server và snapshot của nó mất cùng lúc.

  • Account bị suspend, thanh toán thất bại hoặc có người đánh cắp thông tin đăng nhập.
  • Người dùng hoặc script có quyền truy cập API xóa instance. Với nhiều provider, xóa instance sẽ xóa luôn các snapshot của instance đó. Hãy đọc tài liệu của provider về hành vi này trước khi giả định điều ngược lại.
  • Region gặp sự cố và mọi tài nguyên trong đó cùng lúc không thể truy cập.
  • Một tiến trình chạy dưới quyền root trên server tìm thấy API token của provider mà bạn để trong /root, rồi xóa các snapshot trước khi tác động đến disk.

Backup là bản sao vẫn còn sau cả 4 tình huống trên. Cách kiểm tra chỉ có một câu hỏi: nếu account của provider không còn tồn tại vào chiều nay, bạn vẫn có thể restore được gì và sẽ restore ở đâu? Bất kỳ thứ gì không vượt qua được câu hỏi đó chỉ là công cụ rollback. Vẫn nên tiếp tục tạo snapshot vì không có cách nào restore nhanh hơn. Sau đó, hãy giữ thêm một bản sao trên hệ thống lưu trữ nằm ngoài quyền kiểm soát của provider.

Quy tắc cũ vẫn đúng: có 3 bản sao dữ liệu, trên 2 loại storage, trong đó 1 bản nằm ngoài platform. Một snapshot của provider cộng với repository backup của restic trên hạ tầng riêng đáp ứng quy tắc này với 2 thành phần cần quản lý.

Vì sao snapshot của database đang chạy có thể khôi phục thành trạng thái hỏng

Snapshot của provider sao chép block device đúng tại một thời điểm. Nó không yêu cầu ứng dụng dừng trước, và không thể thấy dữ liệu vẫn còn trong page cache. Vì vậy, nhiều nhất image chỉ nhất quán theo trạng thái crash. Nó giống hệt trạng thái của disk nếu ai đó rút cáp nguồn.

Phần lớn stack có thể tự xử lý việc này. ext4 và XFS replay journal khi mount, nên filesystem vẫn hoạt động. PostgreSQL replay write-ahead log khi khởi động, và log sẽ ghi rõ:

LOG:  database system was not properly shut down; automatic recovery in progress

InnoDB cũng làm tương tự và in các dòng crash recovery riêng trong lúc khởi động. Đây là database recovery theo đúng thiết kế. Vì vậy, snapshot một volume của PostgreSQL hoặc MySQL đang ít hoạt động thường vẫn khôi phục được.

Những trường hợp trạng thái nhất quán theo crash là không đủ vẫn có thật, và đó là các trường hợp gây hậu quả nghiêm trọng. Nếu dữ liệu nằm trên hai volume, root disk và data disk riêng được snapshot tại các thời điểm khác nhau. Khi đó, data file và log directory có thể không khớp, nên recovery không có dữ liệu đúng để replay. Bất kỳ file nào được ứng dụng ghi mà không gọi fsync, chẳng hạn upload mới nhận một phần hoặc queue file, đều có thể được khôi phục trong trạng thái bị cắt ngắn. Dữ liệu ứng dụng giữ trong memory rồi flush theo timer thì hoàn toàn không có trong image.

Vì vậy, hãy ghi dump ra disk trước khi tạo snapshot. Khi đó image chứa một file mà bạn biết là nhất quán nội bộ, bất kể các data file đang ở trạng thái nào.

sudo -u postgres pg_dumpall --clean --file=/var/backups/pg-$(date +%F).sql
sudo mysqldump --single-transaction --routines --all-databases > /var/backups/mysql-$(date +%F).sql

--single-transaction tạo dump nhất quán cho các bảng InnoDB mà không chặn writer, vì dump chạy bên trong một transaction có mức cô lập repeatable-read. Nó không áp dụng cho các bảng MyISAM; các bảng này cần lock hoặc server phải dừng. Hãy kiểm tra dump không rỗng và không bị cắt ngắn trước khi tin dùng: tail -n 1 /var/backups/mysql-$(date +%F).sql trên một mysqldump hoàn chỉnh sẽ kết thúc bằng comment Dump completed.

Nếu có data volume riêng, bạn có thể freeze volume đó trong vài giây cần thiết để tạo snapshot:

sudo fsfreeze -f /srv
# take the snapshot from your provider's panel or API
sudo fsfreeze -u /srv

Chỉ freeze data volume. Tuyệt đối không freeze /. Filesystem root bị freeze sẽ chặn mọi thao tác ghi trên máy, kể cả shell bạn dùng để nhập lệnh unfreeze. Khi đó bạn tự khóa mình khỏi máy và phải chờ hard reset.

Phần lưu trữ ngoài site: restic hoặc Borg

Snapshot là phần nhanh. Bản sao ngoài site là phần vẫn còn tồn tại nếu provider của bạn gặp sự cố. restic là lựa chọn mặc định tốt vì nó khử trùng lặp, mã hóa ở phía client và ghi dữ liệu vào object storage tương thích S3, SFTP hoặc một thư mục thông thường. Storage VPS làm đích lưu trữ ngoài site phù hợp trong trường hợp này vì repository backup cần dung lượng hơn là IOPS.

sudo apt update && sudo apt install -y restic
sudo sh -c 'umask 077; head -c 24 /dev/urandom | base64 > /root/.restic-pass'
sudo chmod 600 /root/.restic-pass
sudo cat /root/.restic-pass

Hãy sao chép passphrase đó vào password manager ngay bây giờ, trên một thiết bị không phải server này. Không thể mở restic repository nếu thiếu passphrase và không có cách khôi phục. Nếu bản duy nhất của mật khẩu nằm trên chính máy bạn vừa mất, backup chỉ còn là dữ liệu đã mã hóa không thể sử dụng.

export RESTIC_REPOSITORY="s3:https://s3.example.com/vps-backups"
export RESTIC_PASSWORD_FILE=/root/.restic-pass
export AWS_ACCESS_KEY_ID="..."
export AWS_SECRET_ACCESS_KEY="..."
sudo -E restic init
sudo -E restic backup /etc /srv /var/backups
sudo -E restic snapshots

sudo -E giữ lại các biến đó. Nếu không có nó, root sẽ nhận một environment sạch và restic sẽ báo chưa chỉ định vị trí repository. restic snapshots phải liệt kê lần chạy bạn vừa thực hiện, cùng với hostname và các path. Hãy xác minh chính repository theo lịch định kỳ, đồng thời đọc thử một phần dữ liệu thay vì chỉ kiểm tra cấu trúc:

sudo -E restic check --read-data-subset=5%
sudo -E restic restore latest --target /tmp/restore-check
sudo -E restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune

Backup chưa được kiểm thử chỉ là phỏng đoán. Hãy restore lên một VPS khác ít nhất một lần, đo thời gian và ghi lại kết quả, vì con số đó là mục tiêu khôi phục thực tế của bạn. Borg là lựa chọn đáng tin cậy khác và lưu repository qua SSH thay vì object storage; các đánh đổi được trình bày trong bài so sánh restic và BorgBackup.

Việc cần xử lý trước khi đưa VPS đã clone vào production

Clone là một bản sao chính xác. Đó vừa là ưu điểm vừa là vấn đề. Mọi thành phần làm cho máy gốc có tính duy nhất đều bị sao chép, và các bản sao sẽ xung đột.

Tạo lại SSH host key. Bản clone chứa các file /etc/ssh/ssh_host_* của máy gốc, nên hai server có cùng host identity. Bất kỳ ai kiểm soát một server đều có thể giả mạo server còn lại với mọi client đã chấp nhận key đó. SSH không cảnh báo vì key này đúng là key mà client đã mong đợi.

sudo rm -f /etc/ssh/ssh_host_*
sudo ssh-keygen -A
sudo systemctl restart ssh
ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub

ssh-keygen -A ghi một key mới cho mọi loại key mà daemon yêu cầu. Fingerprint từ lệnh cuối phải khác fingerprint trên máy gốc. Session hiện tại vẫn hoạt động sau khi restart, vì restart sshd không đóng các connection đã thiết lập. Hãy làm việc này trước khi có người kết nối đến clone. Nếu để đến sau, mọi client đã tin cậy key được kế thừa sẽ nhận WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! và trước tiên phải chạy ssh-keygen -R <host>.

Đặt lại machine ID. /etc/machine-id là một identifier duy nhất do systemd tạo một lần trong lần boot đầu tiên, và clone kế thừa identifier này.

sudo truncate -s 0 /etc/machine-id
sudo rm -f /var/lib/dbus/machine-id
sudo ln -s /etc/machine-id /var/lib/dbus/machine-id
sudo reboot

Một /etc/machine-id rỗng sẽ khiến systemd tạo giá trị mới trong lần boot tiếp theo. Vì vậy, bạn truncate file thay vì xóa file. Khi giá trị này bị trùng, hai vấn đề sẽ xảy ra. Với các image nhận địa chỉ qua DHCP, systemd-networkd mặc định tạo DHCP client identifier từ machine ID. Vì vậy, cả hai clone yêu cầu lease với cùng một client, và server cấp cùng một địa chỉ cho cả hai. Ngoài ra, journald gắn machine ID vào mọi entry, nên central log collector ghi nhận cả hai server dưới cùng một machine. Chạy cat /etc/machine-id sau khi reboot và xác nhận giá trị đã thay đổi.

Đổi hostname.

sudo hostnamectl set-hostname web-02
grep 127.0.1.1 /etc/hosts

hostnamectl ghi /etc/hostname và áp dụng tên mới ngay lập tức. Lệnh này không sửa /etc/hosts, nên hãy chỉnh dòng 127.0.1.1 cho khớp. Nếu bỏ qua bước này, tên mới không phân giải được. Vì vậy, mọi lệnh gọi sudo phải chờ một lần lookup thất bại và in sudo: unable to resolve host web-02: Name or service not known.

Đổi mọi credential được tích hợp sẵn trong image. Clone chứa secret của máy gốc, và giờ có hai máy có thể hoạt động như máy gốc. Hãy kiểm tra các file SSH authorized_keys, token API của provider và DNS, các file .env của ứng dụng, mật khẩu database, TLS private key, monitoring enrolment token và mật khẩu repository của restic. Lệnh sau tìm được phần lớn các credential này:

sudo grep -rIlE 'PASSWORD|SECRET|TOKEN|API_KEY' /etc /srv /opt /home 2>/dev/null
sudo find / -name '.env' -not -path '/proc/*' -not -path '/sys/*' 2>/dev/null

Nếu clone chỉ là bản sao để test và sẽ không bao giờ phục vụ traffic, hãy revoke thay vì rotate. Một staging box chứa live production API token thực chất là một production box có quy trình patch kém an toàn hơn.

Tắt các job hiện đang chạy hai lần. Hai server chạy cùng crontab sẽ truy cập cùng các hệ thống bên ngoài vào cùng một phút.

systemctl list-timers --all
sudo crontab -l
sudo ls -l /etc/cron.d /etc/cron.daily

Trường hợp restic cần được giải thích rõ, vì nó làm hỏng retention chứ không chỉ fail với lỗi rõ ràng. restic gắn hostname vào mỗi snapshot, còn restic forget --keep-daily 7 áp dụng policy theo từng host. Hai máy báo cáo cùng hostname sẽ được xem là một host. Vì vậy, 7 snapshot "daily" có thể đều đến từ clone, trong khi snapshot của máy gốc bị prune. Hãy sửa hostname trước lần backup đầu tiên hoặc dừng timer trên clone. Trường hợp certbot đơn giản hơn: hai server cùng renew các name giống nhau sẽ chạm giới hạn duplicate certificate rate limit của certificate authority. Lần chạy không thành công sẽ fail với lỗi cho biết đã phát hành quá nhiều certificate cho đúng tập hợp name đó. Clone vẫn trỏ domain về máy gốc cũng không thể vượt qua HTTP challenge, nên hãy tắt việc renewal trên clone.

Xử lý monitoring agent. Phần lớn agent nhận diện bằng hostname hoặc bằng ID file được ghi lúc cài đặt. Vì vậy, hai agent báo cáo dưới cùng một host sẽ trộn metric vào một series duy nhất. Biểu đồ CPU khi đó hiển thị các giá trị không do một máy nào tạo ra, và alert sẽ flap. Hãy stop và remove agent trên clone, hoặc enrol lại agent dưới hostname mới theo quy trình được vendor tài liệu hóa.

Kiểm tra cấu hình network để tìm địa chỉ của máy gốc. Nếu image chứa địa chỉ static trong netplan, clone sẽ chiếm một IP thuộc về máy khác.

ip -br addr
sudo grep -r addresses /etc/netplan/

Xóa trạng thái cloud-init nếu clone này sẽ trở thành template.

sudo cloud-init clean --logs

Thao tác này xóa state của cloud-init trong /var/lib/cloud. Vì vậy, lần boot tiếp theo sẽ chạy lại các module first-boot, bao gồm việc tạo SSH host key khi chưa có key. Một số version cũng có flag để reset machine ID. Hãy chạy cloud-init clean --help trên image của bạn để xem version đó hỗ trợ gì, thay vì tin vào danh sách flag từ nguồn khác.

Dùng cách nào trong trường hợp nào

Hoàn tác một lần nâng cấp có rủi ro: tạo snapshot. Tạo snapshot vài phút trước khi thay đổi, chạy nâng cấp rồi khôi phục image nếu có lỗi. Khôi phục sẽ xóa mọi dữ liệu ghi từ thời điểm tạo snapshot, vì vậy nếu server đang nhận network traffic thực tế, hãy dump database trước và xác định chính xác khoảng thời gian dữ liệu bạn có thể mất. Với một do-release-upgrade trên máy có thể đưa offline trong mười phút, snapshot là toàn bộ kế hoạch.

Chuyển sang plan lớn hơn: triển khai clone. Tạo clone từ snapshot trên plan lớn hơn, thực hiện lần lượt danh sách kiểm tra identity ở trên, rồi test clone bằng IP riêng trước khi chuyển network traffic. Giảm DNS TTL trước một ngày để cutover diễn ra nhanh, đồng thời giữ server cũ chạy cho đến khi máy mới đã xử lý network traffic thực tế. Trước tiên, hãy xác nhận plan lớn hơn thực sự nhanh hơn với workload của bạn bằng cách dùng cùng một phương pháp benchmark trên cả hai server, vì nhiều vCPU hơn trên phần cứng đang bận hơn không phải lúc nào cũng là nâng cấp.

Tạo template: tạo snapshot từ một máy đã làm sạch. Cài đặt và harden một server, sau đó xóa mọi thành phần riêng của máy trước khi tạo image. Không để lại host key, đặt machine ID về rỗng, không có authorized_keys cá nhân, không có credential và đã clean cloud-init. Tạo snapshot từ máy đó. Mỗi instance triển khai từ snapshot này sẽ tự tạo identity riêng trong lần boot đầu tiên, nên danh sách kiểm tra ở trên không còn là việc phải làm thủ công. Kết hợp template với 10 phút đầu tiên tiêu chuẩn trên một VPS mới để template đã chứa sẵn những việc bạn otherwise phải lặp lại.

FAQ

Snapshot của VPS có phải là backup không?

Không, vì nó dùng chung failure domain với server nguồn. Snapshot nằm trên storage của provider, trong account của bạn, thường ở cùng region. Việc account bị suspend, API key bị đánh cắp hoặc instance bị xóa nhầm có thể xóa server và các snapshot của nó trong cùng một thao tác. Với nhiều provider, xóa instance cũng mặc định xóa các snapshot của instance đó. Snapshot là cách rollback nhanh nhất, vì vậy hãy tiếp tục tạo snapshot và giữ thêm một bản sao được mã hóa trên hạ tầng mà provider không kiểm soát.

Có cần dừng database trước khi tạo snapshot không?

Không phải lúc nào cũng cần, nhưng bạn phải chấp nhận trạng thái dữ liệu nhận được. Snapshot của provider có tính nhất quán như sau sự cố (crash-consistent), tức image tương ứng với trạng thái của disk sau khi bị mất điện. PostgreSQL và InnoDB có thể khôi phục trạng thái này khi khởi động, và PostgreSQL ghi log database system was not properly shut down; automatic recovery in progress trong quá trình đó. Việc khôi phục không được đảm bảo nếu dữ liệu của bạn nằm trên hai volume được snapshot ở các thời điểm khác nhau, hoặc khi ứng dụng ghi dữ liệu mà không dùng fsync. Hãy ghi một pg_dumpall hoặc mysqldump --single-transaction xuống disk trước, để image chứa một file mà bạn biết là nhất quán.

Vì sao hai server clone lại tranh chấp cùng một địa chỉ IP?

Vì chúng dùng chung /etc/machine-id. Trên các image dùng DHCP, systemd-networkd mặc định tạo DHCP client identifier từ machine ID. Do đó, cả hai clone gửi yêu cầu lease với cùng một client và DHCP server cấp cùng một địa chỉ cho cả hai. Truncate /etc/machine-id về 0 byte, xóa /var/lib/dbus/machine-id, tạo symlink trỏ lại đến /etc/machine-id rồi reboot để systemd tạo giá trị mới. Nguyên nhân phổ biến khác là địa chỉ tĩnh được ghi trong /etc/netplan/ và clone đã sao chép nguyên giá trị đó; kiểm tra bằng ip -br addr.

Cách nhanh nhất để kiểm tra clone có an toàn để đưa vào production là gì?

So sánh 4 yếu tố với server gốc. Chạy ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub trên cả hai server và xác nhận fingerprint khác nhau. Chạy cat /etc/machine-id trên cả hai server và xác nhận các giá trị khác nhau. Chạy hostnamectl status và xác nhận hostname là giá trị mới, đồng thời hostname phân giải được để sudo không cảnh báo. Sau đó chạy systemctl list-timers --all và dừng mọi timer kết nối đến hệ thống dùng chung, chẳng hạn backup, gia hạn chứng chỉ hoặc monitoring agent, cho đến khi bạn quyết định máy nào sẽ thực hiện tác vụ đó.