Blog

记录一次真实的服务器迁移:把 New API、Nginx 反代、生图调度、画布产品四个容器从旧机迁到新机,DNS 切换后端到端验证全绿。过程比想象中多踩了不少「环境差异」的坑,一并整理在这里。

背景

旧机 155.103.156.11 → 新机 38.175.195.160(Debian 12)。计划是用 rsync 搬数据、docker save/load 搬本地构建的镜像,在 1 分钟停服窗口内同步数据库,再切 DNS、重签证书。

八个真实踩坑

  1. PowerShell 管道传 tar 会损坏文件:Windows PowerShell 的管道不是字节保真的二进制通道,把 tar 经管道传给远端会损坏归档,必须落盘后再用文件方式传输。
  2. bash -s 的 stdin 被内层 ssh 吃掉:多层 ssh 嵌套时,外层传给远程 bash -s 的脚本内容会被内层 ssh 抢占 stdin,需要显式分隔标准输入。
  3. rsync -n 失效:在部分远程 shell 组合下 dry-run 的预测和实际不一致,不能只靠 -n 判断会传哪些文件。
  4. Debian 缺 AppArmor:部分容器运行依赖 AppArmor 配置,Debian 12 默认未启用,会导致相关容器起不来或告警。
  5. 挂载文件的 gid 漂移(本次最典型):nginx 官方镜像里 nginx 组的数字 ID 从旧机容器里的 999 变成了新版镜像的 101。迁移后挂载的 .htpasswd-canvas 权限失效,直接导致 canvas 全站 500。修复:chown 0:101 + chmod 640
  6. 本机代理 7897 排障假象:排查”远端连不上”时,本机的代理端口会让你误判成远端网络问题,先排除本地代理再下结论。
  7. nginx 镜像日志软链阻塞:官方 nginx 镜像把 access/error log 软链到 stdout,如果把挂载目录覆盖到日志路径,会阻塞容器输出。
  8. grep -c 的退出码grep -c 在无匹配时返回 1,在 set -e 脚本里会误触发退出,写脚本要小心它的退出码语义。

经验小结

迁移的关键不在「搬得动」,而在「环境差异的隐性假设」。数据库同步用 sha256 校验一致性、镜像用 docker save/load 保持本地构建版本、证书从手动改自动续期,这几件事比搬运本身更决定成败。

完整过程与回滚矩阵记录在个人知识库 60-sources/2026-08-03-服务器迁移执行记录.md