跳到主要内容
注册无需实名认证
安云主机 AnYun VPS

教程 · 运维与排障

把服务器迁移到另一个机房:一次不丢数据的搬家

难度

高级

预计时长

60 分钟

步骤

9

最近修订

基准系统

Debian 12

要点

把服务器迁移到另一个机房:一次不丢数据的搬家

迁移的正确顺序是:提前 24 小时把 DNS 的 TTL 降到 300 秒、在新机器上装好版本对齐的环境、用 rsync 预同步文件、停写之后做最后一次增量同步与数据库导入、切 DNS、观察 48 小时再销毁旧机器。真正的风险不在传输,而在切换期间新旧两台机器同时被写入造成的数据分叉。

前置条件

开始之前,先确认这些都具备

  • 新旧两台服务器都能 SSH 登录,且已配置密钥
  • 域名的 DNS 管理权限
  • 一次可以接受的短暂停写窗口(通常几分钟到半小时)

操作步骤

9

  1. 步骤 01 / 09

    先把要搬的东西列全 #

    迁移事故几乎都出在「忘了带走某样东西」上,而不是文件传丢了。清单至少包括:站点文件、数据库、定时任务、systemd 单元、环境变量与 .env、TLS 证书、防火墙规则、以及所有在第三方服务里登记过本机 IP 的地方(API 白名单、数据库远程访问白名单、邮件服务)。换机房意味着 IP 一定会变,这一条最容易被漏掉。

    Shell
    # 在旧机器上把这些导出来存好
    crontab -l > ~/migrate/crontab.txt
    sudo systemctl list-units --type=service --state=running > ~/migrate/services.txt
    sudo ufw status numbered > ~/migrate/ufw.txt
    dpkg --get-selections > ~/migrate/packages.txt
    ls /etc/letsencrypt/live/ > ~/migrate/certs.txt
  2. 步骤 02 / 09

    提前 24 小时把 DNS TTL 降下来 #

    TTL 决定了全球的 DNS 缓存多久之后才会看到新 IP。平时设成 3600 秒没问题,但切换前必须提前降到 300 秒,而且要提前一个旧 TTL 的时长 —— 现在就改,明天再切。这一步没做,切换后会有访客持续几小时还在访问旧机器。

    Shell
    # 在 DNS 服务商处把 A 记录 TTL 改为 300,然后确认
    dig +noall +answer example.com @1.1.1.1
  3. 步骤 03 / 09

    在新机器上装好版本对齐的环境 #

    版本不一致是迁移后「数据导进去了但跑不起来」的主要原因:PHP 8.2 的站点跑在 8.3 上、PostgreSQL 15 的导出文件恢复到 14 上,都会出问题。照着旧机器的版本装,先把环境跑通,再谈数据。

    Shell
    # 在旧机器上确认版本
    php -v; nginx -v; psql --version; node -v
    
    # 在新机器上装同样的大版本,并做一次空跑测试
    sudo nginx -t
  4. 步骤 04 / 09

    预同步:先搬大头 #

    在业务照常运行的情况下先同步一次,把绝大部分数据挪过去。这一次不用停机,可以慢慢传。-a 保留权限与时间戳,-H 保留硬链接,--numeric-ids 避免因两边用户 ID 不同而把属主搞乱。传输中断了重跑同一条命令即可续传。

    Shell
    rsync -aHAX --numeric-ids --partial --info=progress2 \
      -e 'ssh -p 22022' \
      /var/www/ deploy@新机器IP:/var/www/
    
    rsync -aHAX --numeric-ids --partial --info=progress2 \
      -e 'ssh -p 22022' \
      /etc/letsencrypt/ deploy@新机器IP:/etc/letsencrypt/
  5. 步骤 05 / 09

    停写窗口:最后一次增量 + 数据库导入 #

    这是唯一需要停服务的一段时间。先停掉会写数据的服务(Web 应用、队列、定时任务),再做最后一次 rsync 和数据库导出导入。数据库必须在停写之后导出,否则导出的那一刻之后又产生的写入会永久丢失。

    Shell
    # 旧机器:停止写入
    sudo systemctl stop nginx
    pm2 stop all
    crontab -r          # 或者临时注释掉所有任务(先确认已备份 crontab)
    
    # 最后一次增量同步
    rsync -aHAX --numeric-ids --delete -e 'ssh -p 22022' /var/www/ deploy@新机器IP:/var/www/
    
    # 数据库:导出并传输
    sudo -u postgres pg_dump -Fc appdb -f /tmp/appdb-final.dump
    scp -P 22022 /tmp/appdb-final.dump deploy@新机器IP:/tmp/
    
    # 新机器:导入
    sudo -u postgres createdb appdb
    sudo -u postgres pg_restore -d appdb /tmp/appdb-final.dump
  6. 步骤 06 / 09

    切 DNS 之前,先用本地 hosts 验证新机器 #

    改本地 hosts 文件把域名强行指向新机器,就能在不影响任何访客的前提下完整地验证一遍:首页、登录、上传、支付回调、后台。全部通过了再动 DNS。这一步能拦下大部分「切过去才发现少装了个扩展」的事故。

    Shell
    # 本地电脑的 hosts 文件
    #   Linux/macOS: /etc/hosts
    #   Windows: C:\Windows\System32\drivers\etc\hosts
    # 加一行:
    新机器IP  example.com www.example.com
    
    # 验证完记得删掉这一行
  7. 步骤 07 / 09

    切 DNS 并观察 #

    改 A 记录,然后从多个公共解析器确认已经更新。旧机器的 Web 服务保持停止状态 —— 让还在用旧 IP 的访客看到错误,也好过让他们在旧机器上继续写数据造成分叉。观察 48 小时,确认没有请求再落到旧机器上。

    Shell
    dig +short example.com @1.1.1.1
    dig +short example.com @8.8.8.8
    
    # 在新机器上确认流量确实进来了
    sudo tail -f /var/log/nginx/example.com.access.log
    
    # 在旧机器上确认没有新请求了
    sudo tail -n 100 /var/log/nginx/example.com.access.log
  8. 步骤 08 / 09

    证书与自动续期 #

    如果整份 /etc/letsencrypt 都同步过来了,证书本身可以直接用。但 DNS 切换完成后建议重新跑一次续期演练,确认新机器上的验证路径通畅 —— 证书是那种「三个月后才会暴露问题」的东西。

    Shell
    sudo certbot certificates
    sudo certbot renew --dry-run
    systemctl list-timers | grep -i certbot
  9. 步骤 09 / 09

    旧机器至少保留一周 #

    省下几美元而立刻销毁旧机器,是这套流程里最不划算的决定。保留一周,期间你随时可以回滚,也可以回去取那些当初漏掉的文件。确认无误之后再删除 —— 注意删除之前先把数据备份到第三处。

    Shell
    # 销毁前的最后一次全量备份(存到第三个位置)
    restic backup /etc /var/www /home /var/backups --exclude-caches
    restic snapshots

常见错误

这一步最容易踩的坑

下面每一条都对应一个真实会发生的故障。先读完再动手,比出问题之后再回来查要省时间。

  • 切换期间新旧两台机器都在接收写入,数据从此分叉。这是迁移里最难修复的事故 —— 必须有一个明确的停写窗口。
  • DNS 的 TTL 没有提前降低。切换后仍有访客被导向旧机器,持续时间等于旧的 TTL。
  • 忘了搬定时任务、systemd 单元、环境变量或证书。它们不在 /var/www 里,rsync 抓不到。
  • 数据库大版本不一致。导出文件恢复不进去,而这通常是在停写窗口里才发现的。
  • 忘记 IP 变更会影响第三方白名单:支付回调、API 白名单、数据库远程访问、邮件发信。新 IP 的邮件发信声誉也需要重新积累。
  • 切完立刻销毁旧机器,出问题时无法回滚。至少保留一周。
  • rsync 没加 --numeric-ids,两边用户 ID 不同导致文件属主全乱,表现为各种莫名其妙的权限错误。

这篇教程的边界

  • 命令以 Debian 12 为准。Ubuntu 与 Rocky Linux 的差异只在正文明确标注之处,未标注的部分请以你所用发行版的官方文档为准。
  • 示例中的 IP 来自文档保留段 203.0.113.0/24,域名为 example.com,端口为示意值。复制后必须替换成自己的值, 原样执行不会有任何效果。
  • 教程不能替代备份。任何会改动数据或线上流量的步骤,执行前先确认有一份验证过能恢复的备份。
  • 本站不提供任何用于绕过网络审查的配置或说明,这篇也不例外。

发现命令过时或有误,请发邮件到 [email protected]。 指出错误的邮件比沉默的旧文档有价值得多。

联系 工单
扫码
手机访问

扫码在手机上打开