1. 總覽
在第一個程式碼研究室中,您會將圖片儲存在值區中。這項操作會產生檔案建立事件,並由部署在 Cloud Run 的服務處理。服務會呼叫 Vision API 進行圖片分析,並將結果儲存至資料儲存庫。
課程內容
- Cloud Storage
- Cloud Run
- Cloud Vision API
- Cloud Firestore
2. 設定和需求
自修環境設定
- 登入 Google Cloud 控制台,建立新專案或重複使用現有專案。如果您還沒有 Gmail 或 Google Workspace 帳戶,請先建立帳戶。
- 「專案名稱」是這項專案參與者的顯示名稱。這是 Google API 未使用的字元字串。您隨時可以更新這項資訊。
- 所有 Google Cloud 專案的專案 ID 均不得重複,且設定後即無法變更。Cloud 控制台會自動產生一個不重複的字串。但通常是在乎它何在在大部分的程式碼研究室中,您必須參照專案 ID (通常為
PROJECT_ID
)。如果您對產生的 ID 不滿意,可以隨機產生一個 ID。此外,您也可以自行嘗試,看看系統是否提供該付款方式。在完成這個步驟後就無法變更,而且在專案期間仍會保持有效。 - 資訊中的第三個值是專案編號,部分 API 會使用這個編號。如要進一步瞭解這三個值,請參閱說明文件。
- 接下來,您需要在 Cloud 控制台中啟用計費功能,才能使用 Cloud 資源/API。執行這個程式碼研究室並不會產生任何費用,如果有的話。如要關閉資源,以免系統產生本教學課程結束後產生的費用,您可以刪除自己建立的資源,或刪除整個專案。Google Cloud 的新使用者符合 $300 美元免費試用計畫的資格。
啟動 Cloud Shell
雖然 Google Cloud 可以從筆記型電腦遠端操作,但在本程式碼研究室中,您將使用 Google Cloud Shell,這是一種在 Cloud 中執行的指令列環境。
在 Google Cloud 控制台,按一下右上方的工具列上的 Cloud Shell 圖示:
佈建並連線至環境的作業只需幾分鐘的時間。完成後,您應該會看到類似下方的內容:
這部虛擬機器都裝載了您需要的所有開發工具。提供永久的 5 GB 主目錄,而且在 Google Cloud 中運作,大幅提高網路效能和驗證能力。本程式碼研究室的所有工作都可以在瀏覽器中完成。不必安裝任何程式。
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. 建立值區 (控制台)
為圖片建立儲存空間值區。如要進行這項操作,可以透過 Google Cloud Platform 控制台 ( console.cloud.google.com),或是透過 Cloud Shell 或本機開發環境中的 gsutil 指令列工具進行操作。
前往「儲存空間」
來自「漢堡」(ICON) 選單,前往 Storage
頁面。
為值區命名
按一下「CREATE BUCKET
」按鈕。
按一下「CONTINUE
」。
選擇位置
在您選擇的區域建立多區域值區 (這個頁面 Europe
)。
按一下「CONTINUE
」。
選擇預設儲存空間級別
請為資料選擇 Standard
儲存空間級別。
按一下「CONTINUE
」。
設定存取權控管
由於您將使用可公開存取的圖片,因此您希望這個值區中儲存的所有圖片都具備相同的統一存取權控管機制。
選擇 Uniform
存取權控管選項。
按一下「CONTINUE
」。
設定保護/加密功能
保留預設值 (Google-managed key)
,因為您不會用到自己的加密金鑰)。
按一下 CREATE
,最後完成值區建立作業。
將 allUsers 新增為儲存空間檢視者
前往「Permissions
」分頁:
將 allUsers
成員新增至值區,角色為 Storage > Storage Object Viewer
,如下所示:
按一下「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
」存取權標籤旁邊還會顯示一個小連結圖示。點選圖片後,瀏覽器就會開啟該圖片的公開網址,格式如下:
https://storage.googleapis.com/BUCKET_NAME/PICTURE_FILE.png
將 BUCKET_NAME
是您為值區選擇的全域不重複名稱,以及圖片的檔案名稱。
按下圖片名稱旁的核取方塊後,系統會啟用「DELETE
」按鈕,並刪除第一張圖片。
7. 準備資料庫
請將 Vision API 提供的圖片相關資訊儲存在 Cloud Firestore 資料庫,這是運作快速、全代管的無伺服器雲端原生 NoSQL 文件資料庫。前往 Cloud 控制台的 Firestore
部分,準備資料庫:
提供兩種選項:Native mode
或 Datastore mode
。使用原生模式,提供離線支援和即時同步處理等額外功能。
按一下 SELECT NATIVE MODE
。
選擇一個多區域 (這裡位於歐洲,但最好至少應與函式和 Storage 值區位於相同區域)。
按一下 CREATE DATABASE
按鈕。
資料庫建立完成後,您應該會看到以下內容:
按一下「+ START COLLECTION
」按鈕,建立新的珍藏內容。
為集合「pictures
」命名。
不必建立文件,當新圖片儲存在 Cloud Storage 並由 Vision API 分析時,您即可透過程式輔助方式新增圖片。
按一下「Save
」。
Firestore 會在新建立的集合中建立第一個預設文件,但其中不含任何實用資訊,您可以放心刪除該文件:
透過程式在集合中建立的文件會含有 4 個欄位:
- name (字串):上傳圖片的檔案名稱,也是文件金鑰
- labels (字串陣列):Vision API 可識別項目的標籤
- color (字串):主色 (即#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. 複製程式碼
複製程式碼 (如果您尚未在先前的程式碼研究室中):
git clone https://github.com/GoogleCloudPlatform/serverless-photosharing-workshop
接著,您可以前往包含服務的目錄,開始建構研究室:
cd serverless-photosharing-workshop/services/image-analysis/java
服務的檔案版面配置如下:
9. 瞭解服務程式碼
首先,我們來看看如何在 pom.xml
使用 BOM 中啟用 Java 用戶端程式庫:
首先,請編輯 pom.xml
檔案,列出 Java 函式的依附元件。更新程式碼以新增 Cloud Vision API Maven 依附元件:
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cloudfunctions</groupId>
<artifactId>gcs-function</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.target>11</maven.compiler.target>
<maven.compiler.source>11</maven.compiler.source>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>libraries-bom</artifactId>
<version>26.1.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>com.google.cloud.functions</groupId>
<artifactId>functions-framework-api</artifactId>
<version>1.0.4</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>
</dependencies>
這項功能是在 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);
}
您現在可以建立要求,程式碼會準備一項要求並傳送至 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 的 3 項主要功能:
- 標籤偵測:瞭解相片內容
- 圖片屬性:為相片提供有趣的屬性 (我們有意瞭解相片的主要顏色)
- 安全搜尋:瞭解圖片是否安全 (不得包含成人 / 醫療 / 兒童不宜 / 暴力內容)
此時,我們可以呼叫 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 (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) {
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());
ApiFuture<WriteResult> writeResult = doc.set(data, SetOptions.merge());
logger.info("Picture metadata saved in Firestore at " + writeResult.get().getUpdateTime());
}
10. 使用 GraalVM 建構應用程式映像檔 (選用)
在這個選用步驟中,您將使用 GraalVM 建構 JIT(JVM) based app image
,然後建構 AOT(Native) Java app image
。
如要執行版本,您必須確保安裝及設定合適的 JDK,並安裝原生映像檔建構工具。我們提供幾種選項,
To start
,請下載 GraalVM 22.2.x Community Edition,然後按照 GraalVM 安裝頁面中的指示操作。
有了 SDKMAN!,就能大幅簡化這項程序。
如要透過 SDKman
安裝適當的 JDK 發行版本,請先使用安裝指令:
sdk install java 22.2.r17-grl
指示 SDKman 在 JIT 和 AOT 版本中使用這個版本:
sdk use java 22.2.0.r17-grl
安裝 GraalVM 適用的 native-image utility
:
gu install native-image
為了方便起見,您可以在 Cloudshell
中使用以下簡單的指令,安裝 GraalVM 和 native-image 公用程式:
# install GraalVM in your home directory cd ~ # download GraalVM wget https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-22.2.0/graalvm-ce-java17-linux-amd64-22.2.0.tar.gz ls tar -xzvf graalvm-ce-java17-linux-amd64-22.2.0.tar.gz # configure Java 17 and GraalVM 22.2 echo Existing JVM: $JAVA_HOME cd graalvm-ce-java17-22.2.0 export JAVA_HOME=$PWD cd bin export PATH=$PWD:$PATH echo JAVA HOME: $JAVA_HOME echo PATH: $PATH # install the native image utility java -version gu install native-image cd ../..
首先,設定 GCP 專案環境變數:
export GOOGLE_CLOUD_PROJECT=$(gcloud config get-value project)
接著,您可以前往包含服務的目錄,開始建構研究室:
cd serverless-photosharing-workshop/services/image-analysis/java
建構 JIT(JVM) 應用程式映像檔:
./mvnw package -Pjvm
觀察終端機中的建構記錄:
... [INFO] --- spring-boot-maven-plugin:2.7.3:repackage (repackage) @ image-analysis --- [INFO] Replacing main artifact with repackaged archive [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 24.009 s [INFO] Finished at: 2022-09-26T22:17:32-04:00 [INFO] ------------------------------------------------------------------------
建構 AOT(原生) 映像檔:
./mvnw package -Pnative -DskipTests
觀察終端機中的建構記錄,包括原生映像檔建構記錄:
請注意,視測試的機器而定,建構作業花費的時間會較長。
... [2/7] Performing analysis... [**********] (95.4s @ 3.57GB) 23,346 (94.42%) of 24,725 classes reachable 44,625 (68.71%) of 64,945 fields reachable 163,759 (70.79%) of 231,322 methods reachable 989 classes, 1,402 fields, and 11,032 methods registered for reflection 63 classes, 69 fields, and 55 methods registered for JNI access 5 native libraries: -framework CoreServices, -framework Foundation, dl, pthread, z [3/7] Building universe... (10.0s @ 5.35GB) [4/7] Parsing methods... [***] (9.7s @ 3.13GB) [5/7] Inlining methods... [***] (4.5s @ 3.29GB) [6/7] Compiling methods... [[6/7] Compiling methods... [********] (67.6s @ 5.72GB) [7/7] Creating image... (8.7s @ 4.59GB) 62.21MB (54.80%) for code area: 100,371 compilation units 50.98MB (44.91%) for image heap: 465,035 objects and 365 resources 337.09KB ( 0.29%) for other data 113.52MB in total ------------------------------------------------------------------------------------------------------------------------ Top 10 packages in code area: Top 10 object types in image heap: 2.36MB com.google.protobuf 12.70MB byte[] for code metadata 1.90MB i.g.xds.shaded.io.envoyproxy.envoy.config.core.v3 6.66MB java.lang.Class 1.73MB i.g.x.shaded.io.envoyproxy.envoy.config.route.v3 6.47MB byte[] for embedded resources 1.67MB sun.security.ssl 4.61MB byte[] for java.lang.String 1.54MB com.google.cloud.vision.v1 4.37MB java.lang.String 1.46MB com.google.firestore.v1 3.38MB byte[] for general heap data 1.37MB io.grpc.xds.shaded.io.envoyproxy.envoy.api.v2.core 1.96MB com.oracle.svm.core.hub.DynamicHubCompanion 1.32MB i.g.xds.shaded.io.envoyproxy.envoy.api.v2.route 1.80MB byte[] for reflection metadata 1.09MB java.util 911.80KB java.lang.String[] 1.08MB com.google.re2j 826.48KB c.o.svm.core.hub.DynamicHub$ReflectionMetadata 45.91MB for 772 more packages 6.45MB for 3913 more object types ------------------------------------------------------------------------------------------------------------------------ 15.1s (6.8% of total time) in 56 GCs | Peak RSS: 7.72GB | CPU load: 4.37 ------------------------------------------------------------------------------------------------------------------------ Produced artifacts: /Users/ddobrin/work/dan/serverless-photosharing-workshop/services/image-analysis/java/target/image-analysis (executable) /Users/ddobrin/work/dan/serverless-photosharing-workshop/services/image-analysis/java/target/image-analysis.build_artifacts.txt (txt) ======================================================================================================================== Finished generating 'image-analysis' in 3m 41s. [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 03:56 min [INFO] Finished at: 2022-09-26T22:22:29-04:00 [INFO] ------------------------------------------------------------------------
11. 建構並發布容器映像檔
讓我們建構兩種不同版本的容器映像檔:一個為 JIT(JVM) image
,另一個做為 AOT(Native) Java image
。
首先,設定 GCP 專案環境變數:
export GOOGLE_CLOUD_PROJECT=$(gcloud config get-value project)
建構 JIT(JVM) 映像檔:
./mvnw package -Pjvm-image
觀察終端機中的建構記錄:
[INFO] [creator] Adding layer 'process-types' [INFO] [creator] Adding label 'io.buildpacks.lifecycle.metadata' [INFO] [creator] Adding label 'io.buildpacks.build.metadata' [INFO] [creator] Adding label 'io.buildpacks.project.metadata' [INFO] [creator] Adding label 'org.opencontainers.image.title' [INFO] [creator] Adding label 'org.opencontainers.image.version' [INFO] [creator] Adding label 'org.springframework.boot.version' [INFO] [creator] Setting default process type 'web' [INFO] [creator] Saving docker.io/library/image-analysis-jvm:r17... [INFO] [creator] *** Images (03a44112456e): [INFO] [creator] docker.io/library/image-analysis-jvm:r17 [INFO] [creator] Adding cache layer 'paketo-buildpacks/syft:syft' [INFO] [creator] Adding cache layer 'cache.sbom' [INFO] [INFO] Successfully built image 'docker.io/library/image-analysis-jvm:r17' [INFO] [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 02:11 min [INFO] Finished at: 2022-09-26T13:09:34-04:00 [INFO] ------------------------------------------------------------------------
建構 AOT(原生) 映像檔:
./mvnw package -Pnative-image
在終端機中觀察建構記錄,包括使用 UPX 的原生映像檔建構記錄檔和圖片壓縮。
請注意,視測試的機器而定,建構作業需要較長時間
... [INFO] [creator] [2/7] Performing analysis... [***********] (147.6s @ 3.10GB) [INFO] [creator] 23,362 (94.34%) of 24,763 classes reachable [INFO] [creator] 44,657 (68.67%) of 65,029 fields reachable [INFO] [creator] 163,926 (70.76%) of 231,656 methods reachable [INFO] [creator] 981 classes, 1,402 fields, and 11,026 methods registered for reflection [INFO] [creator] 63 classes, 68 fields, and 55 methods registered for JNI access [INFO] [creator] 4 native libraries: dl, pthread, rt, z [INFO] [creator] [3/7] Building universe... (21.1s @ 2.66GB) [INFO] [creator] [4/7] Parsing methods... [****] (13.7s @ 4.16GB) [INFO] [creator] [5/7] Inlining methods... [***] (9.6s @ 4.20GB) [INFO] [creator] [6/7] Compiling methods... [**********] (107.6s @ 3.36GB) [INFO] [creator] [7/7] Creating image... (14.7s @ 4.87GB) [INFO] [creator] 62.24MB (51.35%) for code area: 100,499 compilation units [INFO] [creator] 51.99MB (42.89%) for image heap: 473,948 objects and 473 resources [INFO] [creator] 6.98MB ( 5.76%) for other data [INFO] [creator] 121.21MB in total [INFO] [creator] -------------------------------------------------------------------------------- [INFO] [creator] Top 10 packages in code area: Top 10 object types in image heap: [INFO] [creator] 2.36MB com.google.protobuf 12.71MB byte[] for code metadata [INFO] [creator] 1.90MB i.g.x.s.i.e.e.config.core.v3 7.59MB byte[] for embedded resources [INFO] [creator] 1.73MB i.g.x.s.i.e.e.config.route.v3 6.66MB java.lang.Class [INFO] [creator] 1.67MB sun.security.ssl 4.62MB byte[] for java.lang.String [INFO] [creator] 1.54MB com.google.cloud.vision.v1 4.39MB java.lang.String [INFO] [creator] 1.46MB com.google.firestore.v1 3.66MB byte[] for general heap data [INFO] [creator] 1.37MB i.g.x.s.i.e.envoy.api.v2.core 1.96MB c.o.s.c.h.DynamicHubCompanion [INFO] [creator] 1.32MB i.g.x.s.i.e.e.api.v2.route 1.80MB byte[] for reflection metadata [INFO] [creator] 1.09MB java.util 910.41KB java.lang.String[] [INFO] [creator] 1.08MB com.google.re2j 826.95KB c.o.s.c.h.DynamicHu~onMetadata [INFO] [creator] 45.94MB for 776 more packages 6.69MB for 3916 more object types [INFO] [creator] -------------------------------------------------------------------------------- [INFO] [creator] 20.4s (5.6% of total time) in 81 GCs | Peak RSS: 6.75GB | CPU load: 4.53 [INFO] [creator] -------------------------------------------------------------------------------- [INFO] [creator] Produced artifacts: [INFO] [creator] /layers/paketo-buildpacks_native-image/native-image/services.ImageAnalysisApplication (executable) [INFO] [creator] /layers/paketo-buildpacks_native-image/native-image/services.ImageAnalysisApplication.build_artifacts.txt (txt) [INFO] [creator] ================================================================================ [INFO] [creator] Finished generating '/layers/paketo-buildpacks_native-image/native-image/services.ImageAnalysisApplication' in 5m 59s. [INFO] [creator] Executing upx to compress native image [INFO] [creator] Ultimate Packer for eXecutables [INFO] [creator] Copyright (C) 1996 - 2020 [INFO] [creator] UPX 3.96 Markus Oberhumer, Laszlo Molnar & John Reiser Jan 23rd 2020 [INFO] [creator] [INFO] [creator] File size Ratio Format Name [INFO] [creator] -------------------- ------ ----------- ----------- 127099880 -> 32416676 25.50% linux/amd64 services.ImageAnalysisApplication ... [INFO] [creator] ===> EXPORTING ... [INFO] [creator] Adding cache layer 'paketo-buildpacks/native-image:native-image' [INFO] [creator] Adding cache layer 'cache.sbom' [INFO] [INFO] Successfully built image 'docker.io/library/image-analysis-native:r17' ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 05:28 min [INFO] Finished at: 2022-09-26T13:19:53-04:00 [INFO] ------------------------------------------------------------------------
請確認映像檔已建構完成:
docker images | grep image-analysis
標記兩個映像檔並推送至 GCR:
# JIT(JVM) image docker tag image-analysis-jvm:r17 gcr.io/${GOOGLE_CLOUD_PROJECT}/image-analysis-jvm:r17 docker push gcr.io/${GOOGLE_CLOUD_PROJECT}/image-analysis-jvm:r17 # AOT(Native) image docker tag image-analysis-native:r17 gcr.io/${GOOGLE_CLOUD_PROJECT}/image-analysis-native:r17 docker push gcr.io/${GOOGLE_CLOUD_PROJECT}/image-analysis-native:r17
12. 部署至 Cloud Run
部署服務的時間。
您必須部署服務兩次,一次使用 JIT(JVM) 映像檔,第二次使用 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(JVM) 映像檔,並觀察控制台中的部署記錄:
gcloud run deploy image-analysis-jvm \ --image gcr.io/${GOOGLE_CLOUD_PROJECT}/image-analysis-jvm:r17 \ --region europe-west1 \ --memory 2Gi --allow-unauthenticated ... Deploying container to Cloud Run service [image-analysis-jvm] in project [...] region [europe-west1] ✓ Deploying... Done. ✓ Creating Revision... ✓ Routing traffic... ✓ Setting IAM Policy... Done. Service [image-analysis-jvm] revision [image-analysis-jvm-00009-huc] has been deployed and is serving 100 percent of traffic. Service URL: https://image-analysis-jvm-...-ew.a.run.app
部署 AOT(原生) 映像檔,並在控制台中觀察部署記錄:
gcloud run deploy image-analysis-native \ --image gcr.io/${GOOGLE_CLOUD_PROJECT}/image-analysis-native:r17 \ --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 訂閱項目將這些事件轉送至多個目的地 (請參閱本文中的「事件目的地」),並為您管理傳送、安全性、授權、觀測能力和錯誤處理工作。
您可以建立 Eventarc 觸發條件,讓 Cloud Run 服務接收特定或一組事件的通知。指定觸發條件的篩選條件後,就能設定事件的轉送方式,包括事件來源和目標 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
將 pubsub.publisher
授予 Cloud Storage 服務帳戶:
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'
為 JVM(JIT) 和 AOT(原生) 服務映像檔設定 Eventarc 觸發條件,以便處理圖片:
gcloud eventarc triggers list --location=eu gcloud eventarc triggers create image-analysis-jvm-trigger \ --destination-run-service=image-analysis-jvm \ --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
您會發現這兩個觸發條件已建立完成:
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-jvm
,後面接 image-analysis-native
。
來自「漢堡」(ICON) 選單,前往 Cloud Run > image-analysis-jvm
服務。
按一下「記錄檔」,觀察輸出內容:
事實上,在記錄清單中,可以看到系統已叫用 JIT(JVM) 服務 image-analysis-jvm
。
記錄會指出服務執行作業的開始與結束。兩者之間,我們可以看到函式中所放入的記錄,以及 INFO 層級的記錄陳述式。例如:
- 觸發函式的事件詳細資料
- Vision API 呼叫的原始結果。
- 在上傳圖片中找到的標籤
- 主要顏色資訊
- 相片是否安全
- 最終的圖片中繼資料則儲存在 Firestore 中。
您將重複執行 image-analysis-native
服務的程序。
來自「漢堡」(ICON) 選單,前往 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 圖片