Rustのデプロイ用イメージを作成したら苦労した話
この記事は物工/計数 Advent Calendar 2021 2日目の記事です。
1日目はこちら:研究室選びの豆知識
先日某所で開発しているRustのプロジェクトをECSに載せることになり、デプロイ用のDockerイメージを作成したらそれなりに苦労したので備忘録としてまとめます。
TL;DR
- rust-musl-builderを使う
- MySQL使っているならmysqlclient-sysも追加
- ECR Public Gallery活用していこう
まずは普通にやってみる
定石通りマルチステージビルドにして、buildステージではreleaseオプションをつけてコンパイル cargo build --release
し、できたバイナリファイルをdeployステージで実行します
FROM rust:1.54.0 as build
WORKDIR /app
COPY Cargo.toml Cargo.toml
RUN mkdir src
RUN echo "fn main(){}" > src/main.rs
RUN cargo build --release
COPY . .
RUN rm -f target/release/deps/app*
RUN cargo build --release
FROM debian:10.4 as deploy
COPY /app/target/release/main /usr/local/bin/myapp
CMD ["myapp"]
すると次のようなエラーが表示されます。
error while loading shared libraries: libssl.so.1.0.0: cannot open shared object file: No such file or directory
私はこれまで任意の言語はコンパイルしたら完全な実行可能ファイルを作ってくれるものだと勝手に思い込んでいましたが、どうやら世の中にはshared objectなるものが存在し、ランタイムに動的リンクを構成して実行するものも存在しているらしい。
静的リンクのみでコンパイルする
軽く調べたところmuslというライブラリを用いてビルドすると静的リンクのみでバイナリを書き出してくれるようです。そこで次のようにビルドターゲットを変更して再挑戦(事前に x86_64-unknown-linux-musl
ターゲットを追加しておく必要がある)
rustup target add x86_64-unknown-linux-musl
cargo build --release --target=x86_64-unknown-linux-musl
これで行けるかと思いきや新たに次のようなエラーが発生。
error: failed to run custom build command for `openssl-sys v0.9.54`
--- stderr
thread 'main' panicked at '
どうやら openssl
を使用しているとmuslでのビルドがデフォルトでは上手くいかないらしい
巨人の肩に乗る
大変ありがたいことに先人がmuslビルド用の専用イメージを用意してくださっていました。このイメージを使ってビルドするとよしなに openssl
まわりのリンクを解決してくれます。
というわけでこれを使用して Dockerfile
を次のように更新します。私のプロジェクトでは mysqlclient
が必要だったので追加していますが、不要な場合は消してもらって大丈夫です。
FROM ekidd/rust-musl-builder:1.48.0 AS builder
# install mysql client
USER root
RUN sudo apt-get update && sudo apt-get install -y libmysqlclient-dev
# build
USER rust
ADD . ./
RUN cargo build --release
FROM alpine:3.14.3 AS deploy
RUN apk --no-cache add ca-certificates
WORKDIR /app
COPY \
/home/rust/src/target/x86_64-unknown-linux-musl/release/main \
./main
CMD ["/app/main"]
READMEには openssl
使用時はopenssl-probeを入れるよう書いてあったのですが、いまのところ入れなくても動いているのでこの点については後日調べようと思います。
MySQLを使用する場合はもうひと踏ん張り
libmysqlclient-dev
に静的リンクを貼るためにさらに次のクレートを追加する必要があります。
Cargo.toml
に以下を追加
[patch.crates-io]
mysqlclient-sys = { git = "https://github.com/pzmarzly/mysqlclient-sys", rev = "acd1b2b" }
私の場合は以上の手順で正常に動作することが確認できました。
【おまけ】AWSならDockerHubでなくECRを使おう
DockerHubはanonymousからのPullに上限がかけられているのでCodeBuild等でビルドしているとリクエスト上限で失敗することがそれなりの頻度であります。従来はDockerHubに登録してCodeBuild内でユーザー情報を指定する方法がよくとられていたようですが、最近はECR Public Galleryなるものが登場したのでこちらを利用する方が良いかと思います。ただ、現状はまだDockerHubほど公式イメージが充実していないので、そういったものは自前でビルドしてprivateリポジトリに保存しておくのが良いかもしれません。
おわりに
コンパイル奥が深い....
Discussion