1. نظرة عامة
Cloud Run هي منصة مُدارة بالكامل تتيح لك تشغيل الرموز البرمجية مباشرةً على بنية Google الأساسية القابلة للتوسّع. سيوضّح لك هذا الدرس التطبيقي كيفية ربط تطبيق Next.js على Cloud Run بقاعدة بيانات Cloud SQL لـ PostgreSQL باستخدام Cloud SQL Node.js Connector.
في هذه الميزة الاختبارية، ستتعرّف على كيفية:
- إنشاء مثيل Cloud SQL for PostgreSQL
- نشر تطبيق على Cloud Run يتصل بقاعدة بيانات Cloud SQL
2. المتطلبات الأساسية
- إذا لم يكن لديك حساب على Google، عليك إنشاء حساب على Google.
- استخدام حساب شخصي بدلاً من حساب تديره المؤسسة التعليمية أو حساب تابع للعمل. قد تتضمّن حسابات العمل والحسابات المُدارة من المؤسسات التعليمية قيودًا تمنعك من تفعيل واجهات برمجة التطبيقات اللازمة لهذا الدرس التطبيقي.
3- إعداد المشروع
- سجِّل الدخول إلى Google Cloud Console.
- فعِّل الفوترة في Cloud Console.
- يجب أن تكلّف إكمال هذا المختبر أقل من دولار أمريكي واحد من موارد السحابة الإلكترونية.
- يمكنك اتّباع الخطوات في نهاية هذا المختبر لحذف الموارد وتجنُّب المزيد من الرسوم.
- يمكن للمستخدمين الجدد الاستفادة من فترة تجريبية مجانية بقيمة 300 دولار أمريكي.
- أنشِئ مشروعًا جديدًا أو اختَر إعادة استخدام مشروع حالي.
4. فتح "محرّر Cloud Shell"
- الانتقال إلى محرّر Cloud Shell
- إذا لم تظهر المحطة الطرفية في أسفل الشاشة، افتحها باتّباع الخطوات التالية:
- انقر على قائمة الهامبرغر

- انقر على Terminal.
- انقر على نافذة طرفية جديدة
.
- انقر على قائمة الهامبرغر
- في الوحدة الطرفية، اضبط مشروعك باستخدام الأمر التالي:
- التنسيق:
gcloud config set project [PROJECT_ID] - مثال:
gcloud config set project lab-project-id-example - إذا تعذّر عليك تذكُّر رقم تعريف مشروعك، اتّبِع الخطوات التالية:
- يمكنك إدراج جميع أرقام تعريف المشاريع باستخدام:
gcloud projects list | awk '/PROJECT_ID/{print $2}'

- يمكنك إدراج جميع أرقام تعريف المشاريع باستخدام:
- التنسيق:
- إذا طُلب منك منح الإذن، انقر على منح الإذن للمتابعة.

- من المفترض أن تظهر لك هذه الرسالة:
إذا ظهرت لك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
إذا طُلب منك منح الإذن، انقر على منح الإذن للمتابعة. 
قد يستغرق تنفيذ هذا الأمر بضع دقائق، ولكن من المفترض أن يعرض في النهاية رسالة نجاح مشابهة لما يلي:
Operation "operations/acf.p2-73d90d00-47ee-447a-b600" finished successfully.
6. إعداد حساب خدمة
أنشئ حساب خدمة على Google Cloud وأعدّ إعداداته ليتم استخدامه من قِبل Cloud Run، وذلك لضمان حصوله على الأذونات الصحيحة للاتصال بخدمة Cloud SQL.
- نفِّذ الأمر
gcloud iam service-accounts createعلى النحو التالي لإنشاء حساب خدمة جديد:gcloud iam service-accounts create quickstart-service-account \ --display-name="Quickstart Service Account" - نفِّذ الأمر gcloud projects add-iam-policy-binding على النحو التالي لإضافة دور عميل Cloud SQL إلى حساب خدمة 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" - نفِّذ الأمر gcloud projects add-iam-policy-binding على النحو التالي لإضافة دور مستخدم مثيل Cloud SQL إلى حساب خدمة 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" - نفِّذ الأمر gcloud projects add-iam-policy-binding على النحو التالي لإضافة دور كاتب السجلّ إلى حساب خدمة 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
- تشغيل الأمر
gcloud sql instances createلإنشاء مثيل Cloud SQLgcloud sql instances create quickstart-instance \ --database-version=POSTGRES_14 \ --cpu=4 \ --memory=16GB \ --region=us-central1 \ --database-flags=cloudsql.iam_authentication=on
قد يستغرق إكمال هذا الأمر بضع دقائق.
- نفِّذ الأمر
gcloud sql databases createلإنشاء قاعدة بيانات Cloud SQL ضِمنquickstart-instance.gcloud sql databases create quickstart_db \ --instance=quickstart-instance - أنشِئ مستخدمًا لقاعدة بيانات PostgreSQL لحساب الخدمة الذي أنشأته سابقًا للوصول إلى قاعدة البيانات.
gcloud sql users create quickstart-service-account@${GOOGLE_CLOUD_PROJECT}.iam \ --instance=quickstart-instance \ --type=cloud_iam_service_account
8. تجهيز التطبيق
إعداد تطبيق Next.js يستجيب لطلبات HTTP
- لإنشاء مشروع Next.js جديد باسم
task-app، استخدِم الأمر:npx --yes create-next-app@15 task-app \ --ts \ --eslint \ --tailwind \ --no-src-dir \ --turbopack \ --app \ --no-import-alias - غيِّر الدليل إلى
task-app:cd task-app
- ثبِّت
pgومكتبة موصّل Cloud SQL Node.js للتفاعل مع قاعدة بيانات PostgreSQL.npm install pg @google-cloud/cloud-sql-connector google-auth-library - ثبِّت
@types/pgكإحدى تبعيات التطوير لاستخدام تطبيق TypeScript Next.js.npm install --save-dev @types/pg
- افتح الملف
actions.tsفي "محرِّر Cloud Shell": من المفترض أن يظهر ملف فارغ الآن في الجزء العلوي من الشاشة. هذا هو المكان الذي يمكنك فيه تعديل ملفcloudshell edit app/actions.tsactions.tsهذا.
- انسخ الرمز التالي والصِقه في ملف
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; }
- افتح الملف
page.tsxفي "محرِّر Cloud Shell": من المفترض أن يظهر ملف حالي في الجزء العلوي من الشاشة. هذا هو المكان الذي يمكنك فيه تعديل ملفcloudshell edit app/page.tsxpage.tsxهذا.
- احذف المحتوى الحالي لملف
page.tsx. - انسخ الرمز التالي والصِقه في ملف
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
- نفِّذ الأمر أدناه لنشر تطبيقك على 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 - إذا طُلب منك ذلك، اضغط على
YوEnterلتأكيد رغبتك في المتابعة:Do you want to continue (Y/n)? Y
بعد بضع دقائق، من المفترض أن يوفّر لك التطبيق عنوان URL يمكنك الانتقال إليه.
انتقِل إلى عنوان URL للاطّلاع على تطبيقك أثناء عمله. في كل مرة تزور فيها عنوان URL أو تعيد تحميل الصفحة، سيظهر لك تطبيق المهام.
10. تهانينا
في هذه الميزة الاختبارية، تعرّفت على كيفية تنفيذ ما يلي:
- إنشاء مثيل Cloud SQL for PostgreSQL
- نشر تطبيق على Cloud Run يتصل بقاعدة بيانات Cloud SQL
تَنظيم
لا تتوفّر طبقة مجانية في Cloud SQL، وسيتم تحصيل رسوم منك إذا واصلت استخدامها. يمكنك حذف مشروعك على السحابة الإلكترونية لتجنُّب تحمّل رسوم إضافية.
على الرغم من أنّ Cloud Run لا تفرض رسومًا عندما لا تكون الخدمة قيد الاستخدام، قد يتم تحصيل رسوم منك مقابل تخزين صورة الحاوية في Artifact Registry. يؤدي حذف مشروعك على السحابة الإلكترونية إلى إيقاف الفوترة لجميع الموارد المستخدَمة في هذا المشروع.
إذا أردت، يمكنك حذف المشروع باتّباع الخطوات التالية:
gcloud projects delete $GOOGLE_CLOUD_PROJECT
يمكنك أيضًا حذف الموارد غير الضرورية من قرص cloudshell. يمكنك إجراء ما يلي:
- احذف دليل مشروع الدرس البرمجي:
rm -rf ~/task-app - تحذير! لا يمكن التراجع عن الإجراء التالي. إذا أردت حذف كل المحتوى على Cloud Shell لإخلاء بعض المساحة، يمكنك حذف دليل منزلك بأكمله. يجب التأكّد من حفظ كل ما تريد الاحتفاظ به في مكان آخر.
sudo rm -rf $HOME
مواصلة التعلّم
- نشر تطبيق Next.js كامل الميزات على Cloud Run باستخدام Cloud SQL لـ PostgreSQL من خلال Cloud SQL Node.js Connector
- نشر تطبيق Angular كامل الميزات على Cloud Run باستخدام Cloud SQL for PostgreSQL من خلال Cloud SQL Node.js Connector
- نشر تطبيق Angular كامل الميزات على Cloud Run مع Firestore باستخدام حزمة Node.js Admin SDK
- نشر تطبيق Next.js متكامل على Cloud Run باستخدام Firestore من خلال حزمة Node.js Admin SDK