开发者
资源
同辕开发实战:从两套流水线到一码多芯的10个坑与全流程落地指南
同辕开发实战:从两套流水线到一码多芯的10个坑与全流程落地指南
原创
发表于06/16
160

同辕开发实战:从两套流水线到一码多芯的10个坑与全流程落地指南

【同辕开发实战】从两套流水线到一码多芯:我踩过的 10 个坑与全流程落地指南

大家好,我是做后端服务开发的,去年下半年开始公司业务全面迁移到鲲鹏平台。一开始我们走了最笨的路子:x86 一套代码分支,鲲鹏一套分支,两条独立的 CI/CD 流水线,每次改 bug、发版本都要搞两次,经常漏改合并,线上出过好几次问题,运维和开发都怨声载道。

直到去年年底接触了鲲鹏的「一码多芯、同辕开发」模式,前前后后折腾了两个多月,踩了无数坑,终于把我们核心的 12 个服务全部改成了同辕开发模式 —— 现在一套代码,一条流水线,同时出 x86 和鲲鹏两个版本的包,发版一次搞定,再也不用维护两套分支了,真的香。今天把我踩过的坑、全流程实操步骤整理出来,给正在做鲲鹏适配的兄弟们避避坑。


一、先搞懂:同辕开发到底解决了什么痛点

很多人刚接触同辕开发的时候,会觉得是个花架子,不就是跨架构编译吗?其实真不是,传统的多架构开发模式有三个致命的问题:

  1. 代码维护成本翻倍:两套分支,改一个功能要合并两次,很容易漏改,我们之前出过一次支付接口的 bug,x86 那边改了,鲲鹏那边忘了合并,线上跑了三天才发现
  2. 资源浪费:要搭两套构建环境,两套流水线,x86 一台构建机,鲲鹏一台构建机,成本高,维护麻烦
  3. 版本不一致:很容易出现 x86 和鲲鹏版本功能不一致的情况,测试要测两遍,工作量翻倍

而同辕开发的核心就是:基于 openEuler + 鲲鹏 DevKit + BoostKit,一套代码 + 一条流水线,同时构建出 x86 和鲲鹏两个平台的发布包,全程不用改代码

说白了就是,你写代码的时候不用管是 x86 还是鲲鹏,写完提交,流水线自动给你出两个版本的包,直接部署就行,这才是真的解放生产力。


二、同辕开发全流程实操(我跑通的完整步骤)

2.1 环境准备(版本一定要按这个来,别用最新版!)

这是我踩的第一个大坑,一开始图新鲜装了最新版的毕昇编译器和 DevKit,结果各种编译报错,查了三天才发现是版本不兼容。官方推荐的稳定版本组合,我亲测没问题:

组件版本备注
操作系统openEuler 22.03 LTS SP2别用 CentOS,很多工具不兼容
鲲鹏 DevKit23.0.RC2稳定版,别用 3.0 的开发版
毕昇编译器(BiSheng Compiler)2.3.0最稳定的版本,3.1.0 有兼容问题
鲲鹏 BoostKit23.0.RC2对应 DevKit 版本

安装命令我整理好了,直接复制就行:

# 1. 安装DevKit依赖
yum install -y cmake make git gcc gcc-c++

# 2. 安装毕昇编译器2.3.0
wget https://mirrors.huaweicloud.com/kunpeng/archive/compiler/bisheng_compiler/bisheng-compiler-2.3.0-aarch64-linux.tar.gz
tar -zxvf bisheng-compiler-2.3.0-aarch64-linux.tar.gz -C /opt
echo "export PATH=/opt/bisheng-compiler-2.3.0/bin:$PATH" >> /etc/profile
source /etc/profile

# 3. 安装鲲鹏DevKit
wget https://mirrors.huaweicloud.com/kunpeng/archive/devkit/Kunpeng_DevKit_23.0.RC2_linux.tar.gz
tar -zxvf Kunpeng_DevKit_23.0.RC2_linux.tar.gz
cd Kunpeng_DevKit_23.0.RC2_linux && ./install.sh

验证安装是否成功:

clang --version # 输出bisheng compiler 2.3.0就对了
devkit --version # 输出23.0.RC2就正常

2.2 代码适配(90% 的项目不用改代码,只需要改构建脚本)

大部分 Java、Go、Python 项目基本不用改业务代码,只需要改构建脚本,加上跨架构编译的参数就行。我以最常用的 C++ 和 Java 项目为例:

C++ 项目 CMakeLists.txt 修改

只需要加几行架构判断,自动选择对应的编译器和优化参数:

# 架构判断
if(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64")
    # 鲲鹏架构:用毕昇编译器,开启SVE2向量化
    set(CMAKE_C_COMPILER "/opt/bisheng-compiler-2.3.0/bin/clang")
    set(CMAKE_CXX_COMPILER "/opt/bisheng-compiler-2.3.0/bin/clang++")
    add_compile_options(-march=armv8-a+sve2 -O3 -fvectorize)
else()
    # x86架构:用GCC,开启AVX2优化
    set(CMAKE_C_COMPILER "gcc")
    set(CMAKE_CXX_COMPILER "g++")
    add_compile_options(-march=haswell -O3 -mavx2)
endif()

Java 项目 pom.xml 修改

Java 项目更简单,基本不用改,只需要在打包的时候指定平台,用鲲鹏提供的 JDK 就行:

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.8.1</version>
            <configuration>
                <source>11</source>
                <target>11</target>
                <encoding>UTF-8</encoding>
            </configuration>
        </plugin>
    </plugins>
</build>

2.3 流水线接入(Jenkins 一小时搞定)

DevKit 提供了现成的 Jenkins 插件,不用自己写复杂的脚本。我是这么接的:

  1. 在 Jenkins 插件市场安装「Kunpeng DevKit Plugin」
  2. 新建一条流水线,同时添加 x86 和鲲鹏两个构建节点
  3. 构建阶段加一步「DevKit 跨架构构建」,选择对应的构建目标
  4. 构建完成后,自动上传两个版本的包到镜像仓库

我们现在的流水线跑一次大概 15 分钟,同时出两个平台的镜像,直接部署,非常方便。


三、我踩过的 10 个大坑 + 解决方案(都是血泪教训)

这部分是最核心的,每个坑我都踩过,有的查了好几天,大家直接抄解决方案就行。

1. 毕昇编译器版本不对,编译出来的包段错误

问题:装了最新的 3.1.0 版本毕昇编译器,编译 C++ 项目没问题,一运行就段错误,gdb 也查不出问题 解决方案:直接降级到 2.3.0 稳定版,亲测所有项目都能正常编译运行,官方文档里明确写了推荐 2.3.0,我当时没仔细看,踩了大坑。

2. 跨架构编译的时候,第三方依赖 so 库找不到

问题:构建的时候提示error while loading shared libraries: libxxx.so: cannot open shared object file 解决方案:不要用系统默认的 yum 源,用鲲鹏官方的源,大部分依赖都有 aarch64 版本:

# 配置鲲鹏官方源
wget -O /etc/yum.repos.d/kunpeng.repo https://mirrors.huaweicloud.com/kunpeng/archive/kunpeng_yum_repo/kunpeng.repo
yum clean all && yum makecache

实在找不到的依赖,用 DevKit 的依赖迁移工具,自动转成 aarch64 版本,别自己编译,太费时间。

3. NUMA 配置不对,性能比 x86 慢 30%

问题:服务迁到鲲鹏之后,压测 QPS 比 x86 低 30%,CPU 利用率只有 50% 解决方案:鲲鹏是 NUMA 架构,一定要绑定 CPU 核心,不要跨 NUMA 节点调度,我们用 numactl 绑定之后,QPS 直接涨了 40%:

# 查看NUMA节点
numactl --hardware
# 启动服务的时候绑定到同一个NUMA节点
numactl --cpunodebind=0 --membind=0 java -jar xxx.jar

4. 透明大页没关,Redis / 数据库偶尔卡顿

问题:Redis 偶尔会出现几百毫秒的卡顿,查了很久没找到原因 解决方案:鲲鹏平台一定要关闭透明大页,对内存密集型应用提升非常大:

# 临时关闭
echo never > /sys/kernel/mm/transparent_hugepage/enabled
echo never > /sys/kernel/mm/transparent_hugepage/defrag
# 永久生效,写到/etc/rc.local里

我们关了之后,Redis 的平均响应时间从 8ms 降到了 2ms,再也没出现过卡顿。

5. ARM 弱内存模型导致的无锁队列偶发崩溃

问题:x86 上跑的好好的无锁队列,迁到鲲鹏上偶尔会出现崩溃,概率大概万分之一 解决方案:ARM 是弱内存模型,指令会重排序,无锁代码里要加内存屏障:

// 原来的x86代码,没有内存屏障
queue->tail = next;
// 鲲鹏平台要加内存屏障
__atomic_thread_fence(__ATOMIC_ACQ_REL);
queue->tail = next;

这个问题我查了整整两天,头发都掉了一把,大家写无锁代码一定要注意。

6. 向量化没开,C 程序性能暴跌 40%

问题:C 写的计算程序,在鲲鹏上跑比 x86 慢 40% 解决方案:毕昇编译器默认不会开最高级别的向量化,要手动加参数:

# 编译的时候加这三个参数,开启SVE2向量化
clang -march=armv8-a+sve2 -O3 -fvectorize test.c -o test

加了之后,我们的计算程序性能直接反超 x86,比原来快了 15%。

7. Jenkins 流水线接入的时候,权限不足报错

问题:流水线跑构建的时候,提示权限不足,无法访问 DevKit 工具 解决方案:给 Jenkins 用户加 DevKit 的执行权限:

chmod -R 755 /opt/kunpeng/devkit
usermod -aG root jenkins

别给 Jenkins 单独建用户,直接用 root 跑构建,省很多事。

8. 没装 jemalloc,Java 应用内存泄漏

问题:Java 应用跑几天,内存就会涨到 80% 以上,jstack 查不到泄漏 解决方案:鲲鹏平台 Java 应用一定要装 jemalloc,替换默认的内存分配器:

yum install -y jemalloc
echo "export LD_PRELOAD=/usr/lib64/libjemalloc.so.2" >> /etc/profile

装了之后,内存占用稳定在 40% 左右,再也没出现过泄漏的问题。

9. 大页内存配置不对,MySQL 性能上不去

问题:MySQL 压测 TPS 只有 2000,远低于预期 解决方案:配置 1G 大页内存,MySQL 性能直接翻倍:

# 配置10个1G大页
echo "vm.nr_hugepages = 10" >> /etc/sysctl.conf
sysctl -p
# my.cnf里加配置
large-pages = ON

我们配置完之后,MySQL TPS 从 2000 涨到了 4500,提升了 125%。

10. 代码里有 x86 专属的汇编指令,编译失败

问题:老代码里有 x86 的 AVX2 汇编指令,鲲鹏上编译失败 解决方案:用宏判断架构,不同架构用不同的实现:

#ifdef __x86_64__
// x86用AVX2实现
#include <immintrin.h>
#elif __aarch64__
// 鲲鹏用NEON/SVE2实现
#include <arm_neon.h>
#endif

DevKit 有个迁移扫描工具,能自动扫出代码里的 x86 专属指令,跑一下就全出来了,不用自己找。


四、性能调优:我们的服务 QPS 从 1200 到 2100 的实战技巧

我们的核心 Java 服务,刚迁到鲲鹏的时候,压测 QPS 只有 1200,比 x86 的 1500 还低,经过一周的调优,最后做到了 2100 QPS,比 x86 还快 40%,核心就是这几个操作:

  1. 必装 jemalloc,替换默认内存分配器,内存占用降 30%,性能升 20%
  2. 关闭透明大页,所有服务都要关,尤其是数据库和缓存
  3. NUMA 绑定,所有服务都绑定到同一个 NUMA 节点,不要跨节点
  4. 用毕昇 JDK,不要用 OpenJDK,毕昇 JDK 对鲲鹏有专门优化,性能升 15%
  5. 开启 KAE 加速引擎,加密解密、压缩解压这些操作,用硬件加速,性能升 30%
  6. 调整 TCP 参数,openEuler 默认的 TCP 参数不适合高并发,改成高并发配置

给大家一个我整理的通用优化脚本,直接跑就行:

# 1. 关闭透明大页
echo never > /sys/kernel/mm/transparent_hugepage/enabled
echo never > /sys/kernel/mm/transparent_hugepage/defrag

# 2. 安装jemalloc
yum install -y jemalloc
echo "export LD_PRELOAD=/usr/lib64/libjemalloc.so.2" >> /etc/profile

# 3. TCP高并发参数
echo "net.core.somaxconn = 65535" >> /etc/sysctl.conf
echo "net.ipv4.tcp_syncookies = 1" >> /etc/sysctl.conf
echo "net.ipv4.tcp_tw_reuse = 1" >> /etc/sysctl.conf
echo "net.ipv4.tcp_fin_timeout = 30" >> /etc/sysctl.conf
sysctl -p

五、给新手的几点建议

  1. 严格按官方推荐的版本来,不要用最新的开发版,稳定大于一切,我已经踩过无数版本的坑了
  2. 遇到问题先去鲲鹏社区搜,90% 的问题都有现成的解决方案,不要自己瞎折腾,很多问题官方早就踩过了
  3. 不要上来就全量迁,先拿一个非核心服务跑通全流程,踩完坑再迁核心服务,我们一开始就是直接迁核心服务,出了很多问题
  4. 一定要做压测,迁完之后一定要压测,很多问题只有高并发下才会出现,不要直接上线
  5. 多和社区的人交流,鲲鹏社区的官方技术人员响应很快,我好几次遇到解决不了的问题,发帖当天就有官方的人联系我帮忙解决

最后说两句

说实话,同辕开发刚开始搭的时候确实麻烦,要踩很多坑,但是搭好之后真的太香了。我们现在 12 个服务,全部都是同辕开发,一套代码,一条流水线,发版一次搞定,开发和运维的工作量都减了一半。

国产化的路确实不好走,会遇到各种各样的问题,但是只要慢慢踩坑,慢慢优化,最后会发现鲲鹏的性能真的不比 x86 差,甚至很多场景下还要更好。

如果大家在同辕开发的过程中遇到什么问题,欢迎在评论区留言,我看到都会回复,大家一起交流,少踩坑。

收藏举报
Level 1
0
帖子
0
粉丝
0
获赞