Cách chuyển server sang VPS mới không gián đoạn
Di chuyển server sang VPS mới theo runbook: kiểm kê, dựng lại thay vì clone, dump database, giảm DNS TTL sớm, xác minh rồi chuyển DNS an toàn.
Di chuyển server sang VPS mới bằng một đợt chuyển đổi đã diễn tập
Để di chuyển server sang VPS mới, hãy xem việc này là một đợt chuyển đổi đã diễn tập, không phải chỉ là sao chép. Dựng máy mới từ đầu, đồng bộ dữ liệu 2 lần, xác nhận máy mới hoạt động độc lập trên địa chỉ IP riêng trước khi động vào DNS, sau đó chuyển các bản ghi và để máy cũ tiếp tục chạy cho đến khi bạn chắc chắn mọi thứ ổn định. Sao chép dữ liệu là phần dễ. Thứ tự thực hiện mới quyết định việc di chuyển diễn ra suôn sẻ hay gây tốn kém.
Hướng dẫn này áp dụng cho một Linux server chạy web application, database và chứng chỉ TLS (transport layer security). Đây là mô hình phổ biến của hầu hết các hệ thống chạy trên một server. Có 2 host tham gia, vì vậy mỗi ví dụ đều ghi chú host thực thi lệnh. Các địa chỉ dùng trong tài liệu: 198.51.100.10 là server cũ, 203.0.113.20 là server mới.
Đọc toàn bộ runbook trước khi bắt đầu. Bước đầu tiên là giảm DNS TTL và phải thực hiện trước bước bạn thực sự quan tâm vài ngày.
Kiểm kê trước khi xây dựng bất cứ thứ gì
Bạn không thể dựng lại một server nếu chưa mô tả server đó. Hãy dành một giờ để ghi lại server cũ đang làm gì, vì thứ bị hỏng sau khi migration luôn là thứ không ai nhớ đến: một cron job, một ngoại lệ trong firewall hoặc một environment file nằm ngoài thư mục ứng dụng.
Chạy các lệnh sau trên server cũ và lưu output ở nơi bạn có thể đọc từ server mới.
# old server: packages you asked for, not the dependencies they pulled in
apt-mark showmanual > ~/inv-packages.txt
# old server: what runs now, and what starts at boot
systemctl list-units --type=service --state=running --no-pager > ~/inv-services.txt
systemctl list-unit-files --state=enabled --no-pager >> ~/inv-services.txt
systemctl list-timers --all --no-pager > ~/inv-timers.txt
# old server: what is listening, on which address and port
sudo ss -tulpn > ~/inv-ports.txt
# old server: human accounts, skipping the system ones
awk -F: '$3 >= 1000 && $3 < 65534 {print $1, $3, $6, $7}' /etc/passwdapt-mark showmanual là danh sách đáng lưu vì nó chỉ liệt kê các gói đã được cài làm dependency. Một dpkg --get-selections đầy đủ trên một box đã chạy năm năm có thể trả về hai nghìn dòng nhưng không cho biết mục đích sử dụng.
Công việc được lập lịch thường nằm ở hai nơi, vì vậy hãy kiểm tra cả hai. Job chỉ chạy mỗi tháng là thứ bạn sẽ phát hiện ra sáu tuần sau migration.
# old server: per-user crontabs, then the system drop-ins
for u in $(cut -d: -f1 /etc/passwd); do sudo crontab -lu "$u" 2>/dev/null | sed "s/^/$u: /"; done
sudo ls -la /etc/cron.d /etc/cron.daily /etc/cron.hourlyTiếp theo là những thứ không phải file thông thường: rule của firewall, certificate, database và lượng dữ liệu thực tế bạn sẽ di chuyển.
# old server
sudo ufw status verbose # or: sudo nft list ruleset
sudo certbot certificates
sudo -u postgres psql -c '\l' # or: sudo mysql -e 'SHOW DATABASES;'
sudo du -xh --max-depth=1 / | sort -hcertbot certificates in ra tên từng certificate, các domain mà certificate bao phủ, ngày hết hạn và đường dẫn của các file trên disk. Output đó là checklist TLS của bạn. du -x chỉ hoạt động trong cùng một filesystem, nên sẽ không đi vào một backup volume đã mount và báo dung lượng lớn gấp mười lần thực tế.
Có hai hạng mục nằm bên ngoài server và lần nào cũng bị quên. Thứ nhất là mọi bên thứ ba đang allowlist địa chỉ IP của server: payment gateway, managed database, SMTP relay hoặc partner API. Box mới có địa chỉ mới, vì vậy hãy thêm IP mới vào các allowlist đó trước khi cutover, không phải sau đó. Thứ hai là các DNS record bạn không tự tạo, chẳng hạn MX record hoặc SPF record chứa IP cũ trong phần text.
Tại sao bạn dựng lại thay vì clone root filesystem cũ
Clone toàn bộ root filesystem sang VPS mới có vẻ nhanh hơn, và đúng là nhanh hơn, cho đến khi không còn như vậy. Root filesystem đã chạy production nhiều năm thường chứa các cấu hình được chỉnh thủ công mà không ai ghi chép, các package lấy từ repository không còn tồn tại, và cấu hình boot được xây dựng cho phần cứng ảo của nền tảng cũ. Bạn sẽ import tất cả, bao gồm cả nguyên nhân khiến bạn phải migrate.
Dựng lại chậm hơn trong ngày đầu, nhưng tiết kiệm hơn mỗi ngày sau đó. Bạn cài release hiện tại, áp dụng các thiết lập hardening cơ bản, rồi chỉ copy dữ liệu: thư mục ứng dụng, cấu hình site, database dump, certificate và file upload của người dùng. Bất cứ thứ gì bạn không giải thích được thì không chuyển sang. Hãy khởi tạo máy mới như cách bạn khởi tạo mọi máy khác, bắt đầu với 10 phút đầu tiên trên một VPS mới, sau đó thêm từng service từ inventory và xác nhận từng service trước khi thêm service tiếp theo.
Khi nên khôi phục từ image hoặc snapshot
Có một ngoại lệ thực tế khi rebuild. Nếu server cũ không thể boot, hoặc ứng dụng không còn có thể rebuild từ source, thì khôi phục image hoặc snapshot của provider là lựa chọn phù hợp. Cách này có giới hạn rõ ràng: nó chỉ hoạt động trong cùng một provider, thường chỉ trong cùng một dòng plan, vì disk được khôi phục cần các thiết bị ảo và cách đặt tên network của nền tảng đó.
Snapshot của một máy đang chạy cũng gặp vấn đề nhất quán giống như mọi bản sao ở cấp file khác của database đang hoạt động. Hãy xem việc khôi phục image là một phương án recovery thay vì một kế hoạch migration, và đọc vì sao snapshot không giống backup trước khi xây dựng kế hoạch dựa trên snapshot.
Cách các file được truyền: rsync qua SSH
Chạy rsync từ server cũ để đẩy dữ liệu sang server mới. Cách đẩy thường đơn giản hơn vì server cũ đã có dữ liệu và có thể đọc toàn bộ dữ liệu đó dưới quyền sudo.
# old server: dry run first, and read what it says it will do
sudo rsync -aHAX --dry-run --itemize-changes \
-e 'ssh -i /root/.ssh/id_ed25519' \
/srv/app/ deploy@203.0.113.20:/srv/app/
# old server: the real bulk pass
sudo rsync -aHAX --info=progress2 \
-e 'ssh -i /root/.ssh/id_ed25519' \
/srv/app/ deploy@203.0.113.20:/srv/app/Các flag này rất quan trọng. -a giữ nguyên permission, timestamp, symbolic link và ownership. -H giữ hard link dưới dạng hard link thay vì mở rộng chúng thành các bản sao riêng. -A sao chép POSIX ACL (access control list) và -X sao chép extended attribute. Nếu thiếu hai flag cuối, một file trông giống hệt vẫn có thể hoạt động khác, vì SELinux label và ACL được lưu trong extended attribute và không có thông tin nào khác ghi lại chúng.
Có hai chi tiết gây ra phần lớn lỗi ở bước này.
Dấu gạch chéo cuối quyết định nơi dữ liệu được đặt. /srv/app/ có nghĩa là nội dung của thư mục đó. /srv/app có nghĩa là bản thân thư mục đó. Nếu đặt sai, bạn sẽ có /srv/app/app trên server mới. Ứng dụng khởi động rồi báo thiếu file vì các path mà ứng dụng được cấu hình hiện nông hơn một cấp.
Trong sudo, dấu ngã là thư mục home của root. Viết -e 'ssh -i ~/.ssh/id_ed25519' trong sudo rsync sẽ tìm key tại /root/.ssh, không phải trong thư mục home của bạn. Nếu key không nằm ở đó, SSH in ra Permission denied (publickey), rsync in ra rsync: connection unexpectedly closed rồi thoát với mã khác 0. Hãy ghi đầy đủ path của key. Nếu thông báo xác thực đó vẫn xuất hiện sau khi bạn sửa path, lỗi publickey có một danh sách nguyên nhân ngắn và permission của thư mục trên server mới là mục tiếp theo cần kiểm tra.
Ownership cần một quyết định. Khi chạy dưới quyền root, theo mặc định rsync ánh xạ owner và group theo tên, vì vậy file thuộc về www-data trên server cũ sẽ thuộc về www-data trên server mới dù numeric UID (user ID) khác nhau. Đây là điều bạn cần khi dựng lại server. Chỉ thêm --numeric-ids khi sao chép một filesystem có các account không tồn tại trên target. Sau đó kiểm tra kết quả bằng ls -ln, vì file thuộc về UID không có account tương ứng sẽ hiển thị dưới dạng số đơn và mọi service đọc file đó đều bị từ chối.
Hãy chạy lần sao chép chính trước vài ngày, trong khi server cũ vẫn đang phục vụ traffic. Bạn có thể lặp lại bao nhiêu lần tùy ý: rsync chỉ gửi phần đã thay đổi, nên lần thứ hai mất vài phút thay vì vài giờ. Lần sao chép cuối trong thời gian cutover thêm --delete để các file đã bị xóa trên server cũ cũng bị xóa trên server mới.
# old server: final pass, inside the window, after the app has stopped writing
sudo rsync -aHAX --delete --info=progress2 \
-e 'ssh -i /root/.ssh/id_ed25519' \
/srv/app/ deploy@203.0.113.20:/srv/app/--delete xóa trên đích các file không còn tồn tại ở nguồn, vì vậy path nguồn sai kết hợp với --delete có thể làm trống thư mục đích. Lần nào cũng phải chạy với --dry-run trước. Các lần truyền dài cũng bị dừng khi SSH session trên laptop bị ngắt, nên hãy chạy chúng bên trong tmux hoặc screen trên server cũ. Thêm --bwlimit=20M nếu quá trình sao chép chiếm hết băng thông của link trong khi server cũ vẫn đang phục vụ người dùng.
Cách database di chuyển: dump native
Database không phải là một thư mục chứa các file, dù nhìn bề ngoài có vẻ như vậy. Database gồm các file, state trong bộ nhớ và write-ahead log. Nó chỉ nhất quán tại những thời điểm do chính database xác định. Hãy dùng công cụ riêng của database.
PostgreSQL cần 2 dump, vì role có phạm vi toàn cluster và pg_dump không bao gồm chúng:
# old server
sudo -u postgres pg_dumpall --globals-only -f /var/backups/globals.sql
sudo -u postgres pg_dump -Fc -f /var/backups/appdb.dump appdb# new server: restore in this order
sudo -u postgres psql -f /var/backups/globals.sql
sudo -u postgres createdb -O appuser appdb
sudo -u postgres pg_restore -d appdb --no-owner /var/backups/appdb.dumpBỏ qua globals.sql sẽ khiến mọi table được restore nhưng không có application role nào đọc được chúng, vì các câu lệnh GRANT tham chiếu đến một user không tồn tại. -Fc ghi theo custom archive format. Chỉ pg_restore mới đọc được format này, đồng thời format này cho phép bạn restore từng table được chọn về sau. Hãy restore vào cùng major version hoặc version mới hơn. Không hỗ trợ đi ngược version, chẳng hạn từ 17 xuống 16. pg_restore sẽ từ chối archive với lỗi unsupported-version trong file header trước khi ghi bất kỳ dữ liệu nào.
MySQL và MariaDB dùng một command với 4 option không phải giá trị mặc định:
# old server
sudo mysqldump --single-transaction --routines --triggers --events \
--databases appdb > /var/backups/appdb.sql# new server
sudo mysql < /var/backups/appdb.sql--single-transaction tạo snapshot nhất quán mà không chặn writer, nhưng chỉ áp dụng cho table InnoDB. Table MyISAM trong cùng database được copy mà không có bảo đảm này. Vì vậy, hãy kiểm tra storage engine trước khi tin cậy dump. --routines, --triggers và --events mặc định bị tắt. Điều đó có nghĩa là plain dump sẽ restore dữ liệu nhưng âm thầm bỏ lại stored procedure và scheduled event. User database và grant của họ nằm trong system database mysql. --databases appdb không bao giờ dump database này, nên hãy tạo lại chúng trên server mới bằng CREATE USER và GRANT. MariaDB 11 phát hành cùng công cụ dưới tên mariadb-dump và vẫn giữ mysqldump dưới dạng symbolic link, nên từ August 2026 cả 2 tên đều dùng được.
SQLite là một file duy nhất. Nếu copy file trong lúc application đang ghi, bạn sẽ có một file bị ghi dở. SQLite có cách an toàn riêng:
# old server
sqlite3 /var/lib/app/app.db ".backup '/var/backups/app.db'"Dù dùng engine nào, hãy kiểm tra dump trước khi tin cậy nó. Dump bị dừng sớm vì disk đầy vẫn có thể restore mà không báo lỗi, cho đến đúng vị trí dữ liệu bị truncate.
# old server: a complete mysqldump ends with a line reading "-- Dump completed on ..."
tail -n 3 /var/backups/appdb.sql
# new server: after restore, count rows in a table whose size you know
sudo mysql -e 'SELECT COUNT(*) FROM appdb.orders;'Vì sao không thể dùng rsync với database đang chạy
rsync sao chép từng file. Database đang chạy ghi đồng thời vào nhiều file, nên khi rsync đến file cuối cùng thì file đầu tiên đã không còn đồng bộ. Bản sao chứa các page ở những thời điểm khác nhau, tạo thành một trạng thái mà database chưa từng có. Kết quả có thể là server từ chối khởi động. Trường hợp tệ hơn là server vẫn khởi động và trả về kết quả đúng trong một tuần, rồi chỉ fail khi một query cuối cùng truy cập đến page đã hỏng. Trong khoảng thời gian đó không có cảnh báo nào.
Có 2 cách an toàn để di chuyển chính các file này. Dừng database, sao chép, rồi khởi động lại: đúng và đơn giản, nhưng downtime kéo dài bằng thời gian sao chép. Hoặc dùng công cụ được thiết kế để sao chép vật lý một server đang chạy. Với PostgreSQL, đó là pg_basebackup. Công cụ này phối hợp với server để bảo đảm bản sao nhất quán:
# new server: pull a physical copy from the old one
pg_basebackup -h 198.51.100.10 -U replicator -D /var/lib/postgresql/16/main -X stream -PCách này cần một role có attribute REPLICATION và một entry pg_hba.conf tương ứng trên server cũ, nên cần nhiều bước setup hơn dump. Cách này phù hợp khi database đủ lớn và dump rồi restore không thể hoàn tất trong maintenance window. Với migration thông thường giữa các server đơn lẻ, dump là lựa chọn tốt hơn.
Tạo lại certificate trước khi chuyển đổi, không phải sau đó
TLS certificate gắn với domain name, không gắn với địa chỉ IP, nên bản thân file certificate có thể chuyển sang máy khác mà không gặp vấn đề. Phần không chuyển đổi suôn sẻ là việc gia hạn. HTTP-01 challenge mặc định của Certbot yêu cầu certificate authority tải một file qua port 80 tại domain name đang được cấp certificate. Khi DNS chưa trỏ đến server mới, yêu cầu tải file đó sẽ đến server cũ và việc gia hạn trên server mới sẽ fail.
Cách thứ nhất là sao chép các certificate hiện có cùng với trạng thái gia hạn của chúng. Chúng vẫn hợp lệ đến ngày hết hạn, bất kể đang được lưu trên server nào.
# old server
sudo rsync -aHAX -e 'ssh -i /root/.ssh/id_ed25519' \
/etc/letsencrypt/ root@203.0.113.20:/etc/letsencrypt/Mỗi file bên dưới /etc/letsencrypt/renewal/ chỉ rõ authenticator plugin đã cấp certificate. Vì vậy, hãy cài cùng plugin đó trên server mới (ví dụ python3-certbot-nginx), nếu không lần gia hạn đầu tiên sẽ fail với thông báo về authenticator không xác định. Hãy kiểm tra việc gia hạn hoạt động trước khi phụ thuộc vào nó:
# new server, after DNS has moved
sudo certbot renew --dry-runCách thứ hai là cấp certificate mới trên server mới bằng DNS-01 challenge. Cách này xác minh quyền kiểm soát thông qua TXT record và không bao giờ dùng port 80. Nó hoạt động trước khi migration, khi domain name vẫn resolve đến server cũ. Vì vậy, đây là lựa chọn gọn hơn nếu bạn có thể tự động hóa DNS provider. Cấp certificate bằng DNS-01 challenge trình bày cách cài plugin và cấu hình credential.
Dù chọn cách nào, hãy kiểm tra certificate mà server mới thực sự cung cấp mà không thay đổi DNS:
# your laptop
echo | openssl s_client -connect 203.0.113.20:443 -servername example.com 2>/dev/null \
| openssl x509 -noout -subject -dates -issuer-servername gửi SNI (server name indication), giúp web server chọn đúng virtual host. Nếu bỏ qua tham số này, bạn sẽ nhận certificate mặc định của IP đó và gặp lỗi mismatch. Lỗi này trông giống một sự cố thật, nhưng không phải.
Giảm TTL của DNS vài ngày trước khi chuyển đổi
DNS là nơi ngay cả một lần chuyển đổi được chuẩn bị kỹ vẫn có thể gặp lỗi, vì độ trễ đã được tích hợp sẵn và bạn không thể rút ngắn nó vào đúng ngày chuyển đổi. Resolver đã cache bản ghi A của bạn sẽ tiếp tục trả về bản ghi đó trong toàn bộ thời gian TTL (time to live) được cung cấp. Việc giảm TTL ngay bây giờ không có tác dụng với resolver đã cache bản ghi cách đây mười phút theo giá trị cũ: nó giữ giá trị cũ trong phần thời gian còn lại của TTL cũ, rồi mới nhận giá trị mới ngắn hơn. Vì vậy, hãy giảm TTL trước thời điểm chuyển đổi ít nhất một chu kỳ TTL cũ đầy đủ. Thực hiện trước một ngày là phương án an toàn và thoải mái hơn. Nếu các thành phần liên quan còn mới với bạn, phần hướng dẫn về bản ghi, resolver và cơ chế cache sẽ cung cấp thông tin nền cần thiết.
Các con số dưới đây được tính từ chính TTL, không phải số liệu đo thực tế.
The data behind this chart
[
{
"label": "TTL 3600 s (common default)",
"ttl_seconds": 3600,
"worst_case_stale_minutes": 60
},
{
"label": "TTL 900 s",
"ttl_seconds": 900,
"worst_case_stale_minutes": 15
},
{
"label": "TTL 300 s (lowered for cutover)",
"ttl_seconds": 300,
"worst_case_stale_minutes": 5
},
{
"label": "TTL 60 s (short window)",
"ttl_seconds": 60,
"worst_case_stale_minutes": 1
}
]Bản ghi được công bố với TTL là 3600 giây có thể tiếp tục đưa người dùng đến IP cũ trong 60 phút sau khi bạn thay đổi bản ghi. Giảm TTL xuống 300 giây thì trường hợp xấu nhất giảm còn 5 phút. Hãy xem các con số này là mức tối thiểu, không phải cam kết. Một số resolver tự áp dụng TTL tối thiểu và bỏ qua các giá trị ngắn hơn. Một số runtime của ứng dụng cache địa chỉ đã phân giải trong suốt vòng đời của process. Vì vậy, client đã khởi chạy trước khi bạn thay đổi có thể không tra cứu lại cho đến khi được restart.
Khi kiểm tra TTL mới đã có hiệu lực, hãy đọc câu trả lời từ authoritative server thay vì cache của chính bạn:
# your laptop: ask the zone's own nameserver, so no cache is involved
dig +short NS example.com
dig +noall +answer @$(dig +short NS example.com | head -n1) example.com ATrường thứ hai trong dòng trả lời đó là TTL tính bằng giây. Sau đó, hãy kiểm tra các bản ghi thường bị bỏ quên: bản ghi AAAA nếu server cũ có IPv6, tên www khi đây là một bản ghi A riêng thay vì CNAME, mọi bản ghi MX trỏ trực tiếp đến server, bản ghi SPF liệt kê IP cũ và bản ghi reverse DNS (PTR) trên địa chỉ mới. Nếu server gửi mail, hãy đặt PTR thông qua control panel của nhà cung cấp trước khi chuyển đổi, vì mail server nhận sẽ kiểm tra bản ghi này. PTR bị thiếu có thể khiến mail bị từ chối vài giờ sau khi mọi thứ khác vẫn có vẻ hoạt động bình thường.
Xác minh server mới bằng IP trước khi thay đổi DNS
Bạn có thể kiểm tra toàn bộ ứng dụng trên server mới trong khi DNS vẫn trỏ đến server cũ. Ghi đè việc phân giải tên cho một request:
# your laptop: force one name to the new IP, for this request only
curl -sS --resolve example.com:443:203.0.113.20 \
-o /dev/null -w '%{http_code} %{ssl_verify_result}\n' https://example.com/--resolve chỉ thay đổi nơi kết nối được chuyển đến. Chứng chỉ TLS vẫn được kiểm tra theo tên thật, nên cách này xác minh cả chứng chỉ lẫn service. %{ssl_verify_result} in 0 khi chain được xác minh.
Để thao tác qua site bằng browser, hãy ghi đè tên trên toàn bộ máy bằng cách thêm một dòng vào /etc/hosts trên laptop, hoặc vào C:\Windows\System32\drivers\etc\hosts trên Windows:
203.0.113.20 example.com www.example.comSau đó kiểm tra ứng dụng theo đúng cách người dùng sẽ sử dụng. Đăng nhập. Tải một trang đọc dữ liệu từ database. Gửi một form ghi dữ liệu vào database. Upload một file và xác nhận file được ghi xuống disk. Kích hoạt chức năng gửi email và kiểm tra email có đến nơi không, vì outbound SMTP từ một IP mới thường gây ra vấn đề bất ngờ. Xóa dòng trong hosts ngay khi kiểm tra xong. Nếu để lại dòng này, bạn có thể mất cả giờ debug một site mà mọi người khác vẫn truy cập bình thường.
Chuyển đổi, từng bước
- Trước vài ngày: giảm TTL, chạy rsync toàn bộ, dựng máy chủ mới và kiểm thử máy chủ đó thông qua override trong hosts.
- Vào ngày chuyển đổi, trước khi bắt đầu: thêm IP mới vào allowlist của mọi dịch vụ bên thứ ba, đồng thời xác nhận job backup trên máy chủ mới đã được cấu hình và trỏ đến repository của bạn.
- Bắt đầu thời gian chuyển đổi: đưa ứng dụng vào maintenance mode trên máy chủ cũ để máy chủ này ngừng nhận các thao tác ghi.
- Tạo database dump cuối cùng, sau đó chạy lượt rsync cuối cùng với
--delete. - Restore dump trên máy chủ mới và khởi động các service.
- Kiểm thử lại thông qua
--resolvevà override trong hosts, bao gồm một thao tác ghi thực tế. - Đổi các record A và AAAA sang IP mới.
- Theo dõi cả hai máy chủ. Access log trên máy chủ cũ cho biết còn ai đang truy cập máy chủ đó, và con số này sẽ giảm dần về 0 trong thời gian TTL.
- Tắt maintenance page.
- Để máy chủ cũ tiếp tục chạy và không thay đổi trong ít nhất một tuần.
Bước bật maintenance mode là bước mọi người thường bỏ qua, nhưng đây chính là bước bảo vệ bạn. Khi database mới đã nhận một thao tác ghi, rollback sẽ đồng nghĩa với việc mất thao tác ghi đó, hoặc dump database mới rồi nạp lại vào database cũ. Một khoảng thời gian chỉ đọc kéo dài vài phút là cái giá thấp. Hai database đều đã nhận thao tác ghi sẽ khiến bạn phải đối soát thủ công trong nhiều ngày.
Kế hoạch rollback
Rollback chỉ gồm một thao tác: đổi các bản ghi DNS về 198.51.100.10. Cách này chỉ hiệu quả vì bạn đã thực hiện 4 việc trước đó.
- Server cũ vẫn đang chạy, các service vẫn hoạt động và dữ liệu vẫn nguyên vẹn. Bạn đã dừng việc ghi dữ liệu trên đó, chứ chưa decommission server.
- TTL vẫn đang thấp, nên đường quay lại nhanh tương đương với đường chuyển sang server mới.
- Bạn đã thêm IP mới vào các allowlist của bên thứ ba thay vì thay thế IP cũ. Nếu xóa địa chỉ cũ, rollback sẽ thất bại tại payment gateway.
- Server mới chưa nhận bất kỳ dữ liệu ghi nào mà bạn không thể xác định, vì cho đến lúc này, các thao tác ghi duy nhất là các giao dịch kiểm thử của bạn.
Trước khi bắt đầu maintenance window, hãy xác định điều kiện nào sẽ kích hoạt rollback. Chỉ cần 2 điều kiện: bất kỳ lỗi nào bạn không thể chẩn đoán trong số phút cố định, và bất kỳ mất dữ liệu nào. Việc ghi rõ các điều kiện này từ trước sẽ ngăn bạn mất cả giờ để phỏng đoán, khiến một sự cố kéo dài 10 phút trở thành sự cố dài hơn.
Chứng minh quá trình migration đã thành công
Migration chưa hoàn tất chỉ vì site đã tải được. Hãy kiểm tra những thành phần chỉ lỗi sau đó.
# new server
systemctl --failed
journalctl -p err -b --no-pager | tail -n 40
sudo certbot certificates
sudo systemctl list-timers --all --no-pagersystemctl --failed báo cáo 0 loaded units listed là kết quả bạn cần. certbot certificates phải hiển thị các ngày hết hạn như mong đợi, còn list-timers phải hiển thị mọi job đã lên lịch trong inventory, kèm thời gian chạy tiếp theo hợp lệ, không được để trống.
Sau đó, hãy chủ động reboot server mới một lần trong khi bạn trực tiếp theo dõi. Một service được ai đó khởi động thủ công nhưng chưa bao giờ được enable sẽ hoạt động bình thường cho đến lần reboot ngoài kế hoạch đầu tiên vào 3 giờ sáng.
# new server
sudo reboot
# your laptop, once it comes back
curl -sS -o /dev/null -w '%{http_code}\n' https://example.com/Nếu ứng dụng chạy trong container, lỗi tương tự sẽ có dạng khác, vì một compose stack cần restart policy rõ ràng để tự khởi động lại sau reboot.
Bước kiểm tra cuối cùng là bước dễ bị trì hoãn nhất nhưng quan trọng nhất: backup job. Một migration kết thúc với server chưa được backup chỉ chuyển rủi ro này sang rủi ro khác. Hãy chạy backup thủ công trên server mới, sau đó restore một file vào một thư mục tạm. Một restic repository có bản restore đã được bạn thực sự kiểm thử mới là cách xử lý hữu ích khi cần. Nếu bạn chạy server cũ và mới song song trong một tuần, một cách nhất quán để truy cập và cấu hình từng host sẽ ngăn chúng lệch cấu hình trong thời gian cả hai cùng hoạt động.
Sau khi chuyển đổi: máy chủ cũ và các hạng mục cuối cùng
Giữ máy chủ cũ trong một đến hai tuần. Bạn sẽ tốn chi phí của một tháng gói dịch vụ mà vốn định hủy, nhưng đây là phương án rollback duy nhất bạn có. Sau đó xử lý các hạng mục còn lại.
- Việc dùng lại cùng hostname trong
~/.ssh/configcho máy mới khiến bạn gặpWARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!ở lần kết nối đầu tiên, vì hostname đó hiện trả về một host key khác. Xóa entry cũ bằngssh-keygen -R example.comsau khi chắc chắn vì sao nó thay đổi, không xóa theo phản xạ, vì cùng cảnh báo này cũng xuất hiện trong một cuộc tấn công interception. Migration cũng là thời điểm phù hợp để rà soát key nào có thể truy cập tài nguyên nào. Đó là mục đích của quản lý SSH key trên một fleet nhỏ. - Tạo một snapshot hoặc bản backup cuối cùng của máy chủ cũ, rồi lưu ở nơi không thuộc provider cũ.
- Xóa IP cũ khỏi các lượt kiểm tra monitoring, các bản ghi SPF và các allowlist của bên thứ ba, theo đúng thứ tự này và thực hiện sau cùng.
- Chỉ hủy gói dịch vụ cũ sau khi xác nhận bản copy cuối cùng có thể đọc được ở một nơi khác.
FAQ
Mất bao lâu để chuyển một server sang VPS mới?
Thời gian gián đoạn mà người dùng nhận thấy thường chỉ gồm lần dump database cuối, lần chạy rsync cuối và thời gian khởi động service, nên một ứng dụng nhỏ thường mất từ ten đến thirty phút. Thời gian theo lịch dài hơn, vì phải giảm DNS TTL ít nhất một khoảng bằng TTL cũ trước khi chuyển đổi; giảm trước một ngày sẽ an toàn hơn. Hãy lên kế hoạch copy phần lớn dữ liệu từ vài ngày trước. Quá trình này chạy trên server đang hoạt động, và nếu lặp lại sau đó thì chỉ truyền phần dữ liệu đã thay đổi kể từ lần chạy trước.
Tôi có thể rsync database MySQL hoặc PostgreSQL đang chạy thay vì dump không?
Không. rsync copy từng file trong khi database đồng thời ghi vào nhiều file, nên bản copy chứa các page ở những thời điểm khác nhau và biểu diễn một trạng thái mà database chưa từng có. Database có thể từ chối khởi động, hoặc khởi động được rồi mới fail khi query truy cập đến một page bị hỏng. Hãy dùng pg_dump với pg_dumpall --globals-only, hoặc mysqldump --single-transaction, hoặc dừng database trước rồi copy các file. Với một PostgreSQL cluster lớn, pg_basebackup tạo bản copy vật lý nhất quán từ server đang chạy.
Làm cách nào để kiểm tra VPS mới trước khi đổi DNS?
Ghi đè việc phân giải tên trên máy của bạn. Với một request đơn lẻ, curl --resolve example.com:443:203.0.113.20 https://example.com/ chuyển connection đến IP mới nhưng vẫn kiểm tra certificate theo tên thật. Để kiểm tra bằng browser, thêm 203.0.113.20 example.com vào /etc/hosts trên laptop, thử đăng nhập, đọc database, ghi dữ liệu qua form và upload file, rồi xóa dòng đó. Để chỉ kiểm tra certificate, chạy openssl s_client -connect 203.0.113.20:443 -servername example.com.
Tôi nên đặt TTL bao nhiêu và khi nào nên giảm TTL?
Giảm các record A và AAAA xuống 300 giây, và thực hiện việc này ít nhất một khoảng bằng toàn bộ TTL cũ trước khi chuyển đổi. Resolver đã cache record trước khi bạn thay đổi sẽ giữ giá trị cũ trong phần thời gian còn lại của TTL cũ, nên giảm TTL trước một giờ sẽ không có tác dụng nếu TTL cũ là 86400. Khôi phục TTL về giá trị bình thường vài ngày sau khi migration hoàn tất, khi access log của server cũ đã không còn request đáng kể.
Tôi có nên copy TLS certificate hay cấp certificate mới trên server mới?
Cả hai cách đều được. Copy /etc/letsencrypt/ giúp certificate tiếp tục hợp lệ đến ngày hết hạn hiện tại, nhưng bạn phải cài cùng certbot authenticator plugin trên server mới; nếu không, lần renewal đầu tiên sẽ fail. Vì vậy, hãy chạy certbot renew --dry-run sau khi đổi DNS để xác nhận. Cấp certificate mới sẽ gọn hơn nếu bạn có thể dùng DNS-01 challenge, vì cơ chế này xác minh quyền kiểm soát thông qua TXT record và hoạt động trước khi DNS trỏ đến server mới. Không thể dùng HTTP-01 challenge trên server mới trước khi DNS được chuyển, vì validation request sẽ đến server cũ.