上个月公司要把一台跑了3年的老服务器从阿里云迁到腾讯云,老板拍板的时候说得轻巧,“就挪个窝嘛,一天搞定”。结果我搞了整整3天,中间差点把生产数据库弄崩,晚上做梦都在敲rsync。
先说背景。老服务器是CentOS 7.9,跑了Nginx 1.20、PHP 7.4、MySQL 5.7,还有一个用Supervisor托管的任务队列,大概20多个Python脚本。新服务器是腾讯云轻量应用服务器,系统选的Ubuntu 22.04,配置比老机器翻了一倍——4核8G变8核16G。听起来是升级,实际上从系统到软件栈全换了。
第一个坑就出在系统版本上。老机器CentOS用yum,新机器Ubuntu用apt,很多软件包名不一样。比如装Nginx,CentOS上是nginx,Ubuntu上是nginx-full,后者多了几个模块。我一开始没注意,直接apt install nginx,结果发现缺少ngx_http_sub_module,后面做反向代理替换响应内容的时候报404。重新编译?别闹了,直接apt install nginx-extras搞定。
第二个坑是PHP版本。老机器PHP 7.4,新机器Ubuntu 22.04默认源里只有PHP 8.1。项目里有个老插件用了mysql_connect函数,PHP 8.1直接把这个函数删了。我翻Git提交记录才发现这破插件3年没更新了。最后用docker把PHP 7.4单独跑起来,挂载了同一个网站目录,才把问题绕过去。别问我为什么不升级插件,问就是甲方说“别动,能跑就行”。
第三个坑,也是最要命的——MySQL迁移。我一开始图省事,直接用mysqldump全量导出,然后在新机器上source导入。导出文件2.3GB,导入花了40分钟。导入完成后检查数据行数,发现少了7张表。排查了半天,原来是老机器上MySQL的sql_mode配置了ONLY_FULL_GROUP_BY,导出的SQL里有些GROUP BY语句在新机器上跑不了,直接静默跳过。你问我为什么是静默跳过?因为mysqldump默认不会报错,它只会把错误写进日志,而我没看日志。最后加了–force参数重新导入,又等了40分钟。
第四个坑,文件权限。老服务器上网站文件所有者是www用户,uid是500。新服务器我新建了一个www用户,结果uid自动分配到了1001。从老机器打包过来的文件,所有者uid还是500,新机器上这个uid对应的是另一个系统用户。结果Nginx读取静态资源的时候报403。解决方案是chown -R www:www /var/www,但要注意先确认新机器的www用户uid和gid,或者在创建用户时指定uid和gid一致。
第五个坑,crontab。老机器上有12个定时任务,我手动复制到新机器上,漏了一个凌晨3点备份数据库的任务。过了两天才发现没备份。后来学乖了,老机器上直接crontab -l > /tmp/cron_backup.txt,新机器上crontab /tmp/cron_backup.txt,一行不漏。
第六个坑,Supervisor的进程配置。老机器上用Supervisor 3.3,新机器上apt装的是4.2,配置文件的格式有变化。老的.ini配置文件里,command路径写的是/usr/local/bin/python3,新机器上python3在/usr/bin/python3。还有环境变量的问题,老机器上在/etc/supervisor/supervisord.conf里配了全局环境变量,新机器上我忘了配,结果有个脚本跑起来连PATH都没有,直接报错。
第七个坑,SSL证书。老机器上的Let‘s Encrypt证书是certbot自动续期的,但我迁移的时候没有把/etc/letsencrypt目录完整复制过去。新机器上重新申请证书,结果同一个域名一周内申请次数超过了限制。等了3天才能重新申请,这3天网站靠HTTP撑着。正确的做法是把老机器的/etc/letsencrypt整个目录打包,scp到新机器,然后在新机器上certbot renew –dry-run测试一下续期是否正常。
第八个坑,Redis数据。老机器上Redis跑着session缓存,大概有200MB数据。我直接stop了Redis,把dump.rdb文件拷过去,启动新Redis后数据加载正常。但问题是老Redis停掉的那几分钟,用户正在提交订单,session丢了,有几个用户反馈登录状态异常。正确的做法是先在新机器上启动Redis作为slave,同步完数据后再切换主从角色,零停机。
第九个坑,DNS缓存。迁移完成后改了域名的A记录指向新服务器IP,但我忽略了TTL值。老域名的TTL设置的是3600秒,也就是1小时。结果1小时内有些用户还在访问老服务器,而老服务器我已经把Nginx停掉了。后来我在老服务器上临时加了Nginx配置,把所有请求301重定向到新服务器地址,才搞定。
第十个坑,监控告警。新服务器上我忘了装Node Exporter和Prometheus的agent,导致迁移后的头两天我完全不知道服务器负载情况。第三天发现磁盘占用率到了85%,一查是MySQL的binlog没开自动清理,3天积累了40GB日志。赶紧在my.cnf里加了expire_logs_days = 7,然后手动purge binary logs before now()。
第十一个坑,回滚方案。迁移开始前我没有准备完整的回滚流程。中间有一次测试发现API接口响应时间从50ms飙升到800ms,我慌了,想切回老服务器,结果发现老服务器的Nginx已经被我改了配置,回不去了。硬着头皮排查,最后发现是新机器上PHP的opcache没开,加上opcache.enable=1后响应时间降到30ms。从那以后我每次迁移都会先写个回滚文档,哪怕只有3行。
总结一下。服务器迁移这件事,技术细节决定成败。版本差异、配置文件路径、用户权限、定时任务、证书、缓存、DNS TTL、监控、回滚方案——任何一个环节出问题,轻则加班,重则背锅。我这次的教训就是:别信“就挪个窝”这种话,老老实实列清单,一项一项验证,迁移前做全量备份,迁移后至少观察48小时再销毁老机器。
对了,还有一件事。迁移完成后别忘了更新防火墙规则。我新机器上ufw默认只开了22、80、443端口,结果有个内部API跑在8080端口上,被防火墙挡了3天,产品经理天天问我为什么功能挂了。我查了3天才发现是端口没放行。
📎 延伸阅读
看完这篇,如果你想:
- 直接拿工具 → 回复”13“,我把跨境获客工具包发给你
- 系统学习 → 点击菜单”AI训练营”,从0开始跑通AI变现