背景
背景就是上一篇文章提到的,部署gitbook這個文件中心的話,是需要先安裝node,然後,如果你的node版本過高的話,一般會報錯,此時,網上很多文章就是降node版本解決,但其實用高版本也是有辦法的,只是麻煩點,要改改程式碼;但是,我下載了高版本的node安裝時,發現在centos7上還裝不了,可謂一波未平一波又起。
報錯的nodejs版本:v18,我這邊具體的是node-v18.18.2-linux-x64.tar.xz
伺服器版本是centos 7.6,centos 7.9(兩個都試了)
下面這個問題可以看下:
https://github.com/nodejs/node/issues/43246
吵得還是挺厲害。我覺得也是比較坑的是,下載的時候,文件也沒個提示,比如是否在centos7上可用,等到弄下來搞出一堆問題了上網去找才知道版本不相容。
下面具體說下這個問題。
問題原因分析
tar -xvf node-v18.18.2-linux-x64.tar.xz
cd node-v18.18.2-linux-x64/
[root@VM-0-6-centos node-v18.18.2-linux-x64]# bin/node
bin/node: /lib64/libm.so.6: version `GLIBC_2.27' not found (required by bin/node)
bin/node: /lib64/libc.so.6: version `GLIBC_2.25' not found (required by bin/node)
bin/node: /lib64/libc.so.6: version `GLIBC_2.28' not found (required by bin/node)
bin/node: /lib64/libstdc++.so.6: version `CXXABI_1.3.9' not found (required by bin/node)
bin/node: /lib64/libstdc++.so.6: version `GLIBCXX_3.4.20' not found (required by bin/node)
bin/node: /lib64/libstdc++.so.6: version `GLIBCXX_3.4.21' not found (required by bin/node)
這個報錯,是這個意思,node在執行的時候,是依賴了一些動態庫的,依賴了哪些呢:
[root@VM-0-6-centos node-v18.18.2-linux-x64]# ldd bin/node
linux-vdso.so.1 => (0x00007fff34927000)
libdl.so.2 => /lib64/libdl.so.2 (0x00007fd57af20000)
libstdc++.so.6 => /lib64/libstdc++.so.6 (0x00007fd57ac19000)
libm.so.6 => /lib64/libm.so.6 (0x00007fd57a917000)
libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007fd57a701000)
libpthread.so.0 => /lib64/libpthread.so.0 (0x00007fd57a4e5000)
libc.so.6 => /lib64/libc.so.6 (0x00007fd57a117000)
/lib64/ld-linux-x86-64.so.2 (0x00007fd57b124000)
在 => 這個符號左側,就是依賴的動態庫名字,右側,就是根據這個名字,在環境變數LD_LIBRARY_PATH指定的路徑下查詢,最終解析到的動態庫全路徑。
那我們再來看第一行報錯:
[root@VM-0-6-centos node-v18.18.2-linux-x64]# bin/node
bin/node: /lib64/libm.so.6: version `GLIBC_2.27' not found (required by bin/node)
/lib64/libm.so.6
這個是全路徑,看起來找到了,但是還是報錯,好像說要在GLIBC_2.27
這個版本沒找到。
我們可以這樣,在執行ldd
時打個詳細日誌:
[root@VM-0-6-centos]# ldd -v bin/node
Version information:
bin/node:
...
libm.so.6 (GLIBC_2.27) => not found
libm.so.6 (GLIBC_2.2.5) => /lib64/libm.so.6
看起來,它是找libm.so.6(GLIBC_2.27)沒找到,但找到了libm.so.6 (GLIBC_2.2.5)。
這塊其實是這樣,核心提供給使用者的是系統呼叫(system call),但是,在我們編寫c語言程式碼時,一般不是直接去呼叫這些系統呼叫,而是會include一些標頭檔案,如include <stdio.h>
,這些標頭檔案算是介面,這些介面包括其實現,最終編譯成二進位制打成一個庫,供使用者使用。最早是標準的libc庫,後來逐漸被glibc這個取代,glibc是GNU釋出的libc庫,官網:https://www.gnu.org/software/libc/libc.html
這個glibc庫,比如在我的centos7.6上,到底在啥位置呢?
我先看了下本機的glibc版本是2.17:
https://lindevs.com/check-glibc-version-in-linux
方法1:
[root@VM-0-6-centos lib64]# ldd --version
ldd (GNU libc) 2.17
方法2:
[root@VM-0-6-centos lib64]# ldd `which cat` | grep libc
libc.so.6 => /lib64/libc.so.6 (0x00007f27331c8000)
[root@VM-0-6-centos lib64]# /lib64/libc.so.6
GNU C Library (GNU libc) stable release version 2.17, by Roland McGrath et al.
glibc一般也是有rpm包的,我在這個網站上找到了2.17版本的x86-64的glibc的包:
可以看到,它其實包含了非常多檔案,其中就有/lib64/libm.so.6:
那我意思其實就是,/lib64/libm.so.6
就是glibc的一部分,那這個2.17版本的glibc,包含的/lib64/libm.so.6
報的這個錯到底啥意思啊?
[root@VM-0-6-centos]# ldd -v bin/node
Version information:
bin/node:
...
libm.so.6 (GLIBC_2.27) => not found
libm.so.6 (GLIBC_2.2.5) => /lib64/libm.so.6
我找到了一個絕好的回答:
GLIBC_..., GLIBCXX_... etc. are version symbols, which are used in some libraries (including the GNU C library and the GCC libraries) to identify required versions and to manage backward compatibility. A binary (executable or library) will usually end up requiring multiple versions, based on the symbols it really uses from the target library. To satisfy the requirements of a given binary, you need to provide a library which supports all the required versions — i.e. a library matching at least the highest version symbol in the list of requirements.
翻譯過來是,GLIBC_...
等GLIBCXX_...
是版本符號,在某些庫(包括GNU C庫和GCC庫)中使用它們來標識所需的版本並管理向後相容性。二進位制檔案(可執行檔案或庫)通常最終需要多個版本,具體取決於它實際使用的目標庫中的符號。為了滿足給定二進位制檔案的要求,您需要提供一個支援所有所需版本的庫 -即至少匹配要求列表中最高版本符號的庫。
The reason multiple versions can end up being required, is that each imported object (function etc.) can have a version, and a given binary can link against multiple versions across all the functions it uses.
翻譯:最終可能需要多個版本的原因是每個匯入的物件(函式等)都可以有一個版本,並且給定的二進位制檔案可以連結到它使用的所有函式的多個版本。
我這裡也只擷取了一部分,大家還是去看原文吧,反正意思就是,比如node這個程式,它就是會用到/lib64/libm.so.6
裡面不同version symbol
的函式,你需要做的,就是滿足它,否則,它就報錯。
怎麼滿足它呢,就是把/lib64/libm.so.6
的版本升上去,直到包含GLIBC_2.27
這個version symbol。
那怎麼才能升/lib64/libm.so.6
上去呢,那它既然是glibc的一部分,自然是隻能整體升級glibc到指定版本,比如這裡的GLIBC_2.27
。
解決-升級GLIBC
這裡參考了文章:
https://mp.weixin.qq.com/s/Xhm_BmMH2EoVWMPRWfCnaw
跟我遇到的坑差不多。文章裡1、安裝編譯環境devtoolset-8
那部分應該不需要特別關注,我覺得也不用操作,因為這種偷懶方式安裝的gcc,是解決不了node安裝報錯的問題的,往下看就知道了。
開始升級glibc,值得注意的是,大家最好是虛擬機器、個人的雲主機先玩一玩,不要拿著有其他人在用的環境搞這些,很容易把機器徹底搞到不能收場的地步;玩之前也記得備份,比如先複製一個虛擬機器出去
慢的話,可以自己手動下載再上傳
wget https://ftp.gnu.org/gnu/glibc/glibc-2.28.tar.gz --no-check-certificate
tar -xzvf glibc-2.28.tar.gz
cd glibc-2.28
mkdir build && cd build (一定要單獨建個資料夾來build)
在編譯開始前,修改 scripts/test-installation.pl 128行,增加 && $name ne "nss_test2" ,以避免編譯錯誤 nss_test2報錯,反正就是照著加一行。
接下來,是configure命令,尤其注意加--enable-obsolete-nsl,解決undefined reference to '_nsl_default_nss@GLIBC_PRIVATE'
,其他選項用他文章的也行,我的那個命令搞丟了(虛擬機器後來搞別的搞壞了)
../configure --prefix=/usr --disable-profile --enable-add-ons --with-headers=/usr/include --with-binutils=/usr/bin --enable-obsolete-nsl
然後就是:
make 或者 make -j4 (4個執行緒併發跑)
我記得我這邊大概耗時半小時或一小時內,忘了,還是虛擬機器這種效能差的
make install
檢查裡面的version symbol:
strings /lib64/libc.so.6 | grep GLIBC
解決gcc問題--升級gcc
解決上面的問題後,繼續執行node,還是報錯,大概如下:
bin/node: /lib64/libstdc++.so.6: version `CXXABI_1.3.9' not found (required by bin/node)
bin/node: /lib64/libstdc++.so.6: version `GLIBCXX_3.4.20' not found (required by bin/node)
bin/node: /lib64/libstdc++.so.6: version `GLIBCXX_3.4.21' not found (required by bin/node)
檢視這個裡面的symbol version,最新到1.3.7,不滿足1.3.9的要求:
[root@VM-0-6-centos node-v18.18.2-linux-x64]# strings /lib64/libstdc++.so.6 |grep CXXABI_
CXXABI_1.3
CXXABI_1.3.1
CXXABI_1.3.2
CXXABI_1.3.3
CXXABI_1.3.4
CXXABI_1.3.5
CXXABI_1.3.6
CXXABI_1.3.7
最新到3.4.19,不滿足3.4.20和3.4.21的要求:
[root@VM-0-6-centos node-v18.18.2-linux-x64]# strings /lib64/libstdc++.so.6 |grep GLIBCXX
...
GLIBCXX_3.4.11
GLIBCXX_3.4.12
GLIBCXX_3.4.13
GLIBCXX_3.4.14
GLIBCXX_3.4.15
GLIBCXX_3.4.16
GLIBCXX_3.4.17
GLIBCXX_3.4.18
GLIBCXX_3.4.19
按照官方文件來的:
https://gcc.gnu.org/wiki/InstallingGCC
安裝gcc前,需要先安裝依賴的gmp-devel mpfr-devel libmpc-devel,好多文章是說yum安裝,我覺得也可以,但我就怕gcc版本和yum安裝的這些依賴的版本不太匹配,建議還是按照如下方式來安裝:
wget https://ftp.gnu.org/gnu/gcc/gcc-8.5.0/gcc-8.5.0.tar.gz --no-check-certificate
cd gcc-8.5.0
./contrib/download_prerequisites
這一步就會去下載對應的原始碼,需要網際網路:
[root@VM-0-6-centos gcc-8.5.0]# ./contrib/download_prerequisites
2023-10-22 15:40:49 URL: ftp://gcc.gnu.org/pub/gcc/infrastructure/gmp-6.1.0.tar.bz2 [2383840] -> "./gmp-6.1.0.tar.bz2" [1]
2023-10-22 15:42:05 URL: ftp://gcc.gnu.org/pub/gcc/infrastructure/mpfr-3.1.4.tar.bz2 [1279284] -> "./mpfr-3.1.4.tar.bz2" [1]
2023-10-22 15:42:42 URL: ftp://gcc.gnu.org/pub/gcc/infrastructure/mpc-1.0.3.tar.gz [669925] -> "./mpc-1.0.3.tar.gz" [1]
2023-10-22 15:44:31 URL: ftp://gcc.gnu.org/pub/gcc/infrastructure/isl-0.18.tar.bz2 [1658291] -> "./isl-0.18.tar.bz2" [1]
gmp-6.1.0.tar.bz2: OK
mpfr-3.1.4.tar.bz2: OK
mpc-1.0.3.tar.gz: OK
isl-0.18.tar.bz2: OK
All prerequisites downloaded successfully.
下完後,就儲存到了當前目錄下,幾個tar包。
mkdir build
cd build/
我是不需要gcc支援編譯go,需要的話,可以加上
../configure --disable-multilib --enable-languages=c,c++ --prefix=$HOME/local
接下來就是make,我一開始求快,搞得是:
nohup make -j4 2>&1 &
結果後面等了好久,報錯了。。。抱著試試看心理,改成make,結果成功了
nohup make 2>&1 &
tailf nohup.out
make install
export LD_LIBRARY_PATH=$HOME/local/lib64
這裡這個make,要執行很久很久,反正我的雲主機是這樣,1核2g,cpu一直是100%,跑了2個半小時:
從4點20:
跑到6點40了:
搞完這些,再去用node,應該就沒啥問題了。哎,你說你官網文件,就不能好好提示下我,支援centos7的最新版本是哪個,搞到一半報錯,然後一個版本一個版本地往下降,直到不報錯,網上社群也是一堆吐槽,服。