Jib के साथ Google App Engine Java ऐप्लिकेशन से क्लाउड रन पर माइग्रेट करना

1. खास जानकारी

इस कोडलैब सीरीज़ (अपने हिसाब से, प्रैक्टिकल ट्यूटोरियल) का मकसद, Google App Engine (स्टैंडर्ड) के Java डेवलपर को अपने ऐप्लिकेशन को आधुनिक बनाने में मदद करना है. इसके लिए, उन्हें माइग्रेशन की कई सीरीज़ के बारे में बताया गया है. यह तरीका अपनाकर, अपने ऐप्लिकेशन को ज़्यादा पोर्टेबल बनाया जा सकता है. साथ ही, Cloud Run, Google Cloud की कंटेनर-होस्टिंग सेवा, App Engine, और कंटेनर-होस्टिंग की अन्य सेवाओं के लिए, उन्हें कंटेनर में डाला जा सकता है.

इस ट्यूटोरियल में, Jib का इस्तेमाल करके, App Engine ऐप्लिकेशन को पूरी तरह से मैनेज की जाने वाली सेवा में डिप्लॉय करने के लिए, कंटेनर तैयार करने का तरीका बताया गया है. Jib की मदद से, Docker इमेज बनाई जा सकती हैं. यह इंडस्ट्री का एक जाना-माना प्लैटफ़ॉर्म है, जो कंटेनर में ऐप्लिकेशन डेवलप करने, उन्हें भेजने, और उन्हें चलाने के लिए जाना जाता है.

आपको App Engine से Cloud Run पर जाने के ज़रूरी चरण पढ़ाने के साथ-साथ, आप Java 8 App Engine ऐप्लिकेशन को Java 17 पर अपग्रेड करने के तरीके भी सीखेंगे.

अगर आपका ऐप्लिकेशन, App Engine की लेगसी बंडल की गई सेवाओं या App Engine की अन्य सुविधाओं का ज़्यादा इस्तेमाल करता है, तो हमारा सुझाव है कि Cloud Run पर माइग्रेट करने से पहले, उन बंडल की गई सेवाओं को माइग्रेट करें या उन सुविधाओं को बदलें. अगर आपको माइग्रेशन के विकल्पों की जांच करने के लिए ज़्यादा समय चाहिए या आपको फ़िलहाल बंडल की गई लेगसी सेवाओं का इस्तेमाल जारी रखना है, तो नए रनटाइम पर अपग्रेड करते समय, Java 11/17 के लिए App Engine की बंडल की गई सेवाओं का इस्तेमाल जारी रखा जा सकता है. जब आपका ऐप्लिकेशन ज़्यादा पोर्टेबल हो जाए, तो इस कोडलैब पर वापस आकर, अपने ऐप्लिकेशन पर निर्देशों को लागू करने का तरीका जानें.

आपको इनके बारे में जानकारी मिलेगी

  • Cloud Shell का इस्तेमाल करना
  • Cloud Run, Artifact Registry, और Cloud Build API चालू करना
  • Jib और Cloud Build का इस्तेमाल करके, अपने ऐप्लिकेशन को कंटेनर में डालना
  • Cloud Run में अपनी कंटेनर इमेज डिप्लॉय करना

आपको इन चीज़ों की ज़रूरत होगी

सर्वे

इस ट्यूटोरियल का इस्तेमाल कैसे किया जाएगा?

सिर्फ़ इसे पढ़ें इसे पढ़ें और इसमें दिए गए अभ्यास पूरे करें

Java के साथ अपने अनुभव को आप कितनी रेटिंग देंगे?

शुरुआती मध्यम बेहतर

Google Cloud की सेवाओं को इस्तेमाल करने के अपने अनुभव को क्या रेटिंग देंगे?

नौसिखिया मध्यम प्रवीण

2. बैकग्राउंड

App Engine और Cloud Functions जैसे प्लैटफ़ॉर्म के तौर पर सेवा (PaaS) देने वाले सिस्टम, आपकी टीम और ऐप्लिकेशन के लिए कई सुविधाएं उपलब्ध कराते हैं. जैसे, सिस्टम एडमिन और डेवलपमेंट ऑपरेशंस (DevOps) को समाधान बनाने पर फ़ोकस करने में मदद करना. अलग-अलग प्लैटफ़ॉर्म की मदद से, आपका ऐप्लिकेशन ज़रूरत के हिसाब से अपने-आप स्केल अप कर सकता है. साथ ही, हर बार इस्तेमाल के लिए पैसे चुकाने वाली बिलिंग की मदद से, ऐप्लिकेशन को शून्य तक स्केल कर सकता है. इससे, खर्च को कंट्रोल करने में मदद मिलती है और अलग-अलग डेवलपर के लिए अलग-अलग भाषाओं का इस्तेमाल किया जा सकता है.

हालांकि, कंटेनर की सुविधा भी काफ़ी अच्छी है. कंटेनर में किसी भी भाषा, लाइब्रेरी, और बाइनरी को चुना जा सकता है. इससे आपको दोनों दुनिया की सबसे अच्छी सुविधाएं मिलती हैं: कंटेनर की सुविधाओं के साथ-साथ, सर्वरलेस की सुविधा. Cloud Run का मकसद यही है.

इस कोडलैब में, Cloud Run का इस्तेमाल करने का तरीका नहीं बताया गया है. इसके बारे में जानने के लिए, Cloud Run के दस्तावेज़ पढ़ें. इस लेख का मकसद, आपको Cloud Run या कंटेनर में होस्ट की जाने वाली अन्य सेवाओं के लिए, अपने App Engine ऐप्लिकेशन को कंटेनर में बदलने का तरीका बताना है. आगे बढ़ने से पहले, आपको कुछ बातें पता होनी चाहिए. सबसे ज़रूरी बात यह है कि आपको उपयोगकर्ताओं को मिलने वाला अनुभव थोड़ा अलग होगा.

इस कोडलैब में, आपको कंटेनर बनाने और उन्हें डिप्लॉय करने का तरीका पता चलेगा. आपको, इनके बारे में जानकारी मिलेगी:

  • Jib की मदद से अपने ऐप्लिकेशन को कंटेनर में डालना
  • App Engine कॉन्फ़िगरेशन से माइग्रेट करना
  • इसके अलावा, Cloud Build के लिए बिल्ड के चरणों को भी तय किया जा सकता है. हालांकि, ऐसा करना ज़रूरी नहीं है.

इसके लिए, आपको App Engine की कुछ खास सुविधाओं का इस्तेमाल बंद करना होगा. अगर आपको यह तरीका अपनाना नहीं है, तो अपने ऐप्लिकेशन को App Engine पर रखते हुए, Java 11/17 रनटाइम पर अपग्रेड किया जा सकता है.

3. सेटअप/प्रीवर्क

1. प्रोजेक्ट सेट अप करें

इस ट्यूटोरियल में, नए प्रोजेक्ट पर appengine-java-migration-samples रिपॉज़िटरी के सैंपल ऐप्लिकेशन का इस्तेमाल किया जाएगा. पक्का करें कि प्रोजेक्ट के लिए कोई चालू बिलिंग खाता हो.

अगर आपको किसी मौजूदा App Engine ऐप्लिकेशन को Cloud Run पर माइग्रेट करना है, तो उस ऐप्लिकेशन का इस्तेमाल करके यह तरीका अपनाया जा सकता है.

अपने प्रोजेक्ट के लिए ज़रूरी एपीआई चालू करने के लिए, यह कमांड चलाएं:

gcloud services enable artifactregistry.googleapis.com cloudbuild.googleapis.com run.googleapis.com

2. बेसलाइन सैंपल ऐप्लिकेशन डाउनलोड करना

सैंपल ऐप्लिकेशन को अपनी मशीन या Cloud Shell पर क्लोन करें. इसके बाद, बेसलाइन फ़ोल्डर पर जाएं.

यह नमूना Java 8, Servlet-आधारित Datastore ऐप्लिकेशन है. इसे App Engine पर डिप्लॉय करने के लिए बनाया गया है. इस ऐप्लिकेशन को App Engine पर डिप्लॉय करने के लिए, 'रीड मी' में दिए गए निर्देशों का पालन करें.

3. (ज़रूरी नहीं) बेसलाइन ऐप्लिकेशन को डिप्लॉय करना

यह तरीका सिर्फ़ तब अपनाएं, जब आपको यह पुष्टि करनी हो कि Cloud Run पर माइग्रेट करने से पहले, ऐप्लिकेशन App Engine पर काम करता है.

README.md में दिया गया तरीका अपनाएं:

  1. gcloud सीएलआई को इंस्टॉल करना या फिर से इसके बारे में जानना
  2. gcloud init की मदद से, अपने प्रोजेक्ट के लिए gcloud सीएलआई को शुरू करना
  3. gcloud app create की मदद से App Engine प्रोजेक्ट बनाना
  4. सैंपल ऐप्लिकेशन को App Engine पर डिप्लॉय करना
./mvnw package appengine:deploy -Dapp.projectId=$PROJECT_ID
  1. पुष्टि करें कि ऐप्लिकेशन, App Engine पर बिना किसी समस्या के काम करता है

4. Artifact Registry का डेटा स्टोर करने की जगह बनाना

अपने ऐप्लिकेशन को कंटेनर में डालने के बाद, आपको अपनी इमेज को कहीं भी पुश और सेव करना होगा. Google Cloud पर, आर्टफ़ैक्ट रजिस्ट्री का इस्तेमाल करके ऐसा करने का सुझाव दिया जाता है.

gcloud के साथ migration नाम की रिपॉज़िटरी बनाएं. जैसे:

gcloud artifacts repositories create migration --repository-format=docker \
--description="Docker repository for the migrated app" \
--location="northamerica-northeast1"

ध्यान दें कि इस रिपॉज़िटरी में docker फ़ॉर्मैट टाइप का इस्तेमाल किया जाता है. हालांकि, रिपॉज़िटरी के कई टाइप उपलब्ध हैं.

अब आपके पास अपना बेसलाइन App Engine ऐप्लिकेशन है और आपका Google Cloud प्रोजेक्ट, इसे Cloud Run पर माइग्रेट करने के लिए तैयार है.

4. ऐप्लिकेशन फ़ाइलों में बदलाव करें

अगर आपका ऐप्लिकेशन, App Engine की लेगसी बंडल की गई सेवाओं, कॉन्फ़िगरेशन या सिर्फ़ App Engine की अन्य सुविधाओं का ज़्यादा इस्तेमाल करता है, तो हमारा सुझाव है कि नए रनटाइम पर अपग्रेड करते समय, उन सेवाओं को ऐक्सेस करना जारी रखें. इस कोडलैब में, उन ऐप्लिकेशन के लिए माइग्रेशन पाथ दिखाया गया है जो पहले से ही स्टैंडअलोन सेवाओं का इस्तेमाल करते हैं या जिन्हें ऐसा करने के लिए फिर से तैयार किया जा सकता है.

1. Java 17 पर अपग्रेड करना

अगर आपका ऐप्लिकेशन Java 8 पर काम करता है, तो सुरक्षा से जुड़े अपडेट पाने और भाषा से जुड़ी नई सुविधाओं का ऐक्सेस पाने के लिए, 11 या 17 जैसे एलटीएस (लंबे समय तक सहायता) सिस्टम पर अपग्रेड करें.

pom.xml में प्रॉपर्टी अपडेट करके, इन चीज़ों को शामिल करें:

<properties>
    <java.version>17</java.version>
    <maven.compiler.source>17</maven.compiler.source>
    <maven.compiler.target>17</maven.compiler.target>
</properties>

ऐसा करने से, प्रोजेक्ट वर्शन 17 पर सेट हो जाएगा और कंपाइलर प्लगिन को यह सूचना मिलेगी कि आपको java 17 लैंग्वेज की सुविधाओं का ऐक्सेस चाहिए. साथ ही, आपको कंपाइलर की मदद से इकट्ठा की गई क्लास को Java 17 JVM के साथ काम करना है.

2. वेब सर्वर शामिल करना

App Engine और Cloud Run के बीच कई अंतर हैं. इनके बीच स्विच करते समय इन पर ध्यान देना ज़रूरी है. एक अंतर यह है कि App Engine के Java 8 रनटाइम ने होस्ट किए गए ऐप्लिकेशन के लिए Jetty सर्वर उपलब्ध कराया और उसे मैनेज किया, जबकि Cloud Run ऐसा नहीं करता. हम वेब सर्वर और सर्वलेट कंटेनर उपलब्ध कराने के लिए, Spring Boot का इस्तेमाल करेंगे.

ये डिपेंडेंसी जोड़ें:

<dependencies>
<!-- ... -->
   <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-web</artifactId>
       <version>2.6.6</version>
       <exclusions>
           <!-- Exclude the Tomcat dependency -->
           <exclusion>
               <groupId>org.springframework.boot</groupId>
               <artifactId>spring-boot-starter-tomcat</artifactId>
           </exclusion>
       </exclusions>
   </dependency>
   <!-- Use Jetty instead -->
   <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-jetty</artifactId>
       <version>2.6.6</version>
   </dependency>
<!-- ... -->
</dependencies>

Spring Boot, डिफ़ॉल्ट रूप से Tomcat सर्वर को एम्बेड करता है. हालांकि, इस सैंपल में उस आर्टफ़ैक्ट को शामिल नहीं किया जाएगा और माइग्रेशन के बाद डिफ़ॉल्ट व्यवहार में अंतर को कम करने के लिए, Jetty का इस्तेमाल किया जाएगा. हम Jetty वर्शन को भी कॉन्फ़िगर कर सकते हैं, ताकि वह App Engine के उपलब्ध कराए गए वर्शन से मेल खाए.

<properties>
    <java.version>17</java.version>
    <maven.compiler.source>17</maven.compiler.source>
    <maven.compiler.target>17</maven.compiler.target>
    <jetty.version>9.4.46.v20220331</jetty.version>
</properties>

3. Spring Boot सेटअप करना

Spring Boot, आपके सर्वलेट को बिना किसी बदलाव के फिर से इस्तेमाल कर सकता है. हालांकि, उसे खोजे जा सकने के लिए कुछ कॉन्फ़िगरेशन की ज़रूरत होगी.

com.example.appengine पैकेज में, यह MigratedServletApplication.java क्लास बनाएं:

package com.example.appengine;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;

@ServletComponentScan
@SpringBootApplication
@EnableAutoConfiguration
public class MigratedServletApplication {
    public static void main(String[] args) {
        SpringApplication.run(MigratedServletApplication.class, args);
    }
}

ध्यान दें कि इसमें @ServletComponentScan एनोटेशन शामिल है. यह मौजूदा पैकेज में डिफ़ॉल्ट रूप से, किसी भी @WebServlets के लिए दिखेगा और उन्हें उम्मीद के मुताबिक उपलब्ध कराएगा.

4. ऐप्लिकेशन को JAR के तौर पर पैकेज करना

किसी युद्ध के बाद अपने ऐप्लिकेशन को Jib के साथ कंटेनर बनाया जा सकता है. हालांकि, अगर आप अपने ऐप्लिकेशन को एक्ज़ीक्यूटेबल JAR के तौर पर पैकेज में शामिल कर लेते हैं, तो यह काम आसान हो जाता है. इसके लिए, ज़्यादा कॉन्फ़िगरेशन की ज़रूरत नहीं होगी. खास तौर पर, उन प्रोजेक्ट के लिए जिन्हें Maven को बिल्ड टूल के तौर पर इस्तेमाल करना है — क्योंकि डिफ़ॉल्ट रूप से, jar पैकेजिंग का इस्तेमाल किया जाता है.

pom.xml फ़ाइल में मौजूद packaging टैग को हटाने के लिए:

<packaging>war</packaging>

इसके बाद, spring-boot-maven-plugin जोड़ें:

<plugins>
<!-- ... -->
  <plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <version>2.6.6</version>
  </plugin>
<!-- ... -->
</plugins>

5. App Engine के कॉन्फ़िगरेशन, सेवाओं, और डिपेंडेंसी से माइग्रेट करना

जैसा कि कोडलैब की शुरुआत में बताया गया है, Cloud Run और App Engine को अलग-अलग उपयोगकर्ता अनुभव देने के लिए डिज़ाइन किया गया है. App Engine में पहले से मौजूद कुछ सुविधाओं को मैन्युअल तरीके से फिर से बनाना पड़ता है. जैसे, Cron और टास्क कतार सेवाएं. इन सुविधाओं के बारे में ज़्यादा जानकारी, आगे के मॉड्यूल में दी जाएगी.

सैंपल ऐप्लिकेशन में, लेगसी बंडल की सेवाओं का इस्तेमाल नहीं किया जाता. हालांकि, जिन उपयोगकर्ताओं के ऐप्लिकेशन में इन सेवाओं का इस्तेमाल किया जाता है वे नीचे दी गई गाइड देख सकते हैं:

अब से आपको Cloud Run पर डिप्लॉय करना है. इसलिए, appengine-maven-plugin को हटाया जा सकता है:

<plugin>
 <groupId>com.google.cloud.tools</groupId>
 <artifactId>appengine-maven-plugin</artifactId>
 <version>2.4.1</version>
 <configuration>
   <!-- can be set w/ -DprojectId=myProjectId on command line -->
   <projectId>${app.projectId}</projectId>
   <!-- set the GAE version or use "GCLOUD_CONFIG" for an autogenerated GAE version -->
   <version>GCLOUD_CONFIG</version>
 </configuration>
</plugin>

5. ऐप्लिकेशन को कंटेनर में डालना

इस समय, अपने सोर्स कोड से सीधे Cloud Run पर अपने ऐप्लिकेशन को मैन्युअल तरीके से डिप्लॉय किया जा सकता है. यह एक बेहतरीन विकल्प है. इसमें Cloud Build का इस्तेमाल बैकग्राउंड में किया जाता है, ताकि डिप्लॉय करने के दौरान आपको कोई परेशानी न हो. हम बाद के मॉड्यूल में, सोर्स डिप्लॉय के बारे में ज़्यादा जानकारी देंगे.

इसके अलावा, अगर आपको ऐप्लिकेशन के डिप्लॉयमेंट के तरीके पर ज़्यादा कंट्रोल चाहिए, तो ऐसा करने के लिए cloudbuild.yaml फ़ाइल तय करें. यह फ़ाइल, ऐप्लिकेशन बनाने के आपके मकसद के बारे में साफ़ तौर पर बताती है:

1. cloudbuild.yaml फ़ाइल तय करना

pom.xml के लेवल पर, यह cloudbuild.yaml फ़ाइल बनाएं:

steps:
  # Test your build
  - name: maven:eclipse-temurin
    entrypoint: mvn
    args: ["test"]
  # Build with Jib
  - name: maven:eclipse-temurin
    entrypoint: mvn
    args: [ "compile", "com.google.cloud.tools:jib-maven-plugin:3.2.1:build", "-Dimage=northamerica-northeast1-docker.pkg.dev/PROJECT_ID/migration/visitors:jib"]
  # Deploy to Cloud Run
  - name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
    entrypoint: gcloud
    args: [ 'run', 'deploy', 'visitors', '--image', 'northamerica-northeast1-docker.pkg.dev/PROJECT_ID/migration/visitors:jib', '--region', 'northamerica-northeast1', '--allow-unauthenticated']

Cloud Build को यह तरीका अपनाने के लिए कहने के बाद, यह:

  1. ./mvnw test की मदद से टेस्ट चलाना
  2. Jib की मदद से, आर्टफ़ैक्ट रजिस्ट्री में अपनी इमेज बनाएं, उसे पुश करें, और टैग करें
  3. gcloud run deploy की मदद से, अपनी इमेज को Cloud Run पर डिप्लॉय करना

ध्यान दें कि ‘visitors' को Cloud Run में, सेवा के लिए चुने गए नाम के तौर पर दिया जाता है. –allow-unauthenticated फ़्लैग की मदद से, उपयोगकर्ता पुष्टि किए बिना वेब ऐप्लिकेशन पर जा सकते हैं. cloudbuild.yaml फ़ाइल में, PROJECT_ID की जगह अपने प्रोजेक्ट का आईडी डालना न भूलें.

इसके बाद, Cloud Build के सेवा खाते को Artifact Registry का ऐक्सेस देने के लिए, ये आईएएम नीति बाइंडिंग जोड़ें:

export PROJECT_ID=$(gcloud config list --format 'value(core.project)')
export PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format="value(projectNumber)" )

gcloud projects add-iam-policy-binding $PROJECT_ID \
--member=serviceAccount:$PROJECT_NUMBER@cloudbuild.gserviceaccount.com \
--role=roles/run.admin \
--project=$PROJECT_ID
gcloud iam service-accounts add-iam-policy-binding $PROJECT_NUMBER-compute@developer.gserviceaccount.com \
--member=serviceAccount:$PROJECT_NUMBER@cloudbuild.gserviceaccount.com \
--role roles/iam.serviceAccountUser --project=$PROJECT_ID

2. बिल्ड प्रोसेस चलाना

अब आपने Cloud Build को बिल्ड के चरणों के बारे में बता दिया है. अब एक क्लिक में डिप्लॉय करने के लिए तैयार हैं.

यह कमांड चलाएं:

gcloud builds submit

प्रोसेस पूरी होने के बाद, आपकी कंटेनर इमेज बन जाएगी और उसे Artifact Registry में सेव कर दिया जाएगा. साथ ही, Cloud Run पर डिप्लॉय कर दिया जाएगा.

इस कोडलैब के आखिर में, आपका ऐप्लिकेशन java17-and-cloud-run/finish में मौजूद ऐप्लिकेशन जैसा ही दिखेगा.

बस हो गया! आपने Java 8 App Engine ऐप्लिकेशन को Java 17 और Cloud Run पर माइग्रेट कर लिया है. साथ ही, अब आपको यह भी पता है कि होस्टिंग के विकल्पों में से किसी को चुनने और स्विच करने के दौरान क्या करना होगा.

6. खास जानकारी/क्लीनअप

बधाई हो, आपने अपने ऐप्लिकेशन को अपग्रेड किया, कंटेनर बनाया, माइग्रेट किया और जिसके साथ यह ट्यूटोरियल खत्म हुआ!

यहां से, अगला कदम सीआई/सीडी और सॉफ़्टवेयर सप्लाई चेन की सुरक्षा से जुड़ी उन सुविधाओं के बारे में ज़्यादा जानना है जिन्हें अब Cloud Build की मदद से डिप्लॉय किया जा सकता है:

ज़रूरी नहीं: डेटा मिटाना और/या सेवा बंद करना

अगर आपने इस ट्यूटोरियल के दौरान, सैंपल ऐप्लिकेशन को App Engine पर डिप्लॉय किया है, तो शुल्क से बचने के लिए ऐप्लिकेशन को बंद करना न भूलें. जब आप अगले कोडलैब पर जाने के लिए तैयार हों, तब इसे फिर से चालू किया जा सकता है. App Engine ऐप्लिकेशन बंद होने पर, उन पर कोई ट्रैफ़िक नहीं आएगा. इसलिए, उनसे कोई शुल्क नहीं लिया जाएगा. हालांकि, डेटास्टोर के इस्तेमाल पर शुल्क लिया जा सकता है. ऐसा तब होगा, जब बिना शुल्क के इस्तेमाल की तय सीमा से ज़्यादा डेटास्टोर का इस्तेमाल किया जाए. इसलिए, डेटास्टोर में मौजूद डेटा को कम करके, तय सीमा के अंदर रखें.

दूसरी ओर, अगर आपको माइग्रेशन जारी नहीं रखना है और सब कुछ पूरी तरह से मिटाना है, तो या तो अपनी सेवा मिटाएं या अपना प्रोजेक्ट बंद करें.

7. अन्य संसाधन

App Engine माइग्रेशन मॉड्यूल के कोडलैब से जुड़ी समस्याएं/सुझाव/राय

अगर आपको इस कोडलैब में कोई समस्या मिलती है, तो कृपया शिकायत करने से पहले अपनी समस्या खोजें. खोजने और नई समस्याएं बनाने के लिए लिंक:

माइग्रेशन से जुड़े संसाधन

ऑनलाइन संसाधन

यहां कुछ ऑनलाइन संसाधन दिए गए हैं, जो इस ट्यूटोरियल के लिए काम के हो सकते हैं:

App Engine

Cloud से जुड़ी अन्य जानकारी

वीडियो

लाइसेंस

इस काम के लिए, Creative Commons Attribution 2.0 जनरल लाइसेंस के तहत लाइसेंस मिला है.