AlpineでRustのバイナリを動かす

Rustのプログラムをビルドする時に静的リンクにするようにして、ビルドしてできたバイナリをDockerコンテナのAlpineで動かすまでの話です。

cargo buildについて

Rustのパッケージマネージャ兼ビルドシステムのcargoというものがあります。まぁRust書いてる人なら知らない人はいないと思います。デフォルトではcrago buildは動的リンクでバイナリファイルを作成します。確認してみましょう。

$ cargo new hello --bin
     Created binary (application) `hello` project
$ cd hello
$ cargo build --release
   Compiling hello v0.1.0 (file:///usr/src/hello)
    Finished release [optimized] target(s) in 2.78 secs
$ ./target/release/hello
Hello, world!
$ file ./target/release/hello
./target/release/hello: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV),dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=9cdb401d93645d19032e0850e1b283feb270f491, not stripped
$ ldd ./target/release/hello
        linux-vdso.so.1 (0x00007ffdffb94000)
        libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f182e9ec000)
        librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007f182e7e4000)
        libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f182e5c7000)
        libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f182e3b0000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f182e011000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f182ee41000)
$

さて、このバイナリをAlpineで動かしたいんですが、この超軽量のOSにはlibcが入っておらず動的ライブラリは存在しません。つまりこのバイナリをAlpineにコピーしてきてそのまま動かすことはできません。

$ docker run -itd --rm --name al alpine:3.6 /bin/sh
5beae80c6a9b7d45b3820012abf62c1fad28b73f104bb91875ee8b1a2e6a270d
$ docker cp hello al:/
$ docker attach al
/ # ls -l hello
-rwxr-xr-x    1 501      dialout    3655624 Oct 13 15:16 hello
/ # /hello
/bin/sh: /hello: not found
/ #

そんなわけで静的リンクに変更してRustプログラムのビルドを行います。

どうしても動的リンクのバイナリを動かしたい場合はここのQiitaの記事を参考にすると良いと思います。

qiita.com

静的リンクのバイナリを作成する

muslのRustをインストールします。

https://rust-lang-ja.github.io/the-rust-programming-language-ja/1.6/book/advanced-linking.html

手順長いしめんどくせーなと思った人はこれ使うといいかもしれないです

github.com

rust-musl-builderはDockerイメージも用意されてます。実際に使ってみました。

host$ docker run -itd --rm --name rustbuil ekidd/rust-musl-builder:stable /bin/bash
820af56751ce293d22b796c8bc4838b8a91deea01e8528cabbfa6dc7f61a0b36
host$ docker attach rustbuil 
rust@820af56751ce:~/src$ 
rust@820af56751ce:~/src$ export USER=rust
rust@820af56751ce:~/src$ cargo new hello --bin
     Created binary (application) `hello` project
rust@820af56751ce:~/src$ cd hello/
rust@820af56751ce:~/src/hello$ cargo build --release
   Compiling hello v0.1.0 (file:///home/rust/src/hello)
    Finished release [optimized] target(s) in 1.42 secs
rust@820af56751ce:~/src/hello$ cd ./target/x86_64-unknown-linux-musl/release/
rust@820af56751ce:~/src/hello/target/x86_64-unknown-linux-musl/release$ ./hello
Hello, world!
rust@820af56751ce:~/src/hello/target/x86_64-unknown-linux-musl/release$ file hello
hello: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, BuildID[sha1]=e0b4652a02afe0963acacca0c7631c621f39ac3c, not stripped
rust@820af56751ce:~/src/hello/target/x86_64-unknown-linux-musl/release$ ldd hello
        not a dynamic executable
rust@820af56751ce:~/src/hello/target/x86_64-unknown-linux-musl/release$

通常のcargo buildとは違ってバイナリファイルができる場所がtarget/x86_64-unknown-linux-musl/releaseの下になっています。見事に静的リンクでビルドできたようです。ではAlpineに移して実行してみましょう。

host$ docker cp rustbuil:/home/rust/src/hello/target/x86_64-unknown-linux-musl/release/hello .
host$ docker cp hello al:/
host$ docker attach al
/ # ls -l hello
-rwxr-xr-x    1 501      dialout    3896520 Oct 13 15:57 hello
/ # /hello
Hello, world!
/ #

動作しました〜