普段の開発時にソースコードを書き換える度にdocker buildしているとめんどくさいので
ホスト側のソースコードのディレクトリをdockerコンテナにマウントしてコンテナ上でコンパイルして開発しているのですが
どうもコンパイルが異常に遅いので、いろいろ試したのでメモ
環境
- OS
- macOS Mojave 10.14
- rustc
- 1.30.0 (da5f414c2 2018-10-24)
- docker
- Docker for mac Version 2.0.0.0-beta1-mac75 (27117)
ビルドするアプリケーション
ちょっと前にactix-web+vuejsを試してみる為に作ったリポジトリがあるので、こちらで試します。
version: '3.1'
services:
postgres:
container_name: postgres
image: postgres:11
restart: always
environment:
POSTGRES_DB: webapp
POSTGRES_USER: dbuser
POSTGRES_PASSWORD: password
ports:
- "5432:5432"
api:
container_name: api
build:
context: .
command: cargo run
volumes:
- .:/app
- ./tmp/.cargo/registry:/root/.cargo/registry
- ./tmp/.cache:/root/.cache
depends_on:
- postgres
ports:
- "28000:8000"
web:
container_name: web
build:
context: .
working_dir: '/app/webapp'
command: yarn run serve
ports:
- "28080:8080"
volumes:
- .:/app
depends_on:
- api
- ルートディレクトリをまるっと
/app
にマウント - registryはcrate追加した時などにキャッシュしたいのでマウント
-
api
が今回メインとなるRustのアプリケーション
まずは普通にコンテナ内でビルドしてみる
前提
- 一回ビルドしていて、依存ライブラリのコンパイルは終わっている
- アプリケーション内のコードを書き換えて、ビルドする
結果
root@6fd7810d005f:/app# cargo build
Compiling rust_webapp_starter v0.1.0 (/app)
Finished dev [unoptimized + debuginfo] target(s) in 1m 02s
1m 02s
!!!
1分以上かかっています
※今回使っているサンプルアプリケーションは複雑なものでもないので、この程度ですが、今仕事で書いているアプリケーションは5−6分かかってました。。。
何が悪いのか?
registryをマウントしているのが悪いのか?
registryをマウントしないでビルドしてみまず
...
api:
container_name: api
build:
context: .
command: cargo run
volumes:
- .:/app
depends_on:
- postgres
ports:
- "28000:8000"
...
root@5b82f1de979d:/app# cargo build
Compiling rust_webapp_starter v0.1.0 (/app)
Finished dev [unoptimized + debuginfo] target(s) in 57.28s
誤差ですね。ほぼ変わらず。
マウントしているディレクトリ上にtargetがあるのが悪いのか?
cargo buildってtargetディレクトリのpath変えられるのだろうか?
オプションを見てみる
root@5b82f1de979d:/app# cargo build -h
cargo-build
Compile a local package and all of its dependencies
USAGE:
cargo build [OPTIONS]
OPTIONS:
-p, --package <SPEC>... Package to build
--all Build all packages in the workspace
--exclude <SPEC>... Exclude packages from the build
-j, --jobs <N> Number of parallel jobs, defaults to # of CPUs
--lib Build only this package's library
--bin <NAME>... Build only the specified binary
--bins Build all binaries
--example <NAME>... Build only the specified example
--examples Build all examples
--test <NAME>... Build only the specified test target
--tests Build all tests
--bench <NAME>... Build only the specified bench target
--benches Build all benches
--all-targets Build all targets (lib and bin targets by default)
--release Build artifacts in release mode, with optimizations
--features <FEATURES> Space-separated list of features to activate
--all-features Activate all available features
--no-default-features Do not activate the `default` feature
--target <TRIPLE> Build for the target triple
--target-dir <DIRECTORY> Directory for all generated artifacts
--out-dir <PATH> Copy final artifacts to this directory
--manifest-path <PATH> Path to Cargo.toml
--message-format <FMT> Error format [default: human] [possible values: human, json, short]
--build-plan Output the build plan in JSON
-v, --verbose Use verbose output (-vv very verbose/build.rs output)
-q, --quiet No output printed to stdout
--color <WHEN> Coloring: auto, always, never
--frozen Require Cargo.lock and cache are up to date
--locked Require Cargo.lock is up to date
-Z <FLAG>... Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details
-h, --help Prints help information
...
--target-dir <DIRECTORY>
発見!
試してみます。
1回目はtaget-dirが空っぽなので、ライブラリ系のコンパイルも行われるため、2回目以降を比較します。
※ソースコードを何も変更しないとコンパイルされないので、空行を入れたり消したりして再コンパイルしてます
root@5b82f1de979d:/app# cargo build --target-dir /tmp/target
Compiling rust_webapp_starter v0.1.0 (/app)
Finished dev [unoptimized + debuginfo] target(s) in 6.14s
ビンゴ
10倍くらい速くなりました
targetのディレクトリを変更する
target-dir
がマウントしたディレクトリだと遅いことがわかったので、別の場所にします。
が、cargo buildに毎回オプションつけるのは面倒ですし、忘れた時に悲惨です。
じゃあどうするか。
環境変数で設定できないのかな?
Configuration - The Cargo Book
ありました。
For example the build.jobs key can also be defined by CARGO_BUILD_JOBS.
とのこと。
コンテナの環境変数に設定する
...
api:
container_name: api
build:
context: .
command: cargo run
environment:
- CARGO_BUILD_TARGET_DIR=/tmp/target
volumes:
- .:/app
depends_on:
- postgres
ports:
- "28000:8000"
...
コンテナを立ち上げ直して、確認
root@b24c551bd6e4:/app# cargo build
Compiling rust_webapp_starter v0.1.0 (/app)
Finished dev [unoptimized + debuginfo] target(s) in 6.09s
root@b24c551bd6e4:/app# env | grep -i cargo_build
CARGO_BUILD_TARGET_DIR=/tmo/target
ただ、これだとコンテナ立ち上げ直す度にフルビルドが走るので、イマイチ。。。
Dockerfileに書いてしまう
今回のDockerfileでは、Cargo.tomlを先にCOPYして依存ライブラリのみキャッシュされるように先にcargo buildしているので、
その時点でtarget-dirが変わっていたほうがなにかと効率が良い気がします
FROM rust:1.30.0-stretch
MAINTAINER yagince <straitwalk@gmail.com>
RUN apt-get -y -q update \
&& apt-get install -y -q \
curl \
gnupg \
apt-transport-https\
&& curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \
&& echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list \
&& curl -sL https://deb.nodesource.com/setup_10.x | bash - \
&& apt-get install -y -q \
nodejs \
yarn \
libpq-dev \
&& cargo install diesel_cli --no-default-features --features postgres
ENV CARGO_BUILD_TARGET_DIR=/tmp/target
RUN USER=root cargo new --bin app
WORKDIR /app
COPY ./Cargo.toml Cargo.toml
COPY ./Cargo.lock Cargo.lock
RUN cargo build --release --color never && \
rm src/*.rs
まとめ
- コンテナにマウントしたディレクトリでそのままビルドすると大変遅い
- テスト駆動開発とかとてもじゃないけどできない
- target-dirをコンテナ内のディレクトリでマウントしたディレクトリではないところに変更すると速くなる
- target-dirの変更は3つ方法がある
-
.cargo/config
系の設定ファイル - コマンドラインオプション
- 環境変数
-