Alpine镜像中not found引出的gnu libc和musl libc的争论

在一个使用旧版的Oracle的JDK的Alpine版本的镜像时出现了问题,这篇文章作为后续的整理,以此为契机,简单介绍一下Alpine版本中的musl libc和gnu libc的设定。

事前准备

  • 运行alpine容器
    准备一个Alpine镜像,这里使用3.9的版本,并将Alpine容器运行起来,容器名为alpine
liumiaocn:multibytes liumiao$ docker run --name alpine --rm -it alpine:3.9 sh
/ # 
  • 准备可执行文件
    这里准备旧版的JDK,取7u79,并将其拷贝至alpine容器的/tmp目录下
liumiaocn:Desktop liumiao$ docker cp jdk-7u79-linux-x64.tar.gz alpine:/tmp
liumiaocn:Desktop liumiao$
  • 解压JDK至/usr/local/share/java
/ # cd /tmp
/tmp # ls
jdk-7u79-linux-x64.tar.gz
/tmp # 
/tmp # mkdir -p /usr/local/share/java
tar xzf jdk-7u79-linux-x64.tar.gz -C /usr/local/share/java
/tmp # cd /usr/local/share/java
/usr/local/share/java # ls
jdk1.7.0_79
/usr/local/share/java #

现象

JDK只需要设定可执行目录至PATH搜索范围中即可,设定之后发现使用which可以找到java可执行文件,但是执行java -version却提示java not found, 所以现象的提示显得非常诡异。

~ # export PATH=$PATH:/usr/local/share/java/jdk1.7.0_79/bin
~ # which java
/usr/local/share/java/jdk1.7.0_79/bin/java
~ # 
~ # java -version
sh: java: not found
~ # 
  • ldd错误
    动态连接库错误,ldd提示信息如下所示
~ # which java
/usr/local/share/java/jdk1.7.0_79/bin/java
~ # ldd /usr/local/share/java/jdk1.7.0_79/bin/java
	/lib64/ld-linux-x86-64.so.2 (0x7f5c1184c000)
	libpthread.so.0 => /lib64/ld-linux-x86-64.so.2 (0x7f5c1184c000)
	libjli.so => /usr/local/share/java/jdk1.7.0_79/bin/../lib/amd64/jli/libjli.so (0x7f5c11635000)
	libdl.so.2 => /lib64/ld-linux-x86-64.so.2 (0x7f5c1184c000)
	libc.so.6 => /lib64/ld-linux-x86-64.so.2 (0x7f5c1184c000)
Error relocating /usr/local/share/java/jdk1.7.0_79/bin/../lib/amd64/jli/libjli.so: __rawmemchr: symbol not found
~ #

原因与对应

ldd错误看起来好像是jli的的relocating提示错误信息,而实际上由于Alpine镜像使用的根本不是gnu libc而是musl libc,所以/lib64/ld-linux-x86-64.so.2是不存在的,而实际上/lib64都是不存在的。

~ # ls /lib64
ls: /lib64: No such file or directory
~ #

gnu libc和musl libc号称兼容(部分兼容),基于缺什么补什么的原则,做个软链补上即可(stack overflow也有人有同样方法进行过验证)。由于Oracle的Java并不是完整的源码提供,所以Alpine也无法拿到源码去全编译来解决这个问题,大概这就是在Alpine镜像中更多的是OpenJDK的原因。

~ # mkdir /lib64
~ # ln -s /lib/libc.musl-x86_64.so.1 /lib64/ld-linux-x86-64.so.2
~ # 

这个问题一旦得到对应,现象就不再诡异,再次执行java命令,中规中矩地提示了缺少的动态链接库

~ # java -version
Error relocating /usr/local/share/java/jdk1.7.0_79/bin/../lib/amd64/jli/libjli.so: __rawmemchr: symbol not found
~ # 

gnu libc和musl libc的争论

顺这个问题在Alpine的issue中很快找到了一个几年前的issue,写的很详细,jirutka 小哥还提到了Alpine的3S(Small/Simple/Secure)哲学,并坚持认为不应该在Alpine里面添加对于gnu libc的支持, 以下为轻松时间,可以顺便学点,学不到也完全不要紧,我们主要是坐第一排看热闹。

他们的现象跟本文的现象完全一样不同的是Oracle的JDK8,不同的是非常快得就有人帮着定位出了根本原因,而我查了几个小时
Alpine镜像中not found引出的gnu libc和musl libc的争论_第1张图片
jirutka 小哥说,如果使用Oracle版本的JDK,或者类似需求,你应该去找支持glibc的linux发行版
Alpine镜像中not found引出的gnu libc和musl libc的争论_第2张图片
再次甩给Oracle,该他们管,Alpine的已经有MUSL了,谁用Glibc谁自己负责,另外代码不全部公开Alpine也无能为力。而且还认为,使用了glibc的Alpine就不再是Alpine!是的,会变成奥特曼的
Alpine镜像中not found引出的gnu libc和musl libc的争论_第3张图片
有人提出两个libc能不能一起来用,小哥坚持认为两个libc不是好的方法
Alpine镜像中not found引出的gnu libc和musl libc的争论_第4张图片
专门有人去跟Oracle确认了一下,结论是没有好的办法,不过可以花钱来寻求专业帮助
Alpine镜像中not found引出的gnu libc和musl libc的争论_第5张图片
另外一位小哥总结了一下:要么用指定的所谓的标准版本,像使用Alpine这种非标准方式就只好花钱雇人了
在这里插入图片描述
而这些终于在这个issue中给得到了解决,由于没有热闹可看,请读者自行阅读

  • https://github.com/ibmdb/node-ibm_db/issues/217

使用的相关内容在这里:

  • https://github.com/sgerrand/alpine-pkg-glibc

简单来说,解决的方法就是在Alpine里面安装glibc,让Alpine不再是Alpine

  • 详细看这个issue
    觉得还想看的话,自己去看这个issue
    https://github.com/gliderlabs/docker-alpine/issues/11

验证

看完热闹,现在花1分钟快速解决一下遗留问题。重新回到问题现场。按照如下三步骤进行安装

  • 步骤1: 下载key
~ # wget -q -O /etc/apk/keys/sgerrand.rsa.pub https://alpine-pkgs.sgerrand.com/sgerrand.rsa.pub
~ # echo $?
0
~ # ls /etc/apk/keys/sgerrand.rsa.pub
/etc/apk/keys/sgerrand.rsa.pub
~ #
  • 步骤2: 下载apk安装文件
~ # wget https://github.com/sgerrand/alpine-pkg-glibc/releases/download/2.29-r0/glibc-2.29-r0.apk
Connecting to github.com (13.229.188.59:443)
Connecting to github-production-release-asset-2e65be.s3.amazonaws.com (52.216.176.203:443)
glibc-2.29-r0.apk    100% |****************************************************************************************| 2006k  0:00:00 ETA
~ # ls
glibc-2.29-r0.apk
~ #
  • 步骤3: 安装
~ # apk add glibc-2.29-r0.apk
(1/1) Installing glibc (2.29-r0)
OK: 9 MiB in 15 packages
~ # 

虽然ldd不能正常使用,但是实际上已经能够执行了,这是因为设定了RPATH。因为没有readelf命令,这里就不再展示了。

~ # ldd /usr/local/share/java/jdk1.7.0_79/bin/java
	/lib64/ld-linux-x86-64.so.2 (0x7f83ae582000)
	libpthread.so.0 => /lib64/ld-linux-x86-64.so.2 (0x7f83ae582000)
	libjli.so => /usr/local/share/java/jdk1.7.0_79/bin/../lib/amd64/jli/libjli.so (0x7f83ae36b000)
	libdl.so.2 => /lib64/ld-linux-x86-64.so.2 (0x7f83ae582000)
	libc.so.6 => /lib64/ld-linux-x86-64.so.2 (0x7f83ae582000)
Error relocating /usr/local/share/java/jdk1.7.0_79/bin/../lib/amd64/jli/libjli.so: __rawmemchr: symbol not found
~ # 

可以看到已经可以正常使用了, ldd对使用者来说,基本就是透明的。先凑合用吧。

~ # java -version
java version "1.7.0_79"
Java(TM) SE Runtime Environment (build 1.7.0_79-b15)
Java HotSpot(TM) 64-Bit Server VM (build 24.79-b02, mixed mode)
~ # 

总结

其实小哥说的对,这种拧着做的方法确实不值得推荐,但毕竟生活有很多无奈,且行且拧吧。

参考文档

https://stackoverflow.com/questions/34729748/installed-go-binary-not-found-in-path-on-alpine-linux-docker
https://github.com/docker-library/openjdk/issues/77
https://github.com/gliderlabs/docker-alpine/issues/11
https://github.com/sgerrand/alpine-pkg-glibc/releases

你可能感兴趣的:(#,深入浅出Docker,oracle,jdk,alpine,musl,libc)