一、迁移前的背景与准备

我们团队是做电商后台服务的,之前所有服务都跑在x86架构的服务器上,用的是CentOS7系统。后来因为采购成本、能耗的问题,决定把核心服务迁移到鲲鹏架构的服务器上,系统选了官方适配的openEuler。一开始我们以为只是换个CPU、换个系统,直接把服务拷过去跑就行,结果踩了一堆坑,折腾了快两周才完全跑通。

迁移前我们做了两个关键准备:第一,拉了一套和生产环境1:1的测试环境,专门用来试错;第二,把所有服务的依赖、编译脚本、运行命令都整理成了清单,方便后面排查问题。

二、第一个坑:编译时的架构不兼容问题

2.1 踩坑场景

我们的服务是用C写的,之前在x86的CentOS7上直接编译就能跑,拷到鲲鹏的openEuler上编译,直接报错。

2.2 具体报错与原因

当时敲了编译命令gcc main.c -o service,报错内容大概是“不支持的指令集”。后来查资料才搞懂:x86和鲲鹏的CPU指令集完全不一样,x86用的是x86-64指令集,鲲鹏用的是ARM64指令集,编译的时候必须指定对应架构的参数,不然编译器默认按当前CPU的指令集来编译,x86的程序拷到鲲鹏上跑,或者反过来,都会因为指令集不兼容报错。

2.3 解决方案与示例

我们的解决方案是在编译命令里加指定ARM64架构的参数,而且为了让编译过程可复用,把编译命令写成了Shell脚本。这里统一用C语言作为技术栈,示例如下:

#!/bin/bash
# 技术栈:C语言(基于GCC编译器)
# 编译脚本:针对鲲鹏ARM64架构的编译脚本
# 编译参数说明:
# -march=armv8-a:指定鲲鹏CPU支持的ARMv8-A指令集(鲲鹏920完全兼容这个指令集)
# -O2:开启二级优化,提升程序运行效率
# -g:保留调试信息,方便后续排查运行问题
gcc main.c -o service -march=armv8-a -O2 -g

写好脚本后,给脚本加执行权限chmod +x build.sh,再执行./build.sh,编译就顺利通过了。

三、第二个坑:动态库找不到的问题

3.1 踩坑场景

编译通过后,我们运行服务./service,又报错了:“error while loading shared libraries: libcurl.so.4: cannot open shared object file: No such file or directory”。

3.2 具体报错与原因

我们的服务用到了curl库,之前在x86的CentOS7上,系统默认装了x86版本的libcurl.so.4,而鲲鹏的openEuler上装的是ARM64版本的curl库,路径和x86的不一样,服务编译的时候默认找的是x86的库路径,所以找不到。

3.3 解决方案与示例

第一步,先确认openEuler上ARM64版本的curl库路径,执行命令:

# 技术栈:C语言(依赖curl库)
# 查找ARM64版本的curl库路径
find / -name "libcurl.so.4" 2>/dev/null

输出结果是/usr/lib64/libcurl.so.4

第二步,修改编译脚本,指定库路径,同时为了让服务运行时也能找到库,我们在脚本里加了运行时的库路径设置,修改后的编译脚本如下:

#!/bin/bash
# 技术栈:C语言(基于GCC编译器,依赖curl库)
# 编译脚本:适配鲲鹏openEuler的编译脚本
# 编译参数补充说明:
# -L/usr/lib64:指定ARM64版本curl库的路径
# -lcurl:指定链接curl库
# -Wl,-rpath=/usr/lib64:指定运行时的库路径,避免运行时找不到库
gcc main.c -o service -march=armv8-a -O2 -g -L/usr/lib64 -lcurl -Wl,-rpath=/usr/lib64

修改后重新编译,再运行服务,就不会报找不到库的错误了。

四、第三个坑:运行时的性能下降问题

4.1 踩坑场景

服务跑起来后,我们做了压测,发现响应时间比x86上慢了近30%,吞吐量也降了不少。

4.2 具体问题与原因

一开始我们以为是鲲鹏CPU的性能问题,后来查了openEuler的官方文档才发现,是我们的编译参数没选对:之前用的-O2优化级别虽然通用,但鲲鹏CPU有专门的优化参数,-O2没有充分利用鲲鹏的硬件特性,比如鲲鹏的多核心调度、内存带宽优化等。

4.3 解决方案与示例

我们把编译参数里的-O2改成了鲲鹏专用的优化参数-O3 -march=armv8-a+crc+crypto,这里的crccrypto是鲲鹏CPU支持的扩展指令集,专门用来优化校验和加密操作,我们的服务里有订单校验和用户数据加密的逻辑,刚好能用到。修改后的编译脚本如下:

#!/bin/bash
# 技术栈:C语言(基于GCC编译器,依赖curl库)
# 编译脚本:充分利用鲲鹏硬件特性的优化编译脚本
# 编译参数补充说明:
# -O3:开启三级优化,比-O2优化程度更高,更适合计算密集型服务
# -march=armv8-a+crc+crypto:指定鲲鹏CPU支持的扩展指令集,优化校验、加密逻辑
gcc main.c -o service -march=armv8-a+crc+crypto -O3 -g -L/usr/lib64 -lcurl -Wl,-rpath=/usr/lib64

修改后重新编译、压测,响应时间降到了和x86差不多的水平,吞吐量也提升了25%左右。

五、第四个坑:第三方工具的兼容性问题

5.1 踩坑场景

我们的服务依赖一个叫gperftools的性能分析工具,用来排查服务的性能瓶颈,之前在x86上用得好好的,拷到openEuler上运行,直接报错:“illegal instruction”。

5.2 具体报错与原因

gperftools是我们自己编译的x86版本,拷到鲲鹏上,因为指令集不兼容,所以会报非法指令的错误。

5.3 解决方案与示例

我们需要在openEuler上重新编译ARM64版本的gperftools,编译命令如下:

# 技术栈:C语言(依赖gperftools性能分析工具)
# 编译ARM64版本gperftools的命令
# 1. 下载gperftools源码
wget https://github.com/gperftools/gperftools/releases/download/gperftools-2.10/gperftools-2.10.tar.gz
# 2. 解压源码
tar -zxf gperftools-2.10.tar.gz
# 3. 进入源码目录
cd gperftools-2.10
# 4. 配置编译参数,指定ARM64架构
./configure --host=aarch64-linux-gnu
# 5. 编译并安装
make && make install

安装完成后,再在openEuler上运行gperftools的分析命令,就不会报错了。

六、迁移的应用场景、优缺点与注意事项

6.1 应用场景

这次迁移的核心应用场景是:对成本、能耗敏感的企业级服务(比如电商后台、云服务、大数据处理服务等),这些服务对性能有一定要求,但又不想承担x86服务器的高采购和运维成本。

6.2 迁移的优缺点

优点

第一,成本低:鲲鹏服务器的采购价格比同配置的x86服务器低20%左右,能耗也低30%左右,长期来看能节省不少费用;第二,适配性好:openEuler是专门为鲲鹏优化的系统,对鲲鹏的硬件特性支持得很好,后续的维护、升级也更方便;第三,性能达标:只要编译参数调整到位,鲲鹏服务器的性能完全能达到x86的水平,甚至在某些场景下(比如大数据计算、加密操作)性能更好。

缺点

第一,生态还不够完善:有些小众的第三方工具可能没有适配ARM64版本,需要自己编译;第二,迁移成本高:如果服务的依赖比较复杂,迁移时需要调整编译参数、依赖路径等,会花费不少时间;第三,技术积累少:大部分开发者之前都是做x86开发的,对鲲鹏和openEuler的了解比较少,遇到问题时排查难度大。

6.3 注意事项

第一,一定要先搭测试环境:迁移前必须拉一套和生产环境1:1的测试环境,所有的调整、测试都在测试环境做,避免影响生产;第二,整理依赖清单:迁移前把所有服务的依赖、编译脚本、运行命令都整理清楚,方便排查问题;第三,充分利用鲲鹏的硬件特性:编译时一定要用鲲鹏专用的优化参数,不然性能会打折扣;第四,及时关注官方文档:openEuler和鲲鹏的官方文档会更新适配信息,遇到问题时先查官方文档,能少走很多弯路。

七、迁移总结

这次从x86迁移到鲲鹏的openEuler,我们一共踩了四个坑:编译时的架构不兼容、动态库找不到、运行时性能下降、第三方工具兼容性问题。每个坑的解决思路其实都很简单:针对架构不兼容的问题,指定ARM64的编译参数;针对动态库找不到的问题,确认ARM64版本的库路径并指定;针对性能下降的问题,用鲲鹏专用的优化参数;针对第三方工具的问题,重新编译ARM64版本。

整个迁移过程花了两周,虽然过程有点折腾,但最终的结果是好的:服务的性能完全达标,成本也降了不少。这次迁移也让我们积累了不少鲲鹏和openEuler的经验,后续如果有新的服务要迁移,会顺利很多。