Rust编译Linux通用可执行文件

问题描述

在glibc环境编译的Rust可执行文件,不能在Docker的scratch镜像中运行。

把一个Rust二进制项目,编译成一个可执行文件,并且可以在任意Linux系统独立运行—— 这应该是一个Rust二进制项目的默认需求,Golang也能默认做到。 但实际尝试会发现,Rust不行。

因为绝大部分Linux环境,都是基于GNU,包括主流的Ubuntu、Debian、RedHat等。 而Rust在这些环境编译时,glibc默认不会被打包进去。 结果导致编译后的二进制文件,对glibc的so有依赖,而且有版本限制。 不仅不能在非glibc环境运行(如Alpine),也不能在glibc过低的环境运行。

这不知道是出于技术原因,还是GPL协议原因。

解决方案

glibc不行,使用musl的libc即可。

rustup target add x86_64-unknown-linux-musl

本文在Deepin上撰写,默认的target是x86_64-unknown-linux-gnu

实战演示

cargo init demo
cd demo
cargo build -r
cargo build -r --target=x86_64-unknown-linux-musl

这里利用Cargo生成了一个demo项目,它可以编译出一个可执行文件demo。 并且使用默认(gnu)和musl分别编译了一个可执行文件。

然后新增一个Dockerfile文件,添加以下内容:

FROM scratch
COPY ./target/release/demo /demo0
COPY ./target/x86_64-unknown-linux-musl/release/demo /demo1

并且构建一个本地测试镜像:

docker build -t demo .

接下来,分别对demo0demo1进行运行测试:

$ docker run --rm demo /demo0
standard_init_linux.go:207: exec user process caused "no such file or directory"
$ sudo docker run --rm demo /demo1
Hello, world!

可见,在scratch(啥也没有的基础镜像)中, 基于gnu的demo0不能正常运行,而基于musl的demo1则没有问题。

上面的demo0有4.1MB,而demo1由于多打包了libc进去,有4.3MB。

其它信息


相关笔记