ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

CentOS 7升级glibc到2.28避坑指南:编译安装与patchelf配置

CentOS 7升级glibc到2.28避坑指南:编译安装与patchelf配置 CentOS 7 升级 glibc 到 2.28这个需求最近问的人特别多。我自己在做一些新环境部署时也踩过一整轮坑起因其实很简单系统自带的 glibc 版本停留在 2.17好多新编译的二进制工具在安装或启动时直接报GLIBC_2.28 not found比如 .NET 8 运行时、新版 Node.js、部分数据库客户端甚至某些 Docker 镜像里的程序也会出现这种兼容性问题。CentOS 7 官方源又迟迟不更新这个基础库逼得你只能手动处理。这篇文章我会把我实际操作中从评估、备份、编译、安装到验证的完整过程写出来包括遇到的各种报错和对应的解决思路。你可以把它当成一份可以直接照着操作的记录而不是那种只讲概念不说步骤的教程。如果你手头正有一台 CentOS 7 机器跑着老服务同时又想让新软件能正常工作这篇应该能帮你少走不少弯路。1. 升级前必须想清楚的三件事1.1 为什么老版本这么难缠CentOS 7 默认提供的 glibc 版本是 2.17对应的动态链接器是/lib64/ld-linux-x86-64.so.2。glibc 是所有 C/C 程序运行时的基础依赖基本上你运行的任何命令从ls、bash到nginx、mysql底层都离不开它。你要是直接替换掉系统原有的 libc.so.6不是开玩笑系统会在很短时间内崩溃因为几乎所有动态链接程序都会因为这个操作出现问题甚至连ls这种基本命令都无法正常运行。新版本软件要求 glibc 2.28最常见的场景有两个一个是微软的 .NET 8 运行时官方安装脚本检查到 glibc 版本不够就直接拒绝执行另外是一些用新版 GCC 编译的第三方工具包它们在编译时把GLIBC_2.28作为最低符号版本写进了二进制文件里老系统自然跑不了。1.2 风险等级评估这是在给系统做“换心脏”手术我建议你在动手之前先把这次操作的定位搞清楚。CentOS 7 官方是不建议手动升级 glibc 的Red Hat 的支持策略里也明确说了不会单独推送这个基础库的大版本升级。原因很简单glibc 不只是动态库它还负责进程启动、内存管理、文件操作、网络解析这些最底层的能力。整个系统里的二进制程序绝大多数都是针对 glibc 2.17 编译和测试的你贸然换到 2.28表面上看只是版本号变了实际上会影响很多隐性行为。所以我的操作策略是这样的不是在系统原有的/lib64里直接覆盖升级而是把 glibc 2.28 编译安装到独立的目录比如/opt/glibc-2.28然后只对需要的程序进行调整让它们优先使用新版本。这样系统里其余的工具和守护进程仍然用原来的 2.17风险会小很多也不会出现重启后发现 sshd 起不来的情况。你可以理解成给一台老发动机配了一个新油泵但你并没有把整个发动机拆了重装。1.3 先做这些备份动作再动手升级 glibc 属于高风险操作备份动作必须提前做好别偷懒。我自己习惯按这个顺序处理云服务器先做快照或者整机镜像备份确保万一操作失误能回滚。物理服务器如果有条件备份/etc、/opt、/usr/local等关键目录里自己改动过的内容。记住当前内核版本用uname -r查看后续编译需要知道内核相对新还是旧。把当前所有关键运行服务的配置备份一份万一需要重启服务还能用上。这里有个很多人忽略的细节升级 glibc 过程中如果出现意外你重启机器后 sshd 可能连不上原本开机启动的服务也可能起不来。所以我强烈建议在做这个操作前至少确保你有物理控制台或者云控制台的 VNC 访问权限万一 SSH 断了还能从控制台进去救。2. 编译工具链准备先解决 gcc 和 make 版本问题2.1 安装必要依赖一步都不能少编译 glibc 对工具链有要求CentOS 7 自带的 gcc 4.8.5 版本太老直接编译 glibc 2.28 大概率会失败。我当时第一次尝试就是直接用系统默认 gcc结果 configure 阶段就报错提示gcc is too old或者make is too old。所以第一步是先把编译基础环境准备好。依赖清单大致如下gcc、gcc-c建议版本高一点4.9 以上才稳6.x、7.x 更好makeglibc 2.28 需要 make 4.0 及以上系统默认的 3.82 不够用bison、flex编译语法分析器相关模块要用python不是必须但某些测试脚本需要texinfo生成 info 文档时要用到kernel-headers建议和当前系统内核版本保持对应另外就是glibc-devel、glibc-static这些基础开发库。CentOS 7 默认源里没有高版本 gcc我选择安装 Software Collections 仓库里的 devtoolset。这样可以在不影响系统默认 gcc 的情况下单独启用新版本工具链。# 添加 SCL 仓库 yum install -y centos-release-scl # 安装 devtoolset-7里面的 gcc 是 7.x yum install -y devtoolset-7-gcc devtoolset-7-gcc-c devtoolset-7-make # 在当前终端启用新工具链 scl enable devtoolset-7 bash启用后执行gcc --version如果输出显示 7.x说明工具链没问题了。如果你想要 8 或 9 版本也可以装 devtoolset-8 或 devtoolset-9命名规律都一样安装完启用即可。注意启用新的 bash 会话只在当前终端窗口有效重新登录后还要再执行一次scl enable除非你把它写进/etc/profile.d/下的脚本里。2.2 检查当前系统和软件版本做到心里有数在编译之前我习惯把所有版本信息先收集一遍方便后续排查问题。可以在终端执行这几条命令cat /etc/redhat-release uname -r gcc --version make --version ldd --versionldd --version的输出会直接显示 glibc 版本号。我当时看到的是ldd (GNU libc) 2.17这就是所有后续问题的根源。另外还要注意make版本glibc 2.28 对 make 的版本有限制老版本在编译过程中会出现Makefile:303: recipe for target ... failed这类不明不白的错误排查起来很费神所以直接装新的最省事。内核这块也要留意。glibc 2.28 源码里对内核版本有最低要求不过你是在 CentOS 7 上编译运行内核基本都是 3.10 以上满足条件不需要额外操心。如果你是在容器或者特殊内核的机器上操作建议 configure 时加上--enable-kernel3.10这个参数明确告诉 glibc 支持的内核版本。2.3 源码下载与文件解压要注意的细节glibc 源码可以从 GNU 官方镜像下载也可以从国内的镜像站拉速度会好很多。我常用清华的镜像站地址是https://mirrors.tuna.tsinghua.edu.cn/gnu/glibc/glibc-2.28.tar.gz。如果你需要验证文件完整性官方提供的 SHA256 校验值可以和下载后本地计算的结果比对。下载和解压操作如下cd /usr/local/src wget https://mirrors.tuna.tsinghua.edu.cn/gnu/glibc/glibc-2.28.tar.gz tar -xzf glibc-2.28.tar.gz cd glibc-2.28就算你用别的镜像站下载记得选.tar.gz或.tar.xz别下载.diff.gz补丁文件。解压后不要直接在源码目录里跑 configure会在后续 build 时出现各种configure: error或者.d文件缺失的情况。这是 glibc 官方要求强烈建议单独建一个 build 目录这样生成的中间文件和源码目录隔离出问题可以直接删掉重来。3. 编译并安装 glibc 2.28完整实操流程3.1 建立独立的 build 目录我一直习惯这么干在源码目录外面建一个 build 文件夹比如/usr/local/src/glibc-build。这样源码目录保持纯净随时可以从头再来。我的操作如下mkdir -p /usr/local/src/glibc-build cd /usr/local/src/glibc-build如果你对磁盘空间有顾虑编译产生的中间文件会有 1GB 到 2GB提前看一下/usr/local/src所在分区的剩余空间别编译到一半发现磁盘满了那体验真的很差。3.2 configure 配置参数选择在 build 目录下执行 configure注意源码路径指向刚才解压出来的 glibc-2.28 目录。../glibc-2.28/configure \ --prefix/opt/glibc-2.28 \ --disable-werror \ --enable-kernel3.10说明一下几个参数的作用--prefix指定安装目录。我刻意选/opt/glibc-2.28是为了不污染系统原有的/usr和/lib64。这样后续可以用动态链接器或 rpath 来控制特定程序使用新库。--disable-werror把编译告警当作错误处理的选项先关掉因为 glibc 源码在较老的 gcc 版本下编译容易出现警告加上这个参数能减少不必要的失败。--enable-kernel3.10告诉 glibc 最低支持的内核版本CentOS 7 内核是 3.10加这个可以规避一些内核兼容性检查问题。如果 configure 卡在某一步慢吞吞不动不用担心它要检测的东西比较多等一两分钟很正常。configure 结束时如果没有任何 error 提示就可以进入下一步了。3.3 make 编译过程与报错处理编译 glibc 最耗时也最容易因为各种小问题中断。先说明我用的命令make -j$(nproc)如果你机器核心数很多比如 16 核以上可以考虑-j8或-j16不建议直接-j64这种极端值因为 glibc 编译过程中某些步骤对并行任务比较敏感并行数过大会偶发链接阶段错误。我在一台 8 核虚拟机上用-j8整个编译过程大约花了 20 多分钟。编译过程中常见的报错我整理了一下第一个是configure: error: *** These critical programs are missing or too old: gcc make。这个基本就是工具链版本问题确认你启用了 devtoolset并且当前 shell 里gcc、make的路径指向新版本。第二个是/usr/include/gnu/stubs-32.h: No such file or directory。这个是缺少 32 位相关的开发文件。CentOS 7 64 位系统上编译 glibc偶尔会用到 32 位编译相关的头文件。安装对应包解决yum install -y glibc-devel.i686 libgcc.i686 libstdc-devel.i686第三个是链接阶段报cannot find -lnss_files之类的错误一般是某些系统库版本或符号缺失多半还是依赖没装完整。把yum groupinstall Development Tools装一遍再检查 kernel-devel 是否装上基本能解决。编译结束前会有一些测试相关的提示比如make tests没有执行某几个测试因为容器环境或权限被跳过这些不影响最终安装。当时我为了节省时间没有额外跑完整测试套件直接make install。如果你的场景对稳定性极其敏感可以考虑在安装后对关键二进制做冒烟测试而不是全量跑测试。3.4 make install 安装到指定目录编译通过后执行安装make install由于我们指定了--prefix/opt/glibc-2.28安装动作是把编译产物复制到/opt/glibc-2.28不会覆盖系统自带的/usr/lib64/libc.so.6。安装完成后先验证一下这个目录里的内容ls /opt/glibc-2.28/lib/里面的ld-linux-x86-64.so.2和libc.so.6是新版运行库可以直接用这个动态链接器执行程序。验证命令如下/opt/glibc-2.28/lib/ld-linux-x86-64.so.2 --version如果输出里能看到2.28字样说明新库安装成功。这里有个小细节/opt/glibc-2.28/lib下的libc.so.6是一个符号链接指向同目录下的libc-2.28.so。后续设置 rpath 或 patchelf 时要注意链接器找动态库的搜索顺序。4. 让程序真正用上新版 glibc两种可行方案编译安装只是第一步接下来要让目标程序跑起来。我经常看到有人在这里犯迷糊以为只要make install完成系统里所有程序就自动用上新 glibc 了这是不对的。系统里大部分命令通过默认动态链接器加载还是指向老的 2.17只有你明确指定去用/opt/glibc-2.28/lib/ld-linux-x86-64.so.2的程序才会用新库。4.1 方案一用 patchelf 修改目标程序如果你只是想解决某一个具体程序的GLIBC_2.28 not found问题比如某个编译好的二进制工具或者 .NET 运行时的某个 native 库用 patchelf 是最精准的做法。它可以直接修改 ELF 文件里的解释器路径和 rpath让程序启动时用新版动态链接器并从新目录加载库文件。安装 patchelfyum install -y patchelf修改示例patchelf --set-interpreter /opt/glibc-2.28/lib/ld-linux-x86-64.so.2 \ --set-rpath /opt/glibc-2.28/lib:$ORIGIN:/usr/lib64 \ /usr/local/bin/your-binary这里解释一下每个参数--set-interpreter把可执行文件里指定的动态链接器改成新版路径这是关键的切换动作。--set-rpath设置运行时动态库搜索路径$ORIGIN表示可执行文件所在目录/usr/lib64是兜底的系统库路径避免某些基础库找不到。执行后直接运行这个二进制文件它就会使用/opt/glibc-2.28/lib/ld-linux-x86-64.so.2。修改前建议先备份原文件因为 patchelf 的修改是不可逆的如果目标程序带有强校验或签名修改后可能无法启动。4.2 方案二通过 LD_LIBRARY_PATH 临时指定如果你想临时测试某个程序是否能跑了可以设置环境变量。不过这里我要说个重点LD_LIBRARY_PATH/opt/glibc-2.28/lib这种方法要谨慎使用因为这会强制目录下的所有程序尝试用新版 glibc包括ls、bash这类系统基础命令。之前有朋友直接 export 这个变量结果整个终端几乎瘫痪很多命令执行就报段错误或者版本冲突。这是因为新老 glibc 混用会导致 glibc 内部状态不一致进程初始化时直接崩掉。实际可用的一种方式是这样的用新动态链接器来启动你想运行的程序/opt/glibc-2.28/lib/ld-linux-x86-64.so.2 \ --library-path /opt/glibc-2.28/lib:/usr/lib64 \ /path/to/your-binary这样只有当前这个程序使用新库系统其它命令不受影响。如果你觉得每次命令太长可以写个小脚本封装一下。但是请注意如果目标程序还依赖系统里其它非 glibc 的动态库而这些库是基于老 glibc 编译的那么新老库的符号版本混在一起仍然有较大概率报version GLIBC_2.17 not found这种奇怪的错。我自己的经验是优先用 patchelf 方案把老库路径也放到 rpath 里兜底比 LD_LIBRARY_PATH 整体覆盖稳定得多。4.3 验证升级是否成功别只盯着版本号验证方式不要只停留在ldd --version上。因为ldd --version显示的可能是系统默认 glibc 的版本。更可靠的验证方法是看动态链接器本身的版本/opt/glibc-2.28/lib/ld-linux-x86-64.so.2 --version另外在修改过的程序上执行ldd /usr/local/bin/your-binary按正常逻辑会看到linux-vdso.so.1、libc.so.6 /opt/glibc-2.28/lib/libc.so.6这样的引用路径。如果libc.so.6仍然指向/lib64/libc.so.6说明 rpath 没设对或者程序没有真正被修改需要检查一下 patchelf 的参数。5. 常见报错与问题排查这部分我单独拿出来写是因为整个过程中我踩到的坑集中在几个典型报错上。你如果也遇到可以直接对照排查节省很多时间。5.1 报错速查表报错信息可能原因解决思路GLIBC_2.28 not found程序依赖新版 glibc但系统默认库是 2.17用 patchelf 或/opt/glibc-2.28/lib/ld-linux-x86-64.so.2启动configure: error: gcc is too oldgcc 版本低于 4.9安装 devtoolset 并启用新工具链make: error: jobserver unavailablemake 版本太低或并行参数问题升级 make 到 4.0 以上控制-j并行数/usr/include/gnu/stubs-32.h: No such file缺少 32 位开发文件yum install -y glibc-devel.i686 libgcc.i686cannot find -lnss_files系统库路径不对或开发库缺失检查glibc-devel、libnss相关包是否安装程序启动时 segmentation fault新老 glibc 混合动态库加载导致状态冲突修改 rpath 确保所有相关库都从同一个 glibc 目录加载version GLIBC_PRIVATE not found库文件引用关系混乱避免直接在系统/lib64里覆盖安装使用独立目录5.2 编译时内存不足怎么办glibc 编译时比较吃内存如果是低配服务器比如 1GB 内存加 1 核 CPUmake -j1也可能因为内存不足失败。我遇到过的现象是编译进程报internal compiler error: Killed或者直接被系统 OOM Killer 杀死。解决思路有几个临时增大 swap 文件比如创建一个 2GB 的 swapfile编译完成后再清理。降低并行度make -j1虽然慢但能减少同时吃内存的进程数。不要同时跑很多其他服务编译时尽量让服务器处于空闲状态否则内存竞争会加剧。如果你实在不想等可以在 make 时只编译目标库不跑测试减少资源开销。5.3 glibc 安装目录被误删或者配置错乱有时候你改了 rpath 或者 patchelf 之后发现某个程序启动时报error while loading shared libraries: libc.so.6: cannot open shared object file。这类问题多半是 rpath 里把/opt/glibc-2.28/lib放到了后面而系统默认库路径里找不到对应的符号。解决办法是重新执行 patchelf调整 rpath 顺序把/opt/glibc-2.28/lib放在最前面。还有一种更隐蔽的情况patchelf 之后程序可以启动但某些插件或子进程内部会调用dlopen加载别的 so 文件那些共享库可能依赖旧 glibc 的符号版本。这种动态加载场景下单独修改主程序还不够需要排查具体是哪个 so 文件出了问题再对那个文件做同样处理。工作量会大一些但至少比整机升级要精细很多。5.4 升级后无法远程登录或者基础命令报错如果你没有按照我前面说的方法而是直接在/lib64下做了覆盖替换那很有可能会遇到 sshd 连不上或者更常见的ls: error while loading shared libraries这种基础命令都失效的情况。此时不要重启机器因为重启之后可能连系统都进不去如果是在当前 shell 里直接操作导致会话还活着可以尝试把备份的libc.so.6拷贝回/lib64。这就是为什么我一直反复强调备份的重要性。如果连 shell 都进不去只能用救援模式或者通过 VNC 控制台登录然后在单用户模式下从原安装介质或另一台机器上恢复libc.so.6。这个过程非常折腾所以最正确的做法还是从一开始就不要覆盖系统库。6. 我对这个方案的最终看法如果你问我会不会在所有 CentOS 7 的机器上都这么干我的回答是不会。glibc 升级属于非常底层的系统变动即便你用了独立目录加 patchelf 的方式也只是把风险控制在一定范围内并不能完全消除兼容性隐患。我建议把这种方案定位成临时解决方案比如你手上有一批老机器短期内没有迁移计划但需要在一两个新版本程序用它来解决问题很顺手。如果条件允许长期来看我更推荐另一个方向要么使用 Docker 这类容器技术把新版本软件跑在 CentOS Stream、Rocky Linux 或 AlmaLinux 等新系统的容器里要么直接规划系统迁移把生产环境从 CentOS 7 迁到更新的发行版。CentOS 7 本身的生命周期已经进入维护阶段与其不断在老系统上打补丁不如把服务平滑迁移到受支持的新平台上这样后面维护省心得多。回到 glibc 升级本身整个过程说难也不难最核心的就是两点第一千万别直接覆盖系统的/lib64/libc.so.6第二一定要用独立目录安装再通过动态链接器、rpath 或者 patchelf 精准控制目标程序的动态库路径。如果你把这两点记牢了再配合前面记录的编译步骤和错误排查基本不会翻车。希望这篇记录能帮上你的忙。
返回列表