Pic-a-daily: ラボ 5 - イメージ削除後のクリーンアップ

1. 概要

この Codelab では、新しい Cloud Run サービスであるイメージ ガベージ コレクタを作成します。これは、Cloud Run でイベントを受信するための新しいサービスである Eventarc によってトリガーされます。画像バケットから画像が削除されると、サービスは Eventarc からイベントを受信します。次に、サムネイル バケットから画像を削除し、Firestore 画像コレクションからも削除されます。

d93345bfc235f81e.png

学習内容

  • Cloud Run
  • Cloud Storage
  • Cloud Firestore
  • Eventarc

2. 設定と要件

セルフペース型の環境設定

  1. Google Cloud Console にログインして、プロジェクトを新規作成するか、既存のプロジェクトを再利用します。Gmail アカウントも Google Workspace アカウントもまだお持ちでない場合は、アカウントを作成してください。

96a9c957bc475304.png

b9a10ebdf5b5a448.png

a1e3c01a38fa61c2.png

  • プロジェクト名は、このプロジェクトの参加者に表示される名称です。Google API では使用されない文字列で、いつでも更新できます。
  • プロジェクト ID は、すべての Google Cloud プロジェクトにおいて一意でなければならず、不変です(設定後は変更できません)。Cloud Console により一意の文字列が自動生成されます(通常は内容を意識する必要はありません)。ほとんどの Codelab では、プロジェクト ID を参照する必要があります(通常、プロジェクト ID は「PROJECT_ID」の形式です)。好みの文字列でない場合は、別のランダムな ID を生成するか、独自の ID を試用して利用可能であるかどうかを確認することができます。プロジェクトの作成後、ID は「フリーズ」されます。
  • 3 つ目の値として、一部の API が使用するプロジェクト番号があります。これら 3 つの値について詳しくは、こちらのドキュメントをご覧ください。
  1. 次に、Cloud のリソースや API を使用するために、Cloud Console で課金を有効にする必要があります。この Codelab の操作をすべて行って、費用が生じたとしても、少額です。このチュートリアルを終了した後に課金が発生しないようにリソースをシャットダウンするには、Codelab の最後にある「クリーンアップ」の手順を行います。Google Cloud の新規ユーザーは、300 米ドル分の無料トライアル プログラムをご利用いただけます。

Cloud Shell の起動

Google Cloud はノートパソコンからリモートで操作できますが、この Codelab では、Google Cloud Shell(Cloud 上で動作するコマンドライン環境)を使用します。

GCP Console で右上のツールバーにある Cloud Shell アイコンをクリックします。

bce75f34b2c53987.png

プロビジョニングと環境への接続にはそれほど時間はかかりません。完了すると、次のように表示されます。

f6ef2b5f13479f3a.png

この仮想マシンには、必要な開発ツールがすべて用意されています。永続的なホーム ディレクトリが 5 GB 用意されており、Google Cloud で稼働します。そのため、ネットワークのパフォーマンスと認証機能が大幅に向上しています。このラボでの作業はすべて、ブラウザから実行できます。

3. Eventarc の概要

Eventarc を使用すると、Cloud Run サービスとさまざまなソースからのイベントを簡単に接続できます。イベントの取り込み、配信、セキュリティ、認可、エラー処理を自動的に行います。

776ed63706ca9683.png

Google Cloud ソースと、Cloud Pub/Sub にパブリッシュするカスタム アプリケーションからイベントを引き出し、Google Cloud Run シンクに配信できます。

Google Cloud のさまざまなソースからのイベントが、Cloud Audit Logs によって配信されます。これらのソースからのイベント配信のレイテンシと可用性は、Cloud Audit Logs のレイテンシと関連付けられています。Google Cloud ソースからのイベントが発生するたびに、対応する Cloud Audit Logs のエントリが作成されます。

Cloud Pub/Sub にパブリッシュするカスタム アプリケーションは、任意の形式で指定した Pub/Sub トピックにメッセージをパブリッシュできます。

イベント トリガーは、どのイベントをどのシンクに配信するかを指定するフィルタリング メカニズムです。

すべてのイベントは、サービス間の相互運用性を確保するために、CloudEvents v1.0 形式で配信されます。

4. 始める前に

API を有効にする

Cloud Run サービスをトリガーするには、Eventarc サービスが必要です。有効になっていることを確認します。

gcloud services enable eventarc.googleapis.com

オペレーションが正常に完了することを確認できます。

Operation "operations/acf.5c5ef4f6-f734-455d-b2f0-ee70b5a17322" finished successfully.

サービス アカウントの構成

デフォルトのコンピューティング サービス アカウントはトリガーで使用されます。デフォルトのコンピューティング サービス アカウントに eventarc.eventReceiver ロールを付与します。

PROJECT_NUMBER=$(gcloud projects describe $GOOGLE_CLOUD_PROJECT --format='value(projectNumber)')

gcloud projects add-iam-policy-binding $GOOGLE_CLOUD_PROJECT \
    --member serviceAccount:$PROJECT_NUMBER-compute@developer.gserviceaccount.com \
    --role roles/eventarc.eventReceiver

Cloud Storage サービス アカウントに pubsub.publisher ロールを付与します。これは、Eventarc Cloud Storage トリガーに必要です。

SERVICE_ACCOUNT=$(gsutil kms serviceaccount -p $PROJECT_NUMBER)

gcloud projects add-iam-policy-binding $PROJECT_NUMBER \
    --member serviceAccount:$SERVICE_ACCOUNT \
    --role roles/pubsub.publisher

2021 年 4 月 8 日以前に Pub/Sub サービス アカウントを有効にした場合は、Pub/Sub サービス アカウントに iam.serviceAccountTokenCreator ロールを付与します。

gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member serviceAccount:service-$PROJECT_NUMBER@gcp-sa-pubsub.iam.gserviceaccount.com \
  --role roles/iam.serviceAccountTokenCreator

5. コードのクローンを作成する

前の Codelab をまだ実行していない場合は、コードのクローンを作成します。

git clone https://github.com/GoogleCloudPlatform/serverless-photosharing-workshop

その後、サービスを含むディレクトリに移動します。

cd serverless-photosharing-workshop/services/garbage-collector/nodejs

サービスのファイル レイアウトは次のようになります。

services
 |
 ├── garbage-collector
      |
      ├── nodejs
           |
           ├── index.js
           ├── package.json

フォルダ内には 3 つのファイルがあります。

  • index.js には Node.js コードが含まれています。
  • package.json はライブラリの依存関係を定義します。

6. コードを探索する

依存関係

package.json ファイルは、必要なライブラリ依存関係を定義します。

{
  "name": "garbage_collector_service",
  "version": "0.0.1",
  "main": "index.js",
  "scripts": {
    "start": "node index.js"
  },
  "dependencies": {
    "cloudevents": "^4.0.1",
    "express": "^4.17.1",
    "@google/events": "^3.1.0",
    "@google-cloud/firestore": "^4.9.9",
    "@google-cloud/storage": "^5.8.3"
  }
}

Cloud Storage 内の画像の削除は、Cloud Storage ライブラリに依存します。Cloud Firestore への依存関係を宣言して、以前に保存した画像メタデータも削除します。また、Eventarc から送信された CloudEvents を読み取るには、CloudEvents SDK と Google イベント ライブラリを使用します。Express は JavaScript / Node ウェブ フレームワークです。Bluebird は、Promise の処理に使用されます。

index.js

index.js コードを詳しく見てみましょう。

const express = require('express');
const {Storage} = require('@google-cloud/storage');
const Firestore = require('@google-cloud/firestore');
const { HTTP } = require("cloudevents");
const {toStorageObjectData} = require('@google/events/cloud/storage/v1/StorageObjectData');

プログラムを実行するには、さまざまな依存関係が必要です。Express は使用する Node ウェブ フレームワーク、Bluebird は JavaScript Promise を処理するためのライブラリ、Storage と Firestore は Google Cloud Storage(Google の画像のバケット)と Cloud Firestore データストアをそれぞれ操作するためのものです。また、CloudEvent は、CloudEvent の Cloud Storage イベント本文を読み取るために、Google イベント ライブラリから Eventarc StoreObjectData によって送信された CloudEvent を読み取る必要があります。

const app = express();
app.use(express.json());

app.post('/', async (req, res) => {
    try {
        const cloudEvent = HTTP.toEvent({ headers: req.headers, body: req.body });
        console.log(cloudEvent);


        /* ... */

    } catch (err) {
        console.log(`Error: ${err}`);
        res.status(500).send(err);
    }
});

上の図は、Node ハンドラの構造です。作成したアプリは HTTP POST リクエストに応答します。HTTP リクエストから CloudEvent を読み取り、問題が発生した場合に備え、多少のエラー処理を行います。次に、この構造体の内部を見てみましょう。

次のステップでは、CloudEvent 本文を取得して解析し、オブジェクト名を取得します。

const storageObjectData = toStorageObjectData(cloudEvent.data);
console.log(storageObjectData);

const objectName = storageObjectData.name;

画像の名前がわかったら、サムネイル バケットから削除します。

try {
    await storage.bucket(bucketThumbnails).file(objectName).delete();
    console.log(`Deleted '${objectName}' from bucket '${bucketThumbnails}'.`);
}
catch(err) {
    console.log(`Failed to delete '${objectName}' from bucket '${bucketThumbnails}': ${err}.`);
}

最後に、Firestore コレクションから画像メタデータを削除します。

try {
    const pictureStore = new Firestore().collection('pictures');
    const docRef = pictureStore.doc(objectName);
    await docRef.delete();

    console.log(`Deleted '${objectName}' from Firestore collection 'pictures'`);
}
catch(err) {
    console.log(`Failed to delete '${objectName}' from Firestore: ${err}.`);
}

res.status(200).send(`Processed '${objectName}'.`);

次に、Node スクリプトで受信リクエストをリッスンするようにします。必要な環境変数が設定されていることも確認します。

app.listen(PORT, () => {
    if (!bucketThumbnails) throw new Error("BUCKET_THUMBNAILS not set");
    console.log(`Started service on port ${PORT}`);
});

7. ローカルでテストする

コードをローカルでテストして、クラウドにデプロイする前に機能することを確認する。

garbage-collector/nodejs フォルダ内で npm の依存関係をインストールし、サーバーを起動します。

export BUCKET_THUMBNAILS=thumbnails-$GOOGLE_CLOUD_PROJECT

npm install; npm start

問題がなければ、サーバーがポート 8080 で起動します。

Started service on port 8080

CTRL-C を使用して終了します。

8. ビルドして Cloud Run にデプロイする

Cloud Run にデプロイする前に、Cloud Run のリージョンをサポートされているいずれかのリージョンに設定し、プラットフォームを managed に設定します。

REGION=europe-west1
gcloud config set run/region $REGION
gcloud config set run/platform managed

構成が設定されていることを確認できます。

gcloud config list

...
[run]
platform = managed
region = europe-west1

Cloud Build を使用してコンテナ イメージを手動でビルドして公開するのではなく、Cloud Run を利用して Google Cloud Buildpacks を使用してコンテナ イメージをビルドすることもできます。

次のコマンドを実行して、Google Cloud Buildpack を使用してコンテナ イメージをビルドし、そのコンテナ イメージを Cloud Run にデプロイします。

SERVICE_NAME=garbage-collector-service

gcloud run deploy $SERVICE_NAME \
    --source . \
    --no-allow-unauthenticated \
    --update-env-vars BUCKET_THUMBNAILS=$BUCKET_THUMBNAILS

–-source フラグに注意してください。これにより、Cloud Run が Google Cloud Buildpacks を使用して Dockerfile. なしでコンテナ イメージをビルドできるようになります。--no-allow-unauthenticated フラグを指定すると、Cloud Run サービスが内部サービスとなり、特定のサービス アカウントによってのみトリガーされます。後で、内部の Cloud Run サービスを呼び出すための run.invoker ロールを持つデフォルトのコンピューティング サービス アカウントでトリガーを作成します。

9. トリガーを作成する

Eventarc では、トリガーはどのサービスがどのような種類のイベントを取得する必要があるかを定義します。ここでは、バケット内でファイルが削除されたときにサービスがイベントを受信するようにします。

アップロードした画像バケットと同じリージョンにトリガーのロケーションを設定します。

gcloud config set eventarc/location eu

storage.objects.delete イベントをフィルタして Cloud Run サービスに送信する AuditLog トリガーを作成します。

BUCKET_IMAGES=uploaded-pictures-$GOOGLE_CLOUD_PROJECT

gcloud eventarc triggers create trigger-$SERVICE_NAME \
  --destination-run-service=$SERVICE_NAME \
  --destination-run-region=$REGION \
  --event-filters="type=google.cloud.storage.object.v1.deleted" \
  --event-filters="bucket=$BUCKET_IMAGES" \
  --service-account=$PROJECT_NUMBER-compute@developer.gserviceaccount.com

次のコマンドを使用すると、トリガーが作成されたことを再確認できます。

gcloud eventarc triggers list

10. サービスをテストする

サービスが機能しているかどうかをテストするには、uploaded-pictures バケットに移動し、いずれかの画像を削除します。サービスのログを見ると、thumbnails バケット内の関連画像が削除され、そのドキュメントが pictures Firestore コレクションから削除されたことがわかります。

519abf90e7ea4d12.png

11. クリーンアップ(省略可)

シリーズの他のラボを継続する予定がない場合は、リソースをクリーンアップすることで費用を節約し、クラウド全般に精通するようにしてください。次のようにして、リソースを個別にクリーンアップできます。

次のコマンドで Service を削除します。

gcloud run services delete $SERVICE_NAME -q

Eventarc トリガーを削除します。

gcloud eventarc triggers delete trigger-$SERVICE_NAME -q

また、プロジェクト全体を削除することもできます。

gcloud projects delete $GOOGLE_CLOUD_PROJECT

12. 完了

これで、ここでは、Cloud Run でイベントを受信するための新しいサービスである Eventarc によってトリガーされる、イメージ ガベージ コレクタである Cloud Run サービスを作成しました。画像バケットから画像が削除されると、サービスは Eventarc からイベントを受信します。次に、サムネイル バケットから画像を削除し、Firestore 画像コレクションからも削除されます。

学習した内容

  • Cloud Run
  • Cloud Storage
  • Cloud Firestore
  • Eventarc