🦀

Cloud RunでRustのAPI Serverを動かす

2022/02/20に公開

この記事は?

Cloud RunでRustのAPI Serverを動かしてみたメモです。

ServerlessとRustの相性の良さは今更言及するまでもないですよね。

GCPのCloud RunやAWSのLambdaなどのサービスは起動時間とメモリ使用量で従量課金されるので、GCがなく実行性能が高速なRustで実装することでコストを節約することができます。

GCPにServerlessでバックエンドを構築するにあたって、Rustを採用するのは学習コストを除けばやらない理由がないので今回試してみたメモを残しておきます。

Cloud Functions? or Cloud Run?

2022/02/20時点でCloud FunctionにRustのRuntimeが存在しないので、Rustを使いたい場合はCloud Runを使うことになります。

開発環境

  • macOS Monterey Chip Apple M1 Pro
  • rustup 1.24.3
  • cargo 1.58.0
  • Docker version 20.10.12

とりあえず動かすまでの手順

クイックスタート
他の言語でサービスをビルドしてデプロイする を参考にして進めていく

今回はソースコードからdeployします。
(GCP Projectの作成やgcloud CLIのインストールは省略します。)

  1. アプリケーションとDockerfileの作成
  2. ソースからdeploy

アプリケーションとDockerfileの作成

API Serverを用意する

まず新規のプロジェクトを開始します。

cargo new cloud-run-rust-test

次にHttp Serverを実装するのに必要なcrateを追加します。
私の場合大抵 cargo-edit を使って追加します、楽なので。
今回はHyperやactix-webではなくaxumを試してます。

cargo add axum tokio serde serde_json tracing tracing-subscriber

実行するとCargo.tomlに追記されてるはずです。

[package]
edition = "2021"
name = "cloud-run-rust-test"
version = "0.1.0"

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

[dependencies]
axum = "0.4.5"
serde = "1.0.136"
serde_json = "1.0.79"
tokio = {version = "1.17.0", features = ["full"]}
tracing = "0.1.31"
tracing-subscriber = "0.3.9"

とりあえずHello, World!するのに最低限の実装をします。
localhostではなく 0, 0, 0, 0にしておきます。

個人的にハマった点

私はここで何も考えず、localhostのままにしてたので下記のエラーが出ており、コンテナがスタートしませんでした。

Container failed to start. Failed to start and then listen on the port defined by the PORT environment variable.

Cloud Run のトラブルシューティング
をみて解決しました

use axum::{routing::get, Router};
use std::net::SocketAddr;

#[tokio::main]
async fn main() {
    // initialize tracing
    tracing_subscriber::fmt::init();

    // build our application with a route
    let app = Router::new()
        // `GET /` goes to `root`
        .route("/", get(root));

    // run our app with hyper
    // `axum::Server` is a re-export of `hyper::Server`
    let addr = SocketAddr::from(([0, 0, 0, 0], 8080));
    tracing::debug!("listening on {}", addr);
    axum::Server::bind(&addr)
        .serve(app.into_make_service())
        .await
        .unwrap();
}

// basic handler that responds with a static string
async fn root() -> &'static str {
    "Hello, World!"
}

ローカルでサーバーが立ち上がり、 http://0.0.0.0:8080
からHello, World!が見えるのを確認できたら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/cloud-run-rust-test"]

特筆点なし

ソースからdeploy

(他の言語でサービスをビルドしてデプロイする)[https://cloud.google.com/run/docs/quickstarts/build-and-deploy/deploy-service-other-languages]

ソースから Cloud Run にデプロイする
を参考に

gcloud run deploy

を実行したらいろいろ聞かれるのでテキトーにEnterを入力したらURLが表示されるのでアクセスすれば導通を確認できると思います。

成果物は
https://github.com/tktk2o/cloud-run-rust-test
にあります。

Discussion