workers-rsでハローワールド + Github Actionで自動デプロイ
この記事は デジタルキューブ & ヘプタゴン Advent Calendar 2024 の 12月23日分の記事です。
Rust練習したいなーと常々思っていたので、入口としてCloudflare Workers用のRustクレートworkers-rsに入門してみます。
そもそもCloudflare Workersって何?
Cloudflareのエッジロケーションでサーバレスにコードを実行してくれるサービスです。
CDNのエッジで処理するので通信による待ち時間が少なく、V8 isolateによってコールドスタートの影響も非常に少ないことが特徴です。厳密な説明ではありませんが、僕は制約がある代わりに早くて軽量なAWS Lambdaみたいなものだと思ってます。
CDNやエッジロケーションの概念から説明している100秒テックさんの動画の解説がわかりやすいです。
やること:
- workers-rsのhello-worldのテンプレートをCloudflare Workers上で動かす。
- ついでにGitHub Actionsで自動デプロイしてみる。
Cloudflare Workers上でプラットフォームの標準とされるJavaScript/TypeScript以外の言語を実行する際はWebAssembly (Wasm)として実行する必要があります。そのため、Rustを使って開発する場合にはある程度のWasmの知識やJavaScriptとの連携を意識したコーディングが必要でしたが、公式のworkers-rsクレートを使用することで、これらをあまり意識せずにRustを使った開発ができるようになりました。
下記の公式ドキュメントを参考にしながらテンプレートの手動デプロイと、GitHub Actions を使った自動デプロイの2つを行います。(以降の手順はmacOS上で実行したものです。)
Rust環境のセットアップ
まずは、rustのプロジェクト作成とコンパイルをするための下準備を行います。
rustupでWasm用のツールチェーン(コンパイラと関連ツール)をインストール。
rustup target add wasm32-unknown-unknown
テンプレートからプロジェクトを作成するためにcargo-generateをインストール。
cargo install cargo-generate
テンプレートの作成と手動デプロイ
プロジェクトを作成して手動でCloudflare Workersにデプロイしてみます。
テンプレートからプロジェクトを作成
まずはcargo generate
でプロジェクトを作成します。
cargo generate cloudflare/workers-rs
テンプレートはtemplates/hello-world
を選択。
? 🤷 Which template should be expanded? ›
templates/axum
❯ templates/hello-world
templates/hello-world-http
templates/leptos
プロジェクト名はworkers-rs-github-actions
とします。(任意の名前でOK)
🤷 Project Name: workers-rs-github-actions
テンプレート内のファイルの確認
次のファイル・ディレクトリが生成されます。
- Cargo.toml - Cargoの設定ファイル。Workers用のWasmのコンパイルに関する設定と、依存するクレートとの依存関係が記載されています。
- wrangler.toml - Wranglerの用の設定ファイル。wrangler devとwrangler deployの実行時にworker-buildが呼び出されるよう Custom buildsで設定されています。
- src - Rustのコード用のディレクトリです。
.
├── Cargo.toml
├── src
│ └── lib.rs
└── wrangler.toml
src/lib.rs
の中身はこんな感じです。Hello World!
とレスポンスするだけのシンプルなものです。
use worker::*;
#[event(fetch)]
async fn fetch(
_req: Request,
_env: Env,
_ctx: Context,
) -> Result<Response> {
console_error_panic_hook::set_once();
Response::ok("Hello World!")
}
以下、コードの簡単な解説です。
use worker::*
でworkerクレートをインポート
use worker::*;
eventマクロ(#[event]
)で関数に属性を付与します。eventマクロで属性を付与された関数は、イベント発生時に呼び出されるエントリーポイントとなります。#[event(fetch)]
の場合、WorkerがHTTPリクエストを受信した際に呼び出されます。(参照:event macro)
#[event(fetch)]
#[event(fetch)]
でfetchイベントのエントリーポイントに設定された関数は三つのパラメータを受け取ります。(参照:fetch parameters)
-
Request
:受信したリクエストを表すオブジェクトで、HTTPメソッド、ヘッダー情報、ボディのデータなどを含みます。 -
Env
:wrangler.tomlで設定された、環境変数やWorkerにバインドされたリソースへのアクセスするためのオブジェクトです。 -
Context
:何を取り扱っているかよくわかりませんでした🙏 原文→Provides access towaitUntil
(deferred asynchronous tasks) andpassThroughOnException
(fail open) functionality.
async fn fetch(
_req: Request,
_env: Env,
_ctx: Context,
) -> Result<Response> {
...
}
console_error_panic_hook::set_once()
を使用して、Wasmのパニック発生時にデバッグメッセージを表示するようにします。
console_error_panic_hook::set_once();
fetchイベントのエントリーポイントの関数では、Response型の戻り値に基づいてHTTPリクエストにレスポンスします。このテンプレートではResponse::ok("Hello World!")
を使用してステータスコード200
でHello World!
という文字列をレスポンスします。(参照:Response)
Response::ok("Hello World!")
手動デプロイと動作チェック
wrangler deployでデプロイします。(JavaScript/TypeScript用のテンプレートと同じコマンドでデプロイできます)
npx wrangler deploy
worker-buildの実行により、RustコードはWASMにコンパイルされ、WASMモジュールを利用するためのJavaScriptグルーコードと共にCloudflare Workersにデプロイされます。
wrangler deploy
⛅️ wrangler 3.92.0 (update available 3.95.0)
-------------------------------------------------------
Running custom build: cargo install -q worker-build && worker-build --release
[INFO]: 🎯 Checking for the Wasm target...
[INFO]: 🌀 Compiling to Wasm...
...
CloudflareのダッシュボードのWorkers & Pagesで確認するとデプロイされていることが確認できます。
先ほどの画像にもあるVisit
のリンクから動作を確認。Hello World!
が返され、正常に動作していることが確認できました。
GitHub Actionsからの自動デプロイ
GitHub Actionsからプッシュをトリガーにデプロイします。
GitHubにリポジトリを作成
先ほど作成したプロジェクトのコードからGitHubにリポジトリを作成します。
git init
git add .
git commit -m "first commit"
gh repo create workers-rs-github-actions --private --source=. --remote=origin --push
GitHub Actionsのワークフローを追加
GitHub Actionsのワークフローを記述した.github/workflow/deploy.yml
を作成します。
mainブランチへのpushをトリガーに、公式のアクションのcloudflare/wrangler-action@v3
でビルドとデプロイを行います。JS/TSで開発する場合と同じアクションが使えます。
name: Deploy
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
name: Deploy
steps:
- uses: actions/checkout@v3
- name: Deploy to Cloudflare Workers
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CF_API_TOKEN }}
accountId: ${{ secrets.CF_ACCOUNT_ID }}
シークレットを登録
次に、GitHub ActionsからCloudflare WorkersへのデプロイをするためにCloudflareのAPI tokenと account IDを取得して、GitHubのリポジトリにシークレットとして登録します。
API Tokenの取得方法
account IDの取得方法GitHubリポジトリの「Settings > Security > Secret and variables」 から Cloudflare のAPI Tokenを CF_API_TOKEN
、account IDをCF_ACCOUNT_ID
として登録します。
pushして自動デプロイ
デプロイできたことを確認するためにlib.rs内のResponse::okの引数をHello World from GitHub Actions!
に書き換えます。
- Response::ok("Hello World!")
+ Response::ok("Hello World from GitHub Actions!")
書き換え後コミットしてプッシュ。
git add .
git commit -m "Workflowを追加"
git push -u origin main
Workflowが動いた!ここから終わるまで5分ほど待つ。
終わった!(途中環境変数の設定ミスでrerunするなどしたので29 minutes agoになってます。)
再度デプロイ先のリンクを叩くとHello World from GitHub Actions!
が表示され、反映されてることが確認できます。
GitHub Actionsからのデプロイも成功!
おわりに
workers-rsのhello-worldのテンプレートをデプロイしてみました。
Rustを使った開発でも、JavaScript/TypeScriptの時と変わらず手軽に開発とデプロイが行えていい感じです。これを使ってちょっとしたチャットボットを作ったりしながら、気軽にRustコードを書いていきたい。
今回は「シークレットを登録」で永続的な認証情報を登録して自動デプロイを行いましたが、一時的な認証情報を発行して行うデプロイも試したいです。
Discussion