G-gen の佐々木です。当記事では Cloud Run で Cloud Storage バケットをボリュームマウントする方法を解説します。
- 前提知識
- Cloud Storage バケットの作成
- バケットにテキストファイルをアップロード
- Artifact Registry リポジトリの作成
- 使用するコード
- Cloud Run サービスの作成
- 動作確認
前提知識
Cloud Run とは
Cloud Run は Google Cloud のマネージドなコンテナ実行基盤を使用してアプリケーションを実行することができるサーバーレス コンテナコンピューティング サービスです。
HTTP リクエストをトリガーとする Cloud Run services と、手動もしくはスケジュール、ワークフローをトリガーとする Cloud Run jobs の 2種類が提供されています。当記事で紹介する機能は、いずれの Cloud Run でも使用することができます。
それぞれ、以下の記事でサービスの詳細を解説しています。
Cloud Storage(GCS)とは
Cloud Storage は Google Cloud が提供する容量無制限のオブジェクトストレージサービスであり、99.999999999% (イレブンナイン) の堅牢性を持つオブジェクトストレージを安価に利用することができます。
Cloud Storage の詳細は以下の記事で解説しています。
ネイティブ機能によるマウント
Cloud Run では Cloud Storage FUSE を利用することで、Cloud Storage バケットをローカルファイルシステムとしてマウントし、コンテナからバケット内のオブジェクトにアクセスすることができます。従来の方法では、コンテナに Cloud Storage FUSE を明示的にインストールして実行する必要がありました。
2024年1月のアップデートにて、明示的に Cloud Storage FUSE をインストールしなくても、Cloud Run のネイティブ機能として Cloud Storage バケットをマウントすることができるようになりました。
ただし、この機能は2024年1月時点ではプレビュー提供である点にご注意ください。
ネイティブ機能では、従来の方法同様、Cloud Storage FUSE を使用してマウントする点は変わりませんが、FUSE に関する処理はサービスの裏側で行われるため、ユーザー側で FUSE の設定を行う必要がなくなります。Cloud Storage FUSE の制限事項や、従来の利用方法については、以下の記事で解説しています。
当記事ではこれ以降、Cloud Run サービスを使用して、ネイティブ機能によるバケットのマウントを試してみます。
Cloud Storage バケットの作成
Cloud Run からファイルシステムとして使用する Cloud Storage バケットを作成します。
ここではバケット名を mybucket
としていますが、実際のバケット名はグローバルに一意の名前を設定する必要があります。
# Cloud Storage バケットを作成する $ gcloud storage buckets create gs://mybucket --location=asia-northeast1
バケットにテキストファイルをアップロード
作成したバケットにテキストファイル test.txt
を配置しておきます。
# テキストファイルの作成 $ echo 'Hello, GCS!!' > test.txt # テキストファイルをバケットにアップロード $ gcloud storage cp test.txt gs://mybucket
Artifact Registry リポジトリの作成
Cloud Run にデプロイするコンテナイメージを格納するための Artifact Registory リポジトリを作成します。
# Artifact Registry リポジトリを作成する $ gcloud artifacts repositories create myrepo \ --repository-format=docker \ --location=asia-northeast1
使用するコード
当記事では、マウントした Cloud Storage バケットからテキストファイルの中身を読み取り、ブラウザ上に表示するだけの簡単な Web アプリケーションを作成します。
// main.go package main import ( "fmt" "log" "net/http" "os" ) func readGCS() (string, error) { // マウントした Cloud Storage バケットからテキストファイルを読み込む f, err := os.Open("/mnt/gcs/" + "test.txt") if err != nil { return "", err } defer f.Close() text := make([]byte, 1024) count, err := f.Read(text) if err != nil { return "", err } return string(text[:count]), nil } func readGCSHandler(w http.ResponseWriter, r *http.Request) { text, err := readGCS() if err != nil { log.Printf("failed to read GCS: %v", err) http.Error(w, err.Error(), http.StatusInternalServerError) return } // text の内容をブラウザに表示する fmt.Fprintf(w, "%s", text) } func main() { log.Print("Starting server...") http.HandleFunc("/", readGCSHandler) port := os.Getenv("PORT") if port == "" { port = "8080" } log.Printf("Listening on port %s", port) if err := http.ListenAndServe(":"+port, nil); err != nil { log.Fatal(err) } }
Cloud Run サービスの作成
プレビューの時点では、Cloud Storage バケットをマウントしたサービスの作成方法として、CLI もしくは YAML ファイル からの作成がサポートされています。
CLI で Cloud Run サービスを作成する場合
公式ドキュメントでは gcloud run service update
コマンドを使用した方法が紹介されていますが、当記事では gcloud run deploy
コマンドを使用してサービスを作成します。
いずれのコマンドでもサービスを新規作成することができますが、deploy
コマンドでは --allow-unauthenticated
オプションを使用して、未認証のサービス呼び出しを許可した状態でサービスを作成することができます。
なお、当記事ではプレビュー機能を使用するため、gloud beta
コマンドを使用しています。
# Cloud Run サービスの作成 $ gcloud beta run deploy run-gcs \ --image=asia-northeast1-docker.pkg.dev/myproject/myrepo/run-gcs \ --region=asia-northeast1 \ --execution-environment=gen2 \ --allow-unauthenticated \ --add-volume=name=gcs,type=cloud-storage,bucket=mybucket \ --add-volume-mount=volume=gcs,mount-path=/mnt/gcs
使用するオプションは以下のようになります。
オプション | 説明 |
---|---|
--image | Artifact Registory リポジトリにプッシュしたコンテナイメージを指定します。 |
--region | Cloud Run サービスを作成するリージョンを指定します。 |
--execution-environment | Cloud Storage FUSE の使用には第2世代の Cloud Run を使用する必要があるため gen2 を指定します。 |
--allow-unauthenticated | 未認証の Cloud Run サービスの呼び出しを許可します。 |
--add-volume | マウントする Cloud Storage バケットの情報を指定します。name には任意のボリューム名、type には cloud-storage を指定し、bucket で Cloud Storage バケットの名前を指定します。 |
--add-volume-mount | volume には --add-volume の name で指定した任意のボリューム名と同じものを指定します。mount-path にはコンテナからバケットにアクセスするための任意のパスを設定します。当記事で使用するコードでは /mnt/gcs からバケットにアクセスします。 |
YAML ファイルから Cloud Run サービスを作成する場合
YAML ファイルから Cloud Run サービスを作成する場合は、以下のような YAML ファイルを用意します。
apiVersion: serving.knative.dev/v1 kind: Service metadata: name: run-gcs labels: cloud.googleapis.com/location: asia-northeast1 spec: template: metadata: annotations: run.googleapis.com/launch-stage: BETA # プレビュー機能を使用するためのアノテーション run.googleapis.com/execution-environment: GEN2 # Cloud Storage FUSE を使用する場合、第2世代の Cloud Run を使用する spec: containers: - image: asia-northeast1-docker.pkg.dev/myproject/myrepo/run-gcs # Cloud Run で実行するコンテナイメージ ports: - name: http1 containerPort: 8080 resources: limits: cpu: 1000m memory: 512Mi volumeMounts: - name: gcs mountPath: /mnt/gcs # コンテナからバケットにアクセスするためのパスを設定する volumes: - name: gcs # 任意のボリューム名 csi: driver: gcsfuse.run.googleapis.com volumeAttributes: bucketName: mybucket # マウントするバケットの名前を指定する
YAML ファイルを使用した Cloud Run サービスの作成には、gcloud run services replace
コマンドを使用します。今回はプレビュー機能を使用するため、gcloud beta
コマンドを使用しています。
# Cloud Run サービスを作成する
$ gcloud beta run services replace service.yaml
YAML ファイルからのデプロイでは metadata.annotations.run.googleapis.com/ingress
の値として all
を設定しても 未認証の呼び出しを許可
の設定ができないようなので、以下の gcloud コマンド使用して未認証のサービス呼び出しを許可します。
# 未認証のサービス呼び出しを許可する $ gcloud run services add-iam-policy-binding run-gcs \ --role="roles/run.invoker" \ --member="allUsers" \ --region=asia-northeast1
動作確認
Cloud Run サービスのデプロイ後、サービスにアクセスするためのエンドポイントが生成されます(以下の出力例では Service URL
の値)。
# コマンド出力例 Deploying container to Cloud Run service [run-gcs] in project [myproject] region [asia-northeast1] ✓ Deploying new service... Done. ✓ Creating Revision... ✓ Routing traffic... ✓ Setting IAM Policy... Done. Service [run-gcs] revision [run-gcs-00001-f7c] has been deployed and is serving 100 percent of traffic. Service URL: https://run-gcs-ai4xxxxxxx-an.a.run.app
ブラウザでエンドポイントにアクセスすると、Cloud Storage バケットにアップロードしたテキストファイルの中身が表示されます。
佐々木 駿太 (記事一覧)
G-gen最北端、北海道在住のクラウドソリューション部エンジニア
2022年6月にG-genにジョイン。Google Cloud Partner Top Engineer 2024に選出。好きなGoogle CloudプロダクトはCloud Run。
趣味はコーヒー、小説(SF、ミステリ)、カラオケなど。
Follow @sasashun0805