🦀

100日後にRustをちょっと知ってる人になる: [Day 57]はじめての Fermyon Spin

2022/10/27に公開

Day 57 のテーマ

Day 56 では、Cloud Native Wasm Day で発表のあった Fermyon Cloud について使い方とシンプルなアプリケーションの作成&デプロイの流れを見てみました。
しかし、流れを見ただけであって、実際には "何"をしているのかまでは説明を割愛していたところがあります。そこで、今日以降の中で Fermyyon のソリューションについて少し眺めつつ、それが Rust によるアプリケーションと組み合わせの効果がどのようにあるのか、そんなところを考えていきたいと思います。

さて、Day 56 で次のように伝えていたと思います:

WebAssembly アプリケーションの実行環境としての Fermyon Cloud の前に、本来だったら Fermyon が提供している次のプロジェクトを説明しておく必要があるのです。

  • Spin
  • Fermyon Platform

ということで、今日は Spin について少し見ていきたいと思います。

Spin

Spin は WebAssembly コンポーネントを用いてイベント・ドリブンなサーバーサイド アプリケーションを作り、そして動かすためのフレームワークです。つまり、HTTP リクエストに対してレスポンスを返すような機能を持つ WebAssembly モジュールを書くためのインターフェースを提供するが、Spin なのです。

また、この Spin の注目すべき点は、多言語フレームワークであることです。WebAssembly と耳にすると、RustGo を思い浮かべるかもしれません。Spin ではもちろん Rust と Go をサポートしていますが、それ以外の言語もサポートをしています。

Spin のインストール

Spin をインストールしてみます。

現在 Spin は、以下の環境で動作をします。

  • Linux (amd64)
  • macOS (Intel)
  • macOS (Apple Silicon)
  • Windows with WSL2 (amd64)

それらの環境で以下を実行して Spin をカレントディレクトリに取得します。

curl -fsSL https://developer.fermyon.com/downloads/install.sh | bash

そして、必要に応じてパスの通っている /usr/local/bin/ ディレクトリに移動させます。

sudo mv spin /usr/local/bin/

インストール作業は以上です。

spin --help
spin 0.6.0 (12a5037 2022-10-21)
The Spin CLI

USAGE:
    spin <SUBCOMMAND>

OPTIONS:
    -h, --help       Print help information
    -V, --version    Print version information

SUBCOMMANDS:
    bindle       Commands for publishing applications as bindles
    build        Build the Spin application
    deploy       Deploy a Spin application
    help         Print this message or the help of the given subcommand(s)
    login        Log into the server
    new          Scaffold a new application or component based on a template
    plugin       Install/uninstall Spin plugins
    templates    Commands for working with WebAssembly component templates
    up           Start the Spin application

Cargo による Spin のインストール

Rust のパッケージマネージャーとして使われるツールの、cargo を使用してインストールすることも可能です。
以下のコマンドを実行することでインストールが行われます。

git clone https://github.com/fermyon/spin -b v0.6.0
cd spin
rustup target add wasm32-wasi
cargo install --locked --path .

Spin によるアプリケーション開発

Day 56 でもサンプルアプリケーションを作ってみましたが、あらためて作り方をおさらいします。

以下のコマンドでプロジェクトテンプレートを作成します。

spin new <テンプレート名> <プロジェクト名>

現在テンプレートとしては以下のものがあります。

名前 説明
http-c HTTP request handler using C and the Zig toolchain
http-go HTTP request handler using (Tiny)Go
http-grain HTTP request handler using Grain
http-rust HTTP request handler using Rust
http-swift HTTP request handler using SwiftWasm
http-zig HTTP request handler using Zig
redis-go Redis message handler using (Tiny)Go
redis-rust Redis message handler using Rust

http-rust テンプレートによるアプリケーション作成

spin new コマンドによりプロジェクトを作成します。

spin new

Pick a template to start your project with:
  http-c (HTTP request handler using C and the Zig toolchain)
  http-go (HTTP request handler using (Tiny)Go)
  http-grain (HTTP request handler using Grain)
> http-rust (HTTP request handler using Rust)
  http-swift (HTTP request handler using SwiftWasm)
  http-zig (HTTP request handler using Zig)
  redis-go (Redis message handler using (Tiny)Go)
  redis-rust (Redis message handler using Rust)
Pick a template to start your project with: http-rust (HTTP request handler using Rust)
Enter a name for your new project: hello-spin-rust
Project description: はじめてのSpin
HTTP base: /
HTTP path: /...

次のような階層でファイルが生成されます。

hello-spin-rust
├── Cargo.toml
├── spin.toml
└── src
   └── lib.rs

spin.toml

Spin アプリケーションのマニフェストになる、spin.toml が以下のような内容で生成されています。

spin_version = "1"
authors = ["shinyay <shinya.com@gmail.com>"]
description = "はじめてのSpin"
name = "hello-spin-rust"
trigger = { type = "http", base = "/" }
version = "0.1.0"

[[component]]
id = "hello-spin-rust"
source = "target/wasm32-wasi/release/hello_spin_rust.wasm"
[component.trigger]
route = "/hello"
[component.build]
command = "cargo build --target wasm32-wasi --release"

このマニフェストに記載されている内容でポイントになるのは次の 2 点です:

  • source = "target/wasm32-wasi/release/hello_spin_rust.wasm"
  • route = "/hello"

まず source では、Spin が実行対象とする WebAssembly モジュールが指定されています。
次に route が WebAssembly モジュールが HTTP リクエストを受け付けるエンドポイントのアクセスルートです。

Cargo.toml

この Spin アプリケーションプロジェクトのデフォルトで定義されている Dependencies をみてみます。

[dependencies]
# Useful crate to handle errors.
anyhow = "1"
# Crate to simplify working with bytes.
bytes = "1"
# General-purpose crate with common HTTP types.
http = "0.2"
# The Spin SDK.
spin-sdk = { git = "https://github.com/fermyon/spin", tag = "v0.6.0" }
# Crate that generates Rust Wasm bindings from a WebAssembly interface.
wit-bindgen-rust = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "cb871cfa1ee460b51eb1d144b175b9aab9c50aba" }

以下の 5 つの Dependency が設定されていました。

  • anyhow
  • bytes
  • http
  • spin-sdk
  • wit-bindgen-rust

lib.rs

モジュールのコードを見てみます。

use anyhow::Result;
use spin_sdk::{
    http::{Request, Response},
    http_component,
};

/// A simple Spin HTTP component.
#[http_component]
fn hello_spin_rust(req: Request) -> Result<Response> {
    println!("{:?}", req.headers());
    Ok(http::Response::builder()
        .status(200)
        .header("foo", "bar")
        .body(Some("Hello, Fermyon".into()))?)
}

一件したところ、そこまで珍しいコード構成にはなっていないことが分かると思います。一般的な http クレートを使用したアプリケーションコードとほぼ変わらないと感じると思います。

ポイントになるのは、http_component マクロが付いている、というところです。
これが、Spin アプリケーションを作っていく上での大事なマクロだということを覚えておいてください。

Spin によるアプリケーションビルド

spin build コマンドにより、spin.toml に定義したコマンドを実行してビルドを実施します。
このケースでは実行されるコマンド内容は次のものになります。

cargo build --target wasm32-wasi --release

つまり、環境には予め wasm32-wasi ターゲットが必要だということが分かります。
インストールについては、以下の記事にて紹介をしています、

spin build
実行結果
Executing the build command for component hello-spin-rust: cargo build --target wasm32-wasi --release
   Compiling version_check v0.9.4
   Compiling anyhow v1.0.66
   Compiling memchr v2.5.0
   Compiling tinyvec_macros v0.1.0
   Compiling pulldown-cmark v0.8.0
   Compiling bitflags v1.3.2
   Compiling proc-macro2 v1.0.47
   Compiling id-arena v2.2.1
   Compiling unicode-ident v1.0.5
   Compiling unicode-xid v0.2.4
   Compiling quote v1.0.21
   Compiling syn v1.0.103
   Compiling unicode-segmentation v1.10.0
   Compiling wit-bindgen-gen-rust-wasm v0.2.0 (https://github.com/bytecodealliance/wit-bindgen?rev=cb871cfa1ee460b51eb1d144b175b9aab9c50aba#cb871cfa)
   Compiling async-trait v0.1.58
   Compiling bytes v1.2.1
   Compiling fnv v1.0.7
   Compiling itoa v1.0.4
   Compiling percent-encoding v2.2.0
   Compiling tinyvec v1.6.0
   Compiling heck v0.3.3
   Compiling form_urlencoded v1.1.0
   Compiling unicase v2.6.0
   Compiling http v0.2.8
   Compiling unicode-normalization v0.1.22
   Compiling wit-parser v0.2.0 (https://github.com/bytecodealliance/wit-bindgen?rev=cb871cfa1ee460b51eb1d144b175b9aab9c50aba#cb871cfa)
   Compiling wit-bindgen-gen-core v0.2.0 (https://github.com/bytecodealliance/wit-bindgen?rev=cb871cfa1ee460b51eb1d144b175b9aab9c50aba#cb871cfa)
   Compiling wit-bindgen-gen-rust v0.2.0 (https://github.com/bytecodealliance/wit-bindgen?rev=cb871cfa1ee460b51eb1d144b175b9aab9c50aba#cb871cfa)
   Compiling wit-bindgen-rust-impl v0.2.0 (https://github.com/bytecodealliance/wit-bindgen?rev=cb871cfa1ee460b51eb1d144b175b9aab9c50aba#cb871cfa)
   Compiling wit-bindgen-rust v0.2.0 (https://github.com/bytecodealliance/wit-bindgen?rev=cb871cfa1ee460b51eb1d144b175b9aab9c50aba#cb871cfa)
   Compiling spin-macro v0.1.0 (https://github.com/fermyon/spin?tag=v0.6.0#12a50379)
   Compiling spin-sdk v0.6.0 (https://github.com/fermyon/spin?tag=v0.6.0#12a50379)
   Compiling hello-spin-rust v0.1.0 (/Users/yanagiharas/Works/docs/doc-to-zenn/codes/day_57_spin-getting-started/hello-spin-rust)
    Finished release [optimized] target(s) in 13.69s
Successfully ran the build command for the Spin components.

ビルドを行うと、target/wasm32-wasi/release/ 配下に wasm モジュールが生成されます。

ls -l target/wasm32-wasi/release/

total 4136
drwxr-xr-x   3 yanagiharas  staff       96 build/
drwxr-xr-x  34 yanagiharas  staff     1088 deps/
drwxr-xr-x   2 yanagiharas  staff       64 examples/
-rw-r--r--   1 yanagiharas  staff      244 hello_spin_rust.d
-rwxr-xr-x   1 yanagiharas  staff  2110745 hello_spin_rust.wasm*
drwxr-xr-x   2 yanagiharas  staff       64 incremental/

Spin によるアプリケーション実行

spin up コマンドにより WebAssembly アプリケーションを実行します。

spin up
Serving http://127.0.0.1:3000
Available Routes:
  hello-spin-rust: http://127.0.0.1:3000/hello

3000 番ポートでリッスンをしています。そのアクセスルートに対してアクセスをしてみます。

curl -i http://127.0.0.1:3000/hello
HTTP/1.1 200 OK
foo: bar
content-length: 14
date: Thu, 27 Oct 2022 05:27:11 GMT

Hello, Fermyon

装置していたとおりのレスポンスが帰ってきました。

  • リターンコード: 200
  • ヘッダ: foo: bar
  • ボディメッセージ: Hello, Fermyon

実行確認をすることができました。

Day 57 のまとめ

Spin の導入、そして Spin を用いたアプリケーション開発について見てみました。
開発フレームワークとしてだけでなく、実行フレームワークとして機能していることがよくわかったと思います。
Wasm モジュールがどこにあるのか、どんな名称なのか、などは特に意識することなく、実際には Spin コマンドだけで開発、ビルド、実行を行うことが分かったと思います。

今日使用した spin コマンド:

  • spin new
  • spin build
  • spin up
GitHubで編集を提案

Discussion