۱. مرور کلی
در اولین آزمایشگاه کد، تصاویر را در یک سطل ذخیره خواهید کرد. این یک رویداد ایجاد فایل ایجاد میکند که توسط یک سرویس مستقر در Cloud Run مدیریت میشود. این سرویس با Vision API تماس میگیرد تا تجزیه و تحلیل تصویر را انجام دهد و نتایج را در یک مخزن داده ذخیره کند.

آنچه یاد خواهید گرفت
- فضای ذخیرهسازی ابری
- اجرای ابری
- رابط برنامهنویسی کاربردی (API) دید ابری
- فروشگاه ابری فایر استور
۲. تنظیمات و الزامات
تنظیم محیط خودتنظیم
- وارد کنسول گوگل کلود شوید و یک پروژه جدید ایجاد کنید یا از یک پروژه موجود دوباره استفاده کنید. اگر از قبل حساب جیمیل یا گوگل ورک اسپیس ندارید، باید یکی ایجاد کنید .



- نام پروژه ، نام نمایشی برای شرکتکنندگان این پروژه است. این یک رشته کاراکتری است که توسط APIهای گوگل استفاده نمیشود. میتوانید آن را در هر زمانی بهروزرسانی کنید.
- شناسه پروژه باید در تمام پروژههای گوگل کلود منحصر به فرد باشد و تغییرناپذیر است (پس از تنظیم، قابل تغییر نیست). کنسول کلود به طور خودکار یک رشته منحصر به فرد تولید میکند؛ معمولاً برای شما مهم نیست که چیست. در اکثر آزمایشگاههای کد، باید شناسه پروژه را ارجاع دهید (که معمولاً با عنوان
PROJECT_IDشناخته میشود). اگر شناسه تولید شده را دوست ندارید، میتوانید یک شناسه تصادفی دیگر ایجاد کنید. به عنوان یک جایگزین، میتوانید شناسه خودتان را امتحان کنید و ببینید که آیا در دسترس است یا خیر. پس از این مرحله قابل تغییر نیست و در طول پروژه باقی خواهد ماند. - برای اطلاع شما، یک مقدار سوم هم وجود دارد، شماره پروژه که برخی از APIها از آن استفاده میکنند. برای کسب اطلاعات بیشتر در مورد هر سه این مقادیر، به مستندات مراجعه کنید.
- در مرحله بعد، برای استفاده از منابع/API های ابری، باید پرداخت صورتحساب را در کنسول ابری فعال کنید . اجرای این آزمایشگاه کد، اگر اصلاً هزینهای نداشته باشد، هزینه زیادی نخواهد داشت. برای خاموش کردن منابع به طوری که پس از این آموزش متحمل پرداخت صورتحساب نشوید، میتوانید منابعی را که ایجاد کردهاید یا کل پروژه را حذف کنید. کاربران جدید Google Cloud واجد شرایط برنامه آزمایشی رایگان ۳۰۰ دلاری هستند.
شروع پوسته ابری
اگرچه میتوان از راه دور و از طریق لپتاپ، گوگل کلود را مدیریت کرد، اما در این آزمایشگاه کد، از گوگل کلود شل ، یک محیط خط فرمان که در فضای ابری اجرا میشود، استفاده خواهید کرد.
از کنسول گوگل کلود ، روی آیکون Cloud Shell در نوار ابزار بالا سمت راست کلیک کنید:

آمادهسازی و اتصال به محیط فقط چند لحظه طول میکشد. وقتی تمام شد، باید چیزی شبیه به این را ببینید:

این ماشین مجازی با تمام ابزارهای توسعهای که نیاز دارید، مجهز شده است. این ماشین مجازی یک دایرکتوری خانگی پایدار ۵ گیگابایتی ارائه میدهد و روی فضای ابری گوگل اجرا میشود که عملکرد شبکه و احراز هویت را تا حد زیادی بهبود میبخشد. تمام کارهای شما در این آزمایشگاه کد را میتوان در یک مرورگر انجام داد. نیازی به نصب چیزی ندارید.
۳. فعال کردن APIها
برای این آزمایش، شما از توابع ابری و رابط برنامهنویسی کاربردی بینایی (Vision API) استفاده خواهید کرد، اما ابتدا باید آنها را یا در کنسول ابری یا با gcloud فعال کنید.
برای فعال کردن Vision API در Cloud Console، Cloud Vision API را در نوار جستجو جستجو کنید:

شما به صفحه API مربوط به Cloud Vision هدایت خواهید شد:

روی دکمهی 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
۴. ایجاد سطل (کنسول)
یک مخزن ذخیرهسازی برای تصاویر ایجاد کنید. میتوانید این کار را از کنسول پلتفرم ابری گوگل ( console.cloud.google.com ) یا با ابزار خط فرمان gsutil از Cloud Shell یا محیط توسعه محلی خود انجام دهید.
به بخش ذخیرهسازی بروید
از منوی «همبرگری» (☰)، به صفحه Storage بروید.

سطل خود را نامگذاری کنید
روی دکمهی CREATE BUCKET کلیک کنید.

CONTINUE کلیک کنید.
انتخاب مکان

یک سطل چند منطقهای در منطقه مورد نظر خود (در اینجا Europe ) ایجاد کنید.
CONTINUE کلیک کنید.
انتخاب کلاس ذخیرهسازی پیشفرض

کلاس ذخیرهسازی Standard را برای دادههای خود انتخاب کنید.
CONTINUE کلیک کنید.
کنترل دسترسی را تنظیم کنید

از آنجایی که قرار است با تصاویر عمومی کار کنید، میخواهید تمام تصاویر ذخیره شده در این سطل، کنترل دسترسی یکسانی داشته باشند.
گزینه کنترل دسترسی Uniform را انتخاب کنید.
CONTINUE کلیک کنید.
تنظیم حفاظت/رمزگذاری

کلید پیشفرض ( Google-managed key) را نگه دارید، زیرا از کلیدهای رمزگذاری خودتان استفاده نخواهید کرد.
برای نهایی کردن ایجاد سطل، CREATE کلیک کنید.
اضافه کردن همه کاربران به عنوان نمایشگر فضای ذخیرهسازی
به برگه Permissions بروید:

یک عضو allUsers با نقش Storage > Storage Object Viewer به صورت زیر به سطل اضافه کنید:

SAVE کلیک کنید.
۵. ایجاد سطل (gsutil)
همچنین میتوانید از ابزار خط فرمان gsutil در Cloud Shell برای ایجاد سطلها استفاده کنید.
در Cloud Shell، یک متغیر برای نام منحصر به فرد سطل تنظیم کنید. Cloud Shell از قبل GOOGLE_CLOUD_PROJECT را برای شناسه منحصر به فرد پروژه شما تنظیم کرده است. میتوانید آن را به نام سطل اضافه کنید.
برای مثال:
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 عمومی داشته باشید:

همانطور که در مرحله قبل توضیح داده شد، بررسی کنید که آیا میتوانید تصاویر را در سطل بارگذاری کنید و تصاویر بارگذاری شده در دسترس عموم هستند یا خیر.
۶. دسترسی عمومی به سطل را آزمایش کنید
با بازگشت به مرورگر ذخیرهسازی، سطل خود را در لیست مشاهده خواهید کرد که دسترسی «عمومی» دارد (شامل یک علامت هشدار که به شما یادآوری میکند هر کسی به محتوای آن سطل دسترسی دارد).

سطل شما اکنون آماده دریافت تصاویر است.
اگر روی نام سطل کلیک کنید، جزئیات سطل را مشاهده خواهید کرد.

در آنجا، میتوانید دکمهی Upload files را امتحان کنید تا ببینید آیا میتوانید تصویری به سطل اضافه کنید یا خیر. یک پنجرهی انتخاب فایل از شما میخواهد که یک فایل را انتخاب کنید. پس از انتخاب، فایل در سطل شما بارگذاری میشود و دوباره دسترسی public که به طور خودکار به این فایل جدید اختصاص داده شده است را مشاهده خواهید کرد.

در کنار برچسب دسترسی Public ، یک آیکون لینک کوچک نیز مشاهده خواهید کرد. وقتی روی آن کلیک کنید، مرورگر شما به آدرس اینترنتی عمومی آن تصویر هدایت میشود که به شکل زیر خواهد بود:
https://storage.googleapis.com/BUCKET_NAME/PICTURE_FILE.png
با BUCKET_NAME که نام منحصر به فرد سراسری است که برای سطل خود انتخاب کردهاید، و سپس نام فایل تصویر شما.
با کلیک روی کادر کنار نام تصویر، دکمهی DELETE فعال میشود و میتوانید این تصویر اول را حذف کنید.
۷. پایگاه داده را آماده کنید
شما اطلاعات مربوط به تصویر داده شده توسط Vision API را در پایگاه داده Cloud Firestore ، یک پایگاه داده سند NoSQL سریع، کاملاً مدیریت شده، بدون سرور و ابری، ذخیره خواهید کرد. با رفتن به بخش Firestore در Cloud Console، پایگاه داده خود را آماده کنید:

دو گزینه ارائه میشود: Native mode یا Datastore mode . از حالت بومی استفاده کنید که ویژگیهای اضافی مانند پشتیبانی آفلاین و همگامسازی بلادرنگ را ارائه میدهد.
روی SELECT NATIVE MODE کلیک کنید.

یک منطقهی چندمنطقهای انتخاب کنید (اینجا در اروپا، اما در حالت ایدهآل حداقل همان منطقهای که عملکرد و سطل ذخیرهسازی شما در آن قرار دارد).
روی دکمه CREATE DATABASE کلیک کنید.
پس از ایجاد پایگاه داده، باید موارد زیر را مشاهده کنید:

با کلیک روی دکمه + START COLLECTION یک مجموعه جدید ایجاد کنید.
pictures مجموعه اسامی

نیازی به ایجاد سند ندارید. آنها را به صورت برنامهنویسی اضافه خواهید کرد، زیرا تصاویر جدید در فضای ابری ذخیره شده و توسط Vision API تجزیه و تحلیل میشوند.
روی Save کلیک کنید.
Firestore اولین سند پیشفرض را در مجموعه تازه ایجاد شده ایجاد میکند، میتوانید با خیال راحت آن سند را حذف کنید زیرا حاوی هیچ اطلاعات مفیدی نیست:

اسنادی که به صورت برنامهنویسی در مجموعه ما ایجاد میشوند، شامل ۴ فیلد خواهند بود:
- نام (رشته): نام فایل تصویر آپلود شده که کلید سند نیز هست.
- برچسبها (آرایهای از رشتهها): برچسبهای اقلام شناساییشده توسط Vision API
- رنگ (رشته): کد رنگ هگزادسیمال رنگ غالب (مثلاً #ab12ef)
- ایجاد شده (تاریخ): مهر زمانی ذخیره شدن فرادادههای این تصویر
- تصویر بندانگشتی (boolean): یک فیلد اختیاری که در صورت ایجاد تصویر بندانگشتی برای این تصویر، وجود خواهد داشت و مقدار آن true خواهد بود.
از آنجایی که ما در Firestore به دنبال تصاویری خواهیم گشت که تصویر بندانگشتی (thumbnail) آنها موجود باشد و آنها را بر اساس تاریخ ایجاد مرتب کنیم، باید یک فهرست جستجو (search index) ایجاد کنیم.
شما میتوانید ایندکس را با دستور زیر در Cloud Shell ایجاد کنید:
gcloud firestore indexes composite create \
--collection-group=pictures \
--field-config field-path=thumbnail,order=descending \
--field-config field-path=created,order=descending
یا میتوانید این کار را از طریق کنسول ابری، با کلیک روی Indexes ، در ستون ناوبری سمت چپ، و سپس ایجاد یک فهرست ترکیبی مطابق شکل زیر انجام دهید:

روی Create کلیک کنید. ایجاد فهرست میتواند چند دقیقه طول بکشد.
۸. کد را کپی کنید
اگر قبلاً در آزمایشگاه کد قبلی این کار را نکردهاید، کد را کلون کنید:
git clone https://github.com/GoogleCloudPlatform/serverless-photosharing-workshop
سپس میتوانید برای شروع ساخت آزمایشگاه به دایرکتوری حاوی سرویس بروید:
cd serverless-photosharing-workshop/services/image-analysis/java
شما طرح فایل زیر را برای سرویس خواهید داشت:

۹. کد سرویس را بررسی کنید
شما با بررسی نحوه فعالسازی کتابخانههای کلاینت جاوا در pom.xml با استفاده از BOM شروع میکنید:
ابتدا، فایل pom.xml را که وابستگیهای تابع جاوای ما را فهرست میکند، ویرایش کنید. کد را بهروزرسانی کنید تا وابستگی 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 پیادهسازی شده است. هر بار که تصویر جدیدی در bucket آپلود میشود، سرویس یک اعلان برای پردازش دریافت میکند:
@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 را درخواست میکنیم:
- تشخیص برچسب : برای فهمیدن اینکه چه چیزی در آن تصاویر وجود دارد
- ویژگیهای تصویر : برای ارائه ویژگیهای جالب تصویر (ما به رنگ غالب تصویر علاقهمند هستیم)
- جستجوی ایمن : برای اطلاع از اینکه آیا نمایش تصویر بیخطر است یا خیر (نباید حاوی محتوای بزرگسالانه / پزشکی / شهوتانگیز / خشونتآمیز باشد)
در این مرحله، میتوانیم رابط برنامهنویسی Vision را فراخوانی کنیم:
...
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) {
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());
}
۱۰. ساخت تصاویر برنامه با GraalVM (اختیاری)
در این مرحله اختیاری، شما یک JIT(JVM) based app image و سپس یک AOT(Native) Java app image با استفاده از GraalVM خواهید ساخت.
برای اجرای این نسخه، باید مطمئن شوید که JDK مناسب و سازنده تصویر بومی را نصب و پیکربندی کردهاید. گزینههای مختلفی در دسترس است.
To start ، GraalVM 22.2.x Community Edition را دانلود کنید و دستورالعملهای موجود در صفحه نصب GraalVM را دنبال کنید.
این فرآیند را میتوان با کمک SDKMAN تا حد زیادی ساده کرد!
برای نصب توزیع JDK مناسب با SDKman ، با استفاده از دستور install شروع کنید:
sdk install java 22.2.r17-grl
به SDKman دستور دهید که از این نسخه، هم برای ساختهای JIT و هم AOT، استفاده کند:
sdk use java 22.2.0.r17-grl
native-image utility را برای GraalVM نصب کنید:
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(Native):
./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] ------------------------------------------------------------------------
۱۱. ساخت و انتشار ایمیجهای کانتینر
بیایید یک تصویر کانتینر در دو نسخه مختلف بسازیم: یکی به عنوان یک 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(Native):
./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
۱۲. استقرار در Cloud Run
وقت آن رسیده که سرویس را مستقر کنیم.
شما سرویس را دو بار مستقر خواهید کرد، یک بار با استفاده از تصویر JIT(JVM) و بار دوم با استفاده از تصویر AOT(Native). هر دو استقرار سرویس، تصویر یکسانی را از سطل به صورت موازی، برای اهداف مقایسه، پردازش میکنند.
ابتدا، متغیرهای محیطی پروژه 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(Native) را مستقر کنید و گزارش استقرار را در کنسول مشاهده کنید:
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
۱۳. راهاندازی محرکهای Eventarc
Eventarc یک راهکار استاندارد برای مدیریت جریان تغییرات وضعیت، به نام رویدادها، بین میکروسرویسهای جدا شده ارائه میدهد. هنگامی که Eventarc فعال میشود، این رویدادها را از طریق اشتراکهای Pub/Sub به مقاصد مختلف (در این سند، به Event destinations مراجعه کنید) هدایت میکند و در عین حال، تحویل، امنیت، مجوز، قابلیت مشاهده و مدیریت خطا را برای شما مدیریت میکند.
شما میتوانید یک تریگر 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 به حساب سرویس ذخیرهسازی ابری اختصاص دهید:
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'
برای پردازش تصویر، تریگرهای Eventarc را برای هر دو تصویر سرویس JVM(JIT) و AOT(Native) تنظیم کنید:
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
۱۴. نسخههای سرویس را آزمایش کنید
پس از موفقیتآمیز بودن استقرار سرویس، تصویری را در Cloud Storage ارسال خواهید کرد، خواهید دید که آیا سرویسهای ما فراخوانی شدهاند، Vision API چه چیزی را برمیگرداند و آیا فراداده در Firestore ذخیره شده است یا خیر.
به Cloud Storage برگردید و روی سطلی که در ابتدای تمرین ایجاد کردیم کلیک کنید:

وقتی در صفحه جزئیات سطل هستید، برای آپلود تصویر روی دکمه Upload files کلیک کنید.
برای مثال، یک تصویر GeekHour.jpeg در مسیر /services/image-analysis/java به همراه کدبیس شما ارائه میشود. یک تصویر را انتخاب کنید و Open button فشار دهید:

اکنون میتوانید اجرای سرویس را بررسی کنید، که با image-analysis-jvm شروع میشود و به دنبال آن image-analysis-native قرار دارد.
از منوی «همبرگر» (☰)، به سرویس Cloud Run > image-analysis-jvm بروید.
روی Logs کلیک کنید و خروجی را مشاهده کنید:

و در واقع، در لیست گزارشها، میتوانم ببینم که سرویس JIT(JVM) به نام image-analysis-jvm فراخوانی شده است.
لاگها شروع و پایان اجرای سرویس را نشان میدهند. و در این بین، میتوانیم لاگهایی را که در تابع خود با دستورات لاگ در سطح INFO قرار دادهایم، مشاهده کنیم. موارد زیر را میبینیم:
- جزئیات رویدادی که تابع ما را فعال میکند،
- نتایج خام حاصل از فراخوانی Vision API،
- برچسبهایی که در تصویری که آپلود کردیم پیدا شدند،
- اطلاعات رنگهای غالب،
- اینکه آیا نمایش تصویر بیخطر است،
- و در نهایت، آن فرادادههای مربوط به تصویر در Firestore ذخیره شدهاند.
شما این فرآیند را برای سرویس image-analysis-native تکرار خواهید کرد.
از منوی «همبرگری» (☰)، به سرویس Cloud Run > image-analysis-native بروید.
روی Logs کلیک کنید و خروجی را مشاهده کنید:

اکنون باید بررسی کنید که آیا فرادادههای تصویر در Fiorestore ذخیره شدهاند یا خیر.
دوباره از منوی "همبرگر" (☰)، به بخش Firestore بروید. در زیربخش Data (که به طور پیشفرض نشان داده شده است)، باید مجموعه pictures را با یک سند جدید اضافه شده، مربوط به تصویری که تازه آپلود کردهاید، ببینید:

۱۵. تمیز کردن (اختیاری)
اگر قصد ندارید با سایر آزمایشگاههای این مجموعه ادامه دهید، میتوانید منابع را پاکسازی کنید تا در هزینهها صرفهجویی کنید و در کل شهروند ابری خوبی باشید. میتوانید منابع را به صورت جداگانه و به شرح زیر پاکسازی کنید.
سطل را حذف کنید:
gsutil rb gs://${BUCKET_PICTURES}
تابع را حذف کنید:
gcloud functions delete picture-uploaded --region europe-west1 -q
با انتخاب گزینه Delete collection از مجموعه، مجموعه Firestore را حذف کنید:

روش دیگر، حذف کل پروژه است:
gcloud projects delete ${GOOGLE_CLOUD_PROJECT}
۱۶. تبریک میگویم!
تبریک میگویم! شما با موفقیت اولین سرویس کلیدی پروژه را پیادهسازی کردید!
آنچه ما پوشش دادهایم
- فضای ذخیرهسازی ابری
- اجرای ابری
- رابط برنامهنویسی کاربردی (API) دید ابری
- فروشگاه ابری فایر استور
- تصاویر بومی جاوا