文章目录
前言
我总以为这件事发生很久了,但刚刚去翻了聊天记录居然是上周发生的事情,趁着还没忘干净赶紧记录一下,对于服务器安装系统来说这还是第一次。
好久很久以前(大概是1个月前),我找运维装了一台服务器Dell PowerEdge R620,当时说好是Ubuntu20.04版本,但是装完后我也没注意,期间发生了很多事情,终于到上周的时候开始在这台机器上搭建Jenkins打包环境,整个过程还是比较顺利的,但是发现打包机上编译的程序版本无法在本地环境下运行,这时候仔细看才发现,原来运维安装的系统是 Ubuntu22.04,也不知道是因为粗心还是大意,不能运行的原因就是打包机上的glibc版本过高,本地环境无法支持,但这却引起了我的疑惑。
glibc版本和常用命令
先附一个Linux发行版本和默认glibc版本对应关系的图表
Ubuntu | Debian | CentOS | Glibc |
---|---|---|---|
22.04 | - | - | 2.34 |
20.04 | 11 | - | 2.31 |
- | 10 | 8 | 2.28 |
18.04 | - | - | 2.27 |
- | 9 | - | 2.24 |
16.04 | - | - | 2.23 |
14.04 | 8 | - | 2.19 |
13.04 | - | 7 | 2.17 |
12.04 | - | - | 2.15 |
- | 7 | - | 2.13 |
- | - | 6 | 2.12 |
查询glibc版本的命令为
ldd --version
查询动态库和可执行文件中的符号版本
objdump -T my-program-or-so | grep GLIBC_
strings /lib/x86_64-linux-gnu/libc.so.6 | grep GLIBC
查询一个目录下所有动态库中使用符号的glibc版本
ls | grep '.so' | xargs objdump -T | grep GLIBC_ > /tmp/symbol.log
glibc版本差异的疑问
目前我维护了两个项目,对于编译环境和运行环境不一样的问题,两个项目中都存在,但是A项目没问题,但是B项目有问题,具体的情况如下:
A项目
C++11编写,打包机Ubuntu16.04,glibc版本2.23,运行环境有CentOS7和Ubuntu16.04,相同的系统大概率没问题,而CentOS7的glibc是2.17版本,也没有问题,这让我很奇怪
打包机编译环境
root@jenkins1604:~# cat /etc/os-release
NAME="Ubuntu"
VERSION="16.04.2 LTS (Xenial Xerus)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 16.04.2 LTS"
VERSION_ID="16.04"
HOME_URL="http://www.ubuntu.com/"
SUPPORT_URL="http://help.ubuntu.com/"
BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"
VERSION_CODENAME=xenial
UBUNTU_CODENAME=xenial
root@jenkins1604:~# ldd --version
ldd (Ubuntu GLIBC 2.23-0ubuntu5) 2.23
Copyright (C) 2016 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Written by Roland McGrath and Ulrich Drepper.
root@jenkins1604:~#
运行环境1
$ cat /etc/os-release
NAME="Ubuntu"
VERSION="16.04.6 LTS (Xenial Xerus)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 16.04.6 LTS"
VERSION_ID="16.04"
HOME_URL="http://www.ubuntu.com/"
SUPPORT_URL="http://help.ubuntu.com/"
BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"
VERSION_CODENAME=xenial
UBUNTU_CODENAME=xenial
# shz @ 20200116GF in /mnt/e/linux on git:7bd9be2 x [9:47:34]
$ ldd --version
ldd (Ubuntu GLIBC 2.23-0ubuntu11) 2.23
Copyright (C) 2016 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Written by Roland McGrath and Ulrich Drepper.
运行环境2
[root@hk-dev Debug]# cat /etc/os-release
NAME="CentOS Linux"
VERSION="7 (Core)"
ID="centos"
ID_LIKE="rhel fedora"
VERSION_ID="7"
PRETTY_NAME="CentOS Linux 7 (Core)"
ANSI_COLOR="0;31"
CPE_NAME="cpe:/o:centos:centos:7"
HOME_URL="https://www.centos.org/"
BUG_REPORT_URL="https://bugs.centos.org/"
CENTOS_MANTISBT_PROJECT="CentOS-7"
CENTOS_MANTISBT_PROJECT_VERSION="7"
REDHAT_SUPPORT_PRODUCT="centos"
REDHAT_SUPPORT_PRODUCT_VERSION="7"
You have mail in /var/spool/mail/root
[root@hk-dev Debug]# ldd --version
ldd (GNU libc) 2.17
Copyright (C) 2012 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Written by Roland McGrath and Ulrich Drepper.
B项目
C++20编写,原打包机Ubuntu20.04,glibc版本2.31,运行环境有Ubuntu20.04,相同的系统没出问题,后来因打包机替换系统升到Ubuntu22.04后,glibc版本为2.34,再到Ubuntu20.04系统上运行就报错了
./server: /lib/x86_64-linux-gnu/libc.so.6: version
GLIBC_2.34' not found (required by ./server)
看起来是glibc版本问题导致的
原打包机编译环境
root@ubuntu2004:~# cat /etc/os-release
NAME="Ubuntu"
VERSION="20.04.2 LTS (Focal Fossa)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 20.04.2 LTS"
VERSION_ID="20.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=focal
UBUNTU_CODENAME=focal
root@ubuntu2004:~# ldd --version
ldd (Ubuntu GLIBC 2.31-0ubuntu9.16) 2.31
Copyright (C) 2020 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Written by Roland McGrath and Ulrich Drepper.
新打包编译环境环境
- Ubuntu22.04
- glibc 2.34
运行环境
$ cat /etc/os-release
NAME="Ubuntu"
VERSION="20.04.2 LTS (Focal Fossa)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 20.04.2 LTS"
VERSION_ID="20.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=focal
UBUNTU_CODENAME=focal
# shzhen @ 20200116GF in /mnt/c/Users/Administrator [20:42:28]
$ ldd --version
ldd (Ubuntu GLIBC 2.31-0ubuntu9.2) 2.31
Copyright (C) 2020 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Written by Roland McGrath and Ulrich Drepper.
问题疑惑
同样是高版本glibc的机器上编译,在低版本的glibc上运行,为什么A项目没问题,但是B项目出了问题呢,而且A项目的glibc版本2.23–>2.17相差的还多,B项目的glibc版本2.34–>2.31相差的还少,出问题的却是B项目,分析原因glibc应该是向下兼容的,高编译低运行本就不可行,属于正常现象,如果想实现这样运行,就要保证调用的符号在两个版本的库中没有变化,所以我查了A项目和B项目的符号表,发现:
- A项目中符号最高的版本是GLIBC_2.17,所以即使“高编译低运行”也是满足条件的
- B项目中符号最高的版本是GLIBC_2.34,所以不能在glibc.2.31的Ubuntu20.04上运行
但B项目中使用高版本符号的就有几个,剩下的都低于2.29版本,理论上只要去掉这几个符号的引用就可以在Ubuntu20.04上运行,但是为了避免后续的麻烦,我还是把打包编译的机器重新做了系统Ubuntu20.04
0000000000000000 DF *UND* 0000000000000000 (GLIBC_2.33) stat
0000000000000000 w DF *UND* 0000000000000000 (GLIBC_2.34) pthread_once
0000000000000000 w DF *UND* 0000000000000000 (GLIBC_2.34) __pthread_key_create
0000000000000000 DF *UND* 0000000000000000 (GLIBC_2.34) sem_trywait
0000000000000000 DF *UND* 0000000000000000 (GLIBC_2.34) dladdr
0000000000000000 DF *UND* 0000000000000000 (GLIBC_2.34) sem_wait
0000000000000000 DF *UND* 0000000000000000 (GLIBC_2.34) sem_init
0000000000000000 DF *UND* 0000000000000000 (GLIBC_2.34) sem_timedwait
0000000000000000 DF *UND* 0000000000000000 (GLIBC_2.34) sem_destroy
0000000000000000 DF *UND* 0000000000000000 (GLIBC_2.34) dlclose
0000000000000000 DF *UND* 0000000000000000 (GLIBC_2.34) dlsym
0000000000000000 DF *UND* 0000000000000000 (GLIBC_2.34) dlerror
0000000000000000 DF *UND* 0000000000000000 (GLIBC_2.34) dlopen
0000000000000000 w DF *UND* 0000000000000000 (GLIBC_2.34) pthread_once
0000000000000000 w DF *UND* 0000000000000000 (GLIBC_2.34) __pthread_key_create
0000000000000000 DF *UND* 0000000000000000 (GLIBC_2.34) dladdr
将服务器系统改为Ubuntu20.04
对于重装系统我内心是拒绝的,前两天刚安装了一个PC电脑的windows系统,再往前推都好几年没装过了,不想来回折腾,再者说PC电脑安装Windows很熟悉,即使有问题网上搜一下也解决了,但是服务器安装系统之前确实没做过,并且还有一个我不愿意安装服务器系统的原因,就是这些服务器的硬盘配置的是Raid5磁盘阵列,因为不熟悉我真怕给它做坏了。
所以对于改为Ubuntu20.04系统这件事,我首先想到的就是将系统降级,但是尝试了一圈没有成功之后,我不得不硬着头皮重装系统了,当然在重装之前还查了Raid5相关信息,发现硬盘配置有专门的Raid卡控制,与操作系统没有关系,只要安装的时候不要把系统安装到原来的数据盘就不会影响原来的数据信息,单纯对做系统来说,硬盘可以当成普通的硬盘来看待,安装时选对盘符就行了。
开始安装
安装教程可以参考《Dell服务器安装Ubuntu系统》、《dell r620安装Ubuntu全过程》
镜像地址参考 官方镜像下载、清华镜像,注意要选择服务器版本的,不需要带桌面那一套东西,体积会小一点,性能会好一点
首先是做系统启动盘,按照教程选择了UltraISO,之前用这个比较多,本来一直点下一步就行了,但是无论选择官方镜像还是清华的镜像,在制作的时候都会提示一个可能无法引导系统的警告,但最后是成功的,我就用它试了试
把U盘插到服务器上,开机按F11进入System BOIS设置,把U盘调到第一启动项,重启后进入U盘拷贝的进度条,但总是无法正确安装,还多次进入了 Emergency mode,重启关机都不好使,最后不得不强行关机了
然后我换成了使用Rufus来烧制启动U盘,这里涉及到几种选项,比如选BOIS还是UEFI,文件系统选GPT还是MBR,提供一个详细的步骤
- 运行 Rufus:打开 Rufus 应用程序(无需安装)。
- 选择设备:在 “Device” 下拉菜单中选择你的 U 盘。
- 选择引导类型:点击 “Boot selection” 旁边的 “Select” 按钮,选择你下载的 Ubuntu ISO 文件。
- 分区方案:
- 如果 Dell R620 使用 UEFI 固件模式,选择 “GPT” 作为分区方案。
- 如果你希望使用传统的 BIOS 模式,选择 “MBR” 作为分区方案。
- 目标系统:
- 如果选择 “GPT” 分区方案,目标系统应自动设置为 “UEFI (non CSM)”。
- 如果选择 “MBR” 分区方案,目标系统应自动设置为 “BIOS or UEFI”.
- 文件系统:选择 “FAT32”。
- 卷标:可以给你的 U 盘设置一个名称,比如 “Ubuntu_Install”。
- 启动创建:点击 “Start” 按钮,Rufus 会开始创建启动盘。过程大约需要几分钟。
BOIS 和 UEFI的区别
BIOS(Basic Input/Output System)和UEFI(Unified Extensible Firmware Interface)是两种计算机固件接口,用于在操作系统启动之前初始化硬件和加载操作系统。它们在功能、性能和设计上有一些显著的区别。
BIOS,Basic Input/Output System
- 传统固件接口:BIOS是较早的固件接口,起源于20世纪80年代。
- 16位模式:运行在16位处理器模式下,具有1MB的地址空间限制。
- MBR分区表:使用主引导记录(MBR)分区表,最多支持4个主分区,每个分区最大支持2TB。
- 启动速度较慢:由于初始化硬件的方式较为低效,启动速度较慢。
- 用户界面:通常是文本模式的界面,操作较为复杂。
- 扩展性差:扩展性和灵活性较差,难以适应现代硬件和软件的需求。
UEFI,Unified Extensible Firmware Interface
- 现代固件接口:UEFI是BIOS的继任者,设计初衷是替代BIOS,提供更丰富的功能和更好的性能。
- 32位或64位模式:运行在32位或64位处理器模式下,具有更大的地址空间。
- GPT分区表:使用GUID分区表(GPT),支持更多的分区(最多128个),且每个分区可以超过2TB。
- 启动速度较快:初始化硬件的方式更高效,启动速度更快。
- 用户界面:支持图形用户界面(GUI),操作更加直观和友好。
- 扩展性强:具有更好的扩展性和灵活性,支持网络启动、远程诊断和修复等现代功能。
具体区别
-
启动过程:
- BIOS:在启动过程中,BIOS执行一系列的POST(Power-On Self Test)检查,然后查找并加载MBR中的引导代码。
- UEFI:在启动过程中,UEFI固件会查找并执行EFI系统分区(ESP)中的引导管理程序,可以直接加载操作系统。
-
分区表支持:
- BIOS:仅支持MBR分区表,存在4个主分区的限制。
- UEFI:支持GPT分区表,可以有更多的分区,且每个分区大小不受2TB限制。
-
硬件支持:
- BIOS:对现代硬件的支持有限,尤其在大容量存储设备和新型设备的支持上存在不足。
- UEFI:设计时考虑了现代硬件的需求,支持更大容量的存储设备和多种新型设备。
-
安全性:
- BIOS:安全性较低,缺乏现代安全特性。
- UEFI:支持Secure Boot等安全特性,防止恶意软件和未授权操作系统的启动。
BIOS和UEFI是两种不同的固件接口,UEFI是BIOS的继任者,提供了更丰富的功能、更好的性能和更强的扩展性。随着计算机技术的发展,UEFI已经成为现代计算机系统中广泛采用的固件接口,逐渐取代了传统的BIOS。
之前我不太关心这些,这次我听了ChatGPT的建议选择了UEFI、GPT、FAT32的选项,这也为我后面启动埋下了一个坑
再次安装
这次无比顺利,将U盘设置为启动盘
- 选择语言
- 选择键盘
- 选择服务器类型Ubuntu Server
- 手动配置IP和网关
- 跳过代理设置
- 选择安装的盘符,注意别选错了,我这里是小心再小心,这是我最担心的一步了
- 输入主机名、用户名和密码
- 跳过升级Ubuntu Pro
- 安装OpenSSH Server
- 不勾选其他Feature安装
- 然后就等着安装完、更新完重启就行了
然后等它安装完我就等不及了,没等更新我就重启了,因为从早上捣鼓到下午了,然后电脑就打不开了,一直卡在开机界面,启动项也重新调整了,就是不起作用
解决开机问题
每次开机屏幕就卡在下面这些信息上:
No boot device avaiable
Current boot node is set to BIOS
Please ensure aompatible bootable dedia is available
Use the system setup pargram to chaneg the boot mode as neededstrlke F1 to retry boot, F2 for system setup, F11 for BIOS boot manager
一开始我总是把注意力放在最后一句,每个选项都尝试了也不行,找到两篇相关问题的解答,但是没解决问题
- 戴尔服务器安装Ubuntu系统时跳过strlke F1 to retry boot, F2 for system setup,F11 for BIOS boot manager解决方法
- windowsServer2008开机出现strike f1 to retry boot,f2 for system setup ,f11 for BIOS boot manager
于是仔细看到这一句 “Use the system setup pargram to chaneg the boot mode as needed”
按F2 进入 System BOIS,将Boot Mode改为 UEFI 模式,成功进入系统,因为之前是BIOS,我制作的引导盘采用的是UEFI模式,后续买云服务器用的也是 Ubuntu 20.04 64位 UEFI版,保持统一吧
总结
- 保证开发环境、打包环境、运行环境一致,可以避免很多坑,这或许就是docker火起来的意义
- 使用系统自带的glibc版本,千万不要自己升级或降级,会引起连锁反应,这是很多前人给出的忠告
- glibc的特点,兼容低版本的内容,所以理论上“低版本编译高版本运行”没问题,反过来一般不行,但存在特例
- BIOS(Basic Input/Output System)和UEFI(Unified Extensible Firmware Interface)是两种计算机固件接口
- UEFI是BIOS的继任者,提供了更丰富的功能、更好的性能和更强的扩展性,使用GUID分区表(GPT),支持更多的分区
Use the system setup pargram to chaneg the boot mode as needed
是你一直坚持走熟悉舒服的老路,过一眼望到头的人生,那看不见新的风景,遇不到新的经历也就怨不得别人了~