Actixの開発 (Rustrover on Windows)
概要
前回の記事で、Windows上のRust開発環境として、RustroverとGNU-Cツールチェーンをインストールしました。
クライアント、スタンドアローンの開発であればこれで良いのですが、サーバサイドの開発であれば、DBやProxyといった他のサーバ機能と同時に扱いたくなります。
しかし、Windows上でこれらのサーバと同時に開発を進めるのは、少々面倒くささが伴います。対処方法はいろいろありますが、その一つがDockerを使って、コンテナ上にRustや他のサーバ群をまとめることです。
ここで問題があります。Visual Studioであれば、LLVMを使い、Docker上で動作するRustアプリケーションを直接ブレークポイントを貼るなどのデバグが可能です (と聞いています。すみません、直接確認してません)。しかし、現状のRustroverでは、Docker内のRustコンテナを直接デバグすることは少々面倒です (一応、リモートデバグを使ってやれないことはない)。
そこで、直接Rustアプリケーションでデバグするために、RustによるサーバをWindows上で動作させ、その他のサーバ群をDocker内で動作させることにします。
ここに、その方法を残します。
なお、将来的にはRustrover側で良い感じでデバグできるようになると思いますので、あくまで過渡期的なノウハウになるでしょう。
準備
前提条件
前回の記事にある通り、RustRover、Chocolatey、Rustup、cargo-generateはインストール済みとします。
名前 | リビジョン | 確認方法 |
---|---|---|
RustRover | EAP (2023/11/25build) | ヘルプ → バージョン情報 |
Chocolatey | 2.2.2 | > choco --version |
Rustup | 1.26.0 | > rustup --version |
cargo-generate | 0.18.5 | > cargo-generate -V |
おしながき
- WSLへのUbuntuインストール
- UbuntuへのDocker環境構築
- WindowsへのDocker環境構築
- RustRoverの環境設定
- RustRover上のプロジェクト作成
環境一覧
今回環境構築する主なソフトウェアの執筆時のバージョンの一覧です。
バージョンの違いにより動作が異なる場合がありますので、その場合は頑張ってください。
名前 | リビジョン | 確認方法 |
---|---|---|
Windows 11 | 22H2 | 設定アプリ → システム → バージョン情報 |
WSL | 2.0.9.0 | > wsl --version |
Ubuntu | 22.04.3 LTS | $ lsb_release -a |
Docker Server (Linux) | 24.0.7 | $ docker --version |
Docker Client (Windows) | 24.0.7 | > \ProgramData\chocolatey\bin\docker --version |
WSLへのUbuntuインストール
まず、Windows上にWSLをセットアップし、Ubuntuをインストールします。
WSLのセットアップ
DockerをLinux上で稼働させるため、WSL(Windows Subsystem for Linux)をインストールします。
"Windowsのその他の機能"設定アプリを起動(Windowsキー+Rで、"optionalfeatures"を入力して実行で起動できます)し、以下の機能を有効にします。
- Hyper-V
- Linux用Windowsサブシステム
- 仮想マシンプラットフォーム
WSLのアップデート
WSLを最新版に更新するために、コマンドプロンプトまたはPowerShellを管理者モードで起動します。
そして、wslの更新コマンドを実行します。
> wsl --update
更新プログラムを確認しています。
Linux 用 Windows サブシステムを更新しています。
Windows Terminal
Windows Terminalがインストールされていない場合は、PowerShellやUbuntuのターミナル起動を簡単に行うためにWindows Terminalをインストールします。
Microsoft Storeアプリから、Windows Terminalをインストール、または、最新版への更新を行います。
Microsoft Storeアプリ --> アプリ --> Windows Terminalを検索 --> インストールまたは更新
Ubuntuインストール
Ubuntuをインストールするために、コマンドプロンプトまたはPowerShellを管理者モードで起動します。
そして、wslのインストールコマンドを実行します。
> wsl --install Ubuntu-22.04
インストール途中、管理者となるUNIXユーザアカウントとパスワードを尋ねられますので入力します。
Installing, this may take a few minutes...
Please create a default UNIX user account. The username does not need to match your Windows username.
For more information visit: https://aka.ms/wslusers
Enter new UNIX username: <<<管理者UNIXユーザアカウント>>>
New password: *** (上記UNIXユーザアカウント用のパスワード)
Retype new password: *** (確認のためもう一度入力)
wsl.conf
インストールが完了した後、WSL上でUbuntuを起動するための設定ファイル(wsl.conf
)を作成するために、Ubuntu内でエディタを起動します。
$ sudo nano /etc/wsl.conf
[sudo] password for <<<管理者UNIXユーザアカウント>>>
sudo
は、管理者権限でコマンドを実行するためのコマンドです。sudo
を実行するとき、パスワード入力を求められる場合があります。
nano
は、Ubuntuに標準でインストールされているエディタです。
wsl.conf
の内容を、以下の通り入力します(<<<管理者UNIXユーザアカウント>>>は、先ほど実際に登録したUNIXユーザアカウントを入力します)。
[boot]
systemd = true
[interop]
appendWindowsPath = false
[user]
default = <<<管理者UNIXユーザアカウント>>>
- systemd(true): 起動時の最初のプロセスとしてsystemdというコマンドを実行させるためのものです。これが無いと、動作しないコマンドが多いため指定します。
- appendWindowsPath(false): WSL内ではWindowsのコマンドを直接実行することができます。しかし、Ubuntu内でうっかりWindowsのコマンドを実行させると不便な場合もあるため、実行パスからWindowsのコマンドへのパスを取り除きます。
- default(UNIXユーザアカウント): UbuntuにログインするときのデフォルトのUNIXユーザアカウントを指定します。このユーザアカウントは、インストール時にレジストリに書き込まれますが、バックアップ等でレジストリが消えたときに困るため、明示的に指定しておきます。
wsl.conf
の内容を反映させるために、Windows Terminalで開いているUbuntu端末を全て閉じます。
最新状態に更新
Ubuntu内のパッケージをいったん最新版に更新するために、以下のコマンドを実行します。
$ sudo apt update
$ sudo apt upgrade
UbuntuへのDocker環境構築
インストールしたUbuntu上にDocker環境を構築します。
Docker社公式マニュアルにしたがってインストールしていきます。
競合ソフトウェアを削除します
$ sudo apt remove docker.io docker-doc docker-compose podman-docker containerd runc
Dockerをインストールするのに必要なソフトウェアをインストールします
$ sudo apt install ca-certificates curl gnupg
Dockerリポジトリの正当性を確認するためのGPGを取得します
$ sudo install -m 0755 -d /etc/apt/keyrings
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
$ sudo chmod a+r /etc/apt/keyrings/docker.gpg
Dockerリポジトリをインストール用ソースに追加します
$ echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu jammy stable" | sudo tee /etc/apt/sources.list.d/docker.list
Dockerリポジトリから、必要なソフトウェアをインストールします
$ sudo apt update
$ sudo apt install docker-ce docker-ce-cli docker-ce-rootless-extras containerd.io docker-buildx-plugin docker-compose-plugin
dockerグループへのユーザを追加します
$ sudo adduser $USER docker
$ sudo usermod -aG docker $USER
dockerグループへの追加を反映させるためにいったんコンソールを終了します
$ exit
WindowsへのDocker環境構築
Windows側にもDocker(クライアント)等の環境構築を行う必要があります。
Docker(CLI)
Chocolateyを使って、Windows用のdockerコマンドをインストールします。
なお、Chocolateyがインストールされていない場合は、前回の記事を参照してください。
PowerShellを管理者モードで起動し、以下のコマンドを実行します。
> choco install docker-cli -y
> choco install docker-compose -y
RustRoverの環境設定
接続するDocker Engineの設定
設定 --> ビルド、実行、デプロイ --> Docker --> (追加)
設定 | 値 |
---|---|
名前 | (好きな名前) |
Dockerデーモンへの接続方法 | WSL |
WSL | Ubuntu-22.04 |
Docker CLIのパス指定
設定 --> ビルド、実行、デプロイ --> Docker --> ツール
設定 | 値 |
---|---|
Docker実行可能ファイル | C:\ProgramData\chocolatey\bin\docker.exe |
Docker Compose 実行可能ファイル | C:\ProgramData\chocolatey\bin\docker.exe |
RustRover上のプロジェクト作成
プロジェクトの概要
現在のRustRoverのバージョンでは、Dockerまたはdocker composeを前提としたプロジェクトをテンプレートから作成することはできません。
したがって、プロジェクトディレクトリを作って、そのディレクトリをプロジェクトとして読み込みます。
今回のサンプルは、RustのActix Webを使ったAPIサーバと、Nginxを使ったリバースプロキシ兼Webサーバの構成にします。
今回作成したサンプルは、このリポジトリにあります。
ただし、説明に無いファイルや一部内容が違うものがありますが、うまく解釈してください。
cargo-generateを使って、サンプルをテンプレートとしてプロジェクトを作成することができます。
サーバー構成
冒頭に書きましたが、APIサーバをWSLホストであるWindows上(ポート9080)、WebサーバをWSL内のDockerコンテナ(ポート8080)で動かすことにします。
なお、api-serverは、Docker内から見たWSLホスト(Windows)のアドレスを指すこととします。
ディレクトリ構成
適当なところにプロジェクトディレクトリを作成し、その下にapiserverとwebserverの二つのサブディレクトリを作成します。
webapp\
apiserver\
src\
main.rs
Cargo.toml
webserver\
APIサーバ
Actix Webを使ったAPIサーバを構築します。
webapp\
apiserver\
src\
main.rs
Cargo.toml
webserver\
public\
index.html
default.conf
Dockerfile
compose.yml
compose.debug.yml
createoverride.bat
9080ポートの"/"リクエストを受けると、"Hello World! from RUST"を返すだけのAPIサーバです。
use actix_web::{get, App, HttpResponse, HttpServer, Responder};
#[get("/")]
async fn hello() -> impl Responder {
HttpResponse::Ok().body("Hello World! from RUST")
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new().service(hello)
}).bind(("0.0.0.0", 9080))?
.run().await
}
[package]
name = "api-server"
version = "0.1.0"
edition = "2021"
[dependencies]
actix-web = "4"
Webサーバ
nginxを使ったプロキシおよびWebサーバを構築します。
webapp\
webserver\
public\
index.html
default.conf
Dockerfile
Webサーバとしては、サンプルのデフォルトページだけです。
<html>
<p>Hello, World</p>
</html>
Nginxの設定ファイルは、以下の通りです。
"/"へのアクセスはWebサーバのindex.htmlを表示し、"/api"以下のアクセスはAPIサーバにリクエストをフォワードするような設定です。
server {
listen 8080 default_server;
server_name _;
access_log /dev/stdout;
error_log /dev/stderr;
location / {
root /var/www/public;
}
location /api/ {
rewrite /api/(.*) /$1 break;
proxy_pass http://api-server:9080;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
Nginx用のDockerファイルです。SSL関連の設定は省いています。
FROM nginx:1.25.1-alpine
COPY default.conf /etc/nginx/conf.d/default.conf
COPY public /var/www/public
EXPOSE 8080
Compose
docker compose用のファイルを作成します。
compose.ymlには、Webサーバ用の設定しか含まれていませんが、DBサーバなどを追加する場合は、このファイルに追記することになります。
また、compose.debug.ymlには、WSLのDockerコンテナ内のWebサーバから、Windows上のAPIサーバにアクセスするための設定を記述します。
webapp\
compose.yml
compose.debug.yml
compose.ymlは以下の通りです。
services:
web-server:
hostname: web-server
build:
context: webserver
dockerfile: ./Dockerfile
ports:
- "8080:8080"
networks:
- private_net
networks:
private_net:
Nginxの設定で、/apiパス以下は、ホスト名"api-server"の9080ポートにフォワードするように記載しています。
"api-server"のままでは名前解決できないので、WSLのDockerコンテナ内から見た、Windows上のIPを"api-server"に割り振るように、docker.debug.ymlに設定を記述します。
services:
web-server:
extra_hosts:
- "api-server:172.18.160.1"
ここでは、172.18.160.1になっていますが、実際に割り振られるIPが何になるかは分かりません。このIPを調べるには、WSL側のデフォルトゲートウェイまたはネームサーバー(デフォルトではデフォルトゲートウェイがネームサーバになっています)を調べます。
以下は、Docker側から見たWindowsのIPアドレスを調べるコマンドの実行例です(出力は一部省略しています)。
> wsl -- ip route
default via 172.18.160.1 dev eth0 proto kernel
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
172.18.160.0/20 dev eth0 proto kernel scope link src 172.18.175.12
> wsl -- cat /etc/resolv.conf
nameserver 172.18.160.1
api-serverのIP更新バッチ
毎回IPを調べてcompose.debug.ymlを更新するのは面倒なので、コマンドを作成します。
webapp\
createorverride.bat
@echo off
cd /d %~dp0
@setlocal enabledelayedexpansion
for /f "usebackq" %%i in (`wsl -- cat /etc/resolv.conf ^| grep nameserver ^| awk '{print $2}'`) do set WSL_HOST_IP=%%i
set OVERRIDE_FILE=compose.debug.yml
echo.services:>!OVERRIDE_FILE!
echo. web-server:>>!OVERRIDE_FILE!
echo. extra_hosts:>>!OVERRIDE_FILE!
echo. - "api-server:%WSL_HOST_IP%">>!OVERRIDE_FILE!
このバッチは、手動で呼び出すか、ビルドプロセス内で呼び出して自動で更新するようにします。
RustRoverの設定
プロジェクトディレクトリができたら、RustRoverからそれをオープンします。
Dockerの設定
DockerEngineを指定します。
ビルド、実行、デプロイメニューのDockerを選び、Dockerデーモンとして"WSL (Ubuntu-22.04)"を選んで追加します。
WSLを選択したときに、少し経つと設定ダイアログの下の方に"接続完了"とでるはずです。出ない場合は、どこかセットアップが間違えているかもしれません。
Docker/ServersForDebug
次に、Webサーバを起動するための設定を行います。
実行/デバッグ構成メニューから、Docker -> DockerCompose新規構成を選びます。
そして、以下の設定を行います。
- 名前 - 適当な名前(例えばServersForDebug)
- サーバー - 上記Dockerの設定で追加したものをメニューから選択する
- Composeファイル - compose.debug.ymlとcompose.ymlを指定します。GitHubからダウンロードした直後だとcompose.debug.ymlが無いので、手動でcreateoverride.batを起動します。
- 起動前 - 外部ツールとしてcreateoverride.batを指定しておくと、手動でバッチを起動する手間が省けます
Cargo/Run
次に、APIサーバを起動するための設定を行います。
実行/デバッグ構成メニューから、Cargoを選び、以下の設定を行います。
- 名前 - 適当な名前
- 実行場所 - ローカルマシン
- コマンド - run
- 作業ディレクトリ - プロジェクト内のapiserverパスを指定
動作確認
- Docker/ServersForDebugを実行し、その後、Cargo/Runをデバッグ実行します
- main.rsのhello()メソッドにブレークポイントを設定します
- 任意のWebブラウザから"http:// localhost:8080/"にアクセスし、"Hello, World"が表示されるのを確認します
- 次に"http:// localhost:9080/"にアクセスし、ブレークポイントが動作することを確認します。確認したら再び実行します。
- 次に"http:// localhost:8080/api/"にアクセスし、同様にブレークポイントが動作することを確認します。
リリース版
ここまでは、Windows上でAPIサーバを動作させる設定にしていましたが、以下のファイルと設定を追加すると、Docker内でAPIサーバとWebサーバの両方を動作させることができます。
webapp\
apiserver\
Dockerfile
compose.release.yml
APIServer用Dockerfile
FROM rust:1.74-alpine as builder
ARG APPNAME=api-server
RUN apk add --no-cache alpine-sdk build-base
WORKDIR /build
COPY Cargo.toml .
COPY Cargo.lock .
COPY src src
RUN cargo build --release --target x86_64-unknown-linux-musl
RUN cp /build/target/x86_64-unknown-linux-musl/release/$APPNAME /build/target/myapp
FROM alpine:3.18
WORKDIR /app
COPY --from=builder /build/target/myapp /app/myapp
EXPOSE 9080
CMD ["/app/myapp"]
APIServer用docker-compose設定
services:
api-server:
hostname: api-server
build:
context: apiserver
dockerfile: ./Dockerfile
ports:
- "9080:9080"
networks:
- private_net
Docker/ServersForRelease
次に、Webサーバを起動するための設定を行います。
実行/デバッグ構成メニューから、Docker -> DockerCompose新規構成を選びます。
そして、以下の設定を行います。
- 名前 - 適当な名前(例えばServersForRelease)
- サーバー - 上記Dockerの設定で追加したものをメニューから選択する
- Composeファイル - compose.release.ymlとcompose.ymlを指定します。
Discussion