LoginSignup
1
1
個人開発エンジニア応援 - 個人開発の成果や知見を共有しよう!-

Rustで作ったバイナリをscratchコンテナで動かそう

Last updated at Posted at 2023-09-23

Rustで作ったバイナリをscratchコンテナで動かしたい

Rustで作ったバイナリ、scratchコンテナで動かしたいですよね。

なお、この記事を書くにあたってO'Reilly DockerO'Reilly プログラミングRust 第2版を参考にしました。

やってみる

試しに以下の3ファイルを用意してHello Worldを作ってみましょう。

Dockerfile
FROM rust:latest as build

WORKDIR /helloworld
COPY ./helloworld /helloworld
RUN cargo build --release

FROM scratch
COPY --from=build /helloworld/target/release/helloworld /helloworld
CMD [ "/helloworld" ]
helloworld/src/main.rs
fn main() {
    println!("Hello, World!");
}
helloworld/Cargo.toml
[package]
name = "helloworld"
version = "0.1.0"
edition = "2021"

[profile.release]
codegen-units = 1
opt-level = 3 
debug = false
strip = "symbols"
debug-assertions = false
lto = true

[dependencies]

そして以下のように起動します。

$ docker build -t helloworld .
$ docker run --rm helloworld

すると、以下のようなエラーが発生します。

exec /helloworld: no such file or directory

なぜなのでしょうか?

調査

では今回生成されたバイナリをlddで見てみます。

# ldd /helloworld/target/release/helloworld
        linux-vdso.so.1 (0x00007fffcc59a000)
        libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007fd22875e000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fd22857d000)
        /lib64/ld-linux-x86-64.so.2 (0x00007fd2287e3000)

どうやらglibcに動的リンクしているようです。
scratchコンテナはglibcを含んでいないので、エラーが発生しているようです。

解決策1: cc-distrolessで動かす

glibcを含んだ軽量コンテナであるdistroless/cc-debian12で動かしてみましょう。
Dockerfileを修正します。

Dockerfile
FROM rust:latest as build

WORKDIR /helloworld
COPY ./helloworld /helloworld
RUN cargo build --release

FROM gcr.io/distroless/cc-debian12:latest
COPY --from=build /helloworld/target/release/helloworld /helloworld
CMD [ "/helloworld" ]

起動してみましょう。

$ docker build -t cc-helloworld .
$ docker run --rm cc-helloworld
Hello, World!

無事に動かせました。

補足: base-debian12はglibcを含んでいない

ちなみにdistroless/base-debian12はglibcを含んでいないため、

Dockerfile
FROM rust:latest as build

WORKDIR /helloworld
COPY ./helloworld /helloworld
RUN cargo build --release

FROM gcr.io/distroless/base-debian12:latest
COPY --from=build /helloworld/target/release/helloworld /helloworld
CMD [ "/helloworld" ]

このようにbase-debian12を使用すると、

/helloworld: error while loading shared libraries: libgcc_s.so.1: cannot open shared object file: No such file or directory

glibcが無いと怒られます。

解決策2: muslでビルドする

意地でもscratchコンテナで動かしたい場合、単一バイナリに静的リンクさせます。Rustならmuslでビルドすると便利です。また、ビルドにはrust:alpineコンテナを使用すると便利です。

Dockerfile
FROM rust:alpine as build

WORKDIR /helloworld
COPY ./helloworld /helloworld
RUN cargo build --release --target=x86_64-unknown-linux-musl

FROM scratch
COPY --from=build /helloworld/target/x86_64-unknown-linux-musl/release/helloworld /helloworld
CMD [ "/helloworld" ]

すると、

$ docker build -t musl-helloworld .
$ docker run --rm musl-helloworld
Hello, World!

無事、scratchコンテナで動きました。
今回生成されたバイナリをlddで見てみると、

$ ldd /helloworld/target/x86_64-unknown-linux-musl/release/helloworld
        /lib/ld-musl-x86_64.so.1 (0x7f3b10e8e000)

musl libcに静的リンクしたバイナリを生成しています。つまりバイナリファイルにライブラリが埋め込まれている状態になっています。そのためscratchコンテナでも動くのです。

感想

今回生成したcc-helloworldコンテナは27.98MB、musl-helloworldコンテナは440.44KBでした。
どちらも非常に軽量です。
みなさんもdistrolessやscratchを利用して快適なDockerライフを!

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1