首页 / 资讯中心 / 文章详情

在麒麟 V10 + 鲲鹏 920 上离线部署 bge-m3 Embedding向量服务:一次完整的踩坑实录

在麒麟 V10 + 鲲鹏 920 上离线部署 bge-m3 Embedding向量服务:一次完整的踩坑实录 ★ FEATURED ARTICLE
环境Kylin V10 SP3 / aarch64 / 鲲鹏 920 × 4 核 / 14GB 内存 / 无外网 / glibc 2.28 目标CPU 上跑通 bge-m3 Embedding 服务对外提供 OpenAI 兼容的/v1/embeddings接口✅ 结果llama.cpp 自编译 bge-m3 Q4_K_M单条 380ms吞吐 4 条/秒常驻内存 1.9GB一、背景与约束业务需要在内网做语义检索FAQ 去重、相似问题匹配选型 bge-m31024 维、中英双语效果好、支持长文本。但目标服务器是内网的麒麟 V10 鲲鹏机没有外网访问权限所有软件必须离线带入。这个约束直接决定了整个技术路线不能apt install不能git clone make一切都要在别处编译好、打包好、传进去。二、三次失败的尝试坑 1官方预编译包跑不起来llama.cpp 官方提供ubuntu-arm64预编译包下载即用看似最省事。但在麒麟上报错version GLIBC_2.33 not found (required by llama-server) version GLIBC_2.38 not found原因Ubuntu 24.04 构建环境链接了新版 glibc 符号麒麟 V10 只有 glibc 2.28。预编译包的原则是“构建环境的 glibc ≤ 运行环境的 glibc”跨发行版直接拿来用大概率死在这。教训无外网 老发行版环境永远自己编译别信任何预编译产物。坑 2手动装 gcc-9 的 .deb 全军覆没为了满足 llama.cpp 的编译要求CMake 3.14、C17第一反应是离线下载 gcc-9 的 deb 包带过去。从镜像站逐个下载了 21 个包gcc-9、libstdc-9-dev、libisl23……结果dpkg -i时依赖链雪崩。原因那些 9.3.0-22 版本的包是Debian 11 (bullseye) 构建的同样要求 glibc ≥ 2.29。而且 bullseye 中的 libgcc-s1、libstdc6 等运行库实际由 gcc-10 源码包构建想凑齐 gcc-9 的依赖要跨好几个源码池去挖工作量大且依然绕不开 glibc 门槛。教训给老 glibc 环境找编译器应该找与目标系统同源的包——即 Debian 10 (buster) 自己的 gcc-8而不是更新的 gcc-9。坑 3snapshot.debian.org 把 apt 卡死即便在匹配的 Debian 10 容器里装依赖最初配置的snapshot.debian.org源限速严重几十 KB/s一个apt update能挂半小时不动。解决buster 已 EOL换用国内镜像的归档源如mirrors.nju.edu.cn/debian-archive速度直接到 MB/s 级。国内做老版本 Debian 离线准备时debian-archive 的国内镜像是首选。三、正确的技术路线3.1 用 Docker 做同源编译环境Mac 上跑一个debian:10容器与目标系统同为 glibc 2.28 的 buster在容器内编译 aarch64 目标产物。因为容器和目标机同为 aarch64 同版本 glibc这不是严格意义的交叉编译而是“模拟目标环境编译”——最不容易踩兼容坑的方式。docker run -it -v ~/llama.cpp:/workspace debian:10 # 容器内换 debian-archive 国内镜像源 echo deb [check-valid-untilno] http://mirrors.nju.edu.cn/debian-archive/debian buster main \ /etc/apt/sources.list apt-get update apt-get install -y build-essential libopenblas-dev git编译器直接用 buster 自带的gcc 8.3——放弃 gcc-9 之后发现它完全够用。3.2 CMake 版本问题llama.cpp 要求 CMake ≥ 3.14buster 自带 3.13。提前准备好 CMake 3.20 的 aarch64 离线包带进容器解压即可不折腾。3.3 编译配置为鲲鹏定制两个关键决策cmake -B build -DCMAKE_BUILD_TYPERelease \ -DGGML_NATIVEOFF \ -DCMAKE_C_FLAGS-marcharmv8.2-afp16 \ -DCMAKE_CXX_FLAGS-marcharmv8.2-afp16 \ -DLLAMA_CURLOFF -DLLAMA_BUILD_TESTSOFF \ -DGGML_OPENMPONGGML_NATIVEOFF必开默认会按编译机的 CPU 特性生成指令。在 Apple M4 上编译再拿到鲲鹏上跑直接 SIGILL 崩溃-marcharmv8.2-afp16鲲鹏 920 是 armv8.2 核心带 fp16这个档位既启用 NEON fp16 优化又保证兼容。3.4 gcc 8 的两个编译障碍及解法障碍 1NEON x2/x4 intrinsic 缺失。ggml 的代码里用了vld1q_s8_x4、vld1q_u8_x4等四寄存器加载指令这些 intrinsic 需要 gcc 9。报错是编译期的 undeclared而非运行期问题。解法是给ggml-cpu-impl.h打补丁把手写 fallback 塞进 ggml 的宏抽象层ggml 本来就为兼容旧编译器预留了ggml_vld1q_*宏只需在 gcc 8 分支提供实现// 用连续的 vld1q_s8 组合模拟 x4 加载语义等价 static inline int8x16x4_t ggml_vld1q_s8_x4_impl(const int8_t *p) { int8x16x4_t r; r.val[0] vld1q_s8(p); r.val[1] vld1q_s8(p 16); r.val[2] vld1q_s8(p 32); r.val[3] vld1q_s8(p 48); return r; } #define ggml_vld1q_s8_x4 ggml_vld1q_s8_x4_impl // s16_x2 / u8_x2 / u8_x4 / s8_x2 同理障碍 2std::filesystem链接失败。gcc 8 中 filesystem 是独立库且-lstdcfs必须出现在链接命令末尾符号由后端的 libggml 引入。常规的CMAKE_EXE_LINKER_FLAGS会把库放在前段导致顺序错误最终用这个保证追加到末尾cmake -B build -DCMAKE_CXX_STANDARD_LIBRARIES-lstdcfs这两处是本次部署最“硬”的技术点——网上关于 llama.cpp gcc 8 的资料很少报错信息也不会告诉你该往宏抽象层下手。3.5 自包含部署包编译产物 依赖库 模型 启动脚本打包成完全自包含的 tar.gz400MB目标机解压即跑、零安装bge-m3-kylin-deploy/ ├── start.sh # 启动脚本见下 ├── bin/ # llama-server libllama/libggml 系列 .so ├── lib/ # libstdc.so.6.0.25 / libgcc_s / libgomp │ # 从容器内拷出防目标机版本不齐 └── model/bge-m3-Q4_K_M.gguf # 438MB 量化模型启动脚本的核心是把两个库目录都挂进搜索路径ggml 系列 .so 在 bin/ 下这是个容易漏的坑export LD_LIBRARY_PATH$(pwd)/bin:$(pwd)/lib:$LD_LIBRARY_PATH exec ./bin/llama-server -m ./model/bge-m3-Q4_K_M.gguf \ --embedding --pooling cls --host 0.0.0.0 --port 8000 \ --threads 4 --ctx-size 8192 --batch-size 64 --parallel 2 --mlock打包前务必核验 glibc 符号上限readelf 提取所有 GLIBC_ 版本取最大值确认 ≤ 目标机的 2.28readelf -V build/bin/llama-server | grep -oE GLIBC_[0-9.] | sort -Vu | tail -1 # 输出 GLIBC_2.27 ≤ 2.28 ✓四、离线传输堡垒机环境下的“缝隙”服务器仅有天玥运维安全网关一个入口直连端口全被 ACL 过滤堡垒机又禁止 SSH-L端口转发administratively prohibited。但发现一个缝隙网关的本地客户端Bastion会把已建立的 SSH 会话映射为 Mac 上的本地回环端口如127.0.0.1:54023探测该端口的 bannerSSH-2.0-OsmSSH确认后直接对这个本地端口 scp400MB 约 4 分钟传完sha256 校验一致。这个方法的前提是“堡垒机客户端已建立一条活会话”本质是复用网关自己开凿的隧道不涉及绕过策略——但对大量堡垒机管控的离线内网环境来说是比 SFTP 客户端本文场景中网关拉起 FileZilla 一直失败靠谱得多的路径。五、性能实测与调参4 核鲲鹏 920、Q4_K_M 量化、--threads 4指标数值单条请求短文本~380ms批量 10 条合一次请求~2.5s比逐条快 1.5×省的是 HTTP 与调度开销持续吞吐~4 条/秒CPU 物理上限内存常驻~1.9GB模型 450M 双窗口 KV Cachemlock 锁定空闲 CPU≈ 0%推理按需平时不耗调参要点--ctx-size是总量会被--parallel平分。想要“2 并发 × 每窗口 4096”总量要设 8192这是最容易想当然的地方--parallel 2让两个请求同时进两个窗口日志可见id 0/id 1交错处理批量导入之外的并发场景收益明显--mlock把模型钉在物理内存里杜绝 swap 抖动内存充足时建议必加--threads 4与核数持平。与业务应用混部时可下调让核吞吐随之下调——CPU 推理的吞吐本质是核数 × 频率没有免费午餐。六、生产化收尾systemd 托管RestartalwaysRestartSec5替代 nohup实现开机自启和崩溃自愈监听收敛仅本机调用时把--host 0.0.0.0改127.0.0.1相似度阈值bge-m3 余弦相似度经验值——≥0.60 判定相似0.40 过滤中间灰区用 50~100 条真实语料校准接口兼容性红利/v1/embeddings是 OpenAI 兼容格式业务侧直接用现成 OpenAI SDK 改base_url即接入无需手写 HTTP 客户端。七、复盘离线内网 AI 服务部署方法论编译环境与目标系统同源同发行版、同 glibc比任何“高版本编译器 静态链接”的歪招都稳预编译包的 glibc 符号上限是第一检查项readelf -V一条命令避免半天白干EOL 老系统找包先找 debian-archive 的国内镜像别在 snapshot 源上耗时间旧 gcc 编新代码优先找项目的兼容宏抽象层下手ggml 的ggml_vld1q_*就是为此设计的比改业务代码侵入性小
阅读完成 · 觉得有帮助?
咨询建站