分类

在 docker 中编译 rust 程序

2019-08-01 16:08 rust

为了保证一致的编译环境, 可以考虑把代码放在 docker 中编译.

做法也很简单, 下面以之前用到的 musl-demo 示例程序为例来说明一下.

Cargo.toml 文件内容:

[package]
name = "musl-demo"
version = "0.1.0"
authors = ["Xu Shaohua <shaohua@biofan.org>"]
edition = "2018"

[dependencies]
openssl = "0.10.24"
rust_sodium = "0.10.2"

[profile.release]
lto = true

main.rs 文件内容:

extern crate openssl;
extern crate rust_sodium;

use openssl::ssl::{SslConnector, SslMethod};

fn test_openssl() {
    let mut ctx = SslConnector::builder(SslMethod::tls()).unwrap();

    // set_ciphersuites was added in OpenSSL 1.1.1, so we can only call it when linking against that version
    #[cfg(openssl111)]
    ctx.set_ciphersuites("TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256").unwrap();
}

fn test_sodium() {
    println!("version of sodium: {}.{}",
        rust_sodium::version::version_major(),
        rust_sodium::version::version_minor()
        );
}

fn main() {
    test_openssl();
    test_sodium();
}

使用本地的包依赖

默认情况下, cargo 会从 crates.io 下载依赖包, 这对于持续构建的环境来说并不合适. 还好, 在新版本中 cargo 加入了 vendor 命令, 可以将依赖包缓存到本地.

$ cargo vendor

会在项目根目录生成 vendor/ 目录. 然后根据提示, 在 .cargo/config 文件中加入:

[source.crates-io]
replace-with = "vendored-sources"

[source.vendored-sources]
directory = "vendor"

现在来看, cargo vendor 还有一些问题, 比如生成的 vendor 目录比较大, 这个很简单的 项目, 里包含了超过 100M 的文件, 里面竟然还有一些测试用的数据文件, 其实都是可以 删除的.

编写 Dockerfile

上面的 Cargo.toml 里面, 引入了两个本地 C 库的依赖, openssl 和 libsodium. 这个在 编译时要安装了相应的 C 库的, 我们都可以在 Dockerfile 中事先安装完.

Docker hub 中提供了 x86/x86_64/arm64 这三个版本的 rust 稳定版镜像. 我们用的是 1.36-buster.

以下定义了用于构建 musl-demo 项目的 docker 镜像:

FROM rust:1.36-buster
LABEL Build environment for musl-demo project

RUN echo 'deb http://ftp.cn.debian.org/debian/ stable main' > /etc/apt/sources.list
RUN apt update
RUN apt install libssl-dev libsodium-dev build-essential binutils upx -y

CMD ["/bin/bash"]

然后生成编译时的 docker 镜像:

$ sudo docker build -t musl-demo-build .

开始编译

使用刚刚生成的 docker 镜像编译项目:

$ sudo docker run --rm --user ${uid -u}:${uid -g} \
        -v ${PWD}/..:/usr/src/musl-demo \
        -w /usr/src/musl-demo \
        musl-build cargo build --release

可以压缩一下生成的可执行文件:

$ sudo docker run --rm --user ${uid -u}:${uid -g} \
        -v ${PWD}/..:/usr/src/musl-demo \
        -w /usr/src/musl-demo \
        musl-build strip target/release/musl-demo
$ sudo docker run --rm --user ${uid -u}:${uid -g} \
        -v ${PWD}/..:/usr/src/musl-demo \
        -w /usr/src/musl-demo \
        musl-build upx target/release/musl-demo

在服务器上运行 musl-demo

先将生成的 musl-demo 可执行文件同步到远程服务器, 再在服务器上生成一个运行时的 docker 镜像.

FROM musl-build
LABEL Runtime environment for musl-demo project

COPY musl-demo /opt/

CMD ["/opt/musl-demo"]

参考