这次把机器从新加坡迁到东京,起点其实很简单:我觉得链路速度不太行,想试试看东京线路会不会更稳一点。
本来以为这只是一次常规迁移,按镜像恢复、改域名、检查服务、验证 HTTPS 这样的流程走完就可以了。结果真正开始排障之后,问题一个接一个出现,而且三个问题还不是独立的:
- 先是迁移执行要补很多“镜像本身不会帮你处理”的东西
- 然后是
sub2api域名打不开,排查后发现 DNS 同时指向了新旧节点 - 最后又卡在
Tailscale上,看起来东京和新加坡像是同一个节点,手机上始终只能切新加坡出口
这篇文章就按这个顺序,做一次完整复盘。
一、迁移不是“复制镜像然后开机”这么简单
迁东京之前,我先整理了一份执行清单,核心思路不是“把盘拷过去”,而是明确哪些东西会跟着镜像走,哪些东西不会。
真正值得提前确认的,至少有这几类:
- 业务数据和密钥有没有完整保留
- 云侧资源会不会跟着迁移
- 域名和证书切换要怎么做
Tailscale、出口节点、路由这些网络状态会不会一起被复制- 博客和
sub2api的发布链路迁过去后还能不能继续维护
我当时整理出来的重点包括:
- 迁移前要做一次停写后的最终同步,尤其是
PostgreSQL和Redis这种有状态数据,不应该只依赖运行中快照。 - 要保全的关键目录不只是应用代码,还包括
/etc/nginx/、/etc/letsencrypt/、/home/ubuntu/.ssh/、/var/www/blog/这些“运行依赖”。 - 公网 IP、安全组、防火墙、VPC、路由、监控之类云侧资源不会随着镜像自动迁移。
chen-api.cloud和blog.chen-api.cloud的解析必须切到东京的新 IP,否则服务起得再好,外部流量也不会过来。- 如果服务器上保留了
Tailscale,迁移后一定要额外确认 tailnet 身份和出口节点状态,而不是默认它会“自然正确”。
这一步最大的经验是:镜像能帮你复制文件系统,但它不会自动帮你重建一台“逻辑上全新的服务器”。
对于单机应用来说,迁移最容易漏掉的恰恰不是业务代码,而是外围状态:
- DNS 指向
- HTTPS 续期
Tailscale设备身份- 云控制台侧网络规则
这些东西只要有一个没切干净,表面上就会表现成“服务好像起来了,但总有地方不对”。
二、sub2api 打不开,根因不是程序没启动,而是域名同时指向了新旧节点
迁移完成后的第一轮验证里,最直观的问题是:sub2api 对应的网址打不开。
这种时候最容易先怀疑应用本身,比如:
- Docker 容器是不是没起来
Nginx是不是没转发对- HTTPS 证书是不是有问题
但这次真正的根因并不在应用层,而在 DNS。
后来排查发现,域名解析当时并不是“已经完全切到东京”,而是新旧两台机器同时在解析结果里。也就是说,请求打到哪里并不稳定:
- 有的请求会落到东京
- 有的请求会落到新加坡
这样一来,现象就会非常迷惑:
- 你明明在东京已经把服务拉起来了
- 但访问域名时,外部流量并不一定真的落到东京
- 如果旧节点上的配置、证书、容器状态和新节点不完全一致,就会出现“有时能打开、有时打不开”的情况
这个问题的关键不在于 sub2api 本身,而在于迁移切流没有一次性切干净。
回头看,这类问题的正确判断顺序应该是:
- 先确认域名当前到底解析到哪些 IP
- 再确认目标 IP 上的
Nginx和应用是否正常 - 最后才去怀疑应用逻辑或 HTTPS
否则很容易在服务器里来回改配置,结果真正的问题其实还停留在 DNS 层。
所以如果你也在做跨区迁移,我现在会很明确地建议:
- 提前把 TTL 降低
- 切换窗口内确认 A 记录只保留新节点
- 不要让新旧节点长时间同时对外接流量
对静态站点这可能只是偶发错页,但对 sub2api 这类实际业务服务来说,双指向会把排障复杂度直接放大。
三、最难定位的问题:东京和新加坡在 Tailscale 里竟然像是同一个实例
前两个问题处理完之后,本来以为迁移已经差不多了,结果还有一个更绕的现象:
- 理论上,新加坡服务器和东京服务器应该是
Tailscale里的两个节点 - 但我在手机上切换出口节点时,始终只能看到新加坡,或者
none - 东京这台机器怎么都不像一个真正独立的新节点
一开始我怀疑过很多方向:
- 是不是东京机的出口节点路由没有被批准
- 是不是
tailscale up参数没带全 - 是不是手机端列表没有刷新
- 是不是两台机器里有一台根本没连上控制面
结果最后发现,问题比这些都更底层:东京服务器是从新加坡机器的镜像迁出来的,而镜像里把 Tailscale 的状态文件也一起复制过来了。
这意味着两台机器携带的是同一套 Tailscale 设备身份,而不是两个独立身份。
于是就会出现非常符合直觉、但一开始又很难想到的现象:
- 管理后台里它们可能看起来像同一个节点
- 手机上的出口节点列表里,你只会看到其中一个
- 谁最后连上控制面,谁就“代表”这个节点出现
我这次的现场现象就是这样:
- 本机系统主机名已经是东京机器的新名字
- 但
Tailscale里仍然带着旧机器的节点信息 - 新加坡节点还能继续显示为出口节点
- 东京节点虽然本地在广播出口能力,但在 tailnet 里并没有真正作为新的出口候选出现
这个现象并不是我碰巧遇到的“玄学 bug”,而是 Tailscale 官方文档里明确提到过的情况。
Tailscale 在这两篇文档里都说明了类似问题:
- Troubleshoot multiple devices with the same 100.x IP address
https://tailscale.com/docs/reference/troubleshooting/network-configuration/multiple-devices-same-100.x-ip-address - Tailscale identity
https://tailscale.com/docs/concepts/tailscale-identity
官方文档的结论可以概括成一句话:如果你用一台机器的备份或克隆文件系统去创建另一台机器,而把 Tailscale 配置状态也一并带过去,就可能出现重复节点键、多个设备共享同一个 100.x 地址、或者后台把它们识别成同一设备的情况。
这也正好解释了我这次为什么会在手机上看不到东京出口节点。
最后是怎么修好的
真正的修复方法,不是继续反复执行 tailscale up,而是把东京机从旧的节点身份里彻底剥离出来。
我最后做的动作大致是:
- 备份当前
tailscaled.state - 停掉
tailscaled - 移走旧的状态文件
- 重新启动
tailscaled - 用新的主机名重新执行
tailscale up - 再到后台批准东京节点广播的出口路由
做完之后,东京和新加坡终于在 tailnet 里变成了两个真正独立的节点:
- 东京有自己的新节点名
- 东京有自己的新
100.x地址 - 旧的新加坡节点不再和东京“共用身份”
- 手机端也终于可以把东京识别为一个独立的出口节点候选
这一步给我的最大提醒是:
做机器迁移时,不要只想到应用数据和证书,像 Tailscale 这种“设备级身份状态”也要当成迁移对象认真审查。
它不是简单的安装包,而是一个会把“这台机器是谁”记录下来的网络身份系统。
四、这次迁移给我的三个明确教训
这次从新加坡迁到东京,看起来是一次为了“速度更好”的基础设施调整,但真正花时间的,其实都是那些最开始觉得“不至于出问题”的部分。
如果把这次经历压缩成三个结论,我会写成下面这样:
- 迁移时最危险的不是显眼的大组件,而是那些附着在系统上的状态。
- 域名切换如果让新旧节点同时接流量,排障会立刻变得不可靠。
- 从镜像恢复出来的新机器,并不天然等于一个“全新的网络身份”。
现在回头看,最初觉得“东京速度可能更好”这个判断没错;但真正让迁移变复杂的,从来不是跨区本身,而是那些被镜像顺手带过去、却不该继续复用的状态。
如果以后我再做类似迁移,我会把下面这条检查项直接写进迁移 SOP:
- 切流前确认 DNS 只会指向新节点
- 切流后确认
Tailscale身份是重新生成的 - 只有当网络身份、证书、反代和业务流量都验证完成后,旧机器才真正可以退场
这次东京迁移,最后修好的不只是机器,而是我对“服务器迁移”这件事的理解。