我想制作一些奇怪的容器,我的任务之一是替换uname
二进制文件。
我编码了它:
#include <stdio.h>
int main() {
printf("This is a fake uname\n");
return 0;
}
编译它:
gcc uname.c -o uname
当我在我的 ubuntu 上运行它时它工作得很好。
我创建了一个Dockerfile
并将其复制到图像中:
cat > Dockerfile <<EOF
FROM alpine:latest
RUN rm /bin/uname
COPY uname /bin/
ENTRYPOINT sh;
WORKDIR /home
EOF
并构建它docker build -t myimage -f Dockerfile .
当我运行图像时:
docker run -it --rm myimage
该文件存在,但当我尝试运行它时,它写道它不存在:
/home # uname
sh: uname: not found
/home # ls -lia uname
ls: uname: No such file or directory
/home # ls -lia /bin/uname
35 -rwxr-xr-x 1 root root 8600 Jan 29 13:27 /bin/uname
答案1
问题是您构建二进制文件glibc
(因为您使用的是 Ubuntu),但然后在 Alpine 上运行它(它使用的是 Ubuntu musl
)。
当您构建二进制文件时,一些元数据会被硬编码到其中,特别是口译员(动态链接器)位置。在二进制执行期间,内核使用此解释器来加载二进制文件及其所有运行时依赖项。当您针对 构建时glibc
,二进制文件会请求glibc
的解释器(类似于/lib64/ld-linux-x86-64.so.2
):
$ readelf -l /bin/uname
<...>
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
但Alpine下没有这样的解释器。相反, Alpine/lib/ld-musl-x86_64.so.1
的所有二进制文件都是为了请求这个解释器而构建的:
# readelf -l /bin/busybox
<...>
[Requesting program interpreter: /lib/ld-musl-x86_64.so.1]
所以,当你构建你的uname
在 Ubuntu 中构建然后在 Alpine 中运行它时,内核无法找到解释器,因此它返回“未找到”。
有趣的是,您可以手动调用解释器,如果问题是这样的话,这可能会起作用仅有的在解释器中:
# /lib/ld-musl-x86_64.so.1 /bin/uname
This is a fake uname
但这当然只是为了排除故障。
那么该怎么办?
您的选择是静态链接您的二进制文件(从而不需要解释器和所有共享库,以便内核可以自行加载和运行二进制文件):
gcc uname.c -static -o uname
或者在 Alpine 下构建它(也许可以尝试 Docker 的多阶段构建?我会推荐这种方法,因为在这种情况下,您明确声明您的意图并表明您在 Alpine 中构建自定义二进制文件以替换原始 Alpine 的二进制文件)。