centos7升级node镜像到22版本遇到的问题

背景

最近需要用到 RAG,使用了 LlamaIndex,这个库需要 Node 版本 20+。我们之前的 Node 镜像的版本是 18 的,因此需要升级。
考虑到目前 Node 的最新稳定版本是 22,所以打算直接升级到 22。

由于镜像的制作环境是公司的内网,不能联公网,因此只能选择下载源码包,然后在内网环境下编译安装。

流程

Node22 需要 g++ 10+,之前我们是 g++ 8,所以需要先升级 g++。
然后再编译安装 Node22。

升级 g++

参考这个文章

1
2
3
4
yum install gcc gcc-c++ -y
yum install -y centos-release-scl
yum install -y devtoolset-7-gcc devtoolset-7-gcc-c++
source /opt/rh/devtoolset-7/enable

但是我们公司是在内网,所以需要先下载好这些包,然后再安装。

注意:gcc、g++的编译巨慢无比,在内网开发机(配置很差)上,花了几个小时,下次注意预留足够的时间。

(TODO)检查测试

确保 GCC(GNU Compiler Collection)的路径正确是编译软件和开发应用程序的关键步骤。以下是一些方法来验证和确保 GCC 路径正确:

  1. 检查 GCC 是否已安装
    首先,检查 GCC 是否已经安装在你的系统上:

bash
gcc –version
如果已安装,该命令将显示 GCC 的版本信息。

  1. 确认 GCC 可执行文件的路径
    使用 which 命令来查找 GCC 可执行文件的路径:

bash
which gcc
这将显示 GCC 可执行文件的完整路径。

  1. 查看环境变量
    检查 PATH 环境变量是否包含了 GCC 的路径。你可以通过以下命令来查看 PATH:

bash
echo $PATH
确保 GCC 的安装目录(通常是 /usr/bin、/usr/local/bin 或其他自定义目录)包含在 PATH 中。

  1. 手动指定 GCC 路径
    如果你知道 GCC 的安装路径,但 PATH 环境变量没有正确设置,你可以手动指定路径来使用 GCC。例如:

bash
/usr/local/bin/gcc –version 5. 更新环境变量
如果 GCC 的路径不在 PATH 中,你可以更新 PATH 环境变量。在你的 shell 配置文件(如 ~/.bashrc 或 ~/.bash_profile)中添加以下行:

bash
export PATH=/usr/local/bin:$PATH
然后,重新加载配置文件:

bash
source ~/.bashrc 6. 使用模块化环境(如 module 命令)
在一些系统上,你可以使用模块化环境来加载 GCC 模块,这将自动设置正确的环境变量:

bash
module load gcc 7. 检查 GCC 配置
如果你从源代码编译安装了 GCC,确保在编译过程中没有错误,并且 GCC 被安装到了预期的目录。

  1. 重新安装 GCC
    如果路径问题仍然存在,考虑重新安装 GCC。使用系统的包管理器或从源代码编译安装。

  2. 检查符号链接
    确保没有错误的符号链接指向旧的或错误的 GCC 版本。你可以使用 ls -l 来检查链接:

bash
ls -l /usr/bin/gcc
通过这些步骤,你可以确保 GCC 的路径正确,从而避免在编译过程中遇到问题。如果你在设置路径时遇到问题,可能需要查看系统日志或寻求社区帮助以获取更具体的指导。

安装 Node 22

configure 没啥问题,但是 make 的时候报错:

1
2
3
4
5
6

```shell
make -j4

../deps/cares/src/lib/util/ares_rand.c:37:12: fatal error: sys/random.h: No such file or directory

node 有个 issue,根据这个思路排查,看起来是 glibc 版本太老的问题:
https://github.com/nodejs/node/issues/52223

sys/random.h 是干嘛的?

sys/random.h 是一个 Linux 系统下的 C 语言头文件,它提供了访问操作系统提供的加密随机数生成器的接口。这个头文件是在 Linux 内核 3.17 版本中引入的,目的是为了提供一个比传统的 /dev/random 和 /dev/urandom 设备更高效、更易于使用的随机数生成方法。

sys/random.h 在老版本(<2.25)的 glibc 中是没有的

sys/random.h was added in glibc 2.25. glibc 2.28 has been the minimum supported version since Node.js 18, so this isn’t an issue for Node.js.
If you are building Node.js for an older glibc you’ll need to make adjustments accordingly.

所以有 2 个解决方案:

  • 升级 glibc 至 2.25+版本
  • 手动修改源码逻辑,使其不使用 sys/random.h。

如何查看 glibc 版本?

1
2
3
getconf GNU_LIBC_VERSION
// or
ldd --version

我们的 glibc 版本是 2.17

手动改逻辑,跳过使用 sys/random.h

issue 中的建议是修改这个文件:deps/cares/config/linux/ares_config.h,将这个文件中的 #include <sys/random.h> undef 掉。

1
2
3
4
5
6
7
8
#if defined(__GLIBC__) && (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 25))
#undef HAVE_SYS_RANDOM_H
#undef HAVE_GETRANDOM
#else
// 当 GLIBC 版本 >= 2.25 时,确保这些宏被定义
#define HAVE_SYS_RANDOM_H 1
#define HAVE_GETRANDOM 1
#endif

但是我试了不行,而且看报错也不是这个文件,而是 deps/cares/src/lib/util/ares_rand.c

1
2
3
#ifdef #undef HAVE_SYS_RANDOM_H
# include <sys/random.h>
#endif

因此我转为修改 deps/cares/src/lib/util/ares_rand.c 文件,在报错的 37 行上面增加了一行,undef 了HAVE_SYS_RANDOM_H

1
2
3
4
#undef HAVE_SYS_RANDOM_H
#ifdef #undef HAVE_SYS_RANDOM_H
# include <sys/random.h>
#endif

undefined reference to getrandom

(TODO)升级 glibc

glibc2.17 版本升级至 2.31:
https://blog.csdn.net/carefree2005/article/details/117559312

cannot open jobserver /tmp/GMfifo14487r: No such file or directory

似乎是 make 4.4 的问题:
glibc: Add fix for GNU make 4.4 compatibility issue #1861
https://sourceware.org/git/?p=glibc.git;a=commit;h=2d7ed98add14f75041499ac189696c9bd3d757fe

https://www.linuxquestions.org/questions/slackware-14/glibc-2-36-a-4175718346/

WARNING: Backward-incompatibility! GNU Make now uses temporary files in more situations than previous releases. If your build system sets TMPDIR (or TMP or TEMP on Windows) and deletes the contents during the build, or uses restrictive permissions, this may cause problems. You can choose an alternative temporary directory only for use by GNU Make by setting the new MAKE_TMPDIR environment variable before invoking make. Note that this value CANNOT be set inside the makefile, since make needs to find its temporary directory before the makefiles are parsed.

While downgrading to the previous make version solves the problem.

降级为 4.3 解决了

make install 的测试环节报错:/usr/bin/ld: cannot find -lnss_test2

1
2
3
/usr/bin/ld: cannot find -lnss_test2
collect2:error: ld returned 1 exit status
Execution of gcc -B/usr/bin/ failed!

报错的含义

https://superuser.com/questions/1769956/what-exactly-cmake-say-usr-bin-ld-cannot-find-ledit-or-usr-bin-ld-cannot-f

-ledit and -lcurses are loader (ld) parameters. The -l part means “library”.

These messages mean that the edit and curses libraries were not found, which means that the packages containing them were not installed on the computer, but are needed for the link.

You need to find and install the missing packages for the link. They are prerequisites of whatever program you are trying to build.

(精)如何通过报错定位到缺失的库文件?
https://unix.stackexchange.com/questions/506866/what-should-i-install-to-correct-ld-cannot-find-lgbm-and-linput-so-that-i-c

通过这个定位到缺少的库文件:libnss_test2.so
然后 Google 一下就找到这个问题:

https://sourceware.org/bugzilla/show_bug.cgi?id=21911

make clean

尝试 make clean 再重新 configure、make -j4,还是报一样的错。

确认库文件的链接是否正确

尝试确认库文件的链接是否正确:

  1. 检查现有符号链接
    首先,你需要检查当前的符号链接指向哪里。可以使用 ls 命令来查看链接的目标:

bash
ls -l /lib/libm.so
或者,如果你的系统使用 lib64 目录:

bash
ls -l /lib64/libm.so 2. 确认新 glibc 的位置
在尝试更改任何链接之前,确认新安装的 glibc 库文件的位置。通常,它们位于 /usr/lib 或 /lib 目录下。

  1. 更新符号链接
    如果发现 libm.so 或其他库的符号链接指向了旧的 glibc 版本,你可以使用 ln 命令来更新这些链接。首先,删除旧的链接:

bash
sudo rm /lib/libm.so
然后,创建一个新的链接指向新安装的 glibc 版本:

bash
sudo ln -s /usr/lib/libc.so.6 /lib/libm.so
确保 /usr/lib/libc.so.6 是新 glibc 的正确路径。你可能需要根据你的系统和新 glibc 的安装位置调整这个路径。

  1. 检查其他库的链接
    除了 libm.so,还应该检查其他可能受影响的库,如 libc.so、libpthread.so 等,确保它们也都指向新 glibc 的正确版本。

  2. 使用 ldconfig
    在更新了符号链接后,运行 ldconfig 来更新动态链接器的运行时绑定:

bash
sudo ldconfig
这将确保系统在运行时能够正确地找到和使用新的库。

  1. 测试系统
    在更改了库链接后,彻底测试你的系统以确保一切正常运行。如果遇到问题,你可能需要回滚到旧版本的 glibc。

是不是 GNU Binutils 版本过低的问题?

(TODO)直接用 node 的二进制包

node-v22.12.0.tar.gz

(TODO)外网创建镜像

不行,效率太低了,改为外网构建镜像吧!!

CentOS Linux release 7.9.2009 (Core)

1
2
3
4
5
docker pull centos:centos7.9.2009

docker run --name rag -v /Users/leozhou/docker/tmp:/tmp --network=host -tid centos:centos7.9.2009 /bin/bash

docker exec -ti rag /bin/bash

参考之前这个文章:
http://localhost:4000/2020/09/04/制作基于 CentOS7 的 Node 镜像/

yum 报错

我在 docker 容器内,通过 yum 安装软件会爆这种错误:

1
2
3
4
5
6
7
8
9
10
11

perl-Socket-2.010-5.el7.x86_64 FAILED
http://mirrors.aliyun.com/centos/7/os/x86_64/Packages/perl-Socket-2.010-5.el7.x86_64.rpm: [Errno 14] HTTP Error 502 - Bad Gateway

zlib-1.2.7-21.el7_9.x86_64.rpm FAILED
http://mirrors.aliyun.com/centos/7/updates/x86_64/Packages/zlib-1.2.7-21.el7_9.x86_64.rpm: [Errno 14] HTTP Error 502 - Bad Gateway

perl-Socket-2.010-5.el7.x86_64 FAILED
http://mirrors.cloud.aliyuncs.com/centos/7/os/x86_64/Packages/perl-Socket-2.010-5.el7.x86_64.rpm: [Errno 14] HTTP Error 502 - Bad Gateway


我在这个容器里面,直接 curl 是能请求到这些资源的。
发现 ClashXPro 的代理模式改为直连就可以了。

yum 安装软件时,mirrorlist.centos.org 无法解析

安装 devtoolset-7-gcc devtoolset-7-gcc-c++ 时:

1
yum install -y devtoolset-7-gcc devtoolset-7-gcc-c++

报错:

1
Could not retrieve mirrorlist http://mirrorlist.centos.org?arch=x86_64&release=7&repo=sclo-rh error was 14: curl#6 - "Could not resolve host: mirrorlist.centos.org; Unknown error"

原因是 mirrorlist.centos.org 已经没了,得修改 yum 的配置文件。
详见这个问题下的回答:
https://serverfault.com/questions/1161816/mirrorlist-centos-org-no-longer-resolve/1161847#1161847

安装 Python

1
2
Node.js configure: Found Python 2.7.5...
Please use python3.13 or python3.12 or python3.11 or python3.10 or python3.9 or python3.8.
1
2
3
4
5
tar -xvzf Python-3.12.8.tgz
cd Python-3.12.8
./configure --prefix=/usr/local/python
make
make altinstall

报错 1

报错:

1
configure: error: Python requires C99 compatible libm

安装依赖:

1
yum install zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel gdbm-devel db4-devel libpcap-devel xz-devel libffi-devel -y

报错 2

1
2
3
4
5
6
7
8
9
The necessary bits to build these optional modules were not found:
_hashlib _posixshmem _ssl
_tkinter
To find the necessary bits, look in configure.ac and config.log.

Could not build the ssl module!
Python requires a OpenSSL 1.1.1 or newer

Checked 111 modules (31 built-in, 75 shared, 1 n/a on linux-x86_64, 0 disabled, 4 missing, 0 failed on import)
1
yum install openssl tk-devel -y

还是不行,分析是 openssl 版本太低,需要升级。

报错 3

1
2
3
./Modules/socketmodule.c: In function 'socket_gethostbyname_ex':
./Modules/socketmodule.c:5927:22: error: 'data' undeclared (first use in this function)
memset((void *) &data, '\0', sizeof(data));

看起来是新的写法没识别到,那估计是 gcc 版本太低了,得升级。

升级 openssl 1.1.1

参考这个文章

make 报错

1
2
3
4
5
make[1]: \*\*\* [crypto/bio/bf_null.o] Illegal instruction

#每次都不一样
25519_ASM -DPOLY1305_ASM -DOPENSSLDIR="\"/usr/local/openssl/ssl\"" -DENGINESDIR="\"/usr/local/openssl/lib/engines-1.1\"" -DNDEBUG -MMD -MF crypto/pem/pem_pk8.d.tmp -MT crypto/pem/pem_pk8.o -c -o crypto/pem/pem_pk8.o crypto/pem/pem_pk8.c
make[1]: *** [crypto/pem/pem_pk8.o] Illegal instruction

搞了很久,连原因都没搞清楚,然后再某一次 make 突然就成功了,不知道为什么……
make install 成功后,参考这个文章,做了一些后续设置。

然后再回头继续安装 Python。

升级 gcc

参考这个文章

1
2
3
4
5
tar -xvzf gcc-10.5.0.tar.gz
cd gcc-10.5.0
./configure --enable-checking=release --enable-languages=c,c++ --disable-multilib
make
make install

configure 报错:configure: error: Building GCC requires GMP 4.2+, MPFR 3.1.0+ and MPC 0.8.0+.
执行如下命令安装依赖:

1
./contrib/download_prerequisites

执行这个也报错。
曲线救国,先升级 gcc 到 7.

1
2
3
4
yum install gcc gcc-c++ -y
yum install -y centos-release-scl
yum install -y devtoolset-7-gcc devtoolset-7-gcc-c++
source /opt/rh/devtoolset-7/enable

还是不行。
改成 yum 安装依赖

1
yum install  gmp  gmp-devel  mpfr  mpfr-devel  libmpc  libmpc-devel

gcc 可以 configure 了。

make 卡住很久

然后 gcc 的 make,巨慢无比……
关键执行了很久,报错了:

1
2
3
4
5
6
7
8
9
10
11
12
checking whether to enable maintainer-specific portions of Makefiles... no
checking for style of include used by make... GNU
checking for x86_64-pc-linux-gnu-gcc... /tmp/gcc-10.5.0/host-x86_64-pc-linux-gnu/prev-gcc/xgcc -B/tmp/gcc-10.5.0/host-x86_64-pc-linux-gnu/prev-gcc/ -B/usr/local/x86_64-pc-linux-gnu/bin/ -B/usr/local/x86_64-pc-linux-gnu/bin/ -B/usr/local/x86_64-pc-linux-gnu/lib/ -isystem /usr/local/x86_64-pc-linux-gnu/include -isystem /usr/local/x86_64-pc-linux-gnu/sys-include -fno-checking
checking whether the C compiler works... no
configure: error: in `/tmp/gcc-10.5.0/host-x86_64-pc-linux-gnu/lto-plugin':
configure: error: C compiler cannot create executables
See `config.log' for more details
make[2]: *** [configure-stage2-lto-plugin] Error 77
make[2]: Leaving directory `/tmp/gcc-10.5.0'
make[1]: *** [stage2-bubble] Error 2
make[1]: Leaving directory `/tmp/gcc-10.5.0'
make: *** [all] Error 2

试试安装 devtoolset:

1
2
yum install centos-release-scl
yum install devtoolset-10-gcc devtoolset-10-gcc-c++

重新 configure,make,make install。

make 又报错:

1
2
3
4
5
6
configure: creating ./config.status
/tmp/gcc-10.5.0/libgomp/configure: line 18194: 74717 Illegal instruction cat >> $CONFIG_STATUS <<_ACEOF
# 中间一大堆输出

configure: error: write failure creating ./config.status
make[2]: *** [configure-stage1-target-libgomp] Error 1

重新 make,继续报错:

1
2
3
/tmp/gcc-10.5.0/x86_64-pc-linux-gnu/libstdc++-v3/include/ext/pb_ds/hash_policy.hpp:131:10: fatal error: ext/pb_ds/detail/hash_fn/direct_mask_range_hashing_imp.hpp: No such file or directory
131 | #include <ext/pb_ds/detail/hash_fn/direct_mask_range_hashing_imp.hpp>
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

(最终成功版)直接用 node 的二进制包

核心:升级 gcc + 使用 node 的二进制包

从官网下载 node-v22.12.0-linux-x64.tar.xz,解压后,将 bin 目录下的 node、npm、npx 拷贝到 /usr/local/bin 下。

1
2
3
4
tar -xvf node-v22.12.0-linux-x64.tar.xz
cd node-v22.12.0-linux-x64
mkdir /usr/local/node
cp -r * /usr/local/node/

执行 node -v,发现报错:

1
version "GLIBCXX_3.4.21" not found(required by node)

这通常意味着你的系统中的 libstdc++库版本过低,不支持程序所需的 GLIBCXX_3.4.21 版本。
而 libstdc++库是 GCC 的一部分,所以需要升级 GCC。
但是我之前升级过 GCC 到 10 版本了,怀疑是 link 的原因,然后找到这个文章,试了下,果然就 OK 了:
先找到你的系统中的 libstdc++.so.6.0.26 文件,然后将其拷贝到 /usr/lib64 下,然后删除原来的 libstdc++.so.6 文件,再重新建立链接即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 查找 libstdc++.so.6.0.* 文件
find / -name 'libstdc++.so.6.0.*' -type f

# 将下载的包放到 /usr/lib64 下
cp libstdc++.so.6.0.26 /usr/lib64/

# 到 /usr/lib64 目录下
cd /usr/lib64/

# 删除现在的libstdc++.so.6
rm -rf libstdc++.so.6

# 重新建立链接
ln -s libstdc++.so.6.0.28 libstdc++.so.6

相关命令

如何查看 libstdc++版本是多少:

1
strings /usr/lib64/libstdc++.so.6 | grep GLIBC

一些知识点

./configure、make、make install 这三个步骤分别是干什么的?

在 Linux 系统中,./configure、make 和 make install 是编译和安装源代码包的常用步骤,通常用于从源代码构建软件。以下是每个步骤的具体作用:

./configure:
这是一个配置脚本,通常由 autoconf 工具生成。
它的目的是检查你的系统环境,确定系统的特性和可用资源,以便生成适合你的系统的 Makefile。
这个步骤会检查编译所需的依赖库是否已安装,以及系统是否支持必要的功能。
如果系统缺少必要的依赖或功能,configure 脚本可能会报错并提示你需要安装或配置什么。
./configure 还可以接受各种参数,以便用户可以自定义编译选项,例如指定安装目录或禁用某些特性。

make:
这个命令使用由./configure 生成的 Makefile 来编译源代码。
make 会根据 Makefile 中的指令来编译源代码,链接生成可执行文件和库文件。
这个过程可能会包括编译多个源文件、创建对象文件、链接这些对象文件以及生成最终的可执行文件或库。
如果 Makefile 中的任务依赖于特定的顺序,make 会确保按照正确的顺序执行这些任务。

make install:
这个命令用于将编译生成的可执行文件、库文件、配置文件等安装到系统的指定位置。
默认情况下,make install 会将文件安装到类似于/usr/local/bin、/usr/local/lib 等目录下,但这个行为可以通过./configure 时指定的参数来改变。
在某些情况下,你可能需要超级用户权限来执行 make install,因为它需要写入系统的标准库和二进制文件目录。
安装过程也可能涉及到设置文件权限、创建必要的目录结构等。
这三个步骤提供了一种自动化的方式来编译和安装软件,使得从源代码构建软件变得更加容易和可重复。不过,需要注意的是,并不是所有的项目都遵循这个流程,有些项目可能使用不同的构建系统,如 CMake、Meson 等。

gcc 与 glibc 的区别、升级 gcc 会自动附带升级 glibc 吗?

GCC(GNU Compiler Collection)和 glibc(GNU C Library)是两个不同的项目,它们分别维护和发布。

  • GCC:是一个编译器集合,包括 C、C++、Objective-C、Fortran、Ada 和 Go 语言的编译器。它负责将源代码编译成可执行文件。

  • glibc:是 GNU C Library 的缩写,它是 Linux 系统中 C 标准库的实现,提供了基本的系统函数和库支持,是 Linux 操作系统核心组成部分之一。

升级 GCC 通常不会自动升级 glibc。

虽然 GCC 在编译程序时会用到 glibc,但是它们是独立开发的,各自有不同的发布周期和版本。升级 GCC 到新版本,通常意味着你将获得编译器的新功能和改进,但这不会影响系统中 glibc 的版本。

如果你需要升级 glibc,通常需要通过系统的包管理器(如 apt、yum 或 dnf)来安装新版本的 glibc 包,或者升级整个操作系统发行版。但是,需要注意的是,glibc 的升级可能涉及到系统的兼容性问题,因为许多系统程序和库都依赖于 glibc。不兼容的 glibc 版本可能会导致系统不稳定或程序无法运行。因此,在升级 glibc 之前,必须非常谨慎,并确保了解所有相关的依赖性和潜在的兼容性问题。

感触

C 语言和 Linux 的重要性

另外之前的各种安装笔记,得整理下,都忘光了。
我们常用的软件没几个的,把这几个软件的安装和原理彻底搞清楚,对我们的工作效率提升很大。

Docker 操作:外网优先

英语的重要性

排查问题,全是英文的,看起来太费劲了,还很容易错过正确的答案。

总结下问题分类、解决思路