昨晚接到紧急需求,需要增加一个搜索功能。当时在家里,感觉很简单的需求, 随便改改,直接发布到线上服务器就行,毕竟我大 golang 只要能编译通过,基本不会有大问题 😏。
但是,不出意外,出了意外。自动发布脚本,在重启 systemd 服务后, 使用 systemctl status 查看状态,发现 golang 服务没有启动成功。
通过命令行单独启动 go 服务,发现报错:
/lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.34' not found
还好,我发布前,将可执行程序备份了一份。。。挂了一分钟,我就悄悄恢复了,险些提桶跑路。 还是明早回公司再发布吧,太吓人了。
version `GLIBC_2.3x' not found
之前遇到过两次这个问题,都是因为编译的环境和运行的环境不一致导致的。
第一次是测试 DotNet 8 AOT 编译时, 在 Ubuntu 22.04 上编译,然后 scp 到 Ubuntu 20.04 上执行。报错!
./TestAOT ./TestAOT: /lib/x86_64-linux-gnu/libc.so.6: version
GLIBC_2.32' not found (required by ./TestAOT) ./TestAOT: /lib/x86_64-linux-gnu/libc.so.6: version
GLIBC_2.34' not found (required by ./TestAOT)
而在相同版本上的 Ubuntu Server 上,就能正常执行。。。详情参考: https://www.sunzhongwei.com/dotnet-8-aot
第二次是,在 docker alpine 容器内运行编译的 golang 程序,也是报这个错误。 如果使用 alpine 容器,就需要在编译 golang 代码时,增加编译配置。
CGO_ENABLED=0 go build
详情参考这个 https://www.sunzhongwei.com/docker-deploy-golang-in-production
解决方法
这次问题的解决方法也很简单,禁用 cgo 编译即可。
在编译 golang 代码时,设置环境变量 CGO_ENABLED=0
,然后重新编译即可。
CGO_ENABLED=0 go build
为何 glibc 版本不一致
因为我在 WSL Ubuntu 24.04 上编译的 golang 代码,而线上服务器是 Ubuntu 18.04。
家里的电脑环境:
> cat /etc/os-release
PRETTY_NAME="Ubuntu 24.04.1 LTS"
NAME="Ubuntu"
VERSION_ID="24.04"
VERSION="24.04.1 LTS (Noble Numbat)"
> getconf GNU_LIBC_VERSION
glibc 2.39
服务器的环境:
> cat /etc/os-release
NAME="Ubuntu"
VERSION="18.04.5 LTS (Bionic Beaver)"
> getconf GNU_LIBC_VERSION
glibc 2.27
而公司开发机是
- Ubuntu 20.04.2 LTS。
- glibc 2.31
很奇怪,我在公司开发机上编译的 golang 代码,在服务器上运行是正常的。 难道是因为 ubuntu 20.04 的 glibc 版本和 ubuntu 18.04 的 glibc 版本是兼容的? 但是从版本号上看,不应该是兼容的啊。。。摸不着头脑。
golang 与 glibc 的版本兼容性
参考
https://github.com/golang/go/issues/58550
AFAIU, for CGO_ENABLED=1, you'll need to use a glibc version during compile that matches (or is lower) than the one on your target runtime system.
也就是说,编译时,本机的 glibc 版本需要和运行时的 glibc 版本一致或更低,但是不能更高。而我在 WSL Ubuntu 24.04 上编译的代码,使用了 glibc 2.39,而服务器上是 glibc 2.27,所以会报错。
默认情况下,CGO_ENABLED 是开启的。只有交叉编译时,CGO_ENABLED 才会被禁用。
所以,还是手动设置 CGO_ENABLED=0
来禁用 cgo 编译,比较安全。
什么是 CGO 和 glibc
CGO is a feature of the Go programming language that allows Go programs to call C functions and access C data structures. This can be useful for interfacing with existing C libraries. However, using CGO can also introduce glibc dependencies into your Go program.
CGO 是 Go 编程语言的一个特性,它允许 Go 程序调用 C 函数和访问 C 数据结构。这对于与现有的 C 库进行交互非常有用。然而,使用 CGO 也可能会将 glibc 依赖引入到你的 Go 程序中。
glibc is the GNU C library, and it is the default C library on most Linux distributions. However, glibc versions can vary from one distribution to another. This can cause problems if you try to run a Go program that uses CGO on a different distribution than the one it was built on. For example, a Go program built on Ubuntu 22.04 will not run on Ubuntu 20.04.
glibc 是 GNU C 库,它是大多数 Linux 发行版上的默认 C 库。然而,glibc 的版本在不同的发行版之间可能会有所不同。如果你尝试在与构建时不同的发行版上运行使用 CGO 的 Go 程序,这可能会导致问题。例如,在 Ubuntu 22.04 上构建的 Go 程序将无法在 Ubuntu 20.04 上运行。
参考,最棒的一个解释文章:
https://www.elastiflow.com/blog/posts/disabling-cgo-to-remove-glibc-dependency
禁用 CGO 的好处
Portability: Disabling CGO makes Go programs more portable because they no longer depend on C libraries. This means that Go programs can be run on systems that do not have glibc installed, such as Alpine Linux and FreeBSD, or on different versions of the same operating system, like Ubuntu 20.04 and 22.04.
可移植性:禁用 CGO 可以使 Go 程序更具可移植性,因为它们不再依赖 C 库。这意味着 Go 程序可以在没有安装 glibc 的系统上运行,例如 Alpine Linux 和 FreeBSD,或者在同一操作系统的不同版本上运行,如 Ubuntu 20.04 和 22.04。
Security: Disabling CGO can make Go programs more secure because it reduces the program's attack surface. C libraries can contain security vulnerabilities, and disabling CGO prevents these vulnerabilities from being exploited in Go programs. See this vulnerability report of glibc in Debian 11 (Ubuntu 20.04).
安全性:禁用 CGO 可以使 Go 程序更安全,因为它减少了程序的攻击面。C 库可能包含安全漏洞,禁用 CGO 可以防止这些漏洞在 Go 程序中被利用。请参阅 Debian 11(Ubuntu 20.04)中 glibc 的此漏洞报告。
Performance: In most cases, disabling CGO can improve the performance of Go programs. For an example, see CockroachDB's article on The cost and complexity of Cgo.
性能:在大多数情况下,禁用 CGO 可以提高 Go 程序的性能。有关示例,请参阅 CockroachDB 关于 Cgo 的成本和复杂性的文章。
禁用 CGO 的缺点
Functionality: Disabling CGO may limit the functionality of Go programs. There are C libraries that do not have an equivalent Go module, therefore making it more challenging to integrate with such systems.
功能性:禁用 CGO 可能会限制 Go 程序的功能。有些 C 库没有等效的 Go 模块。
Performance: Theoretically, disabling CGO can degrade the performance of Go programs. This is because Go will have to implement some functionality in pure Go, which may be less efficient than using a C library. However, this is theoretical because the overhead of invoking C libraries likely outweighs the implementation performance difference.
性能:理论上,禁用 CGO 可能会降低 Go 程序的性能。这是因为 Go 将不得不在纯 Go 中实现某些功能,这可能不如使用 C 库高效。然而,这只是理论上的,因为调用 C 库的开销可能超过实现性能差异。
总结
- 禁用 CGO 是消除 Go 程序中 glibc 依赖的方法。可以使 go 程序更具可移植性,并且在不同的 Linux 发行版上运行时不太可能出现问题。
- 自动化部署脚本,需要备份之前版本的可执行文件。如果出现类似问题,可以快速恢复。
- 如果不得不启用 CGO, 在 docker 相同版本的 ubuntu 容器中编译 golang 项目
- systemd 服务重启时,如果 status 失败,立即显示错误日志,快速定位问题
微信关注我哦 👍
我是来自山东烟台的一名开发者,有感兴趣的话题,或者软件开发需求,欢迎加微信 zhongwei 聊聊, 查看更多联系方式