1. खास जानकारी
पहले कोड लैब में, आपको एक बकेट में फ़ोटो सेव करनी होंगी. इससे फ़ाइल बनाने का एक इवेंट जनरेट होगा. इसे Cloud Run में डिप्लॉय की गई सेवा मैनेज करेगी. यह सेवा, इमेज का विश्लेषण करने के लिए Vision API को कॉल करेगी और नतीजों को डेटास्टोर में सेव करेगी.

आपको क्या सीखने को मिलेगा
- Cloud Storage
- Cloud Run
- Cloud Vision API
- Cloud Firestore
2. सेटअप और ज़रूरी शर्तें
अपने हिसाब से एनवायरमेंट सेट अप करना
- Google Cloud Console में साइन इन करें और नया प्रोजेक्ट बनाएं या किसी मौजूदा प्रोजेक्ट का फिर से इस्तेमाल करें. अगर आपके पास पहले से कोई Gmail या Google Workspace खाता नहीं है, तो आपको एक खाता बनाना होगा.



- प्रोजेक्ट का नाम, इस प्रोजेक्ट में हिस्सा लेने वाले लोगों के लिए डिसप्ले नेम होता है. यह एक वर्ण स्ट्रिंग है, जिसका इस्तेमाल Google API नहीं करते. इसे कभी भी अपडेट किया जा सकता है.
- प्रोजेक्ट आईडी, सभी Google Cloud प्रोजेक्ट के लिए यूनीक होना चाहिए. साथ ही, इसे बदला नहीं जा सकता. Cloud Console, यूनीक स्ट्रिंग अपने-आप जनरेट करता है. आम तौर पर, आपको इससे कोई फ़र्क़ नहीं पड़ता कि यह क्या है. ज़्यादातर कोडलैब में, आपको प्रोजेक्ट आईडी का रेफ़रंस देना होगा. आम तौर पर, इसे
PROJECT_IDके तौर पर पहचाना जाता है. अगर आपको जनरेट किया गया आईडी पसंद नहीं है, तो कोई दूसरा रैंडम आईडी जनरेट किया जा सकता है. इसके अलावा, आपके पास अपना नाम आज़माने का विकल्प भी है. इससे आपको पता चलेगा कि वह नाम उपलब्ध है या नहीं. इस चरण के बाद, इसे बदला नहीं जा सकता. यह प्रोजेक्ट की अवधि तक बना रहेगा. - आपकी जानकारी के लिए बता दें कि एक तीसरी वैल्यू भी होती है, जिसे प्रोजेक्ट नंबर कहते हैं. इसका इस्तेमाल कुछ एपीआई करते हैं. इन तीनों वैल्यू के बारे में ज़्यादा जानने के लिए, दस्तावेज़ देखें.
- इसके बाद, आपको Cloud Console में बिलिंग चालू करनी होगी, ताकि Cloud संसाधनों/एपीआई का इस्तेमाल किया जा सके. इस कोडलैब को पूरा करने में ज़्यादा खर्च नहीं आएगा. इस ट्यूटोरियल के बाद बिलिंग से बचने के लिए, बनाए गए संसाधनों को बंद किया जा सकता है. इसके लिए, बनाए गए संसाधनों को मिटाएं या पूरे प्रोजेक्ट को मिटाएं. Google Cloud के नए उपयोगकर्ताओं को, मुफ़्त में आज़माने के लिए 300 डॉलर का क्रेडिट मिलता है.
Cloud Shell शुरू करें
Google Cloud को अपने लैपटॉप से रिमोटली ऐक्सेस किया जा सकता है. हालांकि, इस कोडलैब में Google Cloud Shell का इस्तेमाल किया जाएगा. यह क्लाउड में चलने वाला कमांड लाइन एनवायरमेंट है.
Google Cloud Console में, सबसे ऊपर दाएं कोने में मौजूद टूलबार पर, Cloud Shell आइकॉन पर क्लिक करें:

इसे चालू करने और एनवायरमेंट से कनेक्ट करने में सिर्फ़ कुछ सेकंड लगेंगे. यह प्रोसेस पूरी होने के बाद, आपको कुछ ऐसा दिखेगा:

इस वर्चुअल मशीन में, डेवलपमेंट के लिए ज़रूरी सभी टूल पहले से मौजूद हैं. यह 5 जीबी की होम डायरेक्ट्री उपलब्ध कराता है. साथ ही, यह Google Cloud पर काम करता है. इससे नेटवर्क की परफ़ॉर्मेंस और पुष्टि करने की प्रोसेस बेहतर होती है. इस कोडलैब में मौजूद सभी टास्क, ब्राउज़र में किए जा सकते हैं. आपको कुछ भी इंस्टॉल करने की ज़रूरत नहीं है.
3. एपीआई चालू करें
इस लैब के लिए, Cloud Functions और Vision API का इस्तेमाल किया जाएगा. हालांकि, इससे पहले इन्हें Cloud Console या gcloud में चालू करना होगा.
Cloud Console में 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 ( console.cloud.google.com) से किया जा सकता है. इसके अलावा, Cloud Shell या अपने लोकल डेवलपमेंट एनवायरमेंट से gsutil कमांड लाइन टूल का इस्तेमाल करके भी ऐसा किया जा सकता है.
स्टोरेज पर जाएं
"हैमबर्गर" (☰) मेन्यू में जाकर, 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 को पहले से ही आपके यूनीक प्रोजेक्ट आईडी पर सेट किया गया है. इसे बकेट के नाम में जोड़ा जा सकता है.
उदाहरण के लिए:
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. बकेट के सार्वजनिक ऐक्सेस की जांच करना
स्टोरेज ब्राउज़र पर वापस जाने पर, आपको सूची में अपना बकेट दिखेगा. इसका ऐक्सेस "सार्वजनिक" होगा. इसमें एक चेतावनी का निशान भी दिखेगा, जो आपको यह याद दिलाएगा कि किसी के पास भी उस बकेट के कॉन्टेंट का ऐक्सेस है.

अब आपका बकेट, फ़ोटो पाने के लिए तैयार है.
बकेट के नाम पर क्लिक करने से, आपको बकेट की जानकारी दिखेगी.

वहां, Upload files बटन पर क्लिक करके यह जांच की जा सकती है कि बकेट में कोई फ़ोटो जोड़ी जा सकती है या नहीं. फ़ाइल चुनने का विकल्प देने वाला एक पॉप-अप दिखेगा. इसमें आपको कोई फ़ाइल चुनने के लिए कहा जाएगा. चुने जाने के बाद, इसे आपके बकेट में अपलोड कर दिया जाएगा. इसके बाद, आपको public का वह ऐक्सेस फिर से दिखेगा जो इस नई फ़ाइल को अपने-आप मिल गया है.

Public ऐक्सेस लेबल के साथ-साथ, आपको एक छोटा लिंक आइकॉन भी दिखेगा. इस पर क्लिक करने पर, आपका ब्राउज़र उस इमेज के सार्वजनिक यूआरएल पर नेविगेट करेगा. यह यूआरएल इस तरह का होगा:
https://storage.googleapis.com/BUCKET_NAME/PICTURE_FILE.png
इसमें BUCKET_NAME वह यूनीक नाम है जो आपने अपने बकेट के लिए चुना है. इसके बाद, आपकी फ़ोटो का फ़ाइल नाम है.
इमेज के नाम के बगल में मौजूद चेक बॉक्स पर क्लिक करने से, DELETE बटन चालू हो जाएगा. इसके बाद, पहली इमेज को मिटाया जा सकता है.
7. डेटाबेस तैयार करना
Vision API से मिली इमेज की जानकारी को Cloud Firestore डेटाबेस में सेव किया जाएगा. यह एक तेज़, पूरी तरह से मैनेज किया जाने वाला, बिना सर्वर वाला, क्लाउड-नेटिव NoSQL दस्तावेज़ डेटाबेस है. Cloud Console के Firestore सेक्शन में जाकर, अपना डेटाबेस तैयार करें:

इसके दो विकल्प होते हैं: Native mode या Datastore mode. नेटिव मोड का इस्तेमाल करें. इसमें ऑफ़लाइन मोड में काम करने और रीयल-टाइम में सिंक करने जैसी अतिरिक्त सुविधाएं मिलती हैं.
SELECT NATIVE MODE पर क्लिक करें.

एक से ज़्यादा क्षेत्रों वाला विकल्प चुनें. यहां यूरोप को चुना गया है, लेकिन बेहतर होगा कि आप उसी क्षेत्र को चुनें जहां आपका फ़ंक्शन और स्टोरेज बकेट मौजूद हैं.
CREATE DATABASE बटन पर क्लिक करें.
डेटाबेस बन जाने के बाद, आपको यह दिखेगा:

+ START COLLECTION बटन पर क्लिक करके, नया कलेक्शन बनाएं.
नाम इकट्ठा करने की सुविधा pictures.

आपको कोई दस्तावेज़ बनाने की ज़रूरत नहीं है. इन्हें प्रोग्राम के हिसाब से जोड़ा जाएगा, क्योंकि नई फ़ोटो Cloud Storage में सेव की जाती हैं और Vision API उनका विश्लेषण करता है.
Save पर क्लिक करें.
Firestore, नई बनाई गई कलेक्शन में पहला डिफ़ॉल्ट दस्तावेज़ बनाता है. इस दस्तावेज़ को सुरक्षित तरीके से मिटाया जा सकता है, क्योंकि इसमें कोई काम की जानकारी नहीं होती:

हमारे कलेक्शन में प्रोग्राम के हिसाब से बनाए जाने वाले दस्तावेज़ों में चार फ़ील्ड होंगे:
- name (string): अपलोड की गई फ़ोटो का फ़ाइल नाम. यह दस्तावेज़ की कुंजी भी है
- labels (स्ट्रिंग की ऐरे): Vision API से पहचाने गए आइटम के लेबल
- color (string): मुख्य रंग का हेक्साडेसिमल कलर कोड (जैसे, #ab12ef)
- created (date): इस इमेज के मेटाडेटा को सेव किए जाने का टाइमस्टैंप
- 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 Console से भी ऐसा किया जा सकता है. इसके लिए, बाईं ओर मौजूद नेविगेशन कॉलम में Indexes पर क्लिक करें. इसके बाद, यहां दिए गए तरीके से कंपोज़िट इंडेक्स बनाएं:

Create पर क्लिक करें. इंडेक्स बनाने में कुछ मिनट लग सकते हैं.
8. कोड क्लोन करना
अगर आपने पिछले कोड लैब में कोड को क्लोन नहीं किया है, तो इसे क्लोन करें:
git clone https://github.com/GoogleCloudPlatform/serverless-photosharing-workshop
इसके बाद, लैब बनाने के लिए उस डायरेक्ट्री पर जाएं जिसमें सेवा मौजूद है:
cd serverless-photosharing-workshop/services/image-analysis/java
सेवा के लिए, आपके पास फ़ाइल का यह लेआउट होगा:

9. सेवा कोड के बारे में जानें
सबसे पहले, यह देखें कि बीओएम का इस्तेमाल करके, pom.xml में 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 की तीन मुख्य क्षमताओं के बारे में पूछ रहे हैं:
- लेबल का पता लगाना: इससे यह समझने में मदद मिलती है कि उन फ़ोटो में क्या है
- इमेज की प्रॉपर्टी: इमेज के दिलचस्प एट्रिब्यूट के बारे में जानकारी देने के लिए (हमें इमेज के मुख्य रंग के बारे में जानकारी चाहिए)
- सेफ़ सर्च: यह जानने के लिए कि इमेज को दिखाना सुरक्षित है या नहीं. इसमें वयस्कों के लिए बना कॉन्टेंट, मेडिकल कॉन्टेंट, अश्लील कॉन्टेंट या हिंसक कॉन्टेंट नहीं होना चाहिए
इस समय, हम 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) {
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
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'
इमेज को प्रोसेस करने के लिए, 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 दिखेगा.
"हैमबर्गर" (☰) मेन्यू में जाकर, Cloud Run > image-analysis-jvm सेवा पर जाएं.
लॉग पर क्लिक करें और आउटपुट देखें:

मुझे लॉग की सूची में दिख रहा है कि JIT(JVM) सेवा image-analysis-jvm को शुरू किया गया था.
लॉग से पता चलता है कि सेवा कब शुरू हुई और कब खत्म हुई. इसके बीच में, हम अपने फ़ंक्शन में डाले गए लॉग देख सकते हैं. इनमें 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 इमेज