DockerでYew開発環境（RustのWebAssemblyフレームワーク）を構築

## 概要

最近、Rustの勉強を始めました。

基本的な文法や言語機能を簡単に読んでみて、そろそろ自分で色々試してみようと、DockerでのYew（RustのWebAssemblyフレームワーク）環境を構築をしました。

公式イメージで簡単にできるかと思いきや、[こちら](https://qiita.com/robitan/items/456b7b31ddb6a7c6e430)のページにあるようにDockerなしで環境構築するのと同じ方法では妙なところで詰まるので、そのあたりを備忘録として残しておきます

## 目次

- 前提
- 成果物（Dockerfileとcompose.yaml）
- Rust？
- Yew？
- Dockerを用いたYew環境構築 準備
- Dockerなしで環境構築準備する場合（参考）
- Yew環境構築

## 前提

### この記事作成時の開発環境

OS：macOS Venture 13.1
CPU：Apple Silicon M1
（chromebook上のcrostini（bullseye）でも動作確認済み）

### ツール群

インストール済み
- Docker
  （ファイルシステムはgRPC FUSEを推奨）

Dockerfileに記述してコンテナ内にグローバルインストールするもの
- Rust（イメージは 1-slimを使用）
- libssl-dev
- pkf-config
- trunk
- wasm-bindgen-cli
- wasm32-unknown-unknown

プロジェクトにローカルインストールするもの

- yew

## 成果物

まずは出来上がったDockerfileとcompose.yamlを記載

Dockerの運用がわかるけど妙なところで詰まった、という方はこれだけ見てサクッと構築してください。

### ファイル構成

以下の構成でDockerfileとcompose.yamlを作成

```
.
├── Dockerfile
└── compose.yaml
```

### Dockerfile

``` .Dockerfile
FROM rust:1-slim

RUN apt-get update \
    && apt-get install --no-install-recommends -y \
    libssl-dev pkg-config \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/* \
    && cargo install trunk wasm-bindgen-cli \
    && rustup target add wasm32-unknown-unknown
```

### compose.yaml

``` compose.yaml
services:
  front:
    build: ./front
    tty: true
    volumes:
      - ./front:/app
    working_dir: /app
    ports:
      - "8080:8080"
```

## Rust？

### 概要

オープンソースのコミュニティベースで開発が進められている、性能・メモリ安全性・安全な並行性を目指して設計されたマルチパラダイムのプログラミング言語
パフォーマンス・信頼性・生産性に優れていることが公式サイトに明記されています

- パフォーマンス
Rustは非常に高速でメモリ効率が高くランタイムやガベージコレクタがないため、パフォーマンス重視のサービスを実装できますし、組込み機器上で実行したり他の言語との調和も簡単にできます。

- 信頼性
Rustの豊かな型システムと所有権モデルによりメモリ安全性とスレッド安全性が保証されます。さらに様々な種類のバグをコンパイル時に排除することが可能です。

- 生産性
Rustには優れたドキュメント、有用なエラーメッセージを備えた使いやすいコンパイラ、および統合されたパッケージマネージャとビルドツール、多数のエディタに対応するスマートな自動補完と型検査機能、自動フォーマッタといった一流のツール群が数多く揃っています。

### Rustの現状

直近、以下のような理由でOS開発等に採用されてきている

CやC++の独壇場であるOS開発等のシステムプログラミングであるが、Microsoftによると同社製品のセキュリティ脆弱性の約70%がメモリ管理に起因したものであり

> the root cause of approximately 70% of security vulnerabilities that Microsoft fixes and assigns a CVE (Common Vulnerabilities and Exposures) are due to memory safety issues. This is despite mitigations including intense code review, training, static analysis, and more.
> 

C/C++では「大規模な開発においてメモリー安全なコードを記述することがほぼ不可能」

> it’s clear that no matter the amount of mitigations put in place, it is near impossible to write memory-safe code using traditional systems-level programming languages *at scale* .
> 

（[We need a safer systems programming language]([https://msrc.microsoft.com/blog/2019/07/we-need-a-safer-systems-programming-language/](https://msrc.microsoft.com/blog/2019/07/we-need-a-safer-systems-programming-language/))より引用）

一方で、今日のアプリケーション開発によく使われている言語はガベージコレクションを採用しておりメモリー安全が保証されてはいるが、処理のオーバーヘッドが発生するためシステムプログラミングには不向きという事情もある

Rustはガベージコレクションを使っていないがコンパイラーがメモリー安全を保証してくれており、C/C++と同等のパフォーマンスを得られるという特徴があるため、GoogleやMicrosoft、AmazonもOS開発等に採用したことを発表しています

### なぜRustでwebアプリケーションなのか

前述した高速処理と安全性が両立しているRustという言語はシステムプログラミングだけでなくWebサービス開発でも注目されている。

WebアプリのフロントエンドではJavaScriptが活用されているが、プログラムが肥大化すると動作速度が遅くなるという課題がある。これを解決するために登場した仕組みが「WebAssembly」

色々な言語からコンパイルして生成されるWebAssemblyですが、高速な処理が可能でサンドボックスを利用するためセキュリティが強固、そして主要なwebブラウザにはほぼ実装されています。そして、もともと安全かつ高速な処理を特徴とするRustは安全で高速なWebアプリを開発できるというメリットがあり、現在WebAssemblyをターゲットにしたプログラミング言語で最も利用されています。

また、バックエンドのフレームワークとして高速に動作するActix Web等のクレートも多数存在し、コミュニティの活動が活発であるためドキュメントも豊富に揃っています。

Rustを用いれば、高速でセキュアなwebアプリ開発を比較的容易に可能となっています

## Yew？

Yewは、WebAssemblyでマルチスレッドのフロントエンドアプリを作成するためのElmとReactにインスパイアされたモダンなRustフレームワーク （引用： [https://github.com/jetli/awesome-yew](https://github.com/jetli/awesome-yew)）

- Component Based
コンポーネントベースのフレームワークを採用し、インタラクティブなUIを簡単に作成することができます。ReactやElmのようなフレームワークの経験がある開発者なら、Yewを使うことに違和感はないはずです。

- HTML macro
Rust 式でインタラクティブな HTML を宣言するためのマクロを備えています。ReactでJSXを使用した経験のある開発者は、Yewを使用する際に非常に快適に感じるはずです。

- Server Side Rendering
サーバーサイドレンダリングを採用し、SPAの操作性を維持しながら、サーバーレンダリングアプリのSEO対策や機能拡張が可能



## Dockerを使ったYewの環境構築 準備
それでは、Dockerを使ったYew環境の構築を始めていきます

### Dockerfile, compose.yamlの準備

任意のフォルダにDockerfileとcompose.yamlという2つのファイルを作成してください

内容はそれぞれ以下の通りです

- Dockerfile

敢えてslimのイメージを使っている点、Macで通常構築する場合基本的には必要ないlibssl-devやpkg-config、wasm-bindgen-cliを入れている点以外は、公式ドキュメントのtutorial記載の通りで特に特別なことはしていません（2023-03-12時点）
curlやvim等欲しいものがあれば追加して使用ください

``` .Dockerfile
FROM rust:1-slim

RUN apt-get update \
    && apt-get install --no-install-recommends -y \
    libssl-dev pkg-config \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/* \
    && cargo install trunk wasm-bindgen-cli \
    && rustup target add wasm32-unknown-unknown
```

- compose.yaml（docker-compose.yamlとかでもok）

詳しい説明は省きますが、Dockerfileの指定やコンテナ内外で同期するフォルダの設定、コンテナ内での作業フォルダ、ポートフォワーディング（コンテナに届いた信号の転送）設定を書いてます

``` compose.yaml
services:
  front:
    build: .
    tty: true
    volumes:
      - ./front:/app
    working_dir: /app
    ports:
      - "8080:8080"
```

### 注意点

当方の環境では、Dockerのfile sharing implementationをVirtioFSにしていると、ファイル更新時に低確率でコンパイルエラーになることがありました。gRPC FUSEにしておくと今のところ問題が起きていないため、そちらを推奨としておきます。（デフォルトでgRPC FUSEのはずなので、何言ってるかわからない人はスルーして大丈夫です）

## Dockerなしで環境構築（参考）

Dockerfileに記述してある内容なので、Dockerを使って環境構築する方はYewの環境構築に進んでOKです


### Rustをインストール

公式ページの通りにやりましょう[https://www.rust-lang.org/ja/tools/install](https://www.rust-lang.org/ja/tools/install)

Mac, Linuxの場合

```bash
$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
```

### trunkをインストール

wasmに必要なツールであるtrunkiを以下のコマンドでインストール

```bash
$ cargo install trunk
```

WebAssemblyのターゲットアーキテクチャを追加

```bash
$ rustup target add wasm32-unknown-unknown
```

これでDockerを使わない場合の準備は完了です。Yewの環境構築に進んでください

## Yewの環境構築

### Dockerコンテナを起動して中に入る
以下のコマンドでDockerコンテナを起動し、コンテナ内で作業できるようにしてください
``` bash
$ docker compose up -d
$ docker compose exec front bash
```


### Yew用のプロジェクトを作成

cargoを用いて、プロジェクトを作成してディレクトリの中に入る

プロジェクト名はyew-appとしていますが、任意の名前に設定してください

```bash
$ cargo new yew-app
$ cd yew-app
```

（参考）この時点で`cargo run`を実行すると、ターミナルに”Hello world !”が表示されます

### Yewをインストール

`cargo`でプロジェクトにyewの依存関係を追加します。`Cargo.toml`に直接追加しても構いません

`cargo`を使う場合はこちら（`yew`のversionが0.20でないと動かない可能性があります）

``` bash
$ cargo add yew -F csr
```

`Cargo.toml`に直接追加する場合は、ファイル内の[dependencies]に以下の内容を追記してください

``` Cargo.toml
[dependencies]
yew = { version = "0.20", features = ["csr"] }
```

### Index.htmlを追加

下のコマンドでyew-appディレクトリ直下にindex.htmlを作成

``` bash
$ touch index.html 
```

できたファイルの中身を以下のように変更

``` index.html
<!DOCTYPE html>
<html lang="en">
    <head> </head>
    <body></body>
</html>
```

### Yewの記述を追加

src/main.rsの中身を以下のように変更

``` rust
use yew::prelude::*;

#[function_component(App)]
fn app() -> Html {
    html! {
        <h1>{ "Hello World" }</h1>
    }
}

fn main() {
    yew::Renderer::<App>::new().render();
}
```

### 開発サーバーを起動

以下のコマンドでサーバーを起動することで、localhostで開発中のページを確認できます
Dockerで実行する場合は`--address 0.0.0.0`オプションを忘れないよう注意

```bash
$ trunk serve --address 0.0.0.0
```

## 参考

### Qiita既存のyewコンテナ構築記事

以下のページは公式提供のDockerイメージが使えない方向けにdebianのimageをベースに環境を構築したもののようです。

[DockerでYew(Rust+WebAssembly)環境を構築 - Qiita](https://qiita.com/robitan/items/456b7b31ddb6a7c6e430)

### Docker + WasmでWasmコンテナ実行

公式からテクニカルプレビューが発表されたDocker + Wasmを用いるというものもあり

[Docker＋Wasm で WASM をコンテナとして実行する | 豆蔵デベロッパーサイト](https://developer.mamezou-tech.com/blogs/2023/01/25/using-wasm-on-docker/)