🐣

Rust | Cloud Run で Axum 製 Web アプリケーションを動かす

2023/04/29に公開

初めての Publication 投稿です🐣🐣🐣

普段は AWS を使ってお仕事をしていますが、今回はちょっとだけ GCP に浮気します😘

Rust とその Web アプリケーションフレームワークである Axum を使って実装した Web アプリケーション(REST API)を Cloud Run にデプロイしてみたいと思います。

Cloud Run とは

Cloud Run は、Google のスケーラブルなインフラストラクチャ上でコンテナを直接実行できるマネージド コンピューティング プラットフォームです。

コンテナ イメージをビルドできるものであれば、任意のプログラミング言語で記述されたコードを Cloud Run にデプロイできます。

お好きな言語を使って実装することができるとのことで、Rust で実装した Web アプリケーションを動かしてみたいと思います。

引用した説明からも分かる通り、AWS App Runner と比較されることが多いサービスです。

AWS App Runner で試した記事は以下からどうぞ!

https://zenn.dev/collabostyle/articles/76f1c87b743e97

Cloud Run の良いところ

良いところはこんな感じでしょうか。
お勉強駆動開発としては、お財布に優しいかは非常に重要です👮

  • コンテナ資産を活用できる
    • 好きな言語で実装可能(Rust が使えちゃう🥰🥰🥰)
  • コードが実行されている間のみ料金が発生する従量課金
    • お財布に優しい(ありがてぇ😍😍)
  • 高速自動スケーリング
    • 本格的な Kubernetes クラスタが構成され、リクエスト数に応じて自動的にスケールされる
    • コンテナインスタンスはデフォルトで最大 1000 までスケールアウトでき、リクエストのないときはインスタンス数が 0 になるまでスケールインされる(ありがてぇ😍)
  • 自動冗長化
    • 高可用性のために複数のインスタンスを作成する必要なし
  • トラフィック分割
    • トラフィックを複数のリビジョン間で分割できるため、カナリアデプロイや Blue/Green デプロイなどが可能(ほぉ、なんかすごそう😗)

Rust と Axum で Web アプリケーションを実装する

今回は Axum という Web アプリケーションフレームワークを使っていきます。

実行時間での従量課金である Cloud Run では、高速に動作する Rust で実装することでコスト削減が期待できます。

Cargo.toml

使用するクレートは、メインである Axum 、非同期ランタイムである tokio や JSON シリアライズに使用する serde と serde_json です。

Cargo.toml
[package]
name = "axum-cloudrun"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
axum = "0.6.17"
serde = { version = "1.0.160", features = ["derive"] }
serde_json = "1.0.96"
tokio = { version = "1.28.0", features = ["full"] }

main.rs

一定のレスポンスが返る、簡単な REST API を実装しました。

登山が趣味ゆえ、こういうときに山のデータを使いがちです⛰️🏔️

main.rs
use axum::http::StatusCode;
use axum::routing::get;
use axum::{Json, Router};
use serde::Serialize;
use std::net::SocketAddr;

#[tokio::main]
async fn main() {
    let app = Router::new().route("/mountains", get(find_sacred_mountains));

    let addr = SocketAddr::from(([0, 0, 0, 0], 8080));
    axum::Server::bind(&addr)
        .serve(app.into_make_service())
        .await
        .unwrap_or_else(|_| panic!("Server cannot launch."));
}

async fn find_sacred_mountains() -> (StatusCode, Json<JsonResponse>) {
    let response: JsonResponse = Default::default();
    (StatusCode::OK, Json(response))
}

#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct JsonResponse {
    mountains: Vec<SacredMountain>,
    total: usize,
}

impl Default for JsonResponse {
    fn default() -> Self {
        let mountains = vec![
            SacredMountain::new(1, "恐山".to_string()),
            SacredMountain::new(2, "比叡山".to_string()),
            SacredMountain::new(3, "高野山".to_string()),
        ];
        let total = mountains.len();

        Self { mountains, total }
    }
}

#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SacredMountain {
    id: i32,
    name: String,
}

impl SacredMountain {
    fn new(id: i32, name: String) -> Self {
        Self { id, name }
    }
}

Dockerfile

Dockerfile
# Use the official Rust image.
# https://hub.docker.com/_/rust
FROM rust

# Copy local code to the container image.
WORKDIR /usr/src/app
COPY . .

# Install production dependencies and build a release artifact.
RUN cargo build --release

# Service must listen to $PORT environment variable.
# This default value facilitates local development.
ENV PORT 8080

# Run the web service on container startup.
ENTRYPOINT ["target/release/axum-cloudrun"]

Cloud Run にデプロイ

Cloud Run のドキュメントにあるクイックスタートに従って、デプロイします。

https://cloud.google.com/run/docs/quickstarts/build-and-deploy/deploy-service-other-languages?hl=ja

事前準備

  1. (必要であれば)プロジェクトを作成する
  2. プロジェクトで課金が有効になっているかを確認する
  3. Google Cloud CLI をインストールします。
  4. gcloud CLIを初期化する
gcloud init

5.Cloud Run サービスのデフォルトプロジェクトを設定する

 gcloud config set project プロジェクト名

デプロイ

デプロイは以下のコマンドを実行するだけです。超簡単🫶

gcloud run deploy

コマンドを実行すると、以下のようなことと聞かれるので y やエンターで進めます。

  1. ソースコードの場所
  2. サービス名
  3. Artifact Registry API を有効にするか
  4. リージョンの入力(任意のリージョンを数字で指定)
  5. 未認証の呼び出し許可

URL にアクセスしてみる

Cloud Run では、デフォルトで *.run.app ドメインの一意のサブドメインがサービスの HTTPS エンドポイントとして提供されます。

今回は /mountains でネストしているので、https://{発行されたURL}.run.app/mountains にアクセスすると以下のようなレスポンスが200応答で返ってきます。

{
    "mountains": [
        {
            "id": 1,
            "name": "恐山"
        },
        {
            "id": 2,
            "name": "比叡山"
        },
        {
            "id": 3,
            "name": "高野山"
        }
    ],
    "total": 3
}
この三山は何?

「日本三大霊山」です😉
全部登ったことないです笑

ソースコード

ソースコード一式は GitHub で公開してます。

https://github.com/codemountains/axum-cloudrun

おわりに

Rust で実装したものをデプロイしたいなぁと思って、Cloud Run を選択してみました。

コンテナで実行できるようにしておけば良いし、コマンドで簡単にデプロイできるし、とっても便利ですね!!!

いいじゃん、GCP😘

とはいえ、1日でも早く Cloud Functions や AWS Lambda で Rust がネイティブサポートされるのを期待したいところです...

(Cloud Functions Rust でググって、「あ、察し...」ってなった😇)

コラボスタイル Developers

Discussion