docker compose watchとrustとの相性を確認してみる
はじめに
普段、Rustで開発する時にcargo watchを使ってホットリロードしながら開発をしております。
先日、docker compose watchの記事を見かけて、cargo watchのようにできるなら面白そうと思い、この記事を書きました。
合わせて、少し前にdocker initにrustがサポートされていたなので、こちらも気になっていたので、合わせて試してみます。
この記事ではよく使っているActix-web + askama + htmx を使った簡単なサンプルの作成になります。(htmxは完全におまけ)
リポジトリはこちらです
色々、蛇足が多いので、結論から見たい場合は「docker compose watchの設定」から見ていただければと思います。
環境
docker-desktop 4.24.2
rust 1.73.0
内容
rustプロジェクト作成
cargo init
dockerファイル作成
docker init
以下の選択をしました。
? What application platform does your project use? `Rust`
? What version of Rust do you want to use? `1.73.0`
? What port does your server listen on? `8080`
このコマンドで以下のファイルが作成されました。
- .dockerignore
- Dockerfile
- compose.yaml
編集箇所はこちらです。
(最近はdocker-compose.yamlではなく、compose.yamlになっているんですね。)
作成されたファイルの中身をみてみます。
.dockerignore
コンテナにコピーされたくないファイルやディレクトリが自動で貼り付けられてます。
自動で便利です。
Dockerfile
以下の内容が生成されてました。コメントは略してます。
# -----------------------------------------------
ARG RUST_VERSION=1.73.0
ARG APP_NAME=docker_watch_demo_with_rust
FROM rust:${RUST_VERSION}-slim-bullseye AS build
ARG APP_NAME
WORKDIR /app
RUN \
<<EOF
set -e
cargo build --locked --release
cp ./target/release/$APP_NAME /bin/server
EOF
# -----------------------------------------------
FROM debian:bullseye-slim AS final
ARG UID=10001
RUN adduser \
--disabled-password \
--gecos "" \
--home "/nonexistent" \
--shell "/sbin/nologin" \
--no-create-home \
--uid "${UID}" \
appuser
USER appuser
COPY /bin/server /bin/
EXPOSE 8080
CMD ["/bin/server"]
自分で書かずともマルチステージビルドの記載になっているのはかなり便利ですね。
ビルドの部分では、--mount=type=cache
などを使って高速化を図っていますね。
imageを作成する部分で、adduser作成にセキュリティ向上のテクニックがあるので、少しみてみます。
-
--disabled-password
このオプションは、新しいユーザーのパスワードを無効にするので、誰もこのユーザーでログインできなくなリます。 -
--gecos ""
ユーザー名や電話番号などの情報を格納するフィールドのようで、このオプションで空文字列を指定して、不要な情報を残していないです。 -
--home "/nonexistent"
新しいユーザーのホームディレクトリを作成しないという意味で、万が一、不正にログインされても、ホームディレクトリにアクセスできなくなります。 -
--shell "/sbin/nologin"
シェルを起動できなくなるため、実質ログインできなくなります。 -
--no-create-home
このオプションを使用すると、ホームディレクトリが作成されなくなります。これでホームディレクトリが存在しないユーザを作れます。 -
--uid "${UID}"
初回UID1000以外を設定します。
かなり強固な設定に見えますね。流石と思います。
compose.yaml
services:
server:
build:
context: .
target: final
ports:
- 8080:8080
シンプルな設定ですね。特に補足はなさそうです。
actix-webのサンプル作成
編集箇所はこちらです。
シンプルにしたいのと、actix-webに興味持って欲しいので、actix-webの公式ドキュメントの最初のサンプルを使いました。
このサンプルでcargo run
で実行することでapiサーバが起動します。
curl -i localhost:8080
curl -i localhost:8080/hey
などをすると動作確認できます。
シンプルで簡単に実装できるので、actix-webに興味を持ってもらえれば幸いです。
docker向けにアドレスを修正
- .bind(("127.0.0.1", 8080))?
+ .bind(("0.0.0.0", 8080))?
テンプレートエンジンを導入
askamaというjinja2ライクなテンプレートエンジンがあり、それを入れました。
jinja経験者であれば、すんなり理解できると思います。
お気に入りポイントとしては、構造体とhtmlファイルの紐付けるような書き方であるところです。
比較的プロジェクトが大きくなったとしても、index.html
とファイル名検索することですぐに構造体が見つかるのが非常に便利と思っています。
編集箇所はこちらです。
試しに、この時点でcargo run
で実行し、ブラウザでlocalhost:8080
を開いてみます。
actix-webとaskamaの組み合わせを見てみてもらえればと思います。
おまけとして、webページにhtmxを使ったボタンを配置しています。
js書かずにajax通信できるので便利です!
docker compose watchの設定
お待たせしました。ここでやっと本題になります。
ここまで蛇足に突き合わせてしまいましたが、設定はかなりシンプルにでした。
まず、テンプレートエンジン用にhtmlファイルのディレクトリを追加したのでDockerfileを修正します。
RUN --mount=type=bind,source=src,target=src \
+ --mount=type=bind,source=templates,target=templates \
--mount=type=bind,source=Cargo.toml,target=Cargo.toml \
次にメインですが、6行を追加だけになります。
services:
server:
build:
context: .
target: final
ports:
- 8080:8080
+ develop:
+ watch:
+ - action: rebuild
+ path: ./src
+ - action: rebuild
+ path: ./templates
内容はシンプルで、pathで指定した./src
または./templates
ディレクトリ配下のファイルの変更があると、actionが行われます。
actionのrebuild
は字の如く、ビルドしなおしてコンテナに配置します。
actionにはrebuildの他にsyncがあります。
syncは
- action: sync
path: ./web
target: /src/web
のように書きますが、指定したディレクトリのファイルが変更されると、コンテナのtargetにファイルを配置するというものです。
reactなどのフロントエンド開発であれば、かなり便利かと思いますが、
今回のrustではテンプレートエンジンであるため、ビルド時にindex.htmlごとのバイナリに含まれるような形になります。
よって、今回の構成ではsyncが使えないことになります。
ではこの状態で以下のコマンド実行します
docker compose watch
そして、ブラウザでlocalhost:8080
にアクセスしてみます。
動作確認
watchが起動している最中にファイルを変更してみてください。
rebuildが走り、ブラウザの表示やrust側の変更が適用されます。
まとめ
いかがだったでしょうか。
rust開発でdocker compose watch
を使う場合は、基本rebuildにすれば良さそうです。
cargo watchでいいじゃんと思われますが、コンテナ上でcargo watchしているようなものですので、挙動は似ていますが、環境差分はなくなるといったメリットがあるかと思います。
また今回はrustのみの開発を想定していますが、
DBサーバを追加したりすると話が変わってくるので、docker compose watch
の恩恵を受けれるかと思います。
docker compose watch
の可能性を知りつつ、rustの魅力も伝わればと思います!
この記事が役に立てれば幸いです!ありがとうございました!!
Discussion