跳至正文

服务器迁移/升级的一点经验

上个月我被分配了一个任务:把公司跑了两年的老服务器从CentOS 7迁移到Rocky Linux 9,顺带把PHP从7.4升级到8.2,MySQL从5.7换到8.0。听起来就是常规操作对吧?我一开始也是这么想的,结果差点把自己埋进坑里。

先说背景。这套服务器跑的是我们核心的电商后台,日均请求量大概在50万左右,不算特别大但也不能随便宕机。老机器是四年前配的,用的CentOS 7.9,PHP 7.4.33,MySQL 5.7.44,Nginx 1.20.1。新机器是刚采购的,双路E5,128G内存,NVMe固态,系统装的是Rocky Linux 9.3。按理说硬件升级了,软件也该跟上,但迁移这事儿最怕的就是你以为”配置一样就能跑”。

我踩的第一个大坑是PHP版本兼容。代码里大量使用了`each()`函数,这个函数在PHP 7.2就已经标记为废弃,8.0直接删掉了。我们的代码库有大概30多处用了这个函数,散落在各个模块里。你以为我会老老实实改代码?我一开始想的是找替代方案,用`foreach`手动模拟。但后来发现有个第三方支付SDK里也用了这个函数,而且那个SDK已经两年没更新了。这就很蛋疼了,改SDK代码意味着后续升级更难,不改的话PHP 8.2跑不了。

最后我的做法是:先在测试环境装了一个PHP 8.2,把报错日志打开,`error_reporting = E_ALL`,然后跑自动化测试脚本。脚本跑了大概两小时,报了一堆`Deprecated`和`Fatal error`。我把它们分成三类:第一类是`each()`这种直接删除的函数,第二类是`mysql_*`函数(没错,老代码里还有用这个的),第三类是类型声明不匹配的问题。第一类和第二类我写了一个兼容层,用Composer加载一个自定义包,里面用`function_exists`判断,如果函数不存在就自己定义。比如`each()`我就用`foreach`加一个模拟的指针移动来实现。第三类问题更隐蔽,有个地方传参时把string当int传了,PHP 7.4只是报个Notice,8.2直接抛TypeError。这个只能逐行改代码,没捷径。

MySQL升级这块也是坑连坑。从5.7到8.0,最大的变化是默认字符集从latin1变成了utf8mb4,排序规则也变了。我们的数据库里有些表是latin1编码,有些是utf8,还有几个表混用。直接`mysqldump`导出再导入到8.0,结果导入过程中就报错,说某个字段的字符集不兼容。我查了下,是有一个表用了`utf8_general_ci`,而8.0默认的`utf8mb4_0900_ai_ci`跟它排序规则不一样,导致索引重建失败。更坑的是,有个存储过程里用了`GROUP BY`的隐式排序,5.7里默认会按分组字段排序,8.0把这个行为去掉了,结果查询结果顺序全乱套。这个bug在生产环境上跑了两天才被发现,因为前端页面有个列表展示顺序错了,运营同事截图问我:”猫哥,你是不是把排序逻辑改坏了?”

我当时的表情就是一脸懵逼。查了半天才发现是MySQL 8.0的这个改动导致的。解决方法是手动在所有`GROUP BY`后面加`ORDER BY`,或者设置`sql_mode`里去掉`ONLY_FULL_GROUP_BY`。我选了前者,因为改SQL比改全局配置更稳妥,至少下次迁移不会又踩这个坑。

Nginx迁移倒是相对顺利,毕竟配置基本都是文本,直接复制过来改改路径就行。但有个细节要注意:CentOS 7的Nginx用的是`nginx-1.20`,Rocky Linux 9的EPEL源里默认是`nginx-1.24`,配置文件的语法有些微调。比如`ssl_protocols`的默认值变了,旧配置里写`TLSv1 TLSv1.1 TLSv1.2`,新版本建议去掉TLSv1和TLSv1.1。我一开始没改,结果用SSL Labs扫描,直接给了个C级评分,说支持了不安全的协议。改完之后重新扫描,A+。

还有一个容易被忽略的点是systemd服务。老机器用的是SysVinit的启动脚本,新系统全是systemd。我们的应用是用一个shell脚本启动的,以前放在`/etc/init.d/`下面,chkconfig管理。到了Rocky Linux,得改成写service文件。这个倒不难,但要注意`ExecStart`和`ExecStop`的写法,还有`WorkingDirectory`要指定对,不然日志文件会跑到奇怪的地方。我写了个简单的service文件,大概长这样:

“`
[Unit]
Description=Our App
After=network.target mysql.service

[Service]
Type=forking
User=appuser
Group=appgroup
ExecStart=/usr/local/bin/start_app.sh
ExecStop=/usr/local/bin/stop_app.sh
WorkingDirectory=/var/www/app
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
“`

注意那个`Type=forking`,如果应用是前台运行的,应该用`Type=simple`。我一开始写错了,导致`systemctl start`之后看起来启动了,但一查进程发现根本没跑起来,因为systemd认为进程已经退出,自动又给停掉了。这个bug我排查了半小时,最后用`journalctl -u app.service`看日志才发现是Type设置不对。

PHP-FPM的配置也要注意。老机器上用的是`php-fpm`的`listen = 127.0.0.1:9000`,新机器上我改成了`listen = /var/run/php-fpm.sock`,因为Unix socket比TCP快那么一丢丢。但Nginx那边也要对应改`fastcgi_pass unix:/var/run/php-fpm.sock`。如果忘了改,Nginx会报502。这个我倒是记得,因为之前踩过类似的坑。

迁移过程最折磨人的其实是数据校验。数据库有大概200多张表,总数据量在80GB左右。我用了`pt-table-checksum`来对比新旧数据库的数据一致性,这个工具是Percona Toolkit里的,可以逐行对比数据。跑了一遍发现有几个表的checksum对不上,查了半天发现是字符集问题导致的,因为latin1和utf8mb4对某些字符的存储方式不同,导致同样的字符串存进去,字节数不一样。解决方案是在迁移前先把所有表的字符集统一成utf8mb4,然后再导出导入。这一步很耗时,但值得,不然数据不一致排查起来更痛苦。

还有个细节是关于SSL证书的。老机器上用的是Let’s Encrypt的证书,通过certbot自动续期。迁移到新机器后,我把证书文件直接复制过去,结果发现certbot的续期脚本路径不对,因为新机器的系统目录结构跟CentOS 7不一样。我重新运行了一次`certbot –nginx`,让它重新生成配置,顺便把证书也续期了。这里要注意,如果域名解析还没切到新IP,`certbot`验证域名所有权时会失败。我是在内网测试环境先配了一个临时域名,等验证通过后再改回正式域名。

迁移完成后的第一天,监控面板上显示CPU使用率比老机器低了30%,内存占用少了20%,我心里还挺美。结果第二天凌晨,告警响了,说某个API接口响应时间从200ms飙到了5秒。我爬起来查,发现是MySQL的查询缓存问题。老机器上MySQL 5.7的query cache是开启的,而MySQL 8.0彻底移除了这个功能。我们的某个报表查询,每天凌晨跑一次,查询语句写得比较烂,以前靠query cache撑着,现在没了缓存,每次都要全表扫描。解决方案是优化那个查询,加了个联合索引,响应时间降到了150ms。这个教训告诉我,不能依赖MySQL的query cache,它本来就是个坑,8.0把它删了反而是好事。

最后说一个关于SSH的坑。迁移完成后,我把老机器的IP地址从负载均衡器里移除,新机器加进去。结果发现SSH连接新机器特别慢,每次要等好几秒才弹出密码提示。查了`/etc/ssh/sshd_config`,发现`UseDNS yes`是默认开启的,SSH服务器会反向解析客户端的IP。新机器的网络环境里,DNS解析超时了。改成`UseDNS no`之后,连接速度秒开。

总结一下我的踩坑清单:PHP版本升级要检查废弃函数和类型声明,MySQL 8.0注意字符集和排序规则变化,GROUP BY的隐式排序没了,query cache被移除,Nginx配置注意协议版本和socket路径,systemd服务文件要写对Type,数据迁移后一定要做一致性校验,SSL证书要重新配置续期路径,SSH记得关UseDNS。这些坑单独看都不大,但串起来能让人崩溃好几次。

现在这套新系统跑了一个月了,稳得很。唯一不爽的是,运营同事又提了新需求,说想加个AI推荐功能。我心想,刚把PHP和MySQL升级完,又要折腾AI,这节奏真是停不下来。不过话说回来,服务器迁移这事儿,准备的越充分,踩的坑就越少。我的建议是:先在测试环境完整跑一遍迁移流程,把报错全部解决,记录每一步的操作步骤,然后写成文档。正式迁移时严格按照文档来,遇到意外情况就停下来查,不要硬着头皮往下干。毕竟,服务器崩了,背锅的还是自己。


📎 延伸阅读

看完这篇,如果你想:

  • 直接拿工具 → 回复”13“,我把跨境获客工具包发给你
  • 系统学习 → 点击菜单”AI训练营”,从0开始跑通AI变现

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注