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