📦

リモートコンテナでRustの開発環境を整える場合の注意点

2021/04/30に公開

あまり触っていないうちにRustのマイナーバージョンがグングンと上がっていた。
たまにしか使わないPCのバージョンを追いつかせるのはしんどい、という思いからついにリモートコンテナ環境を準備した。
構成などについては次のページがわかりやすい。

Developing inside a Container using Visual Studio Code Remote Development

https://code.visualstudio.com/docs/remote/containers

Create a development container using Visual Studio Code Remote Development

https://code.visualstudio.com/docs/remote/create-dev-container

Rustの場合だと、Microsoft公式だとこの辺りがコンテナイメージのベースとして使うのに良さそうだった。

vscode-remote-try-rust/Dockerfile at main · microsoft/vscode-remote-try-rust · GitHub

https://github.com/microsoft/vscode-remote-try-rust/blob/main/.devcontainer/Dockerfile

vscode-dev-containers/base.Dockerfile at v0.159.0 · microsoft/vscode-dev-containers · GitHub

https://github.com/microsoft/vscode-dev-containers/blob/v0.159.0/containers/rust/.devcontainer/base.Dockerfile

まあただ、どういう便利なことをやってくれているのかが自分であまり理解できなかったので、ひとまずはrust:1.51のイメージを自分で指定して使ってみることに。

.devcontainerディレクトリ内にdevcontainer.jsonというリモートコンテナの設定や必要なプラグインの情報などを書くJSONファイルと、その中から読み込ませる実際のコンテナのイメージのためのDockerfileを置く必要がある。

あとはVS Codeで該当のディレクトリをひらけばDev Containerとして開くかを聞いてくれて、コンテナのビルドを実施する。
devcontainer.jsonやDockerfileに変更があったら再度ビルドなどが必要となる。

一通り終わると、あとは通常通り、cargo buildなどができるのでとても便利。
Macなどの環境の場合は追加で注意点として、targetディレクトリの取り扱いがある。

Qiitaでも次の記事などがよくまとまっていてわかりやすい。

[Rust] dockerコンテナにマウントしたディレクトリでbuildすると遅かった話し - Qiita

https://qiita.com/yagince/items/077d209ecca644398ea3#dockerfileに書いてしまう

Mac環境で開発している場合は顕著かもしれない。わかりやすく妥当性が高い対応としては次の2つをすること。

  1. devcontainer.json
// 前略
  "settings": {
    "files.watcherExclude": {
      "**/target/**": true
    }
  },
// 後略
  1. Dockerfile

ENV CARGO_BUILD_TARGET_DIR=/tmp/target

リモートコンテナの仕組み上、カレントディレクトリについてホストとディレクトリをマウントするが、このマウントしているディレクトリへの書き込み・読み込みがパフォーマンスに影響を与えている、という捉え方ができる様子。
また、VS Codeがコードの変更としてtarget下のようなビルドの結果などがよく書き込まれる箇所をWatchするのも同様にパフォーマンス劣化につながる様子。

処理の一部分の文字列を変更してcargo buildし比べる調査をしたが、変更適用前では1.5分かかっていた(除くcrates.ioのインデックス更新)ビルドが、変更適用後は数秒で終わるようになった。

一応、今回使ったdevcontainer.jsonの全量とDockerfileの全量も置いておく。
コンテキストルートに.dockerignoreも配置したことで、ビルドの速度はかなり速くなった。(作りとして、コンテナをビルドした後それをマウントしているし、 target ディレクトリは送る必要がない)
rustupで足してあるcomponentは普段自分が使っているものや、いざVS Codeでrsファイルを変更しようとするとVS Codeがインストールを進めてくるものについてをいれたというもの、不要と思えば無くしてももちろんOK。

devcontainer.json:

{
  "name": "Rust Dev Container",
  "context": "..",
  "dockerFile": "Dockerfile",
  "settings": {
    "terminal.integrated.shell.linux": "/bin/bash",
    "lldb.executable": "/usr/bin/lldb",
    "files.watcherExclude": {
      "**/target/**": true
    }
  },
  "extensions": [
    "rust-lang.rust",
    "bungcip.better-toml",
    "vadimcn.vscode-lldb",
    "mutantdino.resourcemonitor",
    "ms-azuretools.vscode-docker"
  ],
}

Dockerfile:

FROM rust:1.51

# it makes faster cargo build
ENV CARGO_BUILD_TARGET_DIR=/tmp/target

# target runtime
RUN rustup target add x86_64-unknown-linux-gnu

# linter and formatter
RUN rustup component add clippy
RUN rustup component add rustfmt

# for VS Code Remote Container
RUN rustup component add rust-analysis
RUN rustup component add rust-src
RUN rustup component add rls

# utility for Cargo.toml
RUN cargo install cargo-edit

.dockerignore(配置先は.devcontainerではなくその一つ上のコンテキストルート):

target/

直近感じた困りごとは、このコンテナに何かExtensionなりrustupでcomponentなりを追加した場合に、再度ビルドが必要になるのだけど、そうすると、cargo buildなどを実行した際に、"Updating crates.io index" ということで$CARGO_HOME/.package-cacheの更新が必要になってしまうこと。
(現状だとDockerfile内のcargo install cargo-editの際にそれを発動させているのでビルドに少し時間がかかっている)

$CARGO_HOME/.package-cacheを同じようにマウントしてあげればいいのかもしれないが、うーん。ということでこの点はまだ答えが出ていない。

あと、Remote ContainerのTerminalにVS Codeから入っているのだけど、80文字のところで折り返しされてしまう挙動があり、その折り返しが現在の行内で行われてしまうのが時々不便。(VS Code内でターミナル分割をすると、折り返しが次の行になるようになったので一旦それで対応している)

というわけで、こういう生活の知恵というか、意外な引っかかりどころがまだありそうなので、手放しには進められないしチームに大きく展開するには注意が必要。
ただ、モノとしては本当に便利なので、ぜひRustに限らず、Remote Container、お試しあれ。

Discussion