Cloudflare WorkersでRustのプログラムを動かす

2021.10.30

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

どうも!西村祐二です。

最近、Rustを勉強しています。

Cloudflare WorkersでRustが動かせることを知ったので、Rustのプログラムを動かしてみたいと思います。

環境

今回、試した際の環境情報です。

  • macOS Big Sur: 11.6

  • rust: v1.56.0

  • wrangler: 1.19.4

APIキーを取得

今回、CLIからworkerを作成したりデプロイしたりするので、はじめにAPIキーを取得します。

workersのコンソール画面にAPIキー取得するためのリンクがあるのでそこから移動します。

API Tokensの「Create Token」を選択

Edit Cloudflare Workersの「Use template」を選択

今回は動作確認が目的なので必須な項目の「Account Resources」「Zone Resources」を適当に埋めて、「Continue to summary」を選択

表示される権限の範囲で問題なければ「Create Token」をクリック

出力されているAPI Tokenをコピーしておきます。

CLIをインストール

Cloudflare Workersを操作するCLIの「wrangler」をインストールしていきます。

レポジトリに記載されているとおりに進めていきます。

$ npm i @cloudflare/wrangler -g

npmではなく、cargoからでもインストールできるようです。

$ cargo install wrangler
❯ wrangler -V
wrangler 1.19.4

プロジェクトを作成

--typeにrustを指定することでRustのひな形を生成してくれます。

今回、my-workerというプロジェクト名で作成しています。

❯  wrangler generate --type=rust my-worker
⬇️   Installing cargo-generate v0.5.0...
?   Creating project called `my-worker`...
✨   Done! New project created /Users/yuji/study/cloudflare/my-worker

CLIの設定

CLIからWorkersのデプロイなどできるように設定をしていきます。

下記コマンドを入力して、先程取得したAPIキーを入力します。

❯ wrangler config

                                  ╭──────────────────────────────────────────────────────────────────────────────────────────────────────╮
                                  │                                                                                                      │
                                  │             To find your API Token, go to https://dash.cloudflare.com/profile/api-tokens             │
                                  │                     and create it using the "Edit Cloudflare Workers" template.                      │
                                  │                                                                                                      │
                                  │      Consider using `wrangler login` which only requires your Cloudflare username and password.      │
                                  │                                                                                                      │
                                  │                 If you are trying to use your Global API Key instead of an API Token                 │
                                  │                         (Not Recommended), run `wrangler config --api-key`.                          │
                                  │                                                                                                      │
                                  ╰──────────────────────────────────────────────────────────────────────────────────────────────────────╯

Enter API Token:

?  Validating credentials...
✨  Successfully configured. You can find your configuration file at: /Users/yuji/.wrangler/config/default.toml

ビルド

CLIの設定ができたら、ビルドを試してみます。

下記コマンドでビルドできます。

❯ wrangler build
?  Running cargo install -q worker-build && worker-build --release
[INFO]: ?  Checking for the Wasm target...
[INFO]: ?  Compiling to Wasm...

(snip)
 
   Compiling my-worker v0.1.0 (/Users/yuji/study/cloudflare/my-worker)
    Finished release [optimized] target(s) in 41.68s
[INFO]: ⬇️  Installing wasm-bindgen...
[INFO]: Optimizing wasm binaries with `wasm-opt`...
[INFO]: Optional fields missing from Cargo.toml: 'description', 'repository', and 'license'. These are not necessary, but recommended
[INFO]: ✨   Done in 45.60s
[INFO]: ?   Your wasm pkg is ready to publish at /Users/yuji/study/cloudflare/my-worker/build.
⚠️   [WARN]: There's a newer version of wasm-pack available, the new version is: 0.10.1, you are using: 0.9.1. To update, navigate to: https://rustwasm.github.io/wasm-pack/installer/
✨  Build completed successfully!

デプロイ

次にデプロイを試してみます。

下記コマンドでデプロイできます。

デプロイ後アクセスできるエンドポイントが作成されます。そのエンドポイントはプロジェクト名がサブドメインとして作成されるようです。

今回、プロジェクト名を「my-worker」としたので、https://my-worker.の形でデプロイされていました。

❯ wrangler publish
?  Running cargo install -q worker-build && worker-build --release
[INFO]: ?  Checking for the Wasm target...
[INFO]: ?  Compiling to Wasm...
    Finished release [optimized] target(s) in 0.11s
[INFO]: ⬇️  Installing wasm-bindgen...
[INFO]: Optimizing wasm binaries with `wasm-opt`...
[INFO]: Optional fields missing from Cargo.toml: 'description', 'repository', and 'license'. These are not necessary, but recommended
[INFO]: ✨   Done in 0.98s
[INFO]: ?   Your wasm pkg is ready to publish at /Users/yuji/study/cloudflare/my-worker/build.
⚠️   [WARN]: There's a newer version of wasm-pack available, the new version is: 0.10.1, you are using: 0.9.1. To update, navigate to: https://rustwasm.github.io/wasm-pack/installer/
✨  Build completed successfully!
✨  Successfully published your script to
 https://my-worker.<yout domain>
  • コンソールからもデプロイしたworkerが確認できます。

動作確認

デプロイしたコードが動いているか確認していきます。

ちなみに自動生成されたRustのコードは下記のようになっていました。

use serde_json::json;
use worker::*;

mod utils;

fn log_request(req: &Request) {
    console_log!(
        "{} - [{}], located at: {:?}, within: {}",
        Date::now().to_string(),
        req.path(),
        req.cf().coordinates().unwrap_or_default(),
        req.cf().region().unwrap_or("unknown region".into())
    );
}

#[event(fetch)]
pub async fn main(req: Request, env: Env) -> Result<Response> {
    log_request(&req);

    // Optionally, get more helpful error messages written to the console in the case of a panic.
    utils::set_panic_hook();

    // Optionally, use the Router to handle matching endpoints, use ":name" placeholders, or "*name"
    // catch-alls to match on specific patterns. Alternatively, use `Router::with_data(D)` to
    // provide arbitrary data that will be accessible in each route via the `ctx.data()` method.
    let router = Router::new();

    // Add as many routes as your Worker needs! Each route will get a `Request` for handling HTTP
    // functionality and a `RouteContext` which you can use to  and get route parameters and
    // Environment bindings like KV Stores, Durable Objects, Secrets, and Variables.
    router
        .get("/", |_, _| Response::ok("Hello from Workers!"))
        .post_async("/form/:field", |mut req, ctx| async move {
            if let Some(name) = ctx.param("field") {
                let form = req.form_data().await?;
                match form.get(name) {
                    Some(FormEntry::Field(value)) => {
                        return Response::from_json(&json!({ name: value }))
                    }
                    Some(FormEntry::File(_)) => {
                        return Response::error("`field` param in form shouldn't be a File", 422);
                    }
                    None => return Response::error("Bad Request", 400),
                }
            }

            Response::error("Bad Request", 400)
        })
        .get("/worker-version", |_, ctx| {
            let version = ctx.var("WORKERS_RS_VERSION")?.to_string();
            Response::ok(version)
        })
        .run(req, env)
        .await
}

/worker-versionにアクセスするとバージョン情報が確認できるようなのでブラウザからアクセスしてみます。

下記のように表示されて問題なく動作しているようです!

ソースをみると/form/:field に対してデータをPSOTするとjsonで返してくれるのでこちらも試してみます。

下記のようなコマンドを実行すると想定通りjsonのレスポンスが返ってきました!

curl -X POST "https://my-worker.<your domain>/form/test" -F 'test=this is test'
{"test":"this is test"}

ローカルでの動作確認

下記コマンドを実行することでローカルで動作確認することができます。

❯ wrangler dev
?  Running cargo install -q worker-build && worker-build --release
[INFO]: ?  Checking for the Wasm target...
[INFO]: ?  Compiling to Wasm...
    Finished release [optimized] target(s) in 0.45s
[INFO]: ⬇️  Installing wasm-bindgen...
[INFO]: Optimizing wasm binaries with `wasm-opt`...
[INFO]: Optional fields missing from Cargo.toml: 'description', 'repository', and 'license'. These are not necessary, but recommended
[INFO]: ✨   Done in 1.40s
[INFO]: ?   Your wasm pkg is ready to publish at /Users/yuji/study/cloudflare/my-worker/build.
⚠️   [WARN]: There's a newer version of wasm-pack available, the new version is: 0.10.1, you are using: 0.9.1. To update, navigate to: https://rustwasm.github.io/wasm-pack/installer/
?  Listening on http://127.0.0.1:8787
  • 別ターミナルからcurlでバージョンを確認してみます。

すると想定通りバージョン情報が返ってきました。

$ curl "http://127.0.0.1:8787/worker-version"
0.0.6

さいごに

Cloudflare WorkersでRustが動かしてみました。

事前準備がほとんどいらず、デプロイやローカルでの動作確認などとても簡単でした。

今後は、RustでAPIなんか作っていきたいと思います。

誰かの参考になれば幸いです。

参考サイト