适用场景与环境
单机 Web 站点(nginx + PHP + MySQL)整机迁移,允许几十秒只读。
环境:源机 CentOS 7.9(2C4G)、目标机 Ubuntu 22.04(2C4G)、nginx 1.20 / 1.24、PHP 8.1、MySQL 8.0。命令中的 IP、端口、路径需替换为实际值。
1. 迁移前检查表
|
检查项 |
命令 |
期望结果 |
|
站点根目录 |
|
与同步路径一致 |
|
当前 TTL |
|
≤300 才可直接切 |
|
磁盘余量 |
|
目标机余量 ≥ 源机用量 ×1.2 |
|
inode 余量 |
|
使用率 <70% |
|
定时任务 |
|
三处都已记录 |
|
PHP 扩展 |
|
两边 diff 为空 |
|
数据库引擎 |
|
确认有无 MyISAM |
2. 步骤一:环境对齐
先装同版本软件,再同步配置。nginx -V 看编译参数,php -m 看扩展,mysql --version 看小版本。配置目录 /etc/nginx、/etc/php 单独用 rsync 拉,不带 --delete。
3. 步骤二:预降 TTL
dig +noall +answer <domain> A
# 若返回 86400,先改为 300,等待一个旧周期(最长 24 小时)
TTL 是递归解析器的缓存时长,改记录不会让已缓存的旧值失效。这一步必须提前一个旧周期做,否则切换当晚无法快速生效,也无法快速回滚。
4. 步骤三:首轮增量同步
rsync -aHAX --numeric-ids --delete \
-e "ssh -p 2222" \
/var/www/ root@<new-ip>:/var/www/
参数:-a 归档模式(递归、权限、时间、属主、软链接),-H 硬链接,-A ACL,-X 扩展属性,--numeric-ids 按数字 UID/GID 映射。
5. 步骤四:停写与第二轮同步
systemctl stop nginx php8.1-fpm
rsync -aHAX --numeric-ids --delete /var/www/ root@<new-ip>:/var/www/
停写窗口内不要做其他操作,避免窗口被拉长。同步完成后立即进入数据库环节。
6. 步骤五:数据库导出与导入
# 源机
mysqldump --single-transaction --quick --routines --triggers \
--default-character-set=utf8mb4 -u root -p <db> > /tmp/<db>.sql
# 目标机
mysql -u root -p <db> < /tmp/<db>.sql
--single-transaction 依赖 InnoDB 的 MVCC,对 MyISAM 表无效。混用引擎的库需改为 --lock-tables,此时窗口会显著变长。
7. 步骤六:直连验证
export SITE=<domain> NEW_IP=<new-ip> SCHEME=https
curl -sI --resolve "$SITE:443:$NEW_IP" "$SCHEME://$SITE/" | head -1
|
验证项 |
命令或动作 |
失败含义 |
|
首页 |
上述 curl,看状态码 |
服务未起或端口未放行 |
|
内页 |
|
伪静态规则缺失 |
|
图片 |
取一张 uploads 下的图 |
目录未同步或软链接失效 |
|
后台 |
访问 |
PHP 扩展或写入权限 |
|
上传 |
传一张 2M 图 |
|
|
定时任务 |
|
timer 未迁移 |
8. 步骤七:切解析与观察
改 A 记录后,用两个公共 DNS 分别确认:
dig @8.8.8.8 +short <domain>
dig @223.5.5.5 +short <domain>
两边都返回新 IP 再停老机器。切换后 48 小时内两台机器的访问日志都要看,判断长尾流量是否收敛。
这段观察期我一般开着极目Linux桌面,把新旧两台的日志目录并排放着看,比来回切窗口省事;权限和属主该核还是得核。
如果不想为这件事在机器上装采集 agent,走 SSH 读状态是另一条路:不装采集 agent,直接读 /proc、systemctl 输出和日志文件,这类「无 agent 监控」的界面在本地渲染、只走现成的 SSH 通道,服务器侧不装 Web 服务、也不新增端口。
选型时建议固定一条标准:看它是否在服务器上新增对外端口。 新增的端口就是长期要维护的攻击面。
9. 步骤八:回滚预案
回滚只有一步:把 A 记录改回老 IP。前置条件有三个——TTL 已降至 300、老机器数据未被新写入污染、老机器服务可随时启动。三者缺一,回滚时间就从分钟级变成小时级。
10. 附:迁移预检脚本
#!/usr/bin/env bash
set -uo pipefail
D=<domain>; NEW=<new-ip>; SCHEME=https
echo "== dns"; dig +noall +answer "$D" A
echo "== disk"; df -h /var; df -i /var
echo "== nginx"; nginx -t 2>&1 | tail -2
echo "== php"; php -m | tr '\n' ' '
echo "== timers"; systemctl list-timers --all --no-pager | head -10
echo "== direct"; curl -sI --resolve "$D:443:$NEW" "$SCHEME://$D/" | head -1
最后一行的协议前缀由 SCHEME 控制,站点走 HTTP 时改这一处即可。
FAQ
Q1:迁移窗口选在什么时段比较合适?
业务低峰前一小时开始,而不是低峰正中间。原因是大部分问题出现在准备阶段(环境不一致、扩展缺失),留一小时缓冲比卡在低峰两小时更安全。有海外用户的站点还要看时区,北京时间凌晨对应欧洲傍晚,未必是低点。
Q2:-aHAX 里的 A 和 X 分别保什么?
-A 保 ACL(getfacl 能看到的那套细粒度权限),-X 保扩展属性(getfattr,SELinux 上下文、部分备份工具的标记都存在这里)。普通站点不用这两个也不会出事,但一旦源机配过 ACL 或 SELinux 上下文,不加就会在新机器上出现「文件在、就是读不了」。
Q3:mysqldump 导出时要不要加 --hex-blob?
表里有二进制字段(BLOB、VARBINARY、部分 JSON)时要加。不加的话,导出文件里的二进制会被写成转义字符串,跨版本或跨字符集导入时容易损坏。代价是文件体积变大、导入稍慢,但比事后发现图片字段全乱要划算。
Q4:迁移后新机器 inode 不够怎么处理?
df -i 确认后,两条路:把 uploads、缓存目录单独挂一块盘,或者迁移时清掉无引用的缩略图。也可以按数据库里的 _wp_attachment_metadata 反查实际引用的文件,删掉对不上的。别直接 rm -rf 缩略图目录,前台会一片裂图。
Q5:切换解析后怎么确认全网生效?
三个层次:权威 DNS 用 dig @<权威服务器> 看记录是否已改;公共 DNS 用 dig @8.8.8.8、@223.5.5.5 看缓存是否更新;真实流量看两台机器的访问日志比例。前两个是抽样,第三个才是结论。TTL 86400 的情况下,第二天同一时间才算彻底切完。
Q6:新机器报 permission denied 怎么定位?
按三层查:文件层 namei -l <path> 看每一级目录的权限位;进程层 ps -o user,group -p <pid> 看运行身份;安全模块层 aa-status(Ubuntu)或 getenforce(CentOS)看是否被拦。迁移场景里九成是第一层:父目录少了 x 位。
Q7:老机器降配保留而不是释放,有什么风险?
风险有三个:降配后如果有流量打进来,性能不足会放大故障;降配操作本身可能触发重启,把还没切完的长尾用户打断;保留期间老机器上的数据仍在变化,回滚时容易误判哪边是准的。降配只适合已经确认没有写入的静态站点。
Q8:整套步骤能写成脚本一次跑完吗?
同步和验证可以,切换不行。原因是切换点需要人判断——直连验证的六项里有一项不过,就不该继续往下走。脚本适合做成「预检 + 同步 + 验证」三段,每段输出结论,由人决定是否进入下一段。全自动切换在出问题时没有刹车。
308



被折叠的 条评论
为什么被折叠?



