見出し画像

CloudRunのボリュームマウント機能を試してみる with GCS&Terraform


こんにちは。
この記事では、terraformを使って、GCP の CloudRun をGCSマウントして構築する方法をまとめました。

CloudRun の GCS マウントとは何か

CloudRunとはGCPのフルマネージドコンピューティングサービスです。
必要な時だけインスタンスを立ち上げて、サービスを利用することが可能です。使ってない時はインスタンスを全て落とすことができるので、低コストでWebサービスやAPIを運用できます。

CloudRunでは利用時に、ゼロからコンテナイメージが立ち上がる形になっています。コンテナの仮想ディスクは使うことができますが、インスタンスが落ちるタイミングでディスク内のデータは消えてしまいます。

ただ、場合によっては静的なディスクを確保して永続的にデータを保存・更新したい場合もあるかと思います。

そんな時に活用できるのが、2024年1月のアップデートから使えるようになったCloudRunのボリュームマウント機能です。

追加のコードなどは必要なく、これを使用すると直接 GCS のバケットでマウントでき、インスタンスの普通のストレージと同じような感覚でファイル読み取り・保存ができるようになります。

料金について

料金についてはマウントしたストレージへの操作(ファイルアップロード/更新/ダウンロードなど)は通常のストレージ操作として課金されます。

更新・保存などのファイル操作回数が多い場合、費用が拡大する可能性があるので、確認が必要です。

ちなみに、ファイルのアップロードや更新、ダウンロードはクラス A オペレーションになるので、マウントするストレージがシングルリージョンのStandartストレージの場合、1000オペレーションあたり0.005$になります。

本題

全スクリプトはこちらに置いています。
https://github.com/M0T0-2020/GCP-memorandum/tree/master/cr-gcs-mount-0923

pythonバックエンド コンテナ作成

CloudRunで使用するコンテナイメージを作成します。
https://github.com/M0T0-2020/GCP-memorandum/tree/master/cr-gcs-mount-0923/src

  • 2種類のAPIを作成しました。

    • ディレクトリ下の全ファイル・ディレクトリ名を返す

    • パスパラメータを文字列としてテキストファイルに書き込んで保存

スクリプトを見ると分かると思いますが、GCSにアクセスするためのクライアントライブラリなどを使用していません。
ファイル検索や保存は通常のローカルストレージを使用するような記述になっています。

# main.py

import os
from pathlib import Path

from fastapi import FastAPI

app = FastAPI()


# GCSバケットのファイルを一覧する関数
def list_files():
    bucket_path = Path(os.environ.get("MOUNTPATH", "./"))
    if os.path.exists(bucket_path):
        files = os.listdir(bucket_path)
        return files
    return []


@app.get("/")
def index():
    files = list_files()
    return {"files_in_gcs": files}


@app.post("/upload/{string:str}")
def upload(string: str):
    bucket_path = Path(os.environ.get("MOUNTPATH", "./"))
    with open(bucket_path / f"{string}.txt", "w") as f:
        f.write(string)
        return f"{string}.txt"

requirements.txt とDockerfile も用意します

# requirements.txt
fastapi
uvicorn
# Dockerfile
FROM python:3.9-slim

# 作業ディレクトリ
WORKDIR /app

# アプリケーションのコピー
COPY . .

RUN pip install --no-cache-dir -r requirements.txt

EXPOSE 8080

# アプリケーションの起動
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080"]

これを CloudBuildを使って、Artifact Registry にアップします。

gcloud builds submit . -t us-central1-docker.pkg.dev/$PROJECT_ID/$REPOSITORY/$IMAGE_NAME

terraform

terraform 部分は必要なところだけ抜粋して記述します。
詳しくはこちらを見ていたければと思います。
https://github.com/M0T0-2020/GCP-memorandum/tree/master/cr-gcs-mount-0923/terraform

GCSのバケットを新しく作成して、そのバケットを CloudRun でマウントします。
CloudRunのサービスアカウントにマウントするストレージへの書き込みなどができる権限をつけています。(`roles/storage.objectUser`)

# GCSバケットの作成
resource "google_storage_bucket" "default" {
  name          = var.storage_name
  location      = var.region
  force_destroy = true
}

# サービスアカウントにGCSアクセス権限を付与
resource "google_project_iam_member" "storage" {
  project = var.project
  role    = "roles/storage.objectUser"
  member  = "serviceAccount:${google_service_account.cloud_run_sa.email}"
}

# Cloud Run サービスの作成
resource "google_cloud_run_v2_service" "service" {
  depends_on          = [google_storage_bucket.default]
  name                = "gcs-fuse-sample-0923"
  location            = var.region
  deletion_protection = false

  template {
    service_account = google_service_account.cloud_run_sa.email
    timeout         = "3.5s"


    containers {
      image = var.image_path
      env {
        name  = "MOUNTPATH"
        value = var.mount_path
      }
      volume_mounts {
        name       = "bucket"
        mount_path = var.mount_path
      }
      ports {
        container_port = 8080
      }
    }

    volumes {
      name = "bucket"
      gcs {
        bucket    = google_storage_bucket.default.name
        read_only = false
      }
    }
    scaling {
      min_instance_count = 0
      max_instance_count = 1
    }
  }

  ingress = "INGRESS_TRAFFIC_ALL"
}

resource "google_cloud_run_v2_service_iam_member" "member" {
  project  = google_cloud_run_v2_service.service.project
  location = google_cloud_run_v2_service.service.location
  name     = google_cloud_run_v2_service.service.name
  role     = "roles/run.invoker"
  member   = "user:${var.user_email}"
}

結果

terraformで作成したCloudRunを試用してみます。

まずはuploadの方から試してみます。
しっかりと保存したファイル名が返ってきているので、正しく動いてそうです。

curl -X POST -H "Authorization: Bearer $(gcloud auth print-identity-token)" <CloudRunのURL>/upload/test0923byMoto

# 結果: "test0923byMoto.txt"%   

コンソールの方でバケットを確認しても、正しくファイルが保存されてそうです。(何回かテストしたので、別のファイルも保存されています。)

赤い枠の部分に注目

ファイル一覧表示も試してみます。
しっかりと全ファイル名が返ってきているようです。

curl -X GET -H "Authorization: Bearer $(gcloud auth print-identity-token)" <CloudRunのURL>/              

# 結果: {"files_in_gcs":["test0923byMoto.txt","test1234.txt","test4649.txt","test555.txt"]}%  

まとめ

簡単に CloudRun に静的なストレージをマウントすることができました。手段として把握していると活用の機会もあるかと思います。

最後まで読んでいただきありがとうございました。

この記事が気に入ったらサポートをしてみませんか?