استقرار یک پشته کامل برنامه Next.js در Cloud Run با Cloud SQL برای PostgreSQL با استفاده از رابط Cloud SQL Node.js

استقرار یک پشته کامل برنامه Next.js در Cloud Run با Cloud SQL برای PostgreSQL با استفاده از رابط Cloud SQL Node.js

درباره این codelab

subjectآخرین به‌روزرسانی: مارس ۲۸, ۲۰۲۵
account_circleنویسنده: Luke Schlangen

1. نمای کلی

Cloud Run یک پلت فرم کاملاً مدیریت شده است که به شما امکان می دهد کد خود را مستقیماً در بالای زیرساخت مقیاس پذیر Google اجرا کنید. این Codelab نحوه اتصال یک برنامه Next.js در Cloud Run را به یک پایگاه داده Cloud SQL برای PostgreSQL با استفاده از رابط Cloud SQL Node.js نشان می دهد.

در این آزمایشگاه شما یاد خواهید گرفت که چگونه:

  • یک Cloud SQL برای نمونه PostgreSQL ایجاد کنید
  • برنامه ای را در Cloud Run مستقر کنید که به پایگاه داده Cloud SQL شما متصل می شود

2. پیش نیازها

  1. اگر قبلاً یک حساب Google ندارید، باید یک حساب Google ایجاد کنید .
    • از حساب شخصی به جای حساب کاری یا مدرسه استفاده کنید. حساب‌های محل کار و مدرسه ممکن است محدودیت‌هایی داشته باشند که شما را از فعال کردن APIهای مورد نیاز برای این آزمایشگاه باز می‌دارد.

3. راه اندازی پروژه

  1. به Google Cloud Console وارد شوید.
  2. صورتحساب را در کنسول Cloud فعال کنید .
    • تکمیل این آزمایشگاه باید کمتر از 1 دلار آمریکا در منابع Cloud هزینه داشته باشد.
    • برای جلوگیری از هزینه های بیشتر، می توانید مراحل انتهای این آزمایشگاه را برای حذف منابع دنبال کنید.
    • کاربران جدید واجد شرایط استفاده از نسخه آزمایشی رایگان 300 دلاری هستند.
  3. یک پروژه جدید ایجاد کنید یا استفاده مجدد از یک پروژه موجود را انتخاب کنید.

4. ویرایشگر پوسته ابری را باز کنید

  1. به Cloud Shell Editor بروید
  2. اگر ترمینال در پایین صفحه ظاهر نشد، آن را باز کنید:
    • روی منوی همبرگر کلیک کنید نماد منوی همبرگر
    • روی ترمینال کلیک کنید
    • روی New Terminal کلیک کنید ترمینال جدید را در 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. API ها را فعال کنید

در ترمینال، API ها را فعال کنید:

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. یک حساب سرویس راه اندازی کنید

یک حساب سرویس Google Cloud ایجاد و پیکربندی کنید تا توسط Cloud Run استفاده شود تا مجوزهای صحیح برای اتصال به Cloud SQL را داشته باشد.

  1. دستور gcloud iam service-accounts create را به صورت زیر اجرا کنید تا یک حساب سرویس جدید ایجاد کنید:
    gcloud iam service-accounts create quickstart-service-account \
     
    --display-name="Quickstart Service Account"
  2. دستور add-iam-policy-binding پروژه های gcloud را به صورت زیر اجرا کنید تا نقش Cloud SQL Client را به حساب سرویس Google Cloud که ایجاد کرده اید اضافه کنید.
    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. دستور add-iam-policy-binding پروژه های gcloud را به شرح زیر اجرا کنید تا نقش کاربری Cloud SQL Instance را به حساب سرویس Google Cloud که ایجاد کرده اید اضافه کنید.
    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. دستور add-iam-policy-binding پروژه های gcloud را به صورت زیر اجرا کنید تا نقش Log Writer را به حساب سرویس Google Cloud که ایجاد کرده اید اضافه کنید.
    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. دستور gcloud sql instances create برای ایجاد یک نمونه Cloud SQL اجرا کنید
    gcloud sql instances create quickstart-instance \
        --database-version=POSTGRES_14 \
        --cpu=4 \
        --memory=16GB \
        --region=us-central1 \
        --database-flags=cloudsql.iam_authentication=on

تکمیل این دستور ممکن است چند دقیقه طول بکشد.

  1. دستور gcloud sql databases create برای ایجاد یک پایگاه داده Cloud SQL در quickstart-instance اجرا کنید.
    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 را آماده کنید که به درخواست های HTTP پاسخ دهد.

  1. برای ایجاد یک پروژه Next.js جدید با نام task-app از دستور زیر استفاده کنید:
    npx --yes create-next-app@15.2.4 task-app \
      --ts \
      --eslint \
      --tailwind \
      --no-src-dir \
      --turbopack \
      --app \
      --no-import-alias
  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 را به عنوان وابستگی توسعه دهنده نصب کنید.
    npm install --save-dev @types/pg
  1. فایل actions.ts را در Cloud Shell Editor باز کنید:
    cloudshell edit app/actions.ts
    اکنون باید یک فایل خالی در قسمت بالای صفحه ظاهر شود. اینجاست که می توانید این فایل actions.ts را ویرایش کنید. نشان دهید که کد در قسمت بالای صفحه نمایش داده می شود
  2. کد زیر را کپی کرده و در فایل actions.ts باز شده قرار دهید:
    'use server'
    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';
    };

    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)
       
    );`);
    }

    // CREATE
    export async function addNewTaskToDatabase(newTask: string) {
     
    await tableCreationIfDoesNotExist();
     
    await pool.query(`INSERT INTO tasks(created_at, status, title) VALUES(NOW(), 'IN_PROGRESS', $1)`, [newTask]);
     
    return;
    }

    // READ
    export async function getTasksFromDatabase() {
     
    await tableCreationIfDoesNotExist();
     
    const { rows } = await pool.query(`SELECT id, created_at, status, title FROM tasks ORDER BY created_at DESC LIMIT 100`);
     
    return rows;
    }

    // UPDATE
    export async function updateTaskInDatabase(task: Task) {
     
    await tableCreationIfDoesNotExist();
     
    await pool.query(
       
    `UPDATE tasks SET status = $1, title = $2 WHERE id = $3`,
       
    [task.status, task.title, task.id]
     
    );
     
    return;
    }

    // DELETE
    export async function deleteTaskFromDatabase(taskId: string) {
     
    await tableCreationIfDoesNotExist();
     
    await pool.query(`DELETE FROM tasks WHERE id = $1`, [taskId]);
     
    return;
    }
  1. فایل page.tsx را در Cloud Shell Editor باز کنید:
    cloudshell edit app/page.tsx
    اکنون یک فایل موجود باید در قسمت بالای صفحه ظاهر شود. اینجاست که می توانید این فایل page.tsx را ویرایش کنید. نشان دهید که کد در قسمت بالای صفحه نمایش داده می شود
  2. محتویات موجود فایل page.tsx را حذف کنید.
  3. کد زیر را کپی کرده و در فایل page.tsx باز شده قرار دهید:
    'use client'
    import React, { useEffect, useState } from "react";
    import { addNewTaskToDatabase, getTasksFromDatabase, deleteTaskFromDatabase, updateTaskInDatabase } from "./actions";

    type Task = {
     
    id: string;
     
    title: string;
     
    status: 'IN_PROGRESS' | 'COMPLETE';
     
    createdAt: number;
    };

    export default function Home() {
     
    const [newTaskTitle, setNewTaskTitle] = useState('');
     
    const [tasks, setTasks] = useState<Task[]>([]);

     
    async function getTasks() {
       
    const updatedListOfTasks = await getTasksFromDatabase();
       
    setTasks(updatedListOfTasks);
     
    }

     
    useEffect(() => {
       
    getTasks();
     
    }, []);

     
    async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
       
    e.preventDefault();
       
    await addNewTaskToDatabase(newTaskTitle);
       
    await getTasks();
       
    setNewTaskTitle('');
     
    };

     
    async function updateTask(task: Task, newTaskValues: Partial<Task>) {
       
    await updateTaskInDatabase({ ...task, ...newTaskValues });
       
    await getTasks();
     
    }

     
    async function deleteTask(taskId: string) {
       
    await deleteTaskFromDatabase(taskId);
       
    await getTasks();
     
    }

     
    return (
       
    <main className="p-4">
         
    <h2 className="text-2xl font-bold mb-4">To Do List</h2>
          <
    div className="flex mb-4">
            <
    form onSubmit={handleSubmit} className="flex mb-8">
              <
    input
               
    type="text"
               
    placeholder="New Task Title"
               
    value={newTaskTitle}
               
    onChange={(e) => setNewTaskTitle(e.target.value)}
               
    className="flex-grow border border-gray-400 rounded px-3 py-2 mr-2 bg-inherit"
             
    />
             
    <button
               
    type="submit"
               
    className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded text-nowrap"
             
    >
               
    Add New Task
             
    </button>
            <
    /form>
         
    </div>
          <
    table className="w-full">
            <
    tbody>
             
    {tasks.map(function (task) {
               
    const isComplete = task.status === 'COMPLETE';
               
    return (
                  <
    tr key={task.id} className="border-b border-gray-200">
                    <
    td className="py-2 px-4">
                      <
    input
                       
    type="checkbox"
                       
    checked={isComplete}
                       
    onChange={() => updateTask(task, { status: isComplete ? 'IN_PROGRESS' : 'COMPLETE' })}
                       
    className="transition-transform duration-300 ease-in-out transform scale-100 checked:scale-125 checked:bg-green-500"
                     
    />
                   
    </td>
                    <
    td className="py-2 px-4">
                      <
    span
                       
    className={`transition-all duration-300 ease-in-out ${isComplete ? 'line-through text-gray-400 opacity-50' : 'opacity-100'}`}
                      >
                       
    {task.title}
                      <
    /span>
                   
    </td>
                    <
    td className="py-2 px-4">
                      <
    button
                       
    onClick={() => deleteTask(task.id)}
                       
    className="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded float-right"
                      >
                       
    Delete
                      <
    /button>
                   
    </td>
                  <
    /tr>
               
    );
             
    })}
           
    </tbody>
          <
    /table>
       
    </main>
     
    );
    }

برنامه اکنون آماده استقرار است.

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
    

پس از چند دقیقه، برنامه باید یک URL برای بازدید شما ارائه دهد.

برای مشاهده عملکرد برنامه خود به URL بروید. هر بار که از URL بازدید می کنید یا صفحه را بازخوانی می کنید، برنامه وظیفه را خواهید دید.

10. تبریک میگم

در این آزمایشگاه شما یاد گرفتید که چگونه کارهای زیر را انجام دهید:

  • یک Cloud SQL برای نمونه PostgreSQL ایجاد کنید
  • برنامه ای را در Cloud Run مستقر کنید که به پایگاه داده Cloud SQL شما متصل می شود

پاک کن

Cloud SQL لایه رایگان ندارد و در صورت ادامه استفاده از آن هزینه ای از شما دریافت می کند. برای جلوگیری از تحمیل هزینه های اضافی، می توانید پروژه Cloud خود را حذف کنید.

در حالی که Cloud Run زمانی که از سرویس استفاده نمی‌شود شارژ نمی‌شود، ممکن است همچنان برای ذخیره تصویر ظرف در Artifact Registry هزینه دریافت کنید. با حذف پروژه Cloud، صورت‌حساب تمام منابع مورد استفاده در آن پروژه متوقف می‌شود.

در صورت تمایل پروژه را حذف کنید:

gcloud projects delete $GOOGLE_CLOUD_PROJECT

همچنین ممکن است بخواهید منابع غیر ضروری را از دیسک پوسته ابری خود حذف کنید. شما می توانید:

  1. دایرکتوری پروژه codelab را حذف کنید:
    rm -rf ~/task-app
  2. هشدار! این اقدام بعدی قابل واگرد نیست! اگر می‌خواهید همه چیز را در Cloud Shell خود حذف کنید تا فضا آزاد شود، می‌توانید کل فهرست اصلی خود را حذف کنید . مراقب باشید هر چیزی که می خواهید نگه دارید در جای دیگری ذخیره شود.
    sudo rm -rf $HOME