対象読者
Dockerコンテナ上でRustのコードをデプロイしたいけど、コードを変更するたびにDockerコンテナを立ち上げ直さなければいけない、や一回プロセス終了させて
docker compose exec hoge cargo run
とかやるの面倒だ、と思っている方が対象です!
ある程度、Dockerのコマンドや概念、RustやCargoについて知っている前提で進めていきます。
要約
- Docker Compose Watch を利用してコードの変更を認識する
- cargo-watchをコンテナでインストールする
- compose.ymlにこれを追記する
services:
back:
develop:
watch:
- action: sync
path: ./backend
target: /work/backend
ignore:
- ./backend/target
command: /bin/sh -c "cargo watch -x run"
Rustでホットリロード
皆さん!Rustでサーバーサイド処理書いていますか?
サーバーサイドでなくても、常に動かしておくプログラムっていっぱい書くと思います。
そのようなプログラムを書いているとき、プログラムを書き替えたら再ビルドを自動でしてくれると便利ですよね。
そこでホットリロードの登場です!
ホットリロードは、プログラムを書き替えると再ビルドを自動で行ってくれる機能ですが、それをRustで行う方法として cargo-watch があります。
やり方としては、
cargo install cargo-watch --locked
と、cargo-watchをインストールして、プロジェクトのディレクトリで
cargo watch -x run
とするだけでホットリロードが実現できます。
しかし、Dockerコンテナ上ではそううまくはいきません。
起こったこと
まず、前提としてこのようなディレクトリ構成で作業しています。
/project
├─/backend
│ ├─Cargo.toml
│ ├─Cargo.lock
│ ├─.gitignore
│ ├─Dockerfile
│ ├─/target
│ └─/src
│ └─main.rs
├─/frontend
├─/database
│ ├─init.sql
│ └─Dockerfile
├─.env
└─compose.yml
また、compose.ymlやDockerfileにはこのようなことを記述しています。
services:
back:
build:
context: ./backend
tty: true
hostname: "${APP_HOST}"
ports:
- "${APP_PORT}:${APP_PORT}"
env_file:
- .env
volumes:
- "./cargo-cache:/usr/local/cargo/registry"
- "./rust-target-cache:/work/backend/target"
- "./backend:/work/backend"
command: /bin/sh -c "cargo watch -x run"
volumes:
cargo-cache:
rust-target-cache:
FROM rust:latest
WORKDIR /work/backend
# 必要なツールをインストール
RUN apt-get update && apt-get install -y \
inotify-tools
# cargo-watchをインストール
RUN cargo install cargo-watch
このcompose.ymlとDockerfileを書いて
docker compose up -d
としたところ、
ホットリロードが行われていませんでした😭
いったいなぜ?と思い、私たちはアマゾンのジャングルの中へ・・・
なんとマウントされたファイルは認識しないとのこと!?[1]
となると、どうしたらいいんだ・・・と思い、調査を続けると、どうやら Docker Compose Watch なるものがあるじゃないですか!
Docker Compose Watch
Docker Compose Watchは、compose.ymlファイルに記述したディレクトリ以下で変更があった場合に、コンテナの再スタートや再ビルド、同期等を行ってくれる機能です。
この様にcompose.ymlに記述すると、Docker側でホットリロードを行ってくれます。([3]からの引用)
services:
web:
build: .
command: npm start
develop:
watch:
- action: sync
path: ./web
target: /src/web
ignore:
- node_modules/
- action: rebuild
path: package.json
この設定の場合は、./web以下のファイルに変更があれば/src/webと./webを同期し、package.jsonに変更があればコンテナをビルドし直す設定になっています。
これを利用して早速Rustでホットリロードしていきましょう!
Dockerコンテナ上でのRustのホットリロード
では、まず./backend/Dockerfileを変更していきましょう。
このやり方では、マウントではなくCOPYして同期させる方法でWindows上の変更をDockerコンテナに適用させます。
そのため、COPYを追記し、backendディレクトリ以下のファイルをコンテナにコピーさせています。
FROM rust:latest
WORKDIR /work/backend
# 必要なツールをインストール
RUN apt-get update && apt-get install -y \
inotify-tools
# cargo-watchをインストール
RUN cargo install cargo-watch
COPY . .
次に、変更したファイルをDocker上のファイルに適用させるため、compose.ymlを変更しましょう!
backend以下に変更があれば、コンテナ上のファイルにも適用させてほしいので、./backend以下に変更があれば、/work/backendに対してsyncを行うよう設定します。
また、targetディレクトリ以下が変更されても無視をする設定を行っています。
services:
back:
build:
context: ./backend
tty: true
hostname: "${APP_HOST}"
ports:
- "${APP_PORT}:${APP_PORT}"
env_file:
- .env
volumes:
- "./cargo-cache:/usr/local/cargo/registry"
- "./rust-target-cache:/work/backend/target"
depends_on:
- db
develop:
watch:
- action: sync
path: ./backend
target: /work/backend
ignore:
- ./backend/target
command: /bin/sh -c "cargo watch -x run"
volumes:
cargo-cache:
これで、動かしてみます。
動かす方法は、
docker compose up --watch
で、composeで立ち上げたものをwatchしています。
さて、まずは動作確認・・・
キタ━━━━(゚∀゚)━━━━!!(DBコンテナが動いているのは気にしないでね)
では、ホットリロードが機能するのか・・・
println!("Hello, World!");
を追記してみてみたいと思います!
動くか・・・
キタ━━━━(゚∀゚)━━━━!!
ついにやってやりました!
これでわずらわしさを少し減らして開発ができる・・・
めちゃくちゃ調べるのに時間がかかったので是非参考にしていただければと思います。
また、至らぬ点や気になる点があればぜひコメントお願いします!
参考文献
[1] "WSL+DockerでHot reload が動作しない", https://banatech.net/blog/view/54 (2024/12/27)
[2] "Docker Compose Watchを触ってみる", https://zenn.dev/maybe_dog/articles/bfeeee3b6650a1 (2024/12/27)
[3] "Use Compose Watch", https://docs.docker.com/compose/how-tos/file-watch/ (2024/12/27)