Cloud SQL Node.js कनेक्टर का इस्तेमाल करके, Cloud SQL for PostgreSQL के साथ Cloud Run में फ़ुल स्टैक Angular ऐप्लिकेशन को डिप्लॉय करना

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

Cloud Run, पूरी तरह से मैनेज किया गया एक प्लैटफ़ॉर्म है. इसकी मदद से, Google के स्केलेबल इन्फ़्रास्ट्रक्चर पर सीधे तौर पर अपना कोड चलाया जा सकता है. इस कोडलैब में, Cloud Run पर मौजूद Angular ऐप्लिकेशन को Cloud SQL Node.js कनेक्टर का इस्तेमाल करके, PostgreSQL डेटाबेस के लिए Cloud SQL से कनेक्ट करने का तरीका बताया गया है.

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

  • Cloud SQL for PostgreSQL इंस्टेंस बनाना
  • Cloud Run पर ऐसा ऐप्लिकेशन डिप्लॉय करना जो आपके Cloud SQL डेटाबेस से कनेक्ट हो

2. ज़रूरी शर्तें

  1. अगर आपके पास पहले से कोई Google खाता नहीं है, तो आपको एक Google खाता बनाना होगा.
    • ऑफ़िस या स्कूल वाले खाते के बजाय, निजी खाते का इस्तेमाल करें. ऑफ़िस और स्कूल वाले खातों पर कुछ पाबंदियां हो सकती हैं. इनकी वजह से, इस लैब के लिए ज़रूरी एपीआई चालू नहीं किए जा सकते.

3. प्रोजेक्ट सेटअप करना

  1. Google Cloud Console में साइन इन करें.
  2. Cloud Console में बिलिंग चालू करें.
    • इस लैब को पूरा करने में, Cloud संसाधनों पर 1 डॉलर से कम का खर्च आना चाहिए.
    • ज़्यादा शुल्क से बचने के लिए, इस लैब के आखिर में दिए गए निर्देशों का पालन करके संसाधनों को मिटाया जा सकता है.
    • नए उपयोगकर्ता, 300 डॉलर के मुफ़्त में आज़माने की सुविधा का फ़ायदा पा सकते हैं.
  3. नया प्रोजेक्ट बनाएं या किसी मौजूदा प्रोजेक्ट का फिर से इस्तेमाल करें.

4. Cloud Shell Editor खोलें

  1. Cloud Shell Editor पर जाएं
  2. अगर टर्मिनल स्क्रीन पर सबसे नीचे नहीं दिखता है, तो इसे खोलें:
    • हैमबर्गर मेन्यू हैमबर्गर मेन्यू का आइकॉन पर क्लिक करें
    • टर्मिनल पर क्लिक करें
    • नया टर्मिनलCloud Shell Editor में नया टर्मिनल खोलना पर क्लिक करें
  3. टर्मिनल में, इस निर्देश का इस्तेमाल करके अपना प्रोजेक्ट सेट करें:
    • फ़ॉर्मैट:
      gcloud config set project [PROJECT_ID]
      
    • उदाहरण:
      gcloud config set project lab-project-id-example
      
    • अगर आपको अपना प्रोजेक्ट आईडी याद नहीं है, तो:
      • अपने सभी प्रोजेक्ट आईडी की सूची बनाने के लिए, यह तरीका अपनाएं:
        gcloud projects list | awk '/PROJECT_ID/{print $2}'
        
      Cloud Shell Editor के टर्मिनल में प्रोजेक्ट आईडी सेट करना
  4. अगर अनुमति देने के लिए कहा जाए, तो जारी रखने के लिए अनुमति दें पर क्लिक करें. Cloud Shell को अनुमति देने के लिए क्लिक करें
  5. आपको यह मैसेज दिखेगा:
    Updated property [core/project].
    
    अगर आपको WARNING दिखता है और आपसे Do you want to continue (Y/N)? पूछा जाता है, तो इसका मतलब है कि आपने प्रोजेक्ट आईडी गलत डाला है. N दबाएं, Enter दबाएं, और gcloud config set project निर्देश को फिर से चलाने की कोशिश करें.

5. एपीआई चालू करें

टर्मिनल में, इन एपीआई को चालू करें:

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

अगर अनुमति देने के लिए कहा जाए, तो जारी रखने के लिए अनुमति दें पर क्लिक करें. Cloud Shell को अनुमति देने के लिए क्लिक करें

इस कमांड को पूरा होने में कुछ मिनट लग सकते हैं. हालांकि, इसके बाद आपको इस तरह का मैसेज दिखेगा:

Operation "operations/acf.p2-73d90d00-47ee-447a-b600" finished successfully.

6. सेवा खाता सेट अप करना

Cloud Run के लिए, Google Cloud सेवा खाता बनाएं और उसे कॉन्फ़िगर करें, ताकि उसके पास Cloud SQL से कनेक्ट करने की सही अनुमतियां हों.

  1. नया सेवा खाता बनाने के लिए, gcloud iam service-accounts create कमांड को इस तरह चलाएं:
    gcloud iam service-accounts create quickstart-service-account \
      --display-name="Quickstart Service Account"
    
  2. अभी बनाए गए Google Cloud सेवा खाते में Cloud SQL क्लाइंट की भूमिका जोड़ने के लिए, gcloud projects add-iam-policy-binding कमांड को इस तरह से चलाएं.
    gcloud projects add-iam-policy-binding ${GOOGLE_CLOUD_PROJECT} \
      --member="serviceAccount:quickstart-service-account@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com" \
      --role="roles/cloudsql.client"
    
  3. आपने अभी जो Google Cloud सेवा खाता बनाया है उसमें Cloud SQL इंस्टेंस के उपयोगकर्ता की भूमिका जोड़ने के लिए, gcloud projects add-iam-policy-binding कमांड को इस तरह से चलाएं.
    gcloud projects add-iam-policy-binding ${GOOGLE_CLOUD_PROJECT} \
      --member="serviceAccount:quickstart-service-account@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com" \
      --role="roles/cloudsql.instanceUser"
    
  4. अभी बनाए गए Google Cloud सेवा खाते में Log Writer की भूमिका जोड़ने के लिए, gcloud projects add-iam-policy-binding कमांड को इस तरह से चलाएं.
    gcloud projects add-iam-policy-binding ${GOOGLE_CLOUD_PROJECT} \
      --member="serviceAccount:quickstart-service-account@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com" \
      --role="roles/logging.logWriter"
    

7. Cloud SQL डेटाबेस बनाना

  1. Cloud SQL इंस्टेंस बनाने के लिए, gcloud sql instances create कमांड चलाएं
    gcloud sql instances create quickstart-instance \
        --database-version=POSTGRES_14 \
        --cpu=4 \
        --memory=16GB \
        --region=us-central1 \
        --database-flags=cloudsql.iam_authentication=on
    

इस कमांड को पूरा होने में कुछ मिनट लग सकते हैं.

  1. quickstart-instance में Cloud SQL डेटाबेस बनाने के लिए, gcloud sql databases create कमांड चलाएं.
    gcloud sql databases create quickstart_db \
        --instance=quickstart-instance
    
  2. डेटाबेस को ऐक्सेस करने के लिए, उस सेवा खाते के लिए PostgreSQL डेटाबेस उपयोगकर्ता बनाएं जिसे आपने पहले बनाया था.
    gcloud sql users create quickstart-service-account@${GOOGLE_CLOUD_PROJECT}.iam \
        --instance=quickstart-instance \
        --type=cloud_iam_service_account
    

8. ऐप्लिकेशन तैयार करना

एक ऐसा Next.js ऐप्लिकेशन तैयार करें जो एचटीटीपी अनुरोधों का जवाब दे.

  1. task-app नाम का नया Next.js प्रोजेक्ट बनाने के लिए, इस कमांड का इस्तेमाल करें:
    npx --yes @angular/cli@19.2.5 new task-app \
        --minimal \
        --inline-template \
        --inline-style \
        --ssr \
        --server-routing \
        --defaults
    
  2. डायरेक्ट्री को task-app में बदलें:
    cd task-app
    
  1. PostgreSQL डेटाबेस के साथ इंटरैक्ट करने के लिए, pg और Cloud SQL Node.js कनेक्टर लाइब्रेरी इंस्टॉल करें.
    npm install pg @google-cloud/cloud-sql-connector google-auth-library
    
  2. TypeScript Next.js ऐप्लिकेशन का इस्तेमाल करने के लिए, @types/pg को dev dependency के तौर पर इंस्टॉल करें.
    npm install --save-dev @types/pg
    
  1. Cloud Shell Editor में server.ts फ़ाइल खोलें:
    cloudshell edit src/server.ts
    
    अब स्क्रीन के सबसे ऊपर एक फ़ाइल दिखेगी. यहां इस server.ts फ़ाइल में बदलाव किया जा सकता है. दिखाएं कि कोड को स्क्रीन के सबसे ऊपर वाले सेक्शन में रखा गया है
  2. server.ts फ़ाइल में मौजूद कॉन्टेंट मिटाएं.
  3. यहां दिए गए कोड को कॉपी करें और इसे खोली गई server.ts फ़ाइल में चिपकाएं:
    import {
      AngularNodeAppEngine,
      createNodeRequestHandler,
      isMainModule,
      writeResponseToNodeResponse,
    } from '@angular/ssr/node';
    import express from 'express';
    import { dirname, resolve } from 'node:path';
    import { fileURLToPath } from 'node:url';
    import pg from 'pg';
    import { AuthTypes, Connector } from '@google-cloud/cloud-sql-connector';
    import { GoogleAuth } from 'google-auth-library';
    const auth = new GoogleAuth();
    
    const { Pool } = pg;
    
    type Task = {
      id: string;
      title: string;
      status: 'IN_PROGRESS' | 'COMPLETE';
      createdAt: number;
    };
    
    const projectId = await auth.getProjectId();
    
    const connector = new Connector();
    const clientOpts = await connector.getOptions({
      instanceConnectionName: `${projectId}:us-central1:quickstart-instance`,
      authType: AuthTypes.IAM,
    });
    
    const pool = new Pool({
      ...clientOpts,
      user: `quickstart-service-account@${projectId}.iam`,
      database: 'quickstart_db',
    });
    
    const tableCreationIfDoesNotExist = async () => {
      await pool.query(`CREATE TABLE IF NOT EXISTS tasks (
          id SERIAL NOT NULL,
          created_at timestamp NOT NULL,
          status VARCHAR(255) NOT NULL default 'IN_PROGRESS',
          title VARCHAR(1024) NOT NULL,
          PRIMARY KEY (id)
        );`);
    }
    
    const serverDistFolder = dirname(fileURLToPath(import.meta.url));
    const browserDistFolder = resolve(serverDistFolder, '../browser');
    
    const app = express();
    const angularApp = new AngularNodeAppEngine();
    
    app.use(express.json());
    
    app.get('/api/tasks', async (req, res) => {
      await tableCreationIfDoesNotExist();
      const { rows } = await pool.query(`SELECT id, created_at, status, title FROM tasks ORDER BY created_at DESC LIMIT 100`);
      res.send(rows);
    });
    
    app.post('/api/tasks', async (req, res) => {
      const newTaskTitle = req.body.title;
      if (!newTaskTitle) {
        res.status(400).send("Title is required");
        return;
      }
      await tableCreationIfDoesNotExist();
      await pool.query(`INSERT INTO tasks(created_at, status, title) VALUES(NOW(), 'IN_PROGRESS', $1)`, [newTaskTitle]);
      res.sendStatus(200);
    });
    
    app.put('/api/tasks', async (req, res) => {
      const task: Task = req.body;
      if (!task || !task.id || !task.title || !task.status) {
        res.status(400).send("Invalid task data");
        return;
      }
      await tableCreationIfDoesNotExist();
      await pool.query(
        `UPDATE tasks SET status = $1, title = $2 WHERE id = $3`,
        [task.status, task.title, task.id]
      );
      res.sendStatus(200);
    });
    
    app.delete('/api/tasks', async (req, res) => {
      const task: Task = req.body;
      if (!task || !task.id) {
        res.status(400).send("Task ID is required");
        return;
      }
      await tableCreationIfDoesNotExist();
      await pool.query(`DELETE FROM tasks WHERE id = $1`, [task.id]);
      res.sendStatus(200);
    });
    
    /**
    * Serve static files from /browser
    */
    app.use(
      express.static(browserDistFolder, {
        maxAge: '1y',
        index: false,
        redirect: false,
      }),
    );
    
    /**
    * Handle all other requests by rendering the Angular application.
    */
    app.use('/**', (req, res, next) => {
      angularApp
        .handle(req)
        .then((response) =>
          response ? writeResponseToNodeResponse(response, res) : next(),
        )
        .catch(next);
    });
    
    /**
    * Start the server if this module is the main entry point.
    * The server listens on the port defined by the `PORT` environment variable, or defaults to 4000.
    */
    if (isMainModule(import.meta.url)) {
      const port = process.env['PORT'] || 4000;
      app.listen(port, () => {
        console.log(`Node Express server listening on http://localhost:${port}`);
      });
    }
    
    /**
    * Request handler used by the Angular CLI (for dev-server and during build) or Firebase Cloud Functions.
    */
    export const reqHandler = createNodeRequestHandler(app);
    
  1. Cloud Shell Editor में app.component.ts फ़ाइल खोलें:
    cloudshell edit src/app/app.component.ts
    
    अब स्क्रीन के सबसे ऊपर, मौजूदा फ़ाइल दिखनी चाहिए. यहां इस app.component.ts फ़ाइल में बदलाव किया जा सकता है. दिखाएं कि कोड को स्क्रीन के सबसे ऊपर वाले सेक्शन में रखा गया है
  2. app.component.ts फ़ाइल में मौजूद कॉन्टेंट मिटाएं.
  3. यहां दिए गए कोड को कॉपी करें और इसे खोली गई app.component.ts फ़ाइल में चिपकाएं:
    import { afterNextRender, Component, signal } from '@angular/core';
    import { FormsModule } from '@angular/forms';
    
    type Task = {
      id: string;
      title: string;
      status: 'IN_PROGRESS' | 'COMPLETE';
      createdAt: number;
    };
    
    @Component({
      selector: 'app-root',
      standalone: true,
      imports: [FormsModule],
      template: `
        <section>
          <input
            type="text"
            placeholder="New Task Title"
            [(ngModel)]="newTaskTitle"
            class="text-black border-2 p-2 m-2 rounded"
          />
          <button (click)="addTask()">Add new task</button>
          <table>
            <tbody>
              @for (task of tasks(); track task) {
                @let isComplete = task.status === 'COMPLETE';
                <tr>
                  <td>
                    <input
                      (click)="updateTask(task, { status: isComplete ? 'IN_PROGRESS' : 'COMPLETE' })"
                      type="checkbox"
                      [checked]="isComplete"
                    />
                  </td>
                  <td>{{ task.title }}</td>
                  <td>{{ task.status }}</td>
                  <td>
                    <button (click)="deleteTask(task)">Delete</button>
                  </td>
                </tr>
              }
            </tbody>
          </table>
        </section>
      `,
      styles: '',
    })
    export class AppComponent {
      newTaskTitle = '';
      tasks = signal<Task[]>([]);
    
      constructor() {
        afterNextRender({
          earlyRead: () => this.getTasks()
        });
      }
    
      async getTasks() {
        const response = await fetch(`/api/tasks`);
        const tasks = await response.json();
        this.tasks.set(tasks);
      }
    
      async addTask() {
        await fetch(`/api/tasks`, {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({
            title: this.newTaskTitle,
            status: 'IN_PROGRESS',
            createdAt: Date.now(),
          }),
        });
        this.newTaskTitle = '';
        await this.getTasks();
      }
    
      async updateTask(task: Task, newTaskValues: Partial<Task>) {
        await fetch(`/api/tasks`, {
          method: 'PUT',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ ...task, ...newTaskValues }),
        });
        await this.getTasks();
      }
    
      async deleteTask(task: any) {
        await fetch('/api/tasks', {
          method: 'DELETE',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify(task),
        });
        await this.getTasks();
      }
    }
    

अब ऐप्लिकेशन को डिप्लॉय किया जा सकता है.

9. ऐप्लिकेशन को Cloud Run पर डिप्लॉय करना

  1. अपने ऐप्लिकेशन को Cloud Run पर डिप्लॉय करने के लिए, यहां दी गई कमांड चलाएं:
    gcloud run deploy to-do-tracker \
        --region=us-central1 \
        --source=. \
        --service-account="quickstart-service-account@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com" \
        --allow-unauthenticated
    
  2. अगर कहा जाए, तो Y और Enter दबाकर पुष्टि करें कि आपको जारी रखना है:
    Do you want to continue (Y/n)? Y
    

कुछ मिनटों के बाद, ऐप्लिकेशन आपको एक यूआरएल देगा. इस यूआरएल पर जाकर, आपको यह जानकारी मिल जाएगी.

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

10. बधाई हो

इस लैब में, आपने इन कामों को करने का तरीका सीखा:

  • Cloud SQL for PostgreSQL इंस्टेंस बनाना
  • Cloud Run पर ऐसा ऐप्लिकेशन डिप्लॉय करना जो आपके Cloud SQL डेटाबेस से कनेक्ट हो

व्यवस्थित करें

Cloud SQL का कोई मुफ़्त टियर नहीं है. इसका इस्तेमाल जारी रखने पर, आपसे शुल्क लिया जाएगा. अतिरिक्त शुल्क से बचने के लिए, अपना Cloud प्रोजेक्ट मिटाया जा सकता है.

Cloud Run, सेवा का इस्तेमाल न किए जाने पर कोई शुल्क नहीं लेता. हालांकि, Artifact Registry में कंटेनर इमेज सेव करने के लिए आपसे शुल्क लिया जा सकता है. Cloud प्रोजेक्ट मिटाने पर, उस प्रोजेक्ट में इस्तेमाल किए गए सभी संसाधनों के लिए बिलिंग बंद हो जाती है.

अगर आपको प्रोजेक्ट मिटाना है, तो यह तरीका अपनाएं:

gcloud projects delete $GOOGLE_CLOUD_PROJECT

आपके पास, Cloud Shell डिस्क से ग़ैर-ज़रूरी संसाधन मिटाने का विकल्प भी होता है. आप:

  1. कोड लैब प्रोजेक्ट की डायरेक्ट्री मिटाएं:
    rm -rf ~/task-app
    
  2. चेतावनी! इस कार्रवाई को पहले जैसा नहीं किया जा सकता! अगर आपको Cloud Shell पर मौजूद सभी फ़ाइलें मिटाकर जगह खाली करनी है, तो अपनी पूरी होम डायरेक्ट्री मिटाएं. ध्यान रखें कि आपको जो भी डेटा रखना है वह किसी दूसरी जगह पर सेव हो.
    sudo rm -rf $HOME
    

सीखते रहें