服务器迁移:换机房不中断服务的完整流程

换服务器最怕的不是搬,是「搬完发现有问题,但已经切换回不去了」。把流程拆成可回滚的步骤,就能做到用户几乎无感。

一、总体顺序

bash
1. 新机开出来,做基础加固
2. 装依赖、配服务(Nginx / 数据库 / 应用)
3. 同步数据与文件
4. 【关键】提前把 DNS TTL 调低到 300 秒
5. 【关键】用 hosts 文件预验证新机
6. 切换 DNS 解析
7. 观察 24~48 小时,旧机保留不动
8. 确认无误后再下线旧机

核心思想:先让新机在「看不见」的状态下跑通,再切换流量;切换后旧机继续待命。

二、第一步:降低 TTL(提前一天做)

这是最容易被忘、也最容易导致长时间不可用的步骤。

DNS 记录有 TTL(缓存有效期)。如果你原来设的是 3600 秒(1 小时),那么切换解析后,最多要 1 小时所有用户才会看到新 IP——期间他们访问的还是马上要下线的旧机

在切换前至少 24 小时,把 TTL 改成 300 秒(或 60),这样切换后 5 分钟内全网生效。

bash
# 确认当前 TTL
dig example.com A | grep -A1 "ANSWER SECTION"

# 去 DNS 控制台改成 300(Cloudflare 的自动 TTL 是 300,省钱省心)

三、新机准备

按本站「服务器初始化」那篇做一遍,然后装同样的软件栈。

bash
# 记录旧机的软件版本,保证一致(尤其是数据库大版本)
nginx -v
mysql --version
node -v
python3 --version

# 在新机装相同版本
sudo apt install -y nginx mysql-server

数据库大版本一定要一致。MySQL 5.7 的 dump 导进 8.0 可能报错,PostgreSQL 跨大版本更要谨慎。

四、配置文件迁移

最省事的办法:直接把旧机的配置目录打包拷过去。

bash
# 旧机上打包
sudo tar czf /tmp/conf.tar.gz /etc/nginx /etc/ssh/sshd_config /etc/letsencrypt

# 拷到新机(在新机执行,或旧机 scp 过去)
rsync -avz -e "ssh -p 2222" olduser@OLD_IP:/tmp/conf.tar.gz /tmp/

# 新机解压(先备份自己的默认配置)
sudo cp -r /etc/nginx /etc/nginx.default
sudo tar xzf /tmp/conf.tar.gz -C /
sudo nginx -t

注意 /etc/letsencrypt 拷过去后要确认路径和权限:

bash
sudo chmod 600 /etc/letsencrypt/live/*/privkey.pem
sudo nginx -t

也可以在新机重新签一份证书(certbot 更容易,推荐)。

五、数据迁移

文件

bash
# rsync 直接同步,保留权限
rsync -avz --delete -e "ssh -p 2222" \
  olduser@OLD_IP:/var/www/baize/ /var/www/baize/

sudo chown -R www-data:www-data /var/www/baize

数据库

bash
# 旧机导出
mysqldump -u root -p --single-transaction --routines --triggers baize_db \
  | gzip > /tmp/db.sql.gz

# 传到新机并导入
rsync -avz -e "ssh -p 2222" /tmp/db.sql.gz newuser@NEW_IP:/tmp/
gunzip < /tmp/db.sql.gz | mysql -u root -p baize_db

# 核对条数(必做)
mysql -u root -p -e "SELECT COUNT(*) FROM baize_db.posts;"

数据量大或者要求不停机的话,用主从复制:新机作为从库追上主库,切换时提升为主库。个人站点一般用不上。

六、【关键】用 hosts 预验证

切换 DNS 之前,先让你自己的电脑「以为」域名已经指向新机,完整跑一遍。

bash
# 本地修改 hosts(macOS / Linux)
sudo vim /etc/hosts

# 加一行:新IP 域名
NEW_IP  www.bzii.cn
NEW_IP  www.bzii.cn

# 清 DNS 缓存
sudo dscacheutil -flushcache && sudo killall -HUP mDNSResponder  # macOS
sudo resolvectl flush-caches                                     # Linux
ipconfig /flushdns                                               # Windows

然后像真实用户一样完整访问一遍:

  • 首页能打开,样式正常(不是 404 / 默认页)
  • HTTPS 证书有效(这是最容易出问题的地方)
  • 各页面链接可达,文章能打开
  • 数据库读写正常(能登录、能提交)
  • 上传/写入目录权限正确
  • 定时任务在新机已配好
bash
# 验证证书(hosts 生效后)
curl -sI https://www.bzii.cn | head -1
echo | openssl s_client -connect www.bzii.cn:443 -servername www.bzii.cn 2>/dev/null \
  | openssl x509 -noout -dates -subject

这一步能挡掉 90% 的迁移事故。不要跳过。

验证完记得把 hosts 改回来。

七、切换 DNS

确认无误后,把 A 记录改成新 IP。

bash
# 切换后持续观察,看全球生效情况
watch -n 10 "dig +short A www.bzii.cn"

# 或用 DNS Checker 看全球

因为有 TTL=300,5 分钟内大部分用户会到新机。接下来 24 小时内:

bash
# 看新机访问日志有没有流量进来
sudo tail -f /var/log/nginx/access.log

# 看错误日志
sudo tail -f /var/log/nginx/error.log

八、回滚方案

旧机在确认期内绝对不要关。 一旦新机出问题:

  1. 把 DNS A 记录改回旧机 IP;
  2. 因为 TTL 是 300,5 分钟内用户回流;
  3. 慢慢排查新机问题。

这就是为什么「提前降 TTL」这么重要——它同时决定了切换速度和回滚速度

九、收尾

确认 48 小时无异常后:

  • 旧机打一份最终快照,再保留 7 天;
  • 把 TTL 改回正常值(1800 或 3600);
  • 更新备份脚本里的目标地址;
  • 更新监控/告警里的机器信息;
  • 更新 ~/.ssh/config 里的 IP;
  • 最后再退订旧机(别急着退,快照留着)。

十、checklist 汇总

bash
准备阶段
  - [ ] 记录旧机软件版本
  - [ ] 新机基础加固
  - [ ] 装依赖并核对版本
  - [ ] TTL 降到 300(提前 24h)
  - [ ] 迁移配置文件
  - [ ] 同步文件与数据库
  - [ ] 核对数据条数

验证阶段
  - [ ] 改 hosts 指向新机
  - [ ] 完整走查所有功能
  - [ ] 证书有效
  - [ ] 定时任务已迁移
  - [ ] 恢复 hosts

切换阶段
  - [ ] 改 A 记录
  - [ ] 观察访问/错误日志
  - [ ] 旧机保持在线待命
  - [ ] 48h 后打快照、改回 TTL、下线旧机

迁移本身不难,难的是每一步都可回滚。按这个顺序走,换机房就是改一行 DNS 的事。