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

1. 概要

このコードラボでは、Cloud Run でイベントを受信する新しいサービスである Eventarc によってトリガーされる新しい Cloud Run サービス(イメージ ガベージ コレクタ)を作成します。写真が写真バケットから削除されると、サービスは 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

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

幅広い Google Cloud ソースからのイベントは、Cloud Audit Logs を介して配信されます。これらのソースからのイベント配信のレイテンシと可用性は、Cloud Audit Logs のレイテンシと可用性に依存します。Google Cloud ソースからのイベントがトリガーされるたびに、対応する Cloud Audit Log エントリが作成されます。

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. コードのクローンを作成する

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

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 Events ライブラリを使用します。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(画像のバケット)と Cloud Firestore データストアを操作するためのものです。また、Eventarc StoreObjectData によって送信された CloudEvent を読み取り、Google Events ライブラリから CloudEvent の Cloud Storage イベント本文を読み取るために、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 ロールを持つデフォルトの Compute サービス アカウントを使用してトリガーを作成します。

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