Rust+terraform+cloudrunでHello world

に公開

概要

SAM(lambda)でRustの処理を組み込んだ経験はあるけど、cloudrunとかで試したことがなかったのと、terraformと組み合わせようということで、作ってみました。

lambdaをデプロイする際にクロスコンパイル周りで過去に苦戦したことがあるのですが、同様にcloudrunでもMacでハマったところがあるので、そちらを含めて備忘録としてまとめます。今回はMacで開発を進めていますので、ご容赦ください。

開発手順

構成

ファイル構成は下記になります。

.
├── Cargo.lock
├── Cargo.toml
├── Dockerfile
├── README.md
├── docker-compose.yml
├── src
│   └── main.rs
└── terraform
    ├── main.tf
    ├── terraform.tfvars
    ├── terraform.tfvars.example
    └── variables.tf

認証設定(セットアップ)

GCPで何かしらのプロジェクトを作成してあることを前提としています。

# gcloud認証
gcloud auth login
gcloud config set project <project-id>

# SDK認証とcloudrunで必要なサービスを有効化
gcloud auth application-default login
gcloud services enable run.googleapis.com

また、下記に環境変数を設定します。

.env
DOCKER_IMAGE=*******************
DOCKER_WORKDIR=/root/workspace
REGION=*******************
PROJECT_ID=*******************
REPOSITORY=*******************
PATCH_PHASE=*******************

環境変数を読み込みます。

$ source ./.env

必要なイメージをGARにプッシュ

下記を実行する前に、必ずArtifact Registryでリポジトリを作成するようにしてください。

# 認証
gcloud auth configure-docker <region>-docker.pkg.dev --quiet

# ビルド
docker compose build

# GARにプッシュ
docker compose push

ここで注意したいのは、下記の二点です。

  1. イメージの軽量化

下記でも指摘されていますが、一般的に提供されてるrustイメージを使ってビルドしてpushすると、サイズが膨大(2GB近く)になってしまいます。

https://zenn.dev/kyoheiu/articles/dcefe0c75f0e17

そこで、今回はglibcやlibsslすら含まない、最も小さくて軽量なイメージにlibgcc1とその依存関係を含む distroless/cc を使用しました。
https://zenn.dev/junkor/articles/fe0a0d75ff6451

ビルド用コンテナとpush用コンテナに分けてマルチステージビルドを行うことで、イメージ容量を削減しています。

# Build stage: Compile binary in official Rust image
FROM rust:1.86 as builder
WORKDIR /app
COPY . .
RUN cargo build --release

# Runtime stage: Distroless for security and small image
FROM gcr.io/distroless/cc
COPY --from=builder /app/target/release/icarust /icarust
EXPOSE 8080
CMD ["/icarust"]

2.クロスコンパイル対策

Macでビルドしたものをcloud runにデプロイすると、プラットフォームの問題でビルドした実行ファイルが動かなくなります。この場合、cloud logを見ると、 exec format error と出てきます。

こちらの対策として、docker-compose.ymlに platform: 'linux/x86_64' を追加します。

x-template: &template
  build:
    context: .
  platform: 'linux/x86_64'
  env_file:
    - .env
  working_dir: $DOCKER_WORKDIR
  volumes:
    - .:${DOCKER_WORKDIR}
  tty: true

services:
  icarust-demo:
    <<: *template
    image: ${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPOSITORY}/icarust-dev-${PATCH_PHASE}:dev
    command: /bin/bash
volumes:
  python-packages:

実装

今回は単純なhello worldの表示だけします。
axumで下記のように実装しました。注意すべきは [0, 0, 0, 0] の部分で、このように指定しないとcloudrunでは動作しないので注意してください。

src/main.rs
use axum::{routing::get, Router};
use std::{env, net::SocketAddr};

async fn hello() -> &'static str {
    "Hello from Axum on Cloud Run!"
}

#[tokio::main]
async fn main() {
    let port  = env::var("PORT").unwrap_or_else(|_| "8080".to_string());
    let addr = SocketAddr::from(([0, 0, 0, 0], port.parse().unwrap()));

    let app = Router::new().route("/", get(hello));

    println!("Listening on {}", addr);
    axum_server::bind(addr)
        .serve(app.into_make_service())
        .await
        .unwrap();
}

terraform

terraformの設定を行います。今回はcloudrunのデプロイに必要な基本的な設定のみ行います。

terraform/main.tf
provider "google" {
    project = var.project_id
    region = var.region
}

resource "google_cloud_run_service" "default" {
    name = "icarust-cloudrun"
    location = var.region

    template {
        spec {
            containers {
                image = var.image_url
                ports {
                    container_port = 8080
                }
            }
            timeout_seconds = 180
        }
    }

    traffic {
        percent = 100
        latest_revision = true
    }
}

resource "google_cloud_run_service_iam_member" "invoker" {
    location = var.region
    service  = google_cloud_run_service.default.name
    role     = "roles/run.invoker"
    member   = "allUsers"
}

確認

ローカルでの確認

下記で実行して、指定したURLを開くと文章が表示されます。

$ cargo run

コンテナでの確認

$ docker run -p 8080:8080 <IMAGE ID>

デプロイ

下記の手順で簡単にデプロイできます。

$ terraform init
$ terraform plan
$ terraform apply

# デプロイした環境を破棄したい場合は、下記を実行
$ terraform destroy

参考

https://www.zakioka.net/blog/rust-api-on-gcp-cloudrun/#terraform-のコマンド実行

Discussion