チュートリアル: Cloud Run での Cloud Storage FUSE の使用


このチュートリアルでは、Cloud Storage をネットワーク ファイル システムとして Cloud Run サービスにマウントする方法について説明します。Cloud Run ジョブに同じ方法を使用できます。

このチュートリアルでは、オープンソースの FUSE アダプタを使用して、複数のコンテナとサービス間でデータを共有します。

ファイル システムをマウントするには、サービスに Cloud Run の第 2 世代の実行環境を使用する必要があります。Cloud Run ジョブは第 2 世代環境を自動的に使用します。

第 2 世代の実行環境では、ネットワーク ファイル システムをコンテナ内のディレクトリにマウントできます。ファイル システムをマウントすると、ホストシステムとインスタンスの間でリソースを共有し、インスタンスがガベージ コレクションされた後もリソースを保持できます。

Cloud Run でネットワーク ファイル システムを使用するには、コンテナでファイル システムのマウントやアプリケーション プロセスなどの複数のプロセスを実行する必要があるため、高度な Docker の知識が必要です。このチュートリアルでは、実際の例を示しながら必要なコンセプトについて説明します。ただし、このチュートリアルをアプリケーションに合わせて変更する場合は、変更の影響を十分に確認してください。

デザインの概要

ファイルシステム アーキテクチャ

この図は、gcsfuse FUSE アダプタを介して Cloud Storage バケットに接続する Cloud Run サービスを示しています。Cloud Run サービスと Cloud Storage バケットは、ネットワーク コストをなくし、最高のパフォーマンスが得られるように、同じリージョン内に配置されています。

制限事項

  • このチュートリアルでは、ファイル システムの選択方法や、本番環境に向けた準備については説明しません。POSIX ファイル システムとの主な違いと、Cloud Storage FUSE の他のセマンティクスをご確認ください。

  • このチュートリアルでは、ファイル システムを使用する方法やファイル アクセス パターンについても説明しません。

目標

  • ファイル共有として機能する Cloud Storage バケットを作成する。

  • システム パッケージと init プロセスを使用して、マウント プロセスとアプリケーション プロセスを管理する Dockerfile を構築する。

  • Cloud Run にデプロイし、サービスでファイル システムへのアクセスを確認する。

費用

このチュートリアルでは、Google Cloud の課金対象となる以下のコンポーネントを使用します。

始める前に

  1. Google Cloud アカウントにログインします。Google Cloud を初めて使用する場合は、アカウントを作成して、実際のシナリオでの Google プロダクトのパフォーマンスを評価してください。新規のお客様には、ワークロードの実行、テスト、デプロイができる無料クレジット $300 分を差し上げます。
  2. Google Cloud Console の [プロジェクト セレクタ] ページで、Google Cloud プロジェクトを選択または作成します。

    プロジェクト セレクタに移動

  3. Google Cloud プロジェクトで課金が有効になっていることを確認します

  4. Google Cloud Console の [プロジェクト セレクタ] ページで、Google Cloud プロジェクトを選択または作成します。

    プロジェクト セレクタに移動

  5. Google Cloud プロジェクトで課金が有効になっていることを確認します

  6. Cloud Run, Cloud Storage, Artifact Registry, and Cloud Build API を有効にします。

    API を有効にする

  7. gcloud CLI をインストールして初期化します

必要なロール

最小限のロールでチュートリアルを完了するために必要な権限を取得するには、プロジェクトに対して次の IAM ロールを付与するよう管理者に依頼してください。

ロールの付与の詳細については、アクセスの管理をご覧ください。

必要な権限は、カスタムロールや他の事前定義ロールから取得することもできます。

gcloud のデフォルトを設定する

Cloud Run サービスを gcloud のデフォルトに構成するには:

  1. デフォルト プロジェクトを設定します。

    gcloud config set project PROJECT_ID

    PROJECT_ID は、このチュートリアルで作成したプロジェクトの名前に置き換えます。

  2. 選択したリージョン向けに gcloud を構成します。

    gcloud config set run/region REGION

    REGION は、任意のサポートされている Cloud Run のリージョンに置き換えます。

Cloud Run のロケーション

Cloud Run はリージョナルです。つまり、Cloud Run サービスを実行するインフラストラクチャは特定のリージョンに配置され、そのリージョン内のすべてのゾーンで冗長的に利用できるように Google によって管理されます。

レイテンシ、可用性、耐久性の要件を満たしていることが、Cloud Run サービスを実行するリージョンを選択する際の主な判断材料になります。一般的には、ユーザーに最も近いリージョンを選択できますが、Cloud Run サービスで使用されている他の Google Cloud サービスのロケーションも考慮する必要があります。使用する Google Cloud サービスが複数のロケーションにまたがっていると、サービスの料金だけでなくレイテンシにも影響することがあります。

Cloud Run は、次のリージョンで利用できます。

ティア 1 料金を適用

  • asia-east1(台湾)
  • asia-northeast1(東京)
  • asia-northeast2(大阪)
  • europe-north1(フィンランド) リーフアイコン 低 CO2
  • europe-southwest1(マドリッド)
  • europe-west1(ベルギー) リーフアイコン 低 CO2
  • europe-west4(オランダ)
  • europe-west8(ミラノ)
  • europe-west9(パリ) リーフアイコン 低 CO2
  • me-west1(テルアビブ)
  • us-central1(アイオワ) リーフアイコン 低 CO2
  • us-east1(サウスカロライナ)
  • us-east4(北バージニア)
  • us-east5(コロンバス)
  • us-south1(ダラス)
  • us-west1(オレゴン) リーフアイコン 低 CO2

ティア 2 料金を適用

  • asia-east2(香港)
  • asia-northeast3(ソウル、韓国)
  • asia-southeast1(シンガポール)
  • asia-southeast2 (ジャカルタ)
  • asia-south1(ムンバイ、インド)
  • asia-south2(デリー、インド)
  • australia-southeast1(シドニー)
  • australia-southeast2(メルボルン)
  • europe-central2(ワルシャワ、ポーランド)
  • europe-west10(ベルリン)
  • europe-west12(トリノ)
  • europe-west2(ロンドン、イギリス) リーフアイコン 低 CO2
  • europe-west3(フランクフルト、ドイツ) リーフアイコン 低 CO2
  • europe-west6(チューリッヒ、スイス) リーフアイコン 低 CO2
  • me-central1(ドーハ)
  • me-central2(ダンマーム)
  • northamerica-northeast1(モントリオール) リーフアイコン 低 CO2
  • northamerica-northeast2(トロント) リーフアイコン 低 CO2
  • southamerica-east1(サンパウロ、ブラジル) リーフアイコン 低 CO2
  • southamerica-west1(サンティアゴ、チリ) リーフアイコン 低 CO2
  • us-west2(ロサンゼルス)
  • us-west3(ソルトレイクシティ)
  • us-west4(ラスベガス)

Cloud Run サービスをすでに作成している場合は、Google Cloud コンソールの Cloud Run ダッシュボードにリージョンが表示されます。

コードサンプルを取得する

使用するコードサンプルを取得するには:

  1. ローカルマシンにサンプルアプリのリポジトリのクローンを作成します。

    Node.js

    git clone https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git

    また、zip 形式のサンプルをダウンロードしてファイルを抽出してもかまいません。

    Python

    git clone https://github.com/GoogleCloudPlatform/python-docs-samples.git

    または、zip 形式のサンプルをダウンロードし、ファイルを抽出してもかまいません。

    Java

    git clone https://github.com/GoogleCloudPlatform/java-docs-samples.git

    または、zip 形式のサンプルをダウンロードし、ファイルを抽出してもかまいません。

  2. Cloud Run のサンプルコードが含まれているディレクトリに移動します。

    Node.js

    cd nodejs-docs-samples/run/filesystem/

    Python

    cd python-docs-samples/run/filesystem/

    Java

    cd java-docs-samples/run/filesystem/

コードについて

通常、コンテナ内では、単一のプロセスまたはアプリケーションを実行します。1 つのコンテナにつき 1 つのプロセスを実行すると、複数プロセスのライフサイクルを管理する複雑さが軽減されます(たとえば、再起動の管理、プロセスが失敗した場合のコンテナの終了、シグナルの送信やゾンビの子の刈り取りといった PID 1 問題など)。一方、Cloud Run でネットワーク ファイル システムを使用する場合、ファイル システムのマウント プロセスとアプリケーションの両方を実行するためにマルチプロセス コンテナを使用する必要があります。このチュートリアルでは、プロセスの障害時にコンテナを終了し、PID 1 問題を管理する方法について説明します。mount コマンドには、再試行を扱う機能が組み込まれています。

プロセス マネージャーを使用することで、コンテナのエントリポイントとして複数のプロセスを実行、管理できます。このチュートリアルでは、tini を使用します。これは、init の代わりになるもので、ゾンビプロセスをクリーンアップしてシグナル送信を行います。具体的には、この init プロセスにより、シャットダウン時の SIGTERM シグナルがアプリケーションに伝えられます。SIGTERM シグナルは、アプリケーションを正常に終了するためにキャッチされます。詳細については、Cloud Run 上のコンテナのライフサイクルをご覧ください。

Dockerfile で環境構成を定義する

この Cloud Run サービスには、デフォルト以外のシステム パッケージを 1 つ以上追加する必要があります。RUN 命令によって、init-process としての tini と、FUSE アダプタである gcsfuse がインストールされます。Cloud Run サービスでのシステム パッケージの操作について詳しくは、システム パッケージ・チュートリアルを使用するをご覧ください。

次の一連の手順では、作業ディレクトリの作成、ソースコードのコピー、アプリの依存関係のインストールが行われます。

ENTRYPOINT は、CMD 命令の前に付加される init-process バイナリを指定します(この例では起動スクリプト)。これにより、単一の tini プロセスが起動され、受信したすべてのシグナルが、その子プロセスをルートとするセッションに代理転送されます。

CMD 命令は、イメージを動作させる際に実行されるコマンド(起動スクリプト)を設定します。また、ENTRYPOINT のデフォルトの引数も指定します。CMD と ENTRYPOINT の操作方法をご覧ください。

Node.js


# Use the official Node.js image.
# https://hub.docker.com/_/node
FROM node:20-slim

# Install system dependencies
RUN apt-get update && apt-get install -y \
    curl \
    gnupg \
    lsb-release \
    tini && \
    gcsFuseRepo=gcsfuse-`lsb_release -c -s` && \
    echo "deb https://packages.cloud.google.com/apt $gcsFuseRepo main" | \
    tee /etc/apt/sources.list.d/gcsfuse.list && \
    curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | \
    apt-key add - && \
    apt-get update && \
    apt-get install -y gcsfuse && \
    apt-get clean

# Set fallback mount directory
ENV MNT_DIR /mnt/gcs

# Copy local code to the container image.
ENV APP_HOME /app
WORKDIR $APP_HOME
COPY package*.json ./

# Install production dependencies.
RUN npm install --only=production

# Copy local code to the container image.
COPY . ./

# Ensure the script is executable
RUN chmod +x /app/gcsfuse_run.sh

# Use tini to manage zombie processes and signal forwarding
# https://github.com/krallin/tini
ENTRYPOINT ["/usr/bin/tini", "--"]

# Pass the wrapper script as arguments to tini
CMD ["/app/gcsfuse_run.sh"]

Python

# Use the official lightweight Python image.
# https://hub.docker.com/_/python
FROM python:3.11-buster

# Install system dependencies
RUN set -e; \
    apt-get update -y && apt-get install -y \
    tini \
    lsb-release; \
    gcsFuseRepo=gcsfuse-`lsb_release -c -s`; \
    echo "deb https://packages.cloud.google.com/apt $gcsFuseRepo main" | \
    tee /etc/apt/sources.list.d/gcsfuse.list; \
    curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | \
    apt-key add -; \
    apt-get update; \
    apt-get install -y gcsfuse \
    && apt-get clean

# Set fallback mount directory
ENV MNT_DIR /mnt/gcs

# Copy local code to the container image.
ENV APP_HOME /app
WORKDIR $APP_HOME
COPY . ./

# Install production dependencies.
RUN pip install -r requirements.txt

# Ensure the script is executable
RUN chmod +x /app/gcsfuse_run.sh

# Use tini to manage zombie processes and signal forwarding
# https://github.com/krallin/tini
ENTRYPOINT ["/usr/bin/tini", "--"]

# Pass the startup script as arguments to Tini
CMD ["/app/gcsfuse_run.sh"]

Java

# Use the official maven/Java 11 image to create a build artifact.
# https://hub.docker.com/_/maven
FROM maven:3-eclipse-temurin-17-alpine as builder

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

# Build a release artifact.
RUN mvn package -DskipTests

# Use Eclipse Temurin for base image.
# https://docs.docker.com/develop/develop-images/multistage-build/#use-multi-stage-builds
FROM eclipse-temurin:18-jdk-focal

# Install system dependencies
RUN set -e; \
    apt-get update -y && apt-get install -y \
    gnupg2 \
    tini \
    lsb-release; \
    gcsFuseRepo=gcsfuse-`lsb_release -c -s`; \
    echo "deb https://packages.cloud.google.com/apt $gcsFuseRepo main" | \
    tee /etc/apt/sources.list.d/gcsfuse.list; \
    curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | \
    apt-key add -; \
    apt-get update; \
    apt-get install -y gcsfuse && apt-get clean

# Set fallback mount directory
ENV MNT_DIR /mnt/gcs

# Copy the jar to the production image from the builder stage.
COPY --from=builder /app/target/filesystem-*.jar /filesystem.jar

# Copy the statup script
COPY gcsfuse_run.sh ./gcsfuse_run.sh
RUN chmod +x ./gcsfuse_run.sh

# Use tini to manage zombie processes and signal forwarding
# https://github.com/krallin/tini
ENTRYPOINT ["/usr/bin/tini", "--"]

# Run the web service on container startup.
CMD ["/gcsfuse_run.sh"]

起動スクリプトでプロセスを定義する

起動スクリプトによってマウント ポイント ディレクトリが作成されます。これにより、Cloud Storage バケットへのアクセスが可能になります。次に、このスクリプトは gcsfuse コマンドを使用して Cloud Storage バケットをサービスのマウント ポイントに接続し、アプリケーション サーバーを起動します。gcsfuse コマンドには、再試行機能が組み込まれています。したがって、bash スクリプトをさらに追加する必要はありません。最後に、wait コマンドでは、バックグラウンド プロセスの終了をリッスンし、スクリプトを終了します。

Node.js

#!/usr/bin/env bash
set -eo pipefail

# Create mount directory for service
mkdir -p $MNT_DIR

echo "Mounting GCS Fuse."
gcsfuse --debug_gcs --debug_fuse $BUCKET $MNT_DIR
echo "Mounting completed."

# Start the application
node index.js &

# Exit immediately when one of the background processes terminate.
wait -n

Python

#!/usr/bin/env bash
set -eo pipefail

# Create mount directory for service
mkdir -p $MNT_DIR

echo "Mounting GCS Fuse."
gcsfuse --debug_gcs --debug_fuse $BUCKET $MNT_DIR
echo "Mounting completed."

# Run the web service on container startup. Here we use the gunicorn
# webserver, with one worker process and 8 threads.
# For environments with multiple CPU cores, increase the number of workers
# to be equal to the cores available.
# Timeout is set to 0 to disable the timeouts of the workers to allow Cloud Run to handle instance scaling.
exec gunicorn --bind :$PORT --workers 1 --threads 8 --timeout 0 main:app

Java

#!/usr/bin/env bash
set -eo pipefail

# Create mount directory for service
mkdir -p $MNT_DIR

echo "Mounting GCS Fuse."
gcsfuse --debug_gcs --debug_fuse $BUCKET $MNT_DIR
echo "Mounting completed."

# Start the application
java -jar filesystem.jar

# Exit immediately when one of the background processes terminate.
wait -n

ファイルを操作する

Node.js

ファイルシステムの操作については、index.js をご覧ください。

Python

ファイルシステムの操作については、main.py をご覧ください。

Java

ファイルシステムの操作については、FilesystemApplication.java をご覧ください。

サービスを配布する

  1. Cloud Storage バケットを作成するか、既存のバケットを再利用します。

    gsutil mb -l REGION gs://BUCKET_NAME
    

    BUCKET_NAME は、Cloud Storage バケットの名前で置き換えます(例: my-fuse-bucket)。Cloud Storage バケット名は、グローバルに一意でなければならず、命名要件に従う必要があります。

    -l は、バケットのロケーションを指定するために設定します。例: us-central1最適なパフォーマンスを確保し、リージョン間でのネットワーク料金が発生しないようにするため、Cloud Storage バケットへのアクセスが必要な Cloud Run サービスと同じリージョンに Cloud Storage バケットを配置してください。

  2. サービス ID として機能するサービス アカウントを作成します。

    gcloud iam service-accounts create fs-identity
  3. Cloud Storage バケットへのアクセス権をサービス アカウントに付与します。

    gcloud projects add-iam-policy-binding PROJECT_ID \
         --member "serviceAccount:fs-identity@PROJECT_ID.iam.gserviceaccount.com" \
         --role "roles/storage.objectAdmin"
    
  4. ソースからデプロイするには、余分な Dockerfile を削除して、チュートリアルの Dockerfile の名前を変更します

    rm Dockerfile
    cp gcsfuse.Dockerfile Dockerfile
    
  5. コンテナ イメージをビルドして Cloud Run にデプロイします。

    gcloud run deploy filesystem-app --source . \
        --execution-environment gen2 \
        --allow-unauthenticated \
        --service-account fs-identity \
        --update-env-vars BUCKET=BUCKET_NAME
    

    このコマンドは、第 2 世代の実行環境を指定し、Cloud Run サービスをビルドしてデプロイします。ソースからデプロイすると、Dockerfile に基づいてイメージがビルドされ、Artifact Registry リポジトリ(cloud-run-source-deploy)にイメージが push されます。

    ソースコードからのデプロイで詳細をご確認ください。

デバッグ

デプロイが失敗した場合は、Cloud Logging で詳細を確認してください。

マウント プロセスからのログをすべて取得するには、起動スクリプト(gcsfuse_run.sh)で --foreground フラグをマウント コマンドと組み合わせて使用します。

  gcsfuse --foreground --debug_gcs --debug_fuse GCSFUSE_BUCKET MNT_DIRECTORY &
  

  • HTTP リクエスト / レスポンスのデバッグ出力を得るには、--debug_http を追加します。
  • FUSE 関連のデバッグ出力を有効にするには、--debug_fuse を追加します。
  • GCS のリクエストとタイミング情報を出力するには、--debug_gcs を追加します。

その他のヒントについては、トラブルシューティング ガイドをご覧ください。

試してみる

完成したサービスを試すには:

  1. ブラウザで、前述のデプロイの手順により提供された URL に移動します。
  2. Cloud Storage バケットに新しく作成されたファイルが表示されます。
  3. ファイルをクリックすると内容が表示されます。

これらのサービスの開発を継続する場合、Google Cloud の他のサービスへの Identity and Access Management(IAM)アクセスが制限されます。他の多くのサービスにアクセスするには、追加の IAM ロールをこれらのサービスに与える必要があることにご注意ください。

費用の説明

Cloud Storage の料金は、データ ストレージストレージ クラスで保存されたデータ量、バケットのロケーション、ネットワーク使用量、バケットからのデータの読み込み量やバケット間での移動量に大きく依存します。詳細については、Cloud Storage FUSE で発生する料金をご覧ください。

Cloud Run の料金は、メモリ、CPU、リクエスト数、ネットワーキングなどのリソースの使用量に応じて課金され、100 ミリ秒単位で切り上げて計算されます。そのため、費用はサービスの設定、リクエスト数、実行時間によって異なります。

たとえば、アイオワ(us-central1)でホストされている標準のストレージ バケットに保存された 1 TiB データの費用は、GiB あたりひと月 $0.02 で、約 1,024 GiB × $0.02 = $20.48 となります。この見積もりは、下り(外向き)コストを削減するために同じリージョンでホストされている Cloud Run サービスと Cloud Storage バケットによって変わります。

最新の料金については、各料金ページをご覧いただくか、Google Cloud 料金計算ツールで料金の見積もりをご確認ください。

クリーンアップ

このチュートリアル用に新規プロジェクトを作成した場合は、そのプロジェクトを削除します。既存のプロジェクトを使用し、このチュートリアルで変更を加えずに残す場合は、チュートリアル用に作成したリソースを削除します。

プロジェクトを削除する

課金をなくす最も簡単な方法は、チュートリアル用に作成したプロジェクトを削除することです。

プロジェクトを削除するには:

  1. Google Cloud コンソールで、[リソースの管理] ページに移動します。

    [リソースの管理] に移動

  2. プロジェクト リストで、削除するプロジェクトを選択し、[削除] をクリックします。
  3. ダイアログでプロジェクト ID を入力し、[シャットダウン] をクリックしてプロジェクトを削除します。

チュートリアル リソースを削除する

  1. このチュートリアルでデプロイした Cloud Run サービスを削除します。

    gcloud run services delete SERVICE-NAME

    SERVICE-NAME は、選択したサービス名です。

    Cloud Run サービスは Google Cloud コンソールから削除することもできます。

  2. チュートリアルの設定時に追加した gcloud のデフォルト リージョン構成を削除します。

     gcloud config unset run/region
    
  3. プロジェクト構成を削除します。

     gcloud config unset project
    
  4. このチュートリアルで作成した他の Google Cloud リソースを削除します。

次のステップ