clickhouse如何解决GLIBC不兼容问题--终篇

简介: clickhouse如何解决GLIBC不兼容问题--终篇

共享库与binary文件打包的副作用


test_getaddrinfo.cpp

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <string.h>
#include <stdio.h>
int main()
{
  struct addrinfo hints;
  struct addrinfo *res;
  memset(&hints, 0, sizeof hints);                                         
  hints.ai_flags = 1024;
  hints.ai_family = 0;
  hints.ai_socktype = 1;
  hints.ai_protocol = 6; 
  hints.ai_addrlen = 0;
  hints.ai_addr = 0x0; 
  hints.ai_canonname = 0x0; 
  hints.ai_next = 0x0;
  int err = ::getaddrinfo("localhost", "8000", &hints, &res);
  printf("%d", err);
  return 0;
}

按照上一篇文章 中的方法,我们在ubuntu20环境中编译test_getaddrinfo.cpp文件,连同其依赖的共享库一块打包部署到ubuntu16环境,具体步骤如下


  • u20环境中,依次进行编译、复制共享库、修改ELF信息等操作
$ g++ test_getaddrinfo.cpp  -o test_getaddrinfo
$ ldd ./test_getaddrinfo                                        
  linux-vdso.so.1 (0x00007ffc539ea000)
  libc.so.6 => /usr/lib/x86_64-linux-gnu/libc.so.6 (0x00007fc38ae95000)
  /lib64/ld-linux-x86-64.so.2 (0x00007fc38b08e000)
$ copylib.sh ./test_getaddrinfo
$ patchelf --set-rpath  /home/liyang/lib  test_getaddrinfo
$ patchelf --set-interpreter /home/liyang/lib/ld-linux-x86-64.so.2 test_getaddrinfo
  • 将get_getaddrinfo连同lib文件一块复制到u16运行环境中。


  • 在u16环境中运行test_getaddrinfo, 可以看到程序异常, 因为::getaddrinfo返回的不是零
$ ./test_getaddrinfo 
-11

那么问题在哪里呢?作为对照组,我们直接在u16环境中编译运行test_getaddrinfo.cpp文件,返回为零。


分别strace这两个binary的运行过程,左右分别对应u16和u20编译的bianry。可以看到右边会多加载很多.so文件,而这些.so文件并不能通过copylib工具打包。个人猜想这个原因导致了test_getaddrinfo运行时没有彻底去除对u20内核的依赖,进而导致::getaddrinfo返回异常。


image.png

新方案


原因分析


有了上面这个bad case, 随binary打包lib的方案显然是走不通了。我们回溯到最初是的问题:u20编译的clickhouse, 在u16环境下启动时会报错:version GLIBC_2.27 not found


我们看看u16支持哪些GLIBC版本? 2.27确实不在u16支持之列

$ strings /lib/x86_64-linux-gnu/libc.so.6 | grep GLIBC_ 
GLIBC_2.2.5
GLIBC_2.2.6
GLIBC_2.3
GLIBC_2.3.2
GLIBC_2.3.3
GLIBC_2.3.4
GLIBC_2.4
GLIBC_2.5
GLIBC_2.6
GLIBC_2.7
GLIBC_2.8
GLIBC_2.9
GLIBC_2.10
GLIBC_2.11
GLIBC_2.12
GLIBC_2.13
GLIBC_2.14
GLIBC_2.15
GLIBC_2.16
GLIBC_2.17
GLIBC_2.18
GLIBC_2.22
GLIBC_2.23
GLIBC_PRIVATE

那clickhouse中glibc 2.27到底是哪个动态库引入的呢?

$ objdump -x ./clickhouse
Version References:
  required from libdl.so.2:
    0x09691a75 0x00 02 GLIBC_2.2.5
  required from libpthread.so.0:
    0x09691a75 0x00 03 GLIBC_2.2.5
    0x09691974 0x00 04 GLIBC_2.3.4
  required from libm.so.6:
    0x09691a75 0x00 05 GLIBC_2.2.5
    0x06969187 0x00 06 GLIBC_2.27

libm库与数学计算相关,那么到底其中哪个symbol依赖了glibc 2.27呢?

$ readelf -s clickhouse | grep  "GLIBC_2.27"
   148: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND powf@GLIBC_2.27 (3)

原来是powf函数引入了对GLIBC的依赖。联想到clickhouse中有一个模块专门处理glibc不兼容问题

base/glibc-compatibility/glibc-compatibility.c

/** Allows to build programs with libc 2.27 and run on systems with at least libc 2.4,
  *  such as Ubuntu Hardy or CentOS 5.
  *
  * Also look at http://www.lightofdawn.org/wiki/wiki.cgi/NewAppsOnOldGlibc
  */

base/glibc-compatibility如何解决不兼容问题呢?原理很简单,重新override clickhouse依赖的glibc函数即可。但是clickhouse 20.3版本的base/glibc-compatibility中并没有override powf函数,因此编译ck时就依赖了u20本地的libm共享库, 也就引入了GLIBC 2.27的依赖。


解决方案


原因明确之后,解决方案便一目了然了:在base/glibc-compatibility中override powf的实现,好在clickhouse最新版中已经override了powf, 直接将相关代码copy过来重新编译即可。

image.png

我们验证override powf之后的clickhouse binary是否还依赖GLIBC 2.27?

$ objdump -x ./clickhouse | grep "GLIBC_2.27"
$ readelf -s  clickhouse  | grep powf
372223: 0000000028bb549c   727 FUNC    GLOBAL DEFAULT   15 powf
460362: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS powf.c
460380: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS powf_data.c
460381: 00000000124f7900   296 OBJECT  LOCAL  HIDDEN    11 __powf_log2_data
1274523: 0000000028bb549c   727 FUNC    GLOBAL DEFAULT   15 powf

clickhouse中的symbol: powf已经不依赖GLIBC_2.27了。


总结


在本文中我们首先证明了文章中的方案对于clickhouse行不通,然后分析了clickhouse为何会引入GLIBC 2.27的依赖,最后我们通过在base/glibc-compatibility中补全对powf的override,彻底解决了GLIBC <=2.27时兼容性问题。


顺便一提,社区最近的一个PR 已经实现了Hermetic builds, 使得编译clickhouse时只依赖clickhouse中的libc库,彻底去除了对编译环境中libc的依赖,从根本上解决了GLIBC > 2.27时的兼容性问题。到此不由感叹clickhouse真是一个宝藏社区,在各种工程细节上做到了极致,我们想到的想不到的问题他们都有解决,或是在解决的路上。



相关文章
|
Ubuntu 开发工具 C语言
clickhouse如何解决GLIBC不兼容问题
clickhouse如何解决GLIBC不兼容问题
352 0
|
19天前
|
存储 关系型数据库 数据库
【DDIA笔记】【ch2】 数据模型和查询语言 -- 多对一和多对多
【6月更文挑战第7天】该文探讨数据模型,比较了“多对一”和“多对多”关系。通过使用ID而不是纯文本(如region_id代替&quot;Greater Seattle Area&quot;),可以实现统一、避免歧义、简化修改、支持本地化及优化搜索。在数据库设计中,需权衡冗余和范式。文档型数据库适合一对多但处理多对多复杂,若无Join,需应用程序处理。关系型数据库则通过外键和JOIN处理这些关系。文章还提及文档模型与70年代层次模型的相似性,层次模型以树形结构限制了多对多关系处理。为克服层次模型局限,发展出了关系模型和网状模型。
23 6
|
21天前
|
XML NoSQL 数据库
【DDIA笔记】【ch2】 数据模型和查询语言 -- 概念 + 数据模型
【6月更文挑战第5天】本文探讨了数据模型的分析,关注点包括数据元素、关系及不同类型的模型(关系、文档、图)与Schema模式。查询语言的考量涉及与数据模型的关联及声明式与命令式编程。数据模型从应用开发者到硬件工程师的各抽象层次中起着简化复杂性的关键作用,理想模型应具备简洁直观和可组合性。
15 2
|
18天前
|
SQL 人工智能 关系型数据库
【DDIA笔记】【ch2】 数据模型和查询语言 -- 文档模型中Schema的灵活性
【6月更文挑战第8天】网状模型是层次模型的扩展,允许节点有多重父节点,但导航复杂,需要预知数据库结构。关系模型将数据组织为元组和关系,强调声明式查询,解耦查询语句与执行路径,简化了访问并通过查询优化器提高效率。文档型数据库适合树形结构数据,提供弱模式灵活性,但在Join支持和访问局部性上不如关系型。关系型数据库通过外键和Join处理多对多关系,适合高度关联数据。文档型数据库的模式灵活性体现在schema-on-read,写入时不校验,读取时解析,牺牲性能换取灵活性。适用于不同类型或结构变化的数据场景。
19 0
|
20天前
|
SQL JSON NoSQL
【DDIA笔记】【ch2】 数据模型和查询语言 -- 关系模型与文档模型
【6月更文挑战第6天】关系模型是主流数据库模型,以二维表形式展示数据,支持关系算子。分为事务型、分析型和混合型。尽管有其他模型挑战,如网状和层次模型,但关系模型仍占主导。然而,随着大数据增长和NoSQL的出现(如MongoDB、Redis),强调伸缩性、专业化查询和表达力,关系模型的局限性显现。面向对象编程与SQL的不匹配导致“阻抗不匹配”问题,ORM框架缓解但未完全解决。文档模型(如JSON)提供更自然的嵌套结构,适合表示复杂关系,具备模式灵活性和更好的数据局部性。
20 0
|
22天前
|
敏捷开发 存储 缓存
【DDIA笔记】【ch1】 可靠性、可扩展性和可维护性 -- 可维护性
【6月更文挑战第4天】本文探讨了Twitter面临的一次发推文引发的巨大写入压力问题,指出用户粉丝数分布是决定系统扩展性的关键因素。为解决此问题,Twitter采用混合策略,大部分用户推文扇出至粉丝主页时间线,而少数名人推文则单独处理。性能指标包括吞吐量、响应时间和延迟,其中高百分位响应时间对用户体验至关重要。应对负载的方法分为纵向和横向扩展,以及自动和手动调整。文章强调了可维护性的重要性,包括可操作性、简单性和可演化性,以减轻维护负担和适应变化。此外,良好设计应减少复杂性,提供预测性行为,并支持未来改动。
20 0
|
23天前
|
缓存 关系型数据库 数据库
【DDIA笔记】【ch1】 可靠性、可扩展性和可维护性 -- 可扩展性
【6月更文挑战第3天】可扩展性关乎系统应对负载增长的能力,但在产品初期过度设计可能导致失败。理解基本概念以应对可能的负载增长是必要的。衡量负载的关键指标包括日活、请求频率、数据库读写比例等。推特的扩展性挑战在于&quot;扇出&quot;,即用户关注网络的广度。两种策略包括拉取(按需查询数据库)和推送(预计算feed流)。推送方法在推特案例中更为有效,因为它减少了高流量时的实时计算压力。
24 0
|
24天前
|
存储 消息中间件 缓存
【DDIA笔记】【ch1】 可靠性、可扩展性和可维护性 -- part1 可靠性
【6月更文挑战第2天】本书探讨现代数据系统,阐述其在信息社会中的关键作用,包括数据库、缓存、搜索引擎、流处理、批处理和消息队列等组成部分。随着技术发展,工具如Kafka、Spark和Redis等多功能组件使得系统设计更为复杂。面对可靠性、可扩展性和可维护性的挑战,书中强调了容错和韧性的重要性,区分了硬件故障、软件错误和人为错误,并提出了应对措施。可靠性关乎用户数据、企业声誉和生存,因此是系统设计的核心考量。
25 0
硬件开发笔记(十): 硬件开发基本流程,制作一个USB转RS232的模块(九):创建CH340G/MAX232封装库sop-16并关联原理图元器件
有了原理图,可以设计硬件PCB,在设计PCB之间还有一个协同优先动作,就是映射封装,原理图库的元器件我们是自己设计的。为了更好的表述封装设计过程,本文描述了CH340G和MAX232芯片封装创建(SOP-16),并将原理图的元器件关联引脚封装。
硬件开发笔记(十): 硬件开发基本流程,制作一个USB转RS232的模块(九):创建CH340G/MAX232封装库sop-16并关联原理图元器件

热门文章

最新文章