1. 概要
最初の Codelab では、画像をバケットに保存します。これにより、Cloud Run にデプロイされたサービスによって処理されるファイル作成イベントが生成されます。このサービスは Vision API を呼び出して画像分析を行い、結果をデータストアに保存します。
学習内容
- Cloud Storage
- Cloud Run
- Cloud Vision API
- Cloud Firestore
2. 設定と要件
セルフペース型の環境設定
- Google Cloud Console にログインして、プロジェクトを新規作成するか、既存のプロジェクトを再利用します。Gmail アカウントも Google Workspace アカウントもまだお持ちでない場合は、アカウントを作成してください。
- プロジェクト名は、このプロジェクトの参加者に表示される名称です。Google API では使用されない文字列です。いつでも更新できます。
- プロジェクト ID は、すべての Google Cloud プロジェクトにおいて一意でなければならず、不変です(設定後は変更できません)。Cloud コンソールでは一意の文字列が自動生成されます。通常は、この内容を意識する必要はありません。ほとんどの Codelab では、プロジェクト ID(通常は
PROJECT_ID
と識別されます)を参照する必要があります。生成された ID が好みではない場合は、ランダムに別の ID を生成できます。または、ご自身で試して、利用可能かどうかを確認することもできます。このステップ以降は変更できず、プロジェクトを通して同じ ID になります。 - なお、3 つ目の値として、一部の API が使用するプロジェクト番号があります。これら 3 つの値について詳しくは、こちらのドキュメントをご覧ください。
- 次に、Cloud のリソースや API を使用するために、Cloud コンソールで課金を有効にする必要があります。この Codelab の操作をすべて行って、費用が生じたとしても、少額です。このチュートリアルの終了後に請求が発生しないようにリソースをシャットダウンするには、作成したリソースを削除するか、プロジェクトを削除します。Google Cloud の新規ユーザーは、300 米ドル分の無料トライアル プログラムをご利用いただけます。
Cloud Shell を起動する
Google Cloud はノートパソコンからリモートで操作できますが、この Codelab では、Google Cloud Shell(Cloud 上で動作するコマンドライン環境)を使用します。
Google Cloud Console で、右上のツールバーにある Cloud Shell アイコンをクリックします。
プロビジョニングと環境への接続にはそれほど時間はかかりません。完了すると、次のように表示されます。
この仮想マシンには、必要な開発ツールがすべて用意されています。永続的なホーム ディレクトリが 5 GB 用意されており、Google Cloud で稼働します。そのため、ネットワークのパフォーマンスと認証機能が大幅に向上しています。この Codelab での作業はすべて、ブラウザ内から実行できます。インストールは不要です。
3. API を有効にする
このラボでは Cloud Functions と Vision API を使用しますが、まず、これらを Cloud コンソールまたは gcloud
で有効にする必要があります。
Cloud コンソールで Vision API を有効にするには、検索バーで「Cloud Vision API
」を検索します。
Cloud Vision API のページが表示されます。
[ENABLE
] ボタンをクリックします。
または、gcloud コマンドライン ツールを使用して Cloud Shell で有効にすることもできます。
Cloud Shell で、次のコマンドを実行します。
gcloud services enable vision.googleapis.com
オペレーションが正常に完了することを確認できます。
Operation "operations/acf.12dba18b-106f-4fd2-942d-fea80ecc5c1c" finished successfully.
Cloud Run と Cloud Build も有効にします。
gcloud services enable cloudbuild.googleapis.com \ run.googleapis.com
4. バケットを作成する(コンソール)
画像用の Storage バケットを作成します。これを行うには、Google Cloud Platform コンソール(console.cloud.google.com)を使用するか、Cloud Shell またはローカル開発環境の gsutil コマンドライン ツールを使用します。
Storage に移動
「ハンバーガー」から(✱)メニューから [Storage
] ページに移動します。
バケットに名前を付ける
[CREATE BUCKET
] ボタンをクリックします。
[CONTINUE
] をクリックします。
ロケーションを選択
任意のリージョン(ここでは Europe
)にマルチリージョン バケットを作成します。
[CONTINUE
] をクリックします。
デフォルトのストレージ クラスを選択する
データのストレージ クラスとして Standard
を選択します。
[CONTINUE
] をクリックします。
アクセス制御の設定
一般公開されているイメージを扱うため、このバケットに保存されているすべての写真に同じ均一なアクセス制御を適用する必要があります。
Uniform
アクセス制御オプションを選択します。
[CONTINUE
] をクリックします。
保護/暗号化の設定
デフォルトのままにします(独自の暗号鍵は使用しないため、Google-managed key)
。
CREATE
をクリックして、最終的にバケットの作成を完了します。
allUsers をストレージ閲覧者として追加する
[Permissions
] タブに移動します。
次のように、Storage > Storage Object Viewer
のロールを持つ allUsers
メンバーをバケットに追加します。
[SAVE
] をクリックします。
5. バケットを作成する(gsutil)
Cloud Shell の gsutil
コマンドライン ツールを使用してバケットを作成することもできます。
Cloud Shell で、一意のバケット名の変数を設定します。Cloud Shell には、GOOGLE_CLOUD_PROJECT
に一意のプロジェクト ID がすでに設定されています。これをバケット名に追加できます。
例:
export BUCKET_PICTURES=uploaded-pictures-${GOOGLE_CLOUD_PROJECT}
ヨーロッパに標準マルチリージョン ゾーンを作成します。
gsutil mb -l EU gs://${BUCKET_PICTURES}
均一なバケットレベルのアクセスを確保する:
gsutil uniformbucketlevelaccess set on gs://${BUCKET_PICTURES}
バケットを公開します。
gsutil iam ch allUsers:objectViewer gs://${BUCKET_PICTURES}
コンソールの Cloud Storage
セクションに移動すると、公開されている uploaded-pictures
バケットがあるはずです。
前のステップで説明したように、バケットに画像をアップロードできることと、アップロードされた画像が一般公開されることをテストします。
6. バケットへの公開アクセスをテストする
Storage ブラウザに戻ると、バケットに [Public] と表示されているバケットがリストに表示されています。アクセス(そのバケットのコンテンツには誰でもアクセスできることを示す警告マークを含む)を付与する必要があります。
これで、バケットで写真を受け取る準備が整いました。
バケット名をクリックすると、バケットの詳細が表示されます。
そこで Upload files
ボタンをクリックして、バケットに画像を追加できるかどうかテストできます。ファイル選択ツールのポップアップが表示され、ファイルの選択を求められます。選択すると、ファイルがバケットにアップロードされ、この新しいファイルに自動的に割り当てられた public
アクセス権が再び表示されます。
Public
アクセスラベルの横には、小さなリンクアイコンも表示されます。その画像をクリックすると、ブラウザはその画像の公開 URL(次の形式)に移動します。
https://storage.googleapis.com/BUCKET_NAME/PICTURE_FILE.png
BUCKET_NAME
はバケットに選択したグローバルに一意の名前と、画像のファイル名です。
画像名の横にあるチェックボックスをオンにすると、[DELETE
] ボタンが有効になり、この最初の画像を削除できます。
7. データベースを準備する
Vision API から提供された写真に関する情報を Cloud Firestore データベースに保存します。Cloud Firestore データベースは、高速、フルマネージド、サーバーレス、クラウドネイティブの NoSQL ドキュメント データベースです。Cloud コンソールの Firestore
セクションに移動して、データベースを準備します。
Native mode
または Datastore mode
の 2 つのオプションがあります。オフライン サポートやリアルタイム同期などの追加機能を備えたネイティブ モードを使用する。
SELECT NATIVE MODE
をクリックします。
マルチリージョンを選択します(ここではヨーロッパですが、関数とストレージ バケットと同じリージョンが理想的です)。
[CREATE DATABASE
] ボタンをクリックします。
データベースが作成されると、次のように表示されます。
[+ START COLLECTION
] ボタンをクリックして、新しいコレクションを作成します。
コレクション pictures
に名前を付けます。
ドキュメントを作成する必要はありません。新しい画像が Cloud Storage に保存され、Vision API によって分析されるときに、これらの画像をプログラムで追加します。
[Save
] をクリックします。
Firestore は、新しく作成されたコレクションに最初のデフォルト ドキュメントを作成します。このドキュメントには有用な情報が含まれていないため、安全に削除できます。
このコレクションでプログラムによって作成されるドキュメントには、次の 4 つのフィールドが含まれます。
- name(文字列): アップロードされた画像のファイル名。ドキュメントのキーでもあります。
- labels(文字列の配列): Vision API によって認識されたアイテムのラベル
- color(文字列): ドミナント カラーの 16 進数のカラーコード(#ab12ef)
- 作成日(日付): この画像のメタデータが保存されたときのタイムスタンプ
- thumbnail(ブール値): この画像のサムネイル画像が生成された場合に true になるオプション フィールド。
Firestore でサムネイルのある画像を検索し、作成日で並べ替えるため、検索インデックスを作成する必要があります。
インデックスを作成するには、Cloud Shell で次のコマンドを使用します。
gcloud firestore indexes composite create \
--collection-group=pictures \
--field-config field-path=thumbnail,order=descending \
--field-config field-path=created,order=descending
または、Cloud コンソールで左側のナビゲーション列で [Indexes
] をクリックし、以下のように複合インデックスを作成することでも作成できます。
[Create
] をクリックします。インデックスの作成には数分かかることがあります。
8. コードのクローンを作成する
前の Codelab をまだ実行していない場合は、コードのクローンを作成します。
git clone https://github.com/GoogleCloudPlatform/serverless-photosharing-workshop
次に、サービスを含むディレクトリに移動して、ラボの作成を開始できます。
cd serverless-photosharing-workshop/services/image-analysis/java
サービスのファイル レイアウトは次のようになります。
9. サービスコードを確認する
まず、BOM を使用して、pom.xml
で Java クライアント ライブラリを有効にする方法を確認します。
まず、Java アプリの依存関係を含む pom.xml
ファイルを開きます。Vision API、Cloud Storage API、Firestore API の使用に焦点を当てます。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.0-M3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>services</groupId>
<artifactId>image-analysis</artifactId>
<version>0.0.1</version>
<name>image-analysis</name>
<description>Spring App for Image Analysis</description>
<properties>
<java.version>17</java.version>
<maven.compiler.target>17</maven.compiler.target>
<maven.compiler.source>17</maven.compiler.source>
<spring-cloud.version>2023.0.0-M2</spring-cloud.version>
<testcontainers.version>1.19.1</testcontainers.version>
</properties>
...
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>libraries-bom</artifactId>
<version>26.24.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
—
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-function-web</artifactId>
</dependency>
<dependency>
<groupId>com.google.cloud.functions</groupId>
<artifactId>functions-framework-api</artifactId>
<version>1.1.0</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud-firestore</artifactId>
</dependency>
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud-vision</artifactId>
</dependency>
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud-storage</artifactId>
</dependency>
この機能は EventController
クラスに実装されています。新しい画像がバケットにアップロードされるたびに、サービスは次の処理を求める通知を受け取ります。
@RestController
public class EventController {
private static final Logger logger = Logger.getLogger(EventController.class.getName());
private static final List<String> requiredFields = Arrays.asList("ce-id", "ce-source", "ce-type", "ce-specversion");
@RequestMapping(value = "/", method = RequestMethod.POST)
public ResponseEntity<String> receiveMessage(
@RequestBody Map<String, Object> body, @RequestHeader Map<String, String> headers) throws IOException, InterruptedException, ExecutionException {
...
}
コードは Cloud Events
ヘッダーの検証に進みます。
System.out.println("Header elements");
for (String field : requiredFields) {
if (headers.get(field) == null) {
String msg = String.format("Missing expected header: %s.", field);
System.out.println(msg);
return new ResponseEntity<String>(msg, HttpStatus.BAD_REQUEST);
} else {
System.out.println(field + " : " + headers.get(field));
}
}
System.out.println("Body elements");
for (String bodyField : body.keySet()) {
System.out.println(bodyField + " : " + body.get(bodyField));
}
if (headers.get("ce-subject") == null) {
String msg = "Missing expected header: ce-subject.";
System.out.println(msg);
return new ResponseEntity<String>(msg, HttpStatus.BAD_REQUEST);
}
これでリクエストを作成できるようになりました。コードは、そのようなリクエストの 1 つを準備し、Vision API
に送信します。
try (ImageAnnotatorClient vision = ImageAnnotatorClient.create()) {
List<AnnotateImageRequest> requests = new ArrayList<>();
ImageSource imageSource = ImageSource.newBuilder()
.setGcsImageUri("gs://" + bucketName + "/" + fileName)
.build();
Image image = Image.newBuilder()
.setSource(imageSource)
.build();
Feature featureLabel = Feature.newBuilder()
.setType(Type.LABEL_DETECTION)
.build();
Feature featureImageProps = Feature.newBuilder()
.setType(Type.IMAGE_PROPERTIES)
.build();
Feature featureSafeSearch = Feature.newBuilder()
.setType(Type.SAFE_SEARCH_DETECTION)
.build();
AnnotateImageRequest request = AnnotateImageRequest.newBuilder()
.addFeatures(featureLabel)
.addFeatures(featureImageProps)
.addFeatures(featureSafeSearch)
.setImage(image)
.build();
requests.add(request);
Vision API には、
- ラベル検出: 画像の内容を把握
- 画像プロパティ: 画像の興味深い属性を指定します(画像の主な色を確認できます)。
- セーフサーチ: 画像が表示しても安全かどうかを知ることができます(アダルト / 医療 / 際どい / 暴力的なコンテンツを含む画像は使用できません)。
この時点で、Vision API を呼び出せます。
...
logger.info("Calling the Vision API...");
BatchAnnotateImagesResponse result = vision.batchAnnotateImages(requests);
List<AnnotateImageResponse> responses = result.getResponsesList();
...
参考までに、Vision API からのレスポンスは次のようになります。
{
"faceAnnotations": [],
"landmarkAnnotations": [],
"logoAnnotations": [],
"labelAnnotations": [
{
"locations": [],
"properties": [],
"mid": "/m/01yrx",
"locale": "",
"description": "Cat",
"score": 0.9959855675697327,
"confidence": 0,
"topicality": 0.9959855675697327,
"boundingPoly": null
},
✄ - - - ✄
],
"textAnnotations": [],
"localizedObjectAnnotations": [],
"safeSearchAnnotation": {
"adult": "VERY_UNLIKELY",
"spoof": "UNLIKELY",
"medical": "VERY_UNLIKELY",
"violence": "VERY_UNLIKELY",
"racy": "VERY_UNLIKELY",
"adultConfidence": 0,
"spoofConfidence": 0,
"medicalConfidence": 0,
"violenceConfidence": 0,
"racyConfidence": 0,
"nsfwConfidence": 0
},
"imagePropertiesAnnotation": {
"dominantColors": {
"colors": [
{
"color": {
"red": 203,
"green": 201,
"blue": 201,
"alpha": null
},
"score": 0.4175916016101837,
"pixelFraction": 0.44456374645233154
},
✄ - - - ✄
]
}
},
"error": null,
"cropHintsAnnotation": {
"cropHints": [
{
"boundingPoly": {
"vertices": [
{ "x": 0, "y": 118 },
{ "x": 1177, "y": 118 },
{ "x": 1177, "y": 783 },
{ "x": 0, "y": 783 }
],
"normalizedVertices": []
},
"confidence": 0.41695669293403625,
"importanceFraction": 1
}
]
},
"fullTextAnnotation": null,
"webDetection": null,
"productSearchResults": null,
"context": null
}
エラーが返されない場合は先に進みます。これが if ブロックになっている理由です。
if (responses.size() == 0) {
logger.info("No response received from Vision API.");
return new ResponseEntity<String>(msg, HttpStatus.BAD_REQUEST);
}
AnnotateImageResponse response = responses.get(0);
if (response.hasError()) {
logger.info("Error: " + response.getError().getMessage());
return new ResponseEntity<String>(msg, HttpStatus.BAD_REQUEST);
}
写真内で認識されたもの、カテゴリ、テーマのラベルを入手します。
List<String> labels = response.getLabelAnnotationsList().stream()
.map(annotation -> annotation.getDescription())
.collect(Collectors.toList());
logger.info("Annotations found:");
for (String label: labels) {
logger.info("- " + label);
}
写真の主な色を知りたいのです。
String mainColor = "#FFFFFF";
ImageProperties imgProps = response.getImagePropertiesAnnotation();
if (imgProps.hasDominantColors()) {
DominantColorsAnnotation colorsAnn = imgProps.getDominantColors();
ColorInfo colorInfo = colorsAnn.getColors(0);
mainColor = rgbHex(
colorInfo.getColor().getRed(),
colorInfo.getColor().getGreen(),
colorInfo.getColor().getBlue());
logger.info("Color: " + mainColor);
}
画像が安全に見せることができるか確認しましょう。
boolean isSafe = false;
if (response.hasSafeSearchAnnotation()) {
SafeSearchAnnotation safeSearch = response.getSafeSearchAnnotation();
isSafe = Stream.of(
safeSearch.getAdult(), safeSearch.getMedical(), safeSearch.getRacy(),
safeSearch.getSpoof(), safeSearch.getViolence())
.allMatch( likelihood ->
likelihood != Likelihood.LIKELY && likelihood != Likelihood.VERY_LIKELY
);
logger.info("Safe? " + isSafe);
}
成人 / なりすまし / 医療 / 暴力 / 際どい特徴について、その可能性や非常に高いかどうかを確認しています。
セーフサーチの結果に問題がなければ、メタデータを Firestore に保存できます。
// Saving result to Firestore
if (isSafe) {
ApiFuture<WriteResult> writeResult =
eventService.storeImage(fileName, labels,
mainColor);
logger.info("Picture metadata saved in Firestore at " +
writeResult.get().getUpdateTime());
}
...
public ApiFuture<WriteResult> storeImage(String fileName,
List<String> labels,
String mainColor) {
FirestoreOptions firestoreOptions = FirestoreOptions.getDefaultInstance();
Firestore pictureStore = firestoreOptions.getService();
DocumentReference doc = pictureStore.collection("pictures").document(fileName);
Map<String, Object> data = new HashMap<>();
data.put("labels", labels);
data.put("color", mainColor);
data.put("created", new Date());
return doc.set(data, SetOptions.merge());
}
10. GraalVM でアプリイメージをビルドする
このオプションのステップでは、GraalVM を使用して JIT based app image
をビルドし、次に Native Java app image
をビルドします。
ビルドを実行するには、適切な JDK とネイティブ イメージ ビルダーがインストールされ、構成されていることを確認する必要があります。いくつかのオプションがあります。
To start
の場合は、GraalVM 22.3.x Community Edition をダウンロードし、GraalVM インストール ページの手順に沿って操作します。
このプロセスは、SDKMAN!
SDKman
を使用して適切な JDK ディストリビューションをインストールするには、まず install コマンドを使用します。
sdk install java 17.0.8-graal
JIT ビルドと AOT ビルドの両方でこのバージョンを使用するように SDKman に指示します。
sdk use java 17.0.8-graal
Cloudshell
では、次の簡単なコマンドを使用して GraalVM とネイティブ イメージ ユーティリティをインストールできます。
# download GraalVM wget https://download.oracle.com/graalvm/17/latest/graalvm-jdk-17_linux-x64_bin.tar.gz tar -xzf graalvm-jdk-17_linux-x64_bin.tar.gz ls -lart # configure Java 17 and GraalVM for Java 17 # note the name of the latest GraalVM version, as unpacked by the tar command echo Existing JVM: $JAVA_HOME cd graalvm-jdk-17.0.8+9.1 export JAVA_HOME=$PWD cd bin export PATH=$PWD:$PATH echo JAVA HOME: $JAVA_HOME echo PATH: $PATH cd ../.. # validate the version with java -version # observe Java(TM) SE Runtime Environment Oracle GraalVM 17.0.8+9.1 (build 17.0.8+9-LTS-jvmci-23.0-b14) Java HotSpot(TM) 64-Bit Server VM Oracle GraalVM 17.0.8+9.1 (build 17.0.8+9-LTS-jvmci-23.0-b14, mixed mode, sharing)
まず、GCP プロジェクトの環境変数を設定します。
export GOOGLE_CLOUD_PROJECT=$(gcloud config get-value project)
次に、サービスを含むディレクトリに移動して、ラボの作成を開始できます。
cd serverless-photosharing-workshop/services/image-analysis/java
JIT アプリケーション イメージをビルドします。
./mvnw package
ターミナルでビルドログを確認します。
... [INFO] Results: [INFO] [INFO] Tests run: 6, Failures: 0, Errors: 0, Skipped: 0 [INFO] [INFO] [INFO] --- maven-jar-plugin:3.3.0:jar (default-jar) @ image-analysis --- [INFO] Building jar: /home/user/serverless-photosharing-workshop/services/image-analysis/java/target/image-analysis-0.0.1.jar [INFO] [INFO] --- spring-boot-maven-plugin:3.2.0-M3:repackage (repackage) @ image-analysis --- [INFO] Replacing main artifact /home/user/serverless-photosharing-workshop/services/image-analysis/java/target/image-analysis-0.0.1.jar with repackaged archive, adding nested dependencies in BOOT-INF/. [INFO] The original artifact has been renamed to /home/user/serverless-photosharing-workshop/services/image-analysis/java/target/image-analysis-0.0.1.jar.original [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 15.335 s [INFO] Finished at: 2023-10-10T19:33:25Z [INFO] ------------------------------------------------------------------------
Native(AOT を使用)イメージをビルドします。
./mvnw native:compile -Pnative
ターミナルで、ネイティブ イメージのビルドログを含むビルドログを確認します。
テストに使用するマシンによっては、ビルドにかなりの時間がかかります。
... [2/7] Performing analysis... [*********] (124.5s @ 4.53GB) 29,732 (93.19%) of 31,905 classes reachable 60,161 (70.30%) of 85,577 fields reachable 261,973 (67.29%) of 389,319 methods reachable 2,940 classes, 2,297 fields, and 97,421 methods registered for reflection 81 classes, 90 fields, and 62 methods registered for JNI access 4 native libraries: dl, pthread, rt, z [3/7] Building universe... (11.7s @ 4.67GB) [4/7] Parsing methods... [***] (6.1s @ 5.91GB) [5/7] Inlining methods... [****] (4.5s @ 4.39GB) [6/7] Compiling methods... [******] (35.3s @ 4.60GB) [7/7] Creating image... (12.9s @ 4.61GB) 80.08MB (47.43%) for code area: 190,483 compilation units 73.81MB (43.72%) for image heap: 660,125 objects and 189 resources 14.95MB ( 8.86%) for other data 168.84MB in total ------------------------------------------------------------------------------------------------------------------------ Top 10 packages in code area: Top 10 object types in image heap: 2.66MB com.google.cloud.vision.v1p4beta1 18.51MB byte[] for code metadata 2.60MB com.google.cloud.vision.v1 9.27MB java.lang.Class 2.49MB com.google.protobuf 7.34MB byte[] for reflection metadata 2.40MB com.google.cloud.vision.v1p3beta1 6.35MB byte[] for java.lang.String 2.17MB com.google.storage.v2 5.72MB java.lang.String 2.12MB com.google.firestore.v1 4.46MB byte[] for embedded resources 1.64MB sun.security.ssl 4.30MB c.oracle.svm.core.reflect.SubstrateMethodAccessor 1.51MB i.g.xds.shaded.io.envoyproxy.envoy.config.core.v3 4.27MB byte[] for general heap data 1.47MB com.google.cloud.vision.v1p2beta1 2.50MB com.oracle.svm.core.hub.DynamicHubCompanion 1.34MB i.g.x.shaded.io.envoyproxy.envoy.config.route.v3 1.17MB java.lang.Object[] 58.34MB for 977 more packages 9.19MB for 4667 more object types ------------------------------------------------------------------------------------------------------------------------ 13.5s (5.7% of total time) in 75 GCs | Peak RSS: 9.44GB | CPU load: 6.13 ------------------------------------------------------------------------------------------------------------------------ Produced artifacts: /home/user/serverless-photosharing-workshop/services/image-analysis/java/target/image-analysis (executable) /home/user/serverless-photosharing-workshop/services/image-analysis/java/target/image-analysis.build_artifacts.txt (txt) ======================================================================================================================== Finished generating '/home/user/serverless-photosharing-workshop/services/image-analysis/java/target/image-analysis' in 3m 57s. [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 04:28 min [INFO] Finished at: 2023-10-10T19:53:30Z [INFO] ------------------------------------------------------------------------
11. コンテナ イメージをビルドして公開する
コンテナ イメージを 2 つの異なるバージョン(JIT image
と Native Java image
)でビルドします。
まず、GCP プロジェクトの環境変数を設定します。
export GOOGLE_CLOUD_PROJECT=$(gcloud config get-value project)
JIT イメージをビルドします。
./mvnw spring-boot:build-image -Pji
ターミナルでビルドログを確認します。
[INFO] [creator] Timer: Saving docker.io/library/image-analysis-maven-jit:latest... started at 2023-10-10T20:00:31Z [INFO] [creator] *** Images (4c84122a1826): [INFO] [creator] docker.io/library/image-analysis-maven-jit:latest [INFO] [creator] Timer: Saving docker.io/library/image-analysis-maven-jit:latest... ran for 6.975913605s and ended at 2023-10-10T20:00:38Z [INFO] [creator] Timer: Exporter ran for 8.068588001s and ended at 2023-10-10T20:00:38Z [INFO] [creator] Timer: Cache started at 2023-10-10T20:00:38Z [INFO] [creator] Reusing cache layer 'paketo-buildpacks/syft:syft' [INFO] [creator] Adding cache layer 'buildpacksio/lifecycle:cache.sbom' [INFO] [creator] Timer: Cache ran for 200.449002ms and ended at 2023-10-10T20:00:38Z [INFO] [INFO] Successfully built image 'docker.io/library/image-analysis-maven-jit:latest' [INFO] [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 43.887 s [INFO] Finished at: 2023-10-10T20:00:39Z [INFO] ------------------------------------------------------------------------
AOT(ネイティブ)イメージをビルドします。
./mvnw spring-boot:build-image -Pnative
ターミナルのビルドログ(ネイティブ イメージのビルドログを含む)を確認します。
注:
- ビルドにはかなりの時間がかかります。
- 画像は UPX でさらに圧縮できますが、起動のパフォーマンスに小さな悪影響があるため、このビルドでは UPX を使用せず、常にわずかなトレードオフが発生します。
... [INFO] [creator] Saving docker.io/library/image-analysis-maven-native:latest... [INFO] [creator] *** Images (13167702674e): [INFO] [creator] docker.io/library/image-analysis-maven-native:latest [INFO] [creator] Adding cache layer 'paketo-buildpacks/bellsoft-liberica:native-image-svm' [INFO] [creator] Adding cache layer 'paketo-buildpacks/syft:syft' [INFO] [creator] Adding cache layer 'paketo-buildpacks/native-image:native-image' [INFO] [creator] Adding cache layer 'buildpacksio/lifecycle:cache.sbom' [INFO] [INFO] Successfully built image 'docker.io/library/image-analysis-maven-native:latest' [INFO] [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 03:37 min [INFO] Finished at: 2023-10-10T20:05:16Z [INFO] ------------------------------------------------------------------------
イメージがビルドされていることを確認します。
docker images | grep image-analysis
2 つのイメージにタグを付けて GCR に push します。
# JIT image docker tag image-analysis-maven-jit gcr.io/${GOOGLE_CLOUD_PROJECT}/image-analysis-maven-jit docker push gcr.io/${GOOGLE_CLOUD_PROJECT}/image-analysis-maven-jit # Native(AOT) image docker tag image-analysis-maven-native gcr.io/${GOOGLE_CLOUD_PROJECT}/image-analysis-maven-native docker push gcr.io/${GOOGLE_CLOUD_PROJECT}/image-analysis-maven-native
12. Cloud Run にデプロイする
サービスのデプロイにかかる時間。
サービスを 2 回デプロイします。1 回目は JIT イメージを使用し、2 回目は AOT(ネイティブ)イメージを使用します。両方のサービス デプロイが、比較目的でバケットからの同じ画像を並行して処理します。
まず、GCP プロジェクトの環境変数を設定します。
export GOOGLE_CLOUD_PROJECT=$(gcloud config get-value project) gcloud config set project ${GOOGLE_CLOUD_PROJECT} gcloud config set run/region gcloud config set run/platform managed gcloud config set eventarc/location europe-west1
JIT イメージをデプロイし、コンソールでデプロイログを確認します。
gcloud run deploy image-analysis-jit \ --image gcr.io/${GOOGLE_CLOUD_PROJECT}/image-analysis-maven-jit \ --region europe-west1 \ --memory 2Gi --allow-unauthenticated ... Deploying container to Cloud Run service [image-analysis-jit] in project [...] region [europe-west1] ✓ Deploying... Done. ✓ Creating Revision... ✓ Routing traffic... ✓ Setting IAM Policy... Done. Service [image-analysis-jit] revision [image-analysis-jvm-00009-huc] has been deployed and is serving 100 percent of traffic. Service URL: https://image-analysis-jit-...-ew.a.run.app
ネイティブ イメージをデプロイし、コンソールでデプロイログを確認します。
gcloud run deploy image-analysis-native \ --image gcr.io/${GOOGLE_CLOUD_PROJECT}/image-analysis-maven-native \ --region europe-west1 \ --memory 2Gi --allow-unauthenticated ... Deploying container to Cloud Run service [image-analysis-native] in project [...] region [europe-west1] ✓ Deploying... Done. ✓ Creating Revision... ✓ Routing traffic... ✓ Setting IAM Policy... Done. Service [image-analysis-native] revision [image-analysis-native-00005-ben] has been deployed and is serving 100 percent of traffic. Service URL: https://image-analysis-native-...-ew.a.run.app
13. Eventarc トリガーを設定する
Eventarc は、分離されたマイクロサービス間の状態変更(イベント)を管理する標準化されたソリューションを提供します。トリガーされると、Eventarc はこれらのイベントを Pub/Sub サブスクリプションを介してさまざまな宛先(このドキュメントでは「イベントの宛先」を参照)に転送し、配信、セキュリティ、認可、オブザーバビリティ、エラー処理を管理します。
指定したイベントまたは一連のイベントの通知を Cloud Run サービスが受信するように、Eventarc トリガーを作成できます。トリガーのフィルタを指定することで、イベントソースとターゲット Cloud Run サービスを含むイベントのルーティングを構成できます。
まず、GCP プロジェクトの環境変数を設定します。
export GOOGLE_CLOUD_PROJECT=$(gcloud config get-value project) gcloud config set project ${GOOGLE_CLOUD_PROJECT} gcloud config set run/region gcloud config set run/platform managed gcloud config set eventarc/location europe-west1
Cloud Storage サービス アカウントに pubsub.publisher
を付与します。
SERVICE_ACCOUNT="$(gsutil kms serviceaccount -p ${GOOGLE_CLOUD_PROJECT})" gcloud projects add-iam-policy-binding ${GOOGLE_CLOUD_PROJECT} \ --member="serviceAccount:${SERVICE_ACCOUNT}" \ --role='roles/pubsub.publisher'
JIT とネイティブ サービス イメージの両方に Eventarc トリガーを設定して、イメージを処理します。
gcloud eventarc triggers list --location=eu gcloud eventarc triggers create image-analysis-jit-trigger \ --destination-run-service=image-analysis-jit \ --destination-run-region=europe-west1 \ --location=eu \ --event-filters="type=google.cloud.storage.object.v1.finalized" \ --event-filters="bucket=uploaded-pictures-${GOOGLE_CLOUD_PROJECT}" \ --service-account=${PROJECT_NUMBER}-compute@developer.gserviceaccount.com gcloud eventarc triggers create image-analysis-native-trigger \ --destination-run-service=image-analysis-native \ --destination-run-region=europe-west1 \ --location=eu \ --event-filters="type=google.cloud.storage.object.v1.finalized" \ --event-filters="bucket=uploaded-pictures-${GOOGLE_CLOUD_PROJECT}" \ --service-account=${PROJECT_NUMBER}-compute@developer.gserviceaccount.com
2 つのトリガーが作成されたことを確認します。
gcloud eventarc triggers list --location=eu
14. サービスのバージョンをテストする
サービスのデプロイに成功したら、画像を Cloud Storage に投稿して、サービスが呼び出されたかどうか、Vision API が返す内容、メタデータが Firestore に保存されているかどうかを確認します。
Cloud Storage
に戻り、ラボの始めに作成したバケットをクリックします。
バケットの詳細ページが表示されたら、Upload files
ボタンをクリックして画像をアップロードします。
たとえば、GeekHour.jpeg
イメージは、コードベースで /services/image-analysis/java
の下に用意されています。画像を選択して Open button
を押します。
これで、サービスの実行を確認できます。image-analysis-jit
で始まり、その後に image-analysis-native
が続きます。
「ハンバーガー」から(Я)メニューで Cloud Run > image-analysis-jit
サービスに移動します。
[ログ] をクリックして出力を確認します。
ログのリストを見ると、JIT サービス image-analysis-jit
が呼び出されたことがわかります。
ログには、サービスの実行の開始と終了が記録されます。その間に、INFO レベルのログ ステートメントを使用して、関数に入力したログを確認できます。確認されたこと:
- 関数をトリガーするイベントの詳細
- Vision API 呼び出しの生の結果は、
- アップロードした写真に見つかったラベル
- ドミナントカラーの情報は
- 見せても問題ないか、
- そして最終的に、写真に関するこれらのメタデータが Firestore に保存されました。
image-analysis-native
サービスについても同じプロセスを繰り返します。
「ハンバーガー」から(Я)メニューで Cloud Run > image-analysis-native
サービスに移動します。
[ログ] をクリックして出力を確認します。
ここで、画像メタデータが Fiorestore に保存されているかどうかを確認します。
ここも「ハンバーガー」から(Я)メニューで Firestore
セクションに移動します。Data
サブセクション(デフォルト)に、アップロードした画像に対応する新しいドキュメントが追加された pictures
コレクションが表示されます。
15. クリーンアップ(省略可)
シリーズの他のラボを継続する予定がない場合は、リソースをクリーンアップすることで費用を節約し、クラウド全般に精通するようにしてください。次のようにして、リソースを個別にクリーンアップできます。
バケットを削除します。
gsutil rb gs://${BUCKET_PICTURES}
関数を削除します。
gcloud functions delete picture-uploaded --region europe-west1 -q
[コレクションからコレクションを削除] を選択して、Firestore コレクションを削除します。
また、プロジェクト全体を削除することもできます。
gcloud projects delete ${GOOGLE_CLOUD_PROJECT}
16. 完了
これで、プロジェクトの最初の鍵サービスが正常に実装されました。
学習した内容
- Cloud Storage
- Cloud Run
- Cloud Vision API
- Cloud Firestore
- ネイティブ Java イメージ