7zip-bin in Alpine Docker

Docker

Node.js 的 Docker 有基于 Alpine 的版本。在这个 Docker 中使用 7zip-bin 库的时候遇到了错误,无法正常启动。

一个简单的重现 Dockerfile 可以这么写:

FROM node:10-alpine

RUN mkdir -p example && \
  cd example && \
  yarn init -y && \
  yarn add 7zip-bin && \
  mkdir /lib64 && \
  ln -s /lib/libc.musl-x86_64.so.1 /lib64/ld-linux-x86-64.so.2

ADD run.sh /run.sh

RUN chmod +x /run.sh

CMD ["/run.sh"]

其中,run.sh 可以写:

cd example
$(node -e "console.log(require('7zip-bin').path7za)")

报错的内容是:

/run.sh: line 2: /example/node_modules/7zip-bin/linux/x64/7za: not found

通过进入 Docker 内部观察不难发现,/example/node_modules/7zip-bin/linux/x64/7za 这个文件实际是真实存在的,但是在使用的时候系统却报错 not found。造成这一问题的原因,可能是动态库缺失。

通过 ldd 命令可以列出动态库依赖关系(文档):

ldd /example/node_modules/7zip-bin/linux/x64/7za

输出结果是:

/lib64/ld-linux-x86-64.so.2 (0x7febe540e000)
libpthread.so.0 => /lib64/ld-linux-x86-64.so.2 (0x7febe540e000)
libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0x7febe52b9000)
libm.so.6 => /lib64/ld-linux-x86-64.so.2 (0x7febe540e000)
libgcc_s.so.1 => /usr/lib/libgcc_s.so.1 (0x7febe52a5000)
libc.so.6 => /lib64/ld-linux-x86-64.so.2 (0x7febe540e000)

注意到缺少了 /lib64/ld-linux-x86-64.so.2 这个动态库,因此导致了 7zip-bin 这个库无法正常使用。造成这个的原因是,Alpine 使用的是 musl,而 7zip-bin 使用的二进制文件是基于 glibc 编译出来的。要解决这个问题,有两种思路:

  1. 在 Alpine 中安装 libc 的兼容库:RUN apk add --no-cache libc6-compat
  2. 或者,ln -s /lib/libc.musl-x86_64.so.1 /lib/ld-linux-x86-64.so.2 将 musl 的版本软连过去,直接让 7zip-bin 的二进制使用

当然,最佳的方案是不使用 7zip-bin 中的 pre-build 版本,而改用 Alpine 的 p7zip 版本。用 Alpine 的包管理器安装好 pz7ip 之后(apk add p7zip),使用类似下面的代码直接替换脚本就好了:

cp $(type -p 7za) $(node -p "require('7zip-bin').path7za")

参考链接

  • 在 7zip-bin issue 中的相关讨论
  • 重现的配置代码 gist
  • node-gyp 在 Alpine 中也可能会遇到类似的问题,在这里可以找到相关的讨论