我使用
go build -o myApp
在 Ubuntu 20.04 操作系统中构建 Go 应用程序。
当我在 Ubuntu Server 18.04 上运行此应用程序时,出现此错误:
/lib/x86_64-linux-gnu/libm.so.6: version `GLIBC_2.29' not found (required by ./myApp)
当我在 stackoverflow 中搜索时,有人提到在服务器上安装
glibc 2.29
。但也有人回复说这种方式有风险,可能会破坏OS。
其他人建议使用
glibc 2.27
构建应用程序。
我该怎么做?
(实际上是在回答那个问题。
默认情况下,在 Linux for Linux 上构建时,常用的 Go 工具链(可以从 there 获得)会再次动态链接平台的标准 C 库(也称为
libc
)。
这是与“名称解析”相关的两个任务所需要的:
Go 尝试在基于 Linux 的平台上使用
libc
来完成这些任务,因为内核本身(Linux 是内核,而不是操作系统)并没有实现上面列出的名称解析工具,它们都是以以下形式实现的:图书馆。
大多数基于 Linux 的操作系统中使用的标准 C 库是 GNU libc,通常称为“glibc”。它以共享库(
.so
文件)的形式提供,并具有版本化符号——也就是说,它不会导出,比如说,gethostbyname
,它会导出类似gethostbyname@GLIBC_X.YY
的东西,其中那些 X.YY
是 glibc 的某个版本。出于兼容性目的,glibc 的单个版本甚至可能导出同一符号的多个版本:“当前”版本和多个“过去”版本。
现在,当你在一个基于 glibc 且附带 glibc vA.BC 的操作系统上构建一个 Go 程序时,链接器会以一种在其符号表中表示它有一堆未解析的 versioned 符号的方式链接你的程序 –说,
[email protected]
。现在假设您将生成的可执行映像文件复制到运行操作系统的主机,该操作系统具有某些过去版本的glibc,例如X.YZ
< A.BC
。
现在显然 glibc 不包含 [email protected]
,因为它最多只能提供 [email protected]
,因为 vA.BC 在 glibc 版本构建时甚至不存在。
另一个问题部分提出了一个解决方案:强制 Go 不链接到
libc
。
cgo
,因此 Go 工具链被迫使用变通办法。
这种情况下的解决方法是对 DNS 和用户/组解析使用特殊的存根实现,仅处理最常见的情况 - 基本上,DNS 解析代码读取 /etc/resolv.conf
,然后使用 UDP 与此处列出的名称服务器和用户进行通信/group 解析代码为 /etc/passwd
和 /etc/groups
。
虽然在 DNS 解析的情况下,与 libc 提供的解析的差异应该很小,但在用户/组解析的情况下,差异可能很大,因为存根实现完全错过了 NSS 提供的“高级”设置,例如从 LDAP 服务器获取用户和/或组的列表。
请注意,有时程序必须使用
cgo
,除了 DNS 和用户/组解析之外的其他原因 - 例如,它想要链接到自定义 C 库,但不想链接到 libc。
在这种情况下,可以启用 cgo
,但使用特殊的构建标签构建程序:netgo
和 osusergo
,例如:
$ go build -tags netgo,osusergo
另一个解决方案是让链接器使用目标系统将具有的 libc 版本。
chroot
-ed环境或基于具有所需libc版本的操作系统映像的容器。
因为我是个老家伙,我发现 chroots 更容易设置和使用,而且更轻量,但是 YMMV。
TL;博士
如果您希望 Go 程序在 Ubuntu 42 上运行,而您在 Ubuntu 24 上进行开发,并且它们的 glibc 版本恰好不同,请使用特殊环境来进行最终部署构建 - 任何具有包含以下内容的 glibc 版本的环境所需的符号。最简单的方法是在 Ubuntu 42 上构建。