换机最怕两件事:数据没搬全和出问题回不去。所以顺序永远是「先备份、再迁移、留退路」,而不是先动 DNS。
下面以 Ubuntu、旧机 old.example.com、新机 new.example.com 为例。
一、先盘点旧机在跑什么
systemctl list-units --type=service --state=running
ss -tulpn
crontab -l
ls /etc/cron.d /etc/cron.daily
把这几类东西列成清单:
- 运行中的服务(nginx、php-fpm、数据库、应用进程);
- 监听端口与对应程序;
- 定时任务(crontab、systemd timer);
- 站点目录(如
/var/www)、应用目录(如/opt、/srv); - 配置文件(
/etc/nginx、/etc/letsencrypt、环境变量文件); - 数据库名、用户与授权。
清单不全,迁移完一定会漏东西。
二、先做一份可验证的备份
数据库
mariadb-dump --all-databases --single-transaction --routines --events \
| gzip > /root/all-databases-$(date +%F).sql.gz
三个参数的意义(来自 MariaDB 官方文档):
--single-transaction:导出前发START TRANSACTION,对 InnoDB 表取一致快照,不阻塞应用;官方也提醒,MyISAM、MEMORY 这类非事务表不保证一致。--all-databases:导出全部数据库。--routines、--events:把存储过程、函数和事件调度器一并带上,不加就会丢。
文件
tar czf /root/site-files-$(date +%F).tar.gz /var/www /etc/nginx /etc/letsencrypt /opt
验证备份(这步不能省)
tar tzf能看到文件清单;- 把
.sql.gz导入新机上的临时库,确认表数量与关键表行数对得上; - 记下备份文件的校验值:
sha256sum。
没有验证过的备份,只能算「有一份文件」,不能算「有备份」。
三、新机准备
- 装同版本或更新的系统,装同名软件包(nginx、php、数据库版本尽量对齐);
- 建好相同的系统用户,或迁移时用
--numeric-ids保留数字 UID/GID; - 确认新机能 SSH 登录,防火墙放行 22/80/443。
四、第一轮 rsync(业务仍在旧机跑)
先在旧机上执行空跑,看清单:
rsync -aHAXz --numeric-ids --dry-run \
--exclude={/proc,/sys,/dev,/run,/tmp} \
/var/www /etc/nginx /etc/letsencrypt /opt \
[email protected]:/
确认没有意外的大量删除或权限变更后,去掉 --dry-run 实际执行。
参数含义(rsync 官方手册):
-a:归档模式,递归并保留权限、时间戳、属主等;-H:保留硬链接;-A、-X:保留 ACL 与扩展属性;-z:传输时压缩,跨地域省带宽;--numeric-ids:按数字 UID/GID 传输,避免两台机器用户名不一致导致权限错乱。
如果站点很大,第一轮耗时长是正常的;这一轮的作用是把绝大部分文件先搬过去。
五、切换窗口:停机、最后一轮同步
选低峰时段,按这个顺序:
- 旧机停写入(停应用或切维护页):
systemctl stop nginx
- 导出最终数据库(同上一条
mariadb-dump); - 再跑一次 rsync,这次加
--delete,让目标端与源端完全一致:
rsync -aHAXz --numeric-ids --delete \
--exclude={/proc,/sys,/dev,/run,/tmp} \
/var/www /etc/nginx /etc/letsencrypt /opt \
[email protected]:/
--delete 会删除新机上多出来的文件,用之前务必确认目标路径没写错。
- 把最终数据库导入新机:
gunzip -c all-databases-2026-10-27.sql.gz | mariadb
- 新机启动服务并自检:
nginx -t
systemctl start nginx
systemctl status nginx --no-pager
到这一步,用 IP 或临时 hosts 绑定访问新机,确认页面正常。
六、证书处理
/etc/letsencrypt 整目录已同步,路径一致就能直接用。注意私钥权限:
chmod 600 /etc/letsencrypt/live/*/privkey.pem
DNS 切到新机后,验证续期:
certbot renew --dry-run
DNS 没切之前不要在新机跑续期验证,HTTP-01 验证会打到旧机。
七、切 DNS
如果域名在 Cloudflare:
- 官方说明里,代理记录(橙色云)的 TTL 固定为 Auto = 300 秒,不能改;
- 仅 DNS 记录(灰色云)可以自定义 TTL,最低 60 秒(非 Enterprise);
- 官方也提醒:TTL 越长,改记录生效越慢。
建议在切换前 24 小时把 TTL 调短,切换时只改 A 记录指向新机 IP,然后观察:
dig +short a.example.com
curl -I https://a.example.com
同时看旧机访问日志是否在下降、新机日志是否正常。
八、切换后的验证清单
- 首页、文章页、后台都能打开;
- HTTPS 证书域名正确,无混合内容告警;
- 数据库能读能写(发一条测试内容再删掉);
- 上传功能正常(目录属主是 Web 用户);
- 定时任务在跑(看日志时间戳);
- 邮件、队列、第三方回调没有报错;
certbot renew --dry-run通过;- 监控与备份任务已经指向新机。
九、留退路
- 旧机至少保留 72 小时,期间不要释放、不要删数据;
- 出问题时把 DNS 切回旧机 IP,并重新启动旧机服务;
- 确认新机稳定后再退订旧机,退订前再导出一份最终备份。
十、日常备份:迁移之外的另一件事
- 每日:数据库导出(
--single-transaction); - 每周:站点文件全量打包;
- 异地:至少一份不在本机(另一台 VPS 或对象存储);
- 演练:每季度真正恢复一次,确认备份能用;
- 保留:至少留 7 天日备 + 4 周周备。
一句话总结
先盘点、再备份并验证,rsync 两轮(第二轮配短停机),证书整目录带走,DNS 最后切,旧机留三天。 迁移的成功标准不是「新机能打开」,而是「出问题能在十分钟内切回去」。